Merge pull request #912 from freechipsproject/add-mask-rom
tilelink: add mask rom
This commit is contained in:
commit
f988b91575
@ -1,6 +1,7 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# See LICENSE for license details.
|
||||
# See LICENSE.SiFive for license details.
|
||||
# See LICENSE.Berkeley for license details.
|
||||
|
||||
import sys
|
||||
import math
|
138
scripts/vlsi_rom_gen
Executable file
138
scripts/vlsi_rom_gen
Executable file
@ -0,0 +1,138 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# See LICENSE.SiFive for license details.
|
||||
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import doctest
|
||||
import sys
|
||||
import warnings
|
||||
from collections import namedtuple
|
||||
|
||||
verilog_template = """
|
||||
module {name}(
|
||||
input clock,
|
||||
input oe,
|
||||
input me,
|
||||
input [{address_bits_minus_1}:0] address,
|
||||
output [{output_width_minus_1}:0] q
|
||||
);
|
||||
reg [{output_width_minus_1}:0] out;
|
||||
reg [{output_width_minus_1}:0] rom [0:{depth_minus_1}];
|
||||
|
||||
|
||||
// 1024 is the maximum length of $readmemh filename supported by Cadence Incisive
|
||||
reg [1024 * 8 - 1:0] path;
|
||||
|
||||
integer i;
|
||||
initial begin
|
||||
`ifdef RANDOMIZE
|
||||
`ifdef RANDOMIZE_MEM_INIT
|
||||
for (i = 0; i < {depth}; i++) begin
|
||||
rom[i] = {{{num_random_blocks}{{$random}}}};
|
||||
end
|
||||
`endif
|
||||
`endif
|
||||
if (!$value$plusargs("maskromhex=%s", path)) begin
|
||||
path = "{rom_hex_file}";
|
||||
end
|
||||
$readmemh(path, rom);
|
||||
end
|
||||
|
||||
|
||||
always @(posedge clock) begin
|
||||
if (me) begin
|
||||
out <= rom[address];
|
||||
end
|
||||
end
|
||||
|
||||
assign q = oe ? out : {output_width}'bZ;
|
||||
|
||||
endmodule
|
||||
"""
|
||||
|
||||
|
||||
def gen_rom(name, width, depth, rom_hex_file):
|
||||
variables = {
|
||||
'name': name,
|
||||
'address_bits_minus_1': (depth - 1).bit_length() - 1,
|
||||
'depth': depth,
|
||||
'depth_minus_1': depth - 1,
|
||||
'output_width': width,
|
||||
'output_width_minus_1': width - 1,
|
||||
# $random in verilog returns 32 bits; compute how many times to repeat
|
||||
# $random in order to fill the width
|
||||
'num_random_blocks': (width - 1) // 32 + 1,
|
||||
'rom_hex_file': rom_hex_file,
|
||||
}
|
||||
return verilog_template.format(**variables)
|
||||
|
||||
|
||||
def iterate_by_n(it, n):
|
||||
"""Iterate over items in it, yielding n-tuples of successive items.
|
||||
|
||||
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=2))
|
||||
[(1, 2), (3, 4), (5, 6)]
|
||||
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=3))
|
||||
[(1, 2, 3), (4, 5, 6)]
|
||||
>>> list(iterate_by_n([1, 2, 3, 4, 5, 6], n=4))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: Iterable length not evenly divisible by 4
|
||||
"""
|
||||
it = iter(it)
|
||||
while True:
|
||||
batch = ()
|
||||
for i in range(n):
|
||||
try:
|
||||
batch += (next(it),)
|
||||
except StopIteration:
|
||||
if batch: # If this is not the first iteration
|
||||
raise ValueError(
|
||||
'Iterable length not evenly divisible by {}'.format(n)
|
||||
)
|
||||
else:
|
||||
raise
|
||||
yield batch
|
||||
|
||||
|
||||
def try_cast_int(x):
|
||||
try:
|
||||
return int(x)
|
||||
except ValueError:
|
||||
return x
|
||||
|
||||
|
||||
ROMParameters = namedtuple('ROMParameters', ['name', 'depth', 'width'])
|
||||
default_rom_parameters = ROMParameters(name='', depth=0, width=0)
|
||||
|
||||
|
||||
def parse_line(line):
|
||||
kwargs = {key: try_cast_int(val)
|
||||
for key, val in iterate_by_n(line.split(), 2)}
|
||||
rom_parameters = default_rom_parameters._replace(**kwargs)
|
||||
return rom_parameters._asdict()
|
||||
|
||||
|
||||
def main():
|
||||
if '--run-tests' in sys.argv:
|
||||
(failures, total) = doctest.testmod(verbose=True)
|
||||
sys.exit(1 if failures else 0)
|
||||
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit('Please give a .conf file as input')
|
||||
|
||||
print('// This file created by ' + __file__)
|
||||
with open(sys.argv[1]) as fp:
|
||||
lines = fp.readlines()
|
||||
if len(lines) > 1:
|
||||
warnings.warn('vlsi_rom_gen detected multiple ROMs. ROM contents will be duplicated.')
|
||||
for line in lines:
|
||||
verilog = gen_rom(rom_hex_file=sys.argv[2],
|
||||
**parse_line(line))
|
||||
print(verilog)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,4 +1,4 @@
|
||||
// See LICENSE for license details.
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.coreplex
|
||||
|
||||
|
@ -6,6 +6,9 @@ import Chisel._
|
||||
import freechips.rocketchip.config.{Field, Parameters}
|
||||
import freechips.rocketchip.coreplex._
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
import java.nio.{ByteBuffer, ByteOrder}
|
||||
import java.nio.file.{Files, Paths}
|
||||
|
||||
@ -17,6 +20,47 @@ case class BootROMParams(
|
||||
contentFileName: String)
|
||||
case object BootROMParams extends Field[BootROMParams]
|
||||
|
||||
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
|
||||
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
val node = TLManagerNode(beatBytes, TLManagerParameters (
|
||||
address = List(AddressSet(base, size-1)),
|
||||
resources = resources,
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = executable,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0)))
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val contents = contentsDelayed
|
||||
val wrapSize = 1 << log2Ceil(contents.size)
|
||||
require (wrapSize <= size)
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
|
||||
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
|
||||
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
|
||||
|
||||
in.d.valid := in.a.valid
|
||||
in.a.ready := in.d.ready
|
||||
|
||||
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
|
||||
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
|
||||
in.d.bits := edge.AccessAck(in.a.bits, Mux(high.orR, UInt(0), rom(index)))
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
||||
|
||||
/** Adds a boot ROM that contains the DTB describing the system's coreplex. */
|
||||
trait HasPeripheryBootROM extends HasPeripheryBus {
|
||||
val dtb: DTB
|
||||
|
71
src/main/scala/devices/tilelink/MaskROM.scala
Normal file
71
src/main/scala/devices/tilelink/MaskROM.scala
Normal file
@ -0,0 +1,71 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.coreplex.{HasPeripheryBus}
|
||||
import freechips.rocketchip.config.{Field, Parameters}
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
case class MaskROMParams(address: BigInt, name: String, depth: Int = 2048, width: Int = 32)
|
||||
|
||||
case object PeripheryMaskROMKey extends Field[Seq[MaskROMParams]]
|
||||
|
||||
trait HasPeripheryMaskROMSlave extends HasPeripheryBus {
|
||||
val maskROMParams = p(PeripheryMaskROMKey)
|
||||
val maskROMs = maskROMParams map { params =>
|
||||
val maskROM = LazyModule(new TLMaskROM(params))
|
||||
maskROM.node := pbus.toFixedWidthSingleBeatSlave(maskROM.beatBytes)
|
||||
maskROM
|
||||
}
|
||||
}
|
||||
|
||||
class TLMaskROM(c: MaskROMParams)(implicit p: Parameters) extends LazyModule {
|
||||
val beatBytes = c.width/8
|
||||
val node = TLManagerNode(beatBytes, TLManagerParameters(
|
||||
address = AddressSet.misaligned(c.address, c.depth*beatBytes),
|
||||
resources = new SimpleDevice("rom", Seq("sifive,maskrom0")).reg("mem"),
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = true,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0))) // requests are handled in order
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val rom = ROMGenerator(ROMConfig(c.name, c.depth, c.width))
|
||||
rom.io.clock := clock
|
||||
rom.io.address := edge.addr_hi(in.a.bits.address - UInt(c.address))(log2Ceil(c.depth)-1, 0)
|
||||
rom.io.oe := Bool(true) // active high tri state enable
|
||||
rom.io.me := in.a.fire()
|
||||
|
||||
val d_full = RegInit(Bool(false))
|
||||
val d_size = Reg(UInt())
|
||||
val d_source = Reg(UInt())
|
||||
|
||||
// Flow control
|
||||
when (in.d.fire()) { d_full := Bool(false) }
|
||||
when (in.a.fire()) { d_full := Bool(true) }
|
||||
in.d.valid := d_full
|
||||
in.a.ready := in.d.ready || !d_full
|
||||
|
||||
when (in.a.fire()) {
|
||||
d_size := in.a.bits.size
|
||||
d_source := in.a.bits.source
|
||||
}
|
||||
|
||||
in.d.bits := edge.AccessAck(d_source, d_size, rom.io.q)
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
@ -1,52 +0,0 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
// See LICENSE.Berkeley for license details.
|
||||
|
||||
package freechips.rocketchip.devices.tilelink
|
||||
|
||||
import Chisel._
|
||||
import freechips.rocketchip.config.Parameters
|
||||
import freechips.rocketchip.diplomacy._
|
||||
import freechips.rocketchip.tilelink._
|
||||
import freechips.rocketchip.util._
|
||||
|
||||
class TLROM(val base: BigInt, val size: Int, contentsDelayed: => Seq[Byte], executable: Boolean = true, beatBytes: Int = 4,
|
||||
resources: Seq[Resource] = new SimpleDevice("rom", Seq("sifive,rom0")).reg("mem"))(implicit p: Parameters) extends LazyModule
|
||||
{
|
||||
|
||||
val node = TLManagerNode(beatBytes, TLManagerParameters (
|
||||
address = List(AddressSet(base, size-1)),
|
||||
resources = resources,
|
||||
regionType = RegionType.UNCACHED,
|
||||
executable = executable,
|
||||
supportsGet = TransferSizes(1, beatBytes),
|
||||
fifoId = Some(0)))
|
||||
|
||||
lazy val module = new LazyModuleImp(this) {
|
||||
val io = new Bundle {
|
||||
val in = node.bundleIn
|
||||
}
|
||||
|
||||
val contents = contentsDelayed
|
||||
val wrapSize = 1 << log2Ceil(contents.size)
|
||||
require (wrapSize <= size)
|
||||
|
||||
val in = io.in(0)
|
||||
val edge = node.edgesIn(0)
|
||||
|
||||
val words = (contents ++ Seq.fill(wrapSize-contents.size)(0.toByte)).grouped(beatBytes).toSeq
|
||||
val bigs = words.map(_.foldRight(BigInt(0)){ case (x,y) => (x.toInt & 0xff) | y << 8})
|
||||
val rom = Vec(bigs.map(x => UInt(x, width = 8*beatBytes)))
|
||||
|
||||
in.d.valid := in.a.valid
|
||||
in.a.ready := in.d.ready
|
||||
|
||||
val index = in.a.bits.address(log2Ceil(wrapSize)-1,log2Ceil(beatBytes))
|
||||
val high = if (wrapSize == size) UInt(0) else in.a.bits.address(log2Ceil(size)-1, log2Ceil(wrapSize))
|
||||
in.d.bits := edge.AccessAck(in.a.bits, Mux(high.orR, UInt(0), rom(index)))
|
||||
|
||||
// Tie off unused channels
|
||||
in.b.valid := Bool(false)
|
||||
in.c.ready := Bool(true)
|
||||
in.e.ready := Bool(true)
|
||||
}
|
||||
}
|
@ -88,5 +88,6 @@ object Generator extends GeneratorApp {
|
||||
val longName = names.topModuleProject + "." + names.configs
|
||||
generateFirrtl
|
||||
generateTestSuiteMakefrags
|
||||
generateROMs
|
||||
generateArtefacts
|
||||
}
|
||||
|
@ -53,6 +53,21 @@ trait HasGeneratorUtilities {
|
||||
Driver.elaborate(gen)
|
||||
}
|
||||
|
||||
def enumerateROMs(circuit: Circuit): String = {
|
||||
val res = new StringBuilder
|
||||
val configs =
|
||||
circuit.components flatMap { m =>
|
||||
m.id match {
|
||||
case rom: BlackBoxedROM => Some((rom.name, ROMGenerator.lookup(rom)))
|
||||
case _ => None
|
||||
}
|
||||
}
|
||||
configs foreach { case (name, c) =>
|
||||
res append s"name ${name} depth ${c.depth} width ${c.width}\n"
|
||||
}
|
||||
res.toString
|
||||
}
|
||||
|
||||
def writeOutputFile(targetDir: String, fname: String, contents: String): File = {
|
||||
val f = new File(targetDir, fname)
|
||||
val fw = new FileWriter(f)
|
||||
@ -103,6 +118,10 @@ trait GeneratorApp extends App with HasGeneratorUtilities {
|
||||
TestGeneration.addSuite(DefaultTestSuites.singleRegression)
|
||||
}
|
||||
|
||||
def generateROMs {
|
||||
writeOutputFile(td, s"$longName.rom.conf", enumerateROMs(circuit))
|
||||
}
|
||||
|
||||
/** Output files created as a side-effect of elaboration */
|
||||
def generateArtefacts {
|
||||
ElaborationArtefacts.files.foreach { case (extension, contents) =>
|
||||
|
35
src/main/scala/util/ROMGenerator.scala
Normal file
35
src/main/scala/util/ROMGenerator.scala
Normal file
@ -0,0 +1,35 @@
|
||||
// See LICENSE.SiFive for license details.
|
||||
|
||||
package freechips.rocketchip.util
|
||||
|
||||
import Chisel._
|
||||
import scala.collection.mutable.{HashMap}
|
||||
|
||||
case class ROMConfig(name: String, depth: Int, width: Int)
|
||||
|
||||
class BlackBoxedROM(c: ROMConfig) extends BlackBox {
|
||||
val io = new Bundle {
|
||||
val clock = Clock(INPUT)
|
||||
val address = UInt(INPUT, log2Ceil(c.depth))
|
||||
val oe = Bool(INPUT)
|
||||
val me = Bool(INPUT)
|
||||
val q = UInt(OUTPUT, c.width)
|
||||
}
|
||||
|
||||
override def desiredName: String = c.name
|
||||
}
|
||||
|
||||
object ROMGenerator {
|
||||
private var finalized = false
|
||||
private val roms = HashMap[BlackBoxedROM, ROMConfig]()
|
||||
def apply(c: ROMConfig): BlackBoxedROM = {
|
||||
require(!finalized)
|
||||
val m = Module(new BlackBoxedROM(c))
|
||||
roms(m) = c
|
||||
m
|
||||
}
|
||||
def lookup(m: BlackBoxedROM): ROMConfig = {
|
||||
finalized = true
|
||||
roms(m)
|
||||
}
|
||||
}
|
@ -11,7 +11,7 @@ default: all
|
||||
|
||||
base_dir = $(abspath ..)
|
||||
generated_dir = $(abspath ./generated-src)
|
||||
VLSI_MEM_GEN ?= $(base_dir)/vsim/vlsi_mem_gen
|
||||
VLSI_MEM_GEN ?= $(base_dir)/scripts/vlsi_mem_gen
|
||||
mem_gen = $(VLSI_MEM_GEN)
|
||||
sim_dir = .
|
||||
output_dir = $(sim_dir)/output
|
||||
|
Loading…
Reference in New Issue
Block a user