diff --git a/vsim/vlsi_mem_gen b/scripts/vlsi_mem_gen similarity index 98% rename from vsim/vlsi_mem_gen rename to scripts/vlsi_mem_gen index ff68ca47..314ffb9a 100755 --- a/vsim/vlsi_mem_gen +++ b/scripts/vlsi_mem_gen @@ -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 diff --git a/scripts/vlsi_rom_gen b/scripts/vlsi_rom_gen new file mode 100755 index 00000000..a6539725 --- /dev/null +++ b/scripts/vlsi_rom_gen @@ -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() diff --git a/src/main/scala/coreplex/ResetVector.scala b/src/main/scala/coreplex/ResetVector.scala index 1c986d60..1235f6ca 100644 --- a/src/main/scala/coreplex/ResetVector.scala +++ b/src/main/scala/coreplex/ResetVector.scala @@ -1,4 +1,4 @@ -// See LICENSE for license details. +// See LICENSE.SiFive for license details. package freechips.rocketchip.coreplex diff --git a/src/main/scala/devices/tilelink/BootROM.scala b/src/main/scala/devices/tilelink/BootROM.scala index 706401b9..f5add542 100644 --- a/src/main/scala/devices/tilelink/BootROM.scala +++ b/src/main/scala/devices/tilelink/BootROM.scala @@ -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 diff --git a/src/main/scala/devices/tilelink/MaskROM.scala b/src/main/scala/devices/tilelink/MaskROM.scala new file mode 100644 index 00000000..cb187673 --- /dev/null +++ b/src/main/scala/devices/tilelink/MaskROM.scala @@ -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) + } +} diff --git a/src/main/scala/devices/tilelink/ROM.scala b/src/main/scala/devices/tilelink/ROM.scala deleted file mode 100644 index 007891d4..00000000 --- a/src/main/scala/devices/tilelink/ROM.scala +++ /dev/null @@ -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) - } -} diff --git a/src/main/scala/system/Generator.scala b/src/main/scala/system/Generator.scala index 717ebeda..d3d5f1f8 100644 --- a/src/main/scala/system/Generator.scala +++ b/src/main/scala/system/Generator.scala @@ -88,5 +88,6 @@ object Generator extends GeneratorApp { val longName = names.topModuleProject + "." + names.configs generateFirrtl generateTestSuiteMakefrags + generateROMs generateArtefacts } diff --git a/src/main/scala/util/GeneratorUtils.scala b/src/main/scala/util/GeneratorUtils.scala index 572ed632..5b0905a1 100644 --- a/src/main/scala/util/GeneratorUtils.scala +++ b/src/main/scala/util/GeneratorUtils.scala @@ -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) => diff --git a/src/main/scala/util/ROMGenerator.scala b/src/main/scala/util/ROMGenerator.scala new file mode 100644 index 00000000..4b65bb1f --- /dev/null +++ b/src/main/scala/util/ROMGenerator.scala @@ -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) + } +} diff --git a/vsim/Makefile b/vsim/Makefile index 769a4c3f..31d3dd2b 100644 --- a/vsim/Makefile +++ b/vsim/Makefile @@ -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