aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoralbertchen-sifive2018-08-30 13:51:44 -0700
committerJack Koenig2018-08-30 13:51:44 -0700
commit8a4893dc6d9ce994ebbecfefe049e9f5cb8bd5b1 (patch)
treedc59fd802b511f30c8ba68e5aaa935eaf412807f /src
parenta564d73f35703f8ba35b3e2c3263f1d9a65746fa (diff)
Emit Verilog Comments (#874)
add description nodes, transform; modify VerilogEmitter to emit comments
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/AddDescriptionNodes.scala119
-rw-r--r--src/main/scala/firrtl/Compiler.scala2
-rw-r--r--src/main/scala/firrtl/Emitter.scala343
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala160
4 files changed, 498 insertions, 126 deletions
diff --git a/src/main/scala/firrtl/AddDescriptionNodes.scala b/src/main/scala/firrtl/AddDescriptionNodes.scala
new file mode 100644
index 00000000..6bae6857
--- /dev/null
+++ b/src/main/scala/firrtl/AddDescriptionNodes.scala
@@ -0,0 +1,119 @@
+// See LICENSE for license details.
+
+package firrtl
+
+import firrtl.ir._
+import firrtl.annotations._
+import firrtl.Mappers._
+
+case class DescriptionAnnotation(named: Named, description: String) extends Annotation {
+ def update(renames: RenameMap): Seq[DescriptionAnnotation] = {
+ renames.get(named) match {
+ case None => Seq(this)
+ case Some(seq) => seq.map(n => this.copy(named = n))
+ }
+ }
+}
+
+private sealed trait HasDescription {
+ def description: Description
+}
+
+private abstract class Description extends FirrtlNode
+
+private case class DocString(string: StringLit) extends Description {
+ def serialize: String = "@[" + string.serialize + "]"
+}
+
+private case object EmptyDescription extends Description {
+ def serialize: String = ""
+}
+
+private case class DescribedStmt(description: Description, stmt: Statement) extends Statement with HasDescription {
+ def serialize: String = s"${description.serialize}\n${stmt.serialize}"
+ def mapStmt(f: Statement => Statement): Statement = f(stmt)
+ def mapExpr(f: Expression => Expression): Statement = this.copy(stmt = stmt.mapExpr(f))
+ def mapType(f: Type => Type): Statement = this.copy(stmt = stmt.mapType(f))
+ def mapString(f: String => String): Statement = this.copy(stmt = stmt.mapString(f))
+ def mapInfo(f: Info => Info): Statement = this.copy(stmt = stmt.mapInfo(f))
+}
+
+private case class DescribedMod(description: Description,
+ portDescriptions: Map[String, Description],
+ mod: DefModule) extends DefModule with HasDescription {
+ val info = mod.info
+ val name = mod.name
+ val ports = mod.ports
+ def serialize: String = s"${description.serialize}\n${mod.serialize}"
+ def mapStmt(f: Statement => Statement): DefModule = this.copy(mod = mod.mapStmt(f))
+ def mapPort(f: Port => Port): DefModule = this.copy(mod = mod.mapPort(f))
+ def mapString(f: String => String): DefModule = this.copy(mod = mod.mapString(f))
+ def mapInfo(f: Info => Info): DefModule = this.copy(mod = mod.mapInfo(f))
+}
+
+/** Wraps modules or statements with their respective described nodes.
+ * Descriptions come from [[DescriptionAnnotation]]. Describing a
+ * module or any of its ports will turn it into a [[DescribedMod]].
+ * Describing a Statement will turn it into a [[DescribedStmt]]
+ *
+ * @note should only be used by VerilogEmitter, described nodes will
+ * break other transforms.
+ */
+class AddDescriptionNodes extends Transform {
+ def inputForm = LowForm
+ def outputForm = LowForm
+
+ def onStmt(compMap: Map[String, Seq[String]])(stmt: Statement): Statement = {
+ stmt.map(onStmt(compMap)) match {
+ case d: IsDeclaration if compMap.contains(d.name) =>
+ DescribedStmt(DocString(StringLit.unescape(compMap(d.name).mkString("\n\n"))), d)
+ case other => other
+ }
+ }
+
+ def onModule(modMap: Map[String, Seq[String]], compMaps: Map[String, Map[String, Seq[String]]])
+ (mod: DefModule): DefModule = {
+ val (newMod, portDesc: Map[String, Description]) = compMaps.get(mod.name) match {
+ case None => (mod, Map.empty)
+ case Some(compMap) => (mod.mapStmt(onStmt(compMap)), mod.ports.collect {
+ case p @ Port(_, name, _, _) if compMap.contains(name) =>
+ name -> DocString(StringLit.unescape(compMap(name).mkString("\n\n")))
+ }.toMap)
+ }
+
+ val modDesc = modMap.get(newMod.name).map {
+ desc => DocString(StringLit.unescape(desc.mkString("\n\n")))
+ }
+
+ if (portDesc.nonEmpty || modDesc.nonEmpty) {
+ DescribedMod(modDesc.getOrElse(EmptyDescription), portDesc, newMod)
+ } else {
+ newMod
+ }
+ }
+
+ def collectMaps(annos: Seq[Annotation]): (Map[String, Seq[String]], Map[String, Map[String, Seq[String]]]) = {
+ val modMap = annos.collect {
+ case DescriptionAnnotation(ModuleName(m, CircuitName(c)), desc) => (m, desc)
+ }.groupBy(_._1).mapValues(_.map(_._2))
+
+ val compMap = annos.collect {
+ case DescriptionAnnotation(ComponentName(comp, ModuleName(mod, CircuitName(circ))), desc) =>
+ (mod, comp, desc)
+ }.groupBy(_._1).mapValues(_.groupBy(_._2).mapValues(_.map(_._3)))
+
+ (modMap, compMap)
+ }
+
+ def executeModule(module: DefModule, annos: Seq[Annotation]): DefModule = {
+ val (modMap, compMap) = collectMaps(annos)
+
+ onModule(modMap, compMap)(module)
+ }
+
+ override def execute(state: CircuitState): CircuitState = {
+ val (modMap, compMap) = collectMaps(state.annotations)
+
+ state.copy(circuit = state.circuit.mapModule(onModule(modMap, compMap)))
+ }
+}
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala
index b34782b5..9044c5a8 100644
--- a/src/main/scala/firrtl/Compiler.scala
+++ b/src/main/scala/firrtl/Compiler.scala
@@ -331,7 +331,7 @@ object CompilerUtils extends LazyLogging {
/** Generates a sequence of [[Transform]]s to lower a Firrtl circuit
*
* @param inputForm [[CircuitForm]] to lower from
- * @param outputForm [[CircuitForm to lower to
+ * @param outputForm [[CircuitForm]] to lower to
* @return Sequence of transforms that will lower if outputForm is lower than inputForm
*/
def getLoweringTransforms(inputForm: CircuitForm, outputForm: CircuitForm): Seq[Transform] = {
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index accefffa..0a743321 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -319,6 +319,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
* Gets a reference to a verilog renderer. This is used by the current standard verilog emission process
* but allows access to individual portions, in particular, this function can be used to generate
* the header for a verilog file without generating anything else.
+ *
* @param m the start module
* @param moduleMap a way of finding other modules
* @param writer where rendering will be placed
@@ -329,14 +330,46 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
/**
+ * Gets a reference to a verilog renderer. This is used by the current standard verilog emission process
+ * but allows access to individual portions, in particular, this function can be used to generate
+ * the header for a verilog file without generating anything else.
+ *
+ * @param descriptions comments to be emitted
+ * @param m the start module
+ * @param moduleMap a way of finding other modules
+ * @param writer where rendering will be placed
+ * @return the render reference
+ */
+ def getRenderer(descriptions: Seq[DescriptionAnnotation],
+ m: Module,
+ moduleMap: Map[String, DefModule])(implicit writer: Writer): VerilogRender = {
+ val newMod = new AddDescriptionNodes().executeModule(m, descriptions)
+
+ newMod match {
+ case DescribedMod(d, pds, m: Module) => new VerilogRender(d, pds, m, moduleMap)(writer)
+ case m: Module => new VerilogRender(m, moduleMap)(writer)
+ }
+ }
+
+ /**
* Used by getRenderer, it has machinery to produce verilog from IR.
* Making this a class allows access to particular parts of the verilog emission.
*
- * @param m the start module
- * @param moduleMap a map of modules so submodules can be discovered
- * @param writer where rendered information is placed.
+ * @param description a description of the start module
+ * @param portDescriptions a map of port name to description
+ * @param m the start module
+ * @param moduleMap a map of modules so submodules can be discovered
+ * @param writer where rendered information is placed.
*/
- class VerilogRender(m: Module, moduleMap: Map[String, DefModule])(implicit writer: Writer) {
+ class VerilogRender(description: Description,
+ portDescriptions: Map[String, Description],
+ m: Module,
+ moduleMap: Map[String, DefModule])(implicit writer: Writer) {
+
+ def this(m: Module, moduleMap: Map[String, DefModule])(implicit writer: Writer) {
+ this(EmptyDescription, Map.empty, m, moduleMap)(writer)
+ }
+
val netlist = mutable.LinkedHashMap[WrappedExpression, Expression]()
val namespace = Namespace(m)
namespace.newName("_RAND") // Start rand names at _RAND_0
@@ -484,6 +517,21 @@ class VerilogEmitter extends SeqTransform with Emitter {
Seq("$fwrite(32'h80000002,", strx, ");")
}
+ // turn strings into Seq[String] verilog comments
+ def build_comment(desc: String): Seq[Seq[String]] = {
+ val lines = desc.split("\n").toSeq
+
+ if (lines.size > 1) {
+ val lineSeqs = lines.tail.map {
+ case "" => Seq(" *")
+ case nonEmpty => Seq(" * ", nonEmpty)
+ }
+ Seq("/* ", lines.head) +: lineSeqs :+ Seq(" */")
+ } else {
+ Seq(Seq("// ", lines(0)))
+ }
+ }
+
// Turn ports into Seq[String] and add to portdefs
def build_ports(): Unit = {
def padToMax(strs: Seq[String]): Seq[String] = {
@@ -506,133 +554,163 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
// dirs are already padded
- portdefs ++= (dirs, padToMax(tpes), m.ports).zipped.toSeq.zipWithIndex.map {
+ (dirs, padToMax(tpes), m.ports).zipped.toSeq.zipWithIndex.foreach {
case ((dir, tpe, Port(info, name, _, _)), i) =>
- if (i != m.ports.size - 1) Seq(dir, " ", tpe, " ", name, ",", info)
- else Seq(dir, " ", tpe, " ", name, info)
+ portDescriptions.get(name) match {
+ case Some(DocString(s)) =>
+ portdefs += Seq("")
+ portdefs ++= build_comment(s.string)
+ case other =>
+ }
+
+ if (i != m.ports.size - 1) {
+ portdefs += Seq(dir, " ", tpe, " ", name, ",", info)
+ } else {
+ portdefs += Seq(dir, " ", tpe, " ", name, info)
+ }
}
}
- def build_streams(s: Statement): Statement = s map build_streams match {
- case sx@Connect(info, loc@WRef(_, _, PortKind | WireKind | InstanceKind, _), expr) =>
- assign(loc, expr, info)
- sx
- case sx: DefWire =>
- declare("wire", sx.name, sx.tpe, sx.info)
- sx
- case sx: DefRegister =>
- declare("reg", sx.name, sx.tpe, sx.info)
- val e = wref(sx.name, sx.tpe)
- regUpdate(e, sx.clock)
- initialize(e)
- sx
- case sx: DefNode =>
- declare("wire", sx.name, sx.value.tpe, sx.info)
- assign(WRef(sx.name, sx.value.tpe, NodeKind, MALE), sx.value, sx.info)
- sx
- case sx: Stop =>
- simulate(sx.clk, sx.en, stop(sx.ret), Some("STOP_COND"), sx.info)
- sx
- case sx: Print =>
- simulate(sx.clk, sx.en, printf(sx.string, sx.args), Some("PRINTF_COND"), sx.info)
- sx
- // If we are emitting an Attach, it must not have been removable in VerilogPrep
- case sx: Attach =>
- // For Synthesis
- // Note that this is quadratic in the number of things attached
- for (set <- sx.exprs.toSet.subsets(2)) {
- val (a, b) = set.toSeq match {
- case Seq(x, y) => (x, y)
+ def build_streams(s: Statement): Statement = {
+ val withoutDescription = s match {
+ case DescribedStmt(DocString(desc), stmt) =>
+ val comment = Seq("") +: build_comment(desc.string)
+ stmt match {
+ case sx: IsDeclaration =>
+ declares ++= comment
+ case sx =>
+ }
+ stmt
+ case DescribedStmt(EmptyDescription, stmt) => stmt
+ case other => other
+ }
+ withoutDescription map build_streams match {
+ case sx@Connect(info, loc@WRef(_, _, PortKind | WireKind | InstanceKind, _), expr) =>
+ assign(loc, expr, info)
+ sx
+ case sx: DefWire =>
+ declare("wire", sx.name, sx.tpe, sx.info)
+ sx
+ case sx: DefRegister =>
+ declare("reg", sx.name, sx.tpe, sx.info)
+ val e = wref(sx.name, sx.tpe)
+ regUpdate(e, sx.clock)
+ initialize(e)
+ sx
+ case sx: DefNode =>
+ declare("wire", sx.name, sx.value.tpe, sx.info)
+ assign(WRef(sx.name, sx.value.tpe, NodeKind, MALE), sx.value, sx.info)
+ sx
+ case sx: Stop =>
+ simulate(sx.clk, sx.en, stop(sx.ret), Some("STOP_COND"), sx.info)
+ sx
+ case sx: Print =>
+ simulate(sx.clk, sx.en, printf(sx.string, sx.args), Some("PRINTF_COND"), sx.info)
+ sx
+ // If we are emitting an Attach, it must not have been removable in VerilogPrep
+ case sx: Attach =>
+ // For Synthesis
+ // Note that this is quadratic in the number of things attached
+ for (set <- sx.exprs.toSet.subsets(2)) {
+ val (a, b) = set.toSeq match {
+ case Seq(x, y) => (x, y)
+ }
+ // Synthesizable ones as well
+ attachSynAssigns += Seq("assign ", a, " = ", b, ";", sx.info)
+ attachSynAssigns += Seq("assign ", b, " = ", a, ";", sx.info)
+ }
+ // alias implementation for everything else
+ attachAliases += Seq("alias ", sx.exprs.flatMap(e => Seq(e, " = ")).init, ";", sx.info)
+ sx
+ case sx: WDefInstanceConnector =>
+ val (module, params) = moduleMap(sx.module) match {
+ case DescribedMod(_, _, ExtModule(_, _, _, extname, params)) => (extname, params)
+ case DescribedMod(_, _, Module(_, name, _, _)) => (name, Seq.empty)
+ case ExtModule(_, _, _, extname, params) => (extname, params)
+ case Module(_, name, _, _) => (name, Seq.empty)
+ }
+ val ps = if (params.nonEmpty) params map stringify mkString("#(", ", ", ") ") else ""
+ instdeclares += Seq(module, " ", ps, sx.name, " (", sx.info)
+ for (((port, ref), i) <- sx.portCons.zipWithIndex) {
+ val line = Seq(tab, ".", remove_root(port), "(", ref, ")")
+ if (i != sx.portCons.size - 1) instdeclares += Seq(line, ",")
+ else instdeclares += line
+ }
+ instdeclares += Seq(");")
+ sx
+ case sx: DefMemory =>
+ val fullSize = sx.depth * (sx.dataType match {
+ case GroundType(IntWidth(width)) => width
+ })
+ val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg"
+ declare(decl, sx.name, VectorType(sx.dataType, sx.depth), sx.info)
+ initialize_mem(sx)
+ if (sx.readLatency != 0 || sx.writeLatency != 1)
+ throw EmitterException("All memories should be transformed into " +
+ "blackboxes or combinational by previous passses")
+ for (r <- sx.readers) {
+ val data = memPortField(sx, r, "data")
+ val addr = memPortField(sx, r, "addr")
+ val en = memPortField(sx, r, "en")
+ // Ports should share an always@posedge, so can't have intermediary wire
+ val clk = netlist(memPortField(sx, r, "clk"))
+
+ declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info)
+ declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info)
+ // declare("wire", LowerTypes.loweredName(en), en.tpe)
+
+ //; Read port
+ assign(addr, netlist(addr), NoInfo) // Info should come from addr connection
+ // assign(en, netlist(en)) //;Connects value to m.r.en
+ val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER)
+ val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER)
+ val depthValue = UIntLiteral(sx.depth, IntWidth(BigInt(sx.depth).bitLength))
+ val garbageGuard = DoPrim(Geq, Seq(addr, depthValue), Seq(), UnknownType)
+
+ if ((sx.depth & (sx.depth - 1)) == 0)
+ assign(data, memPort, sx.info)
+ else
+ garbageAssign(data, memPort, garbageGuard, sx.info)
}
- // Synthesizable ones as well
- attachSynAssigns += Seq("assign ", a, " = ", b, ";", sx.info)
- attachSynAssigns += Seq("assign ", b, " = ", a, ";", sx.info)
- }
- // alias implementation for everything else
- attachAliases += Seq("alias ", sx.exprs.flatMap(e => Seq(e, " = ")).init, ";", sx.info)
- sx
- case sx: WDefInstanceConnector =>
- val (module, params) = moduleMap(sx.module) match {
- case ExtModule(_, _, _, extname, params) => (extname, params)
- case Module(_, name, _, _) => (name, Seq.empty)
- }
- val ps = if (params.nonEmpty) params map stringify mkString("#(", ", ", ") ") else ""
- instdeclares += Seq(module, " ", ps, sx.name, " (", sx.info)
- for (((port, ref), i) <- sx.portCons.zipWithIndex) {
- val line = Seq(tab, ".", remove_root(port), "(", ref, ")")
- if (i != sx.portCons.size - 1) instdeclares += Seq(line, ",")
- else instdeclares += line
- }
- instdeclares += Seq(");")
- sx
- case sx: DefMemory =>
- val fullSize = sx.depth * (sx.dataType match {
- case GroundType(IntWidth(width)) => width
- })
- val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg"
- declare(decl, sx.name, VectorType(sx.dataType, sx.depth), sx.info)
- initialize_mem(sx)
- if (sx.readLatency != 0 || sx.writeLatency != 1)
- throw EmitterException("All memories should be transformed into " +
- "blackboxes or combinational by previous passses")
- for (r <- sx.readers) {
- val data = memPortField(sx, r, "data")
- val addr = memPortField(sx, r, "addr")
- val en = memPortField(sx, r, "en")
- // Ports should share an always@posedge, so can't have intermediary wire
- val clk = netlist(memPortField(sx, r, "clk"))
-
- declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info)
- declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info)
- // declare("wire", LowerTypes.loweredName(en), en.tpe)
-
- //; Read port
- assign(addr, netlist(addr), NoInfo) // Info should come from addr connection
- // assign(en, netlist(en)) //;Connects value to m.r.en
- val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER)
- val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER)
- val depthValue = UIntLiteral(sx.depth, IntWidth(BigInt(sx.depth).bitLength))
- val garbageGuard = DoPrim(Geq, Seq(addr, depthValue), Seq(), UnknownType)
-
- if ((sx.depth & (sx.depth - 1)) == 0)
- assign(data, memPort, sx.info)
- else
- garbageAssign(data, memPort, garbageGuard, sx.info)
- }
- for (w <- sx.writers) {
- val data = memPortField(sx, w, "data")
- val addr = memPortField(sx, w, "addr")
- val mask = memPortField(sx, w, "mask")
- val en = memPortField(sx, w, "en")
- //Ports should share an always@posedge, so can't have intermediary wire
- val clk = netlist(memPortField(sx, w, "clk"))
-
- declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info)
- declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info)
- declare("wire", LowerTypes.loweredName(mask), mask.tpe, sx.info)
- declare("wire", LowerTypes.loweredName(en), en.tpe, sx.info)
-
- // Write port
- // Info should come from netlist
- assign(data, netlist(data), NoInfo)
- assign(addr, netlist(addr), NoInfo)
- assign(mask, netlist(mask), NoInfo)
- assign(en, netlist(en), NoInfo)
-
- val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER)
- val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER)
- update(memPort, data, clk, AND(en, mask), sx.info)
- }
+ for (w <- sx.writers) {
+ val data = memPortField(sx, w, "data")
+ val addr = memPortField(sx, w, "addr")
+ val mask = memPortField(sx, w, "mask")
+ val en = memPortField(sx, w, "en")
+ //Ports should share an always@posedge, so can't have intermediary wire
+ val clk = netlist(memPortField(sx, w, "clk"))
+
+ declare("wire", LowerTypes.loweredName(data), data.tpe, sx.info)
+ declare("wire", LowerTypes.loweredName(addr), addr.tpe, sx.info)
+ declare("wire", LowerTypes.loweredName(mask), mask.tpe, sx.info)
+ declare("wire", LowerTypes.loweredName(en), en.tpe, sx.info)
+
+ // Write port
+ // Info should come from netlist
+ assign(data, netlist(data), NoInfo)
+ assign(addr, netlist(addr), NoInfo)
+ assign(mask, netlist(mask), NoInfo)
+ assign(en, netlist(en), NoInfo)
+
+ val mem = WRef(sx.name, memType(sx), MemKind, UNKNOWNGENDER)
+ val memPort = WSubAccess(mem, addr, sx.dataType, UNKNOWNGENDER)
+ update(memPort, data, clk, AND(en, mask), sx.info)
+ }
- if (sx.readwriters.nonEmpty)
- throw EmitterException("All readwrite ports should be transformed into " +
- "read & write ports by previous passes")
- sx
- case sx => sx
+ if (sx.readwriters.nonEmpty)
+ throw EmitterException("All readwrite ports should be transformed into " +
+ "read & write ports by previous passes")
+ sx
+ case sx => sx
+ }
}
def emit_streams() {
+ description match {
+ case DocString(s) => build_comment(s.string).foreach(emit(_))
+ case other =>
+ }
emit(Seq("module ", m.name, "(", m.info))
for (x <- portdefs) emit(Seq(tab, x))
emit(Seq(");"))
@@ -721,6 +799,12 @@ class VerilogEmitter extends SeqTransform with Emitter {
def emitVerilogBind(overrideName: String, body: String): DefModule = {
build_netlist(m.body)
build_ports()
+
+ description match {
+ case DocString(s) => build_comment(s.string).foreach(emit(_))
+ case other =>
+ }
+
emit(Seq("module ", overrideName, "(", m.info))
for (x <- portdefs) emit(Seq(tab, x))
@@ -729,7 +813,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq("endmodule"), top = 0)
m
}
- }
+ }
/** Preamble for every emitted Verilog file */
def transforms = Seq(
@@ -739,16 +823,20 @@ class VerilogEmitter extends SeqTransform with Emitter {
new DeadCodeElimination,
passes.VerilogModulusCleanup,
passes.VerilogRename,
- passes.VerilogPrep)
+ passes.VerilogPrep,
+ new AddDescriptionNodes)
def emit(state: CircuitState, writer: Writer): Unit = {
val circuit = runTransforms(state).circuit
val moduleMap = circuit.modules.map(m => m.name -> m).toMap
circuit.modules.foreach {
+ case dm @ DescribedMod(d, pds, m: Module) =>
+ val renderer = new VerilogRender(d, pds, m, moduleMap)(writer)
+ renderer.emit_verilog()
case m: Module =>
val renderer = new VerilogRender(m, moduleMap)(writer)
renderer.emit_verilog()
- case _: ExtModule => // do nothing
+ case _ => // do nothing
}
}
@@ -764,12 +852,17 @@ class VerilogEmitter extends SeqTransform with Emitter {
val moduleMap = circuit.modules.map(m => m.name -> m).toMap
circuit.modules flatMap {
+ case dm @ DescribedMod(d, pds, module: Module) =>
+ val writer = new java.io.StringWriter
+ val renderer = new VerilogRender(d, pds, module, moduleMap)(writer)
+ renderer.emit_verilog()
+ Some(EmittedVerilogModuleAnnotation(EmittedVerilogModule(module.name, writer.toString)))
case module: Module =>
val writer = new java.io.StringWriter
val renderer = new VerilogRender(module, moduleMap)(writer)
renderer.emit_verilog()
Some(EmittedVerilogModuleAnnotation(EmittedVerilogModule(module.name, writer.toString)))
- case _: ExtModule => None
+ case _ => None
}
case _ => Seq()
}
diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
index 46f3a711..b5ad2f1a 100644
--- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala
+++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
@@ -233,3 +233,163 @@ class VerilogEmitterSpec extends FirrtlFlatSpec {
}
}
+
+class VerilogDescriptionEmitterSpec extends FirrtlFlatSpec {
+ "Port descriptions" should "emit aligned comments on the line above" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Test :
+ | module Test :
+ | input a : UInt<1>
+ | input b : UInt<1>
+ | output c : UInt<1>
+ | c <= add(a, b)
+ |""".stripMargin
+ val check = Seq(
+ """ /* multi
+ | * line
+ | */
+ | input a,""".stripMargin,
+ """ // single line
+ | input b,""".stripMargin
+ )
+ // We don't use executeTest because we care about the spacing in the result
+ val modName = ModuleName("Test", CircuitName("Test"))
+ val annos = Seq(
+ DescriptionAnnotation(ComponentName("a", modName), "multi\nline"),
+ DescriptionAnnotation(ComponentName("b", modName), "single line"))
+ val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos), Seq.empty)
+ val output = finalState.getEmittedCircuit.value
+ for (c <- check) {
+ assert(output.contains(c))
+ }
+ }
+
+ "Declaration descriptions" should "emit aligned comments on the line above" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Test :
+ | module Test :
+ | input clock : Clock
+ | input a : UInt<1>
+ | input b : UInt<1>
+ | output c : UInt<1>
+ |
+ | wire d : UInt<1>
+ | d <= add(a, b)
+ |
+ | reg e : UInt<1>, clock
+ | e <= or(a, b)
+ |
+ | node f = and(a, b)
+ | c <= add(d, add(e, f))
+ |""".stripMargin
+ val check = Seq(
+ """ /* multi
+ | * line
+ | */
+ | wire d;""".stripMargin,
+ """ /* multi
+ | * line
+ | */
+ | reg e;""".stripMargin,
+ """ // single line
+ | wire f;""".stripMargin
+ )
+ // We don't use executeTest because we care about the spacing in the result
+ val modName = ModuleName("Test", CircuitName("Test"))
+ val annos = Seq(
+ DescriptionAnnotation(ComponentName("d", modName), "multi\nline"),
+ DescriptionAnnotation(ComponentName("e", modName), "multi\nline"),
+ DescriptionAnnotation(ComponentName("f", modName), "single line"))
+ val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos), Seq.empty)
+ val output = finalState.getEmittedCircuit.value
+ for (c <- check) {
+ assert(output.contains(c))
+ }
+ }
+
+ "Module descriptions" should "emit aligned comments on the line above" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Test :
+ | module Test :
+ | input clock : Clock
+ | input a : UInt<1>
+ | input b : UInt<1>
+ | output c : UInt<1>
+ |
+ | wire d : UInt<1>
+ | d <= add(a, b)
+ |
+ | reg e : UInt<1>, clock
+ | e <= or(a, b)
+ |
+ | node f = and(a, b)
+ | c <= add(d, add(e, f))
+ |""".stripMargin
+ val check = Seq(
+ """/* multi
+ | * line
+ | */
+ |module Test(""".stripMargin
+ )
+ // We don't use executeTest because we care about the spacing in the result
+ val modName = ModuleName("Test", CircuitName("Test"))
+ val annos = Seq(DescriptionAnnotation(modName, "multi\nline"))
+ val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos), Seq.empty)
+ val output = finalState.getEmittedCircuit.value
+ for (c <- check) {
+ assert(output.contains(c))
+ }
+ }
+
+ "Multiple descriptions" should "be combined" in {
+ val compiler = new VerilogCompiler
+ val input =
+ """circuit Test :
+ | module Test :
+ | input a : UInt<1>
+ | input b : UInt<1>
+ | output c : UInt<1>
+ |
+ | wire d : UInt<1>
+ | d <= add(a, b)
+ |
+ | c <= add(a, d)
+ |""".stripMargin
+ val check = Seq(
+ """/* line1
+ | *
+ | * line2
+ | */
+ |module Test(""".stripMargin,
+ """ /* line3
+ | *
+ | * line4
+ | */
+ | input a,""".stripMargin,
+ """ /* line5
+ | *
+ | * line6
+ | */
+ | wire d;""".stripMargin
+ )
+ // We don't use executeTest because we care about the spacing in the result
+ val modName = ModuleName("Test", CircuitName("Test"))
+ val annos = Seq(
+ DescriptionAnnotation(modName, "line1"),
+ DescriptionAnnotation(modName, "line2"),
+ DescriptionAnnotation(ComponentName("a", modName), "line3"),
+ DescriptionAnnotation(ComponentName("a", modName), "line4"),
+ DescriptionAnnotation(ComponentName("d", modName), "line5"),
+ DescriptionAnnotation(ComponentName("d", modName), "line6")
+ )
+ val writer = new java.io.StringWriter
+ val finalState = compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos), Seq.empty)
+ val output = finalState.getEmittedCircuit.value
+ for (c <- check) {
+ assert(output.contains(c))
+ }
+ }
+}