From 8bca41522cdc4b8ff69734cd159ce29f984d3290 Mon Sep 17 00:00:00 2001 From: sinofp Date: Sat, 22 May 2021 15:16:32 +0800 Subject: Rewrite vlsi_mem_gen into a Firrtl Transform (#2202) * Add GenVerilogMemBehaviorModelAnno & vlsiMemGen * Add CLI support for GenVerilogMemBehaviorModelAnno * Add simple test for GenVerilogMemBehaviorModelAnno * Fix for review 1. rename case class Port(prefix, `type`) to Port(prefix, portType) 2. fix AnnotatedMemoriesAnnotation collect function. 3. fix bug that ModuleName is not correct. * Format DumpMemoryAnnotations & ReplSeqMemTests * Fix for review 1. Inline genDecl, genPortSpec, genSequential, genCombinational 2. Add DefAnnotatedMemory informations in header 3. Change helpText 4. Check output Verilog by Verilator, the code is from FirrtlRunners#lintVerilog * Fix ReadWritePort mask name Co-authored-by: Jiuyang Liu Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- .../passes/memlib/DumpMemoryAnnotations.scala | 138 ++++++++++++++++++++- .../firrtl/passes/memlib/ReplaceMemTransform.scala | 22 +++- 2 files changed, 155 insertions(+), 5 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala b/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala index 5cc1e0bf..f88e7e39 100644 --- a/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala +++ b/src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala @@ -4,7 +4,9 @@ package firrtl package passes package memlib +import firrtl.annotations.{CircuitName, ModuleName} import firrtl.stage.Forms +import firrtl.transforms.BlackBoxInlineAnno class DumpMemoryAnnotations extends Transform with DependencyAPIMigration { @@ -13,16 +15,146 @@ class DumpMemoryAnnotations extends Transform with DependencyAPIMigration { override def optionalPrerequisiteOf = Forms.MidEmitters override def invalidates(a: Transform) = false + private def vlsiMemGen(annotatedMemory: DefAnnotatedMemory, genBlackBox: Boolean): (String, String) = { + // MaskedWritePort => WritePort with masked = true + case class Port(prefix: String, portType: MemPort) + + val masked = annotatedMemory.maskGran.isDefined + + val name = annotatedMemory.name + val width = { + val width = bitWidth(annotatedMemory.dataType) + require(width <= Int.MaxValue) + width.toInt + } + val depth = annotatedMemory.depth.toInt + val maskGran = if (masked) { + val maskGran = annotatedMemory.maskGran.get + require(maskGran <= Int.MaxValue) + maskGran.toInt + } else width + val maskSeg = width / maskGran + val ports = annotatedMemory.readers.indices.map(number => Port(s"R${number}_", ReadPort)) ++ + annotatedMemory.writers.indices.map(number => Port(s"W${number}_", WritePort)) ++ + annotatedMemory.readwriters.indices.map(number => Port(s"RW${number}_", ReadWritePort)) + + val addrWidth = math.max(math.ceil(math.log(depth) / math.log(2)).toInt, 1) + val readPorts = ports.filter(port => port.portType == ReadPort || port.portType == ReadWritePort) + + val portSpec = ports.flatMap(port => + Seq(s"input ${port.prefix}clk", s"input [${addrWidth - 1}:0] ${port.prefix}addr", s"input ${port.prefix}en") ++ + ((port, masked) match { + case (Port(prefix, ReadPort), _) => Seq(s"output [${width - 1}:0] ${prefix}data") + case (Port(prefix, WritePort), false) => Seq(s"input [${width - 1}:0] ${prefix}data") + case (Port(prefix, WritePort), true) => + Seq(s"input [${width - 1}:0] ${prefix}data", s"input [${maskSeg - 1}:0] ${prefix}mask") + case (Port(prefix, ReadWritePort), false) => + Seq( + s"input ${prefix}wmode", + s"input [${width - 1}:0] ${prefix}wdata", + s"output [${width - 1}:0] ${prefix}rdata" + ) + case (Port(prefix, ReadWritePort), true) => + Seq( + s"input ${prefix}wmode", + s"input [${maskSeg - 1}:0] ${prefix}wmask", + s"input [${width - 1}:0] ${prefix}wdata", + s"output [${width - 1}:0] ${prefix}rdata" + ) + }) + ) + + val decl = readPorts.flatMap(port => + Seq(s"reg reg_${port.prefix}ren;", s"reg [${addrWidth - 1}:0] reg_${port.prefix}addr;") + ) ++ Seq( + s"reg [${width - 1}:0] ram [${depth - 1}:0];", + "`ifdef RANDOMIZE_MEM_INIT", + " integer initvar;", + " initial begin", + " #`RANDOMIZE_DELAY begin end", + s" for (initvar = 0; initvar < $depth; initvar = initvar+1)", + s" ram[initvar] = {${(width - 1) / 32 + 1} {$$random}};" + ) ++ readPorts.map(port => s" reg_${port.prefix}addr = {${(addrWidth - 1) / 32 + 1} {$$random}};") ++ Seq( + " end", + "`endif" + ) + + val sequential = { + def genReadSequential(en: String, prefix: String): Seq[String] = Seq( + s"always @(posedge ${prefix}clk)", + s" reg_${prefix}ren <= $en;", + s"always @(posedge ${prefix}clk)", + s" if ($en) reg_${prefix}addr <= ${prefix}addr;" + ) + def genWriteSequential(en: String, prefix: String, inputData: String, maskName: String): Seq[String] = Seq( + s"always @(posedge ${prefix}clk)", + s" if ($en) begin" + ) ++ (0 until maskSeg).map { i => + val ifMask = if (masked) s"if (${prefix}${maskName}[$i]) " else "" + val ram_range = s"${(i + 1) * maskGran - 1}:${i * maskGran}" + s" ${ifMask}ram[${prefix}addr][$ram_range] <= ${prefix}$inputData[$ram_range];" + } ++ Seq(" end") + ports.flatMap(port => + port.portType match { + case ReadPort => genReadSequential(port.prefix + "en", port.prefix) + case WritePort => genWriteSequential(port.prefix + "en", port.prefix, "data", "mask") + case ReadWritePort => + genReadSequential(s"${port.prefix}en && !${port.prefix}wmode", port.prefix) ++ + genWriteSequential(s"${port.prefix}en && ${port.prefix}wmode", port.prefix, "wdata", "wmask") + } + ) + } + + val combinational = readPorts.flatMap { port => + val data = port.prefix + (if (port.portType == ReadWritePort) "rdata" else "data") + Seq( + "`ifdef RANDOMIZE_GARBAGE_ASSIGN", + s"reg [${((width - 1) / 32 + 1) * 32 - 1}:0] ${port.prefix}random;", + "`ifdef RANDOMIZE_MEM_INIT", + " initial begin", + " #`RANDOMIZE_DELAY begin end", + s" ${port.prefix}random = {${Seq.fill((width - 1) / 32 + 1)("$random").mkString(", ")}};", + s" reg_${port.prefix}ren = ${port.prefix}random[0];", + " end", + "`endif", + s"always @(posedge ${port.prefix}clk) ${port.prefix}random <= {${Seq.fill((width - 1) / 32 + 1)("$random").mkString(", ")}};", + s"assign $data = reg_${port.prefix}ren ? ram[reg_${port.prefix}addr] : ${port.prefix}random[${width - 1}:0];", + "`else", + s"assign $data = ram[reg_${port.prefix}addr];", + "`endif" + ) + } + + val body = if (genBlackBox) "" else s""" + | ${decl.mkString("\n ")} + | ${sequential.mkString("\n ")} + | ${combinational.mkString("\n ")}""".stripMargin + + (name, s"""// name:$name depth:$depth width:$width masked:$masked maskGran:$maskGran maskSeg:$maskSeg + |module $name( + | ${portSpec.mkString(",\n ")} + |); + | + |$body + | + |endmodule""".stripMargin) + } + def execute(state: CircuitState): CircuitState = { state.copy(annotations = state.annotations.flatMap { // convert and remove AnnotatedMemoriesAnnotation to CustomFileEmission case AnnotatedMemoriesAnnotation(annotatedMemories) => state.annotations.collect { case a: MemLibOutConfigFileAnnotation => - a.copy(annotatedMemories = annotatedMemories) - // todo convert xxx to verilogs here. - } + Seq(a.copy(annotatedMemories = annotatedMemories)) + case GenVerilogMemBehaviorModelAnno(genBlackBox) => + annotatedMemories.map(vlsiMemGen(_, genBlackBox)).map { + case (name, content) => + BlackBoxInlineAnno(ModuleName(name, CircuitName(state.circuit.main)), name + ".v", content) + } + }.flatten case MemLibOutConfigFileAnnotation(_, Nil) => Nil + case GenVerilogMemBehaviorModelAnno(_) => Nil case a => Seq(a) }) } diff --git a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala index f9df27a7..c7b0fbcd 100644 --- a/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala +++ b/src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala @@ -6,9 +6,10 @@ package memlib import firrtl.Utils.error import firrtl._ import firrtl.annotations._ -import firrtl.options.{CustomFileEmission, HasShellOptions, ShellOption} +import firrtl.options.{CustomFileEmission, Dependency, HasShellOptions, ShellOption} import firrtl.passes.wiring._ import firrtl.stage.{Forms, RunFirrtlTransformAnnotation} +import firrtl.transforms.BlackBoxSourceHelper import java.io.{CharArrayWriter, PrintWriter} @@ -46,6 +47,8 @@ object PassConfigUtil { case class ReplSeqMemAnnotation(inputFileName: String, outputConfig: String) extends NoTargetAnnotation +case class GenVerilogMemBehaviorModelAnno(genBlackBox: Boolean) extends NoTargetAnnotation + /** Generate conf file for a sequence of [[DefAnnotatedMemory]] * @note file already has its suffix adding by `--replSeqMem` */ @@ -131,6 +134,20 @@ class ReplSeqMem extends SeqTransform with HasShellOptions with DependencyAPIMig helpText = "Blackbox and emit a configuration file for each sequential memory", shortOption = Some("frsq"), helpValueName = Some("-c::-i::-o:") + ), + new ShellOption[String]( + longOption = "gen-mem-verilog", + toAnnotationSeq = (a: String) => + Seq( + a match { + case "blackbox" => GenVerilogMemBehaviorModelAnno(genBlackBox = true) + case _ => GenVerilogMemBehaviorModelAnno(genBlackBox = false) + }, + RunFirrtlTransformAnnotation(new ReplSeqMem) + ), + helpText = "Blackbox and emit a Verilog behavior model for each sequential memory", + shortOption = Some("gmv"), + helpValueName = Some("") ) ) @@ -144,6 +161,7 @@ class ReplSeqMem extends SeqTransform with HasShellOptions with DependencyAPIMig new ResolveMemoryReference, new ReplaceMemMacros, new WiringTransform, - new DumpMemoryAnnotations + new DumpMemoryAnnotations, + new BlackBoxSourceHelper ) } -- cgit v1.2.3