aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala
diff options
context:
space:
mode:
authorsinofp2021-05-22 15:16:32 +0800
committerGitHub2021-05-22 07:16:32 +0000
commit8bca41522cdc4b8ff69734cd159ce29f984d3290 (patch)
treecf6d43e872ca40dae86a5e9b8ceca118242c28ca /src/main/scala
parent117b84a15a352451c1217155f96b09d098681baf (diff)
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 <liu@jiuyang.me> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
Diffstat (limited to 'src/main/scala')
-rw-r--r--src/main/scala/firrtl/passes/memlib/DumpMemoryAnnotations.scala138
-rw-r--r--src/main/scala/firrtl/passes/memlib/ReplaceMemTransform.scala22
2 files changed, 155 insertions, 5 deletions
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:<circuit>:-i:<file>:-o:<file>")
+ ),
+ 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("<blackbox|full>")
)
)
@@ -144,6 +161,7 @@ class ReplSeqMem extends SeqTransform with HasShellOptions with DependencyAPIMig
new ResolveMemoryReference,
new ReplaceMemMacros,
new WiringTransform,
- new DumpMemoryAnnotations
+ new DumpMemoryAnnotations,
+ new BlackBoxSourceHelper
)
}