aboutsummaryrefslogtreecommitdiff
path: root/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala')
-rw-r--r--src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala1319
1 files changed, 1319 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala b/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala
new file mode 100644
index 00000000..3ecd1279
--- /dev/null
+++ b/src/main/scala/firrtl/backends/verilog/VerilogEmitter.scala
@@ -0,0 +1,1319 @@
+package firrtl
+
+import java.io.Writer
+
+import firrtl.ir._
+import firrtl.PrimOps._
+import firrtl.Utils._
+import firrtl.WrappedExpression._
+import firrtl.traversals.Foreachers._
+import firrtl.annotations.{CircuitTarget, ReferenceTarget, SingleTargetAnnotation}
+import firrtl.passes.LowerTypes
+import firrtl.passes.MemPortUtils._
+import firrtl.stage.TransformManager
+import firrtl.transforms.FixAddingNegativeLiterals
+
+import scala.collection.mutable
+import scala.collection.mutable.ArrayBuffer
+
+object VerilogEmitter {
+ private val unaryOps: Set[PrimOp] = Set(Andr, Orr, Xorr, Neg, Not)
+
+ // To make uses more self-documenting
+ private val isUnaryOp: PrimOp => Boolean = unaryOps
+
+ /** Maps a [[PrimOp]] to a precedence number, lower number means higher precedence
+ *
+ * Only the [[PrimOp]]s contained in this map will be inlined. [[PrimOp]]s
+ * like [[Neg]] are not in this map because inlining them may result
+ * in illegal verilog like '--2sh1'
+ */
+ private val precedenceMap: Map[PrimOp, Int] = {
+ val precedenceSeq = Seq(
+ Set(Head, Tail, Bits, Shr, Pad), // Shr and Pad emit as bit select
+ unaryOps,
+ Set(Mul, Div, Rem),
+ Set(Add, Sub, Addw, Subw),
+ Set(Dshl, Dshlw, Dshr),
+ Set(Lt, Leq, Gt, Geq),
+ Set(Eq, Neq),
+ Set(And),
+ Set(Xor),
+ Set(Or)
+ )
+ precedenceSeq.zipWithIndex.foldLeft(Map.empty[PrimOp, Int]) {
+ case (map, (ops, idx)) => map ++ ops.map(_ -> idx)
+ }
+ }
+
+ /** true if op1 has equal precendence to op2
+ */
+ private def precedenceEq(op1: PrimOp, op2: PrimOp): Boolean = {
+ precedenceMap(op1) == precedenceMap(op2)
+ }
+
+ /** true if op1 has greater precendence than op2
+ */
+ private def precedenceGt(op1: PrimOp, op2: PrimOp): Boolean = {
+ precedenceMap(op1) < precedenceMap(op2)
+ }
+}
+
+class VerilogEmitter extends SeqTransform with Emitter {
+ import VerilogEmitter._
+
+ def inputForm = LowForm
+ def outputForm = LowForm
+
+ override def prerequisites = firrtl.stage.Forms.AssertsRemoved ++
+ firrtl.stage.Forms.LowFormOptimized
+
+ override def optionalPrerequisiteOf = Seq.empty
+
+ val outputSuffix = ".v"
+ val tab = " "
+ def AND(e1: WrappedExpression, e2: WrappedExpression): Expression = {
+ if (e1 == e2) e1.e1
+ else if ((e1 == we(zero)) | (e2 == we(zero))) zero
+ else if (e1 == we(one)) e2.e1
+ else if (e2 == we(one)) e1.e1
+ else DoPrim(And, Seq(e1.e1, e2.e1), Nil, UIntType(IntWidth(1)))
+ }
+ def wref(n: String, t: Type) = WRef(n, t, ExpKind, UnknownFlow)
+ def remove_root(ex: Expression): Expression = ex match {
+ case ex: WSubField =>
+ ex.expr match {
+ case (e: WSubField) => remove_root(e)
+ case (_: WRef) => WRef(ex.name, ex.tpe, InstanceKind, UnknownFlow)
+ }
+ case _ => throwInternalError(s"shouldn't be here: remove_root($ex)")
+ }
+
+ /** Turn Params into Verilog Strings */
+ def stringify(param: Param): String = param match {
+ case IntParam(name, value) =>
+ val lit =
+ if (value.isValidInt) {
+ s"$value"
+ } else {
+ val blen = value.bitLength
+ if (value > 0) s"$blen'd$value" else s"-${blen + 1}'sd${value.abs}"
+ }
+ s".$name($lit)"
+ case DoubleParam(name, value) => s".$name($value)"
+ case StringParam(name, value) => s".${name}(${value.verilogEscape})"
+ case RawStringParam(name, value) => s".$name($value)"
+ }
+ def stringify(tpe: GroundType): String = tpe match {
+ case (_: UIntType | _: SIntType | _: AnalogType) =>
+ val wx = bitWidth(tpe) - 1
+ if (wx > 0) s"[$wx:0]" else ""
+ case ClockType | AsyncResetType => ""
+ case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
+ }
+ private def getLeadingTabs(x: Any): String = {
+ x match {
+ case seq: Seq[_] =>
+ val head = seq.takeWhile(_ == tab).mkString
+ val tail = seq.dropWhile(_ == tab).lift(0).map(getLeadingTabs).getOrElse(tab)
+ head + tail
+ case _ => tab
+ }
+ }
+ def emit(x: Any)(implicit w: Writer): Unit = {
+ emitCol(x, 0, getLeadingTabs(x), 0)
+ }
+ private def emitCast(e: Expression): Any = e.tpe match {
+ case (t: UIntType) => e
+ case (t: SIntType) => Seq("$signed(", e, ")")
+ case ClockType => e
+ case AnalogType(_) => e
+ case _ => throwInternalError(s"unrecognized cast: $e")
+ }
+ def emit(x: Any, top: Int)(implicit w: Writer): Unit = {
+ emitCol(x, top, "", 0)
+ }
+ private val maxCol = 120
+ private def emitCol(x: Any, top: Int, tabs: String, colNum: Int)(implicit w: Writer): Int = {
+ def writeCol(contents: String): Int = {
+ if ((contents.size + colNum) > maxCol) {
+ w.write("\n")
+ w.write(tabs)
+ w.write(contents)
+ tabs.size + contents.size
+ } else {
+ w.write(contents)
+ colNum + contents.size
+ }
+ }
+
+ def cast(e: Expression): Any = e.tpe match {
+ case (t: UIntType) => e
+ case (t: SIntType) => Seq("$signed(", e, ")")
+ case ClockType => e
+ case AnalogType(_) => e
+ case _ => throwInternalError(s"unrecognized cast: $e")
+ }
+ x match {
+ case (e: DoPrim) => emitCol(op_stream(e), top + 1, tabs, colNum)
+ case (e: Mux) => {
+ if (e.tpe == ClockType) {
+ throw EmitterException("Cannot emit clock muxes directly")
+ }
+ if (e.tpe == AsyncResetType) {
+ throw EmitterException("Cannot emit async reset muxes directly")
+ }
+ emitCol(Seq(e.cond, " ? ", cast(e.tval), " : ", cast(e.fval)), top + 1, tabs, colNum)
+ }
+ case (e: ValidIf) => emitCol(Seq(cast(e.value)), top + 1, tabs, colNum)
+ case (e: WRef) => writeCol(e.serialize)
+ case (e: WSubField) => writeCol(LowerTypes.loweredName(e))
+ case (e: WSubAccess) => writeCol(s"${LowerTypes.loweredName(e.expr)}[${LowerTypes.loweredName(e.index)}]")
+ case (e: WSubIndex) => writeCol(e.serialize)
+ case (e: Literal) => v_print(e, colNum)
+ case (e: VRandom) => writeCol(s"{${e.nWords}{`RANDOM}}")
+ case (t: GroundType) => writeCol(stringify(t))
+ case (t: VectorType) =>
+ emit(t.tpe, top + 1)
+ writeCol(s"[${t.size - 1}:0]")
+ case (s: String) => writeCol(s)
+ case (i: Int) => writeCol(i.toString)
+ case (i: Long) => writeCol(i.toString)
+ case (i: BigInt) => writeCol(i.toString)
+ case (i: Info) =>
+ i match {
+ case NoInfo => colNum // Do nothing
+ case f: FileInfo =>
+ val escaped = FileInfo.escapedToVerilog(f.escaped)
+ w.write(s" // @[$escaped]")
+ colNum
+ case m: MultiInfo =>
+ val escaped = FileInfo.escapedToVerilog(m.flatten.map(_.escaped).mkString(" "))
+ w.write(s" // @[$escaped]")
+ colNum
+ }
+ case (s: Seq[Any]) =>
+ val nextColNum = s.foldLeft(colNum) {
+ case (colNum, e) => emitCol(e, top + 1, tabs, colNum)
+ }
+ if (top == 0) {
+ w.write("\n")
+ 0
+ } else {
+ nextColNum
+ }
+ case x => throwInternalError(s"trying to emit unsupported operator: $x")
+ }
+ }
+
+ //;------------- PASS -----------------
+ def v_print(e: Expression, colNum: Int)(implicit w: Writer) = e match {
+ case UIntLiteral(value, IntWidth(width)) =>
+ val contents = s"$width'h${value.toString(16)}"
+ w.write(contents)
+ colNum + contents.size
+ case SIntLiteral(value, IntWidth(width)) =>
+ val stringLiteral = value.toString(16)
+ val contents = stringLiteral.head match {
+ case '-' if value == FixAddingNegativeLiterals.minNegValue(width) => s"$width'sh${stringLiteral.tail}"
+ case '-' => s"-$width'sh${stringLiteral.tail}"
+ case _ => s"$width'sh${stringLiteral}"
+ }
+ w.write(contents)
+ colNum + contents.size
+ case _ => throwInternalError(s"attempt to print unrecognized expression: $e")
+ }
+
+ // NOTE: We emit SInts as regular Verilog unsigned wires/regs so the real type of any SInt
+ // reference is actually unsigned in the emitted Verilog. Thus we must cast refs as necessary
+ // to ensure Verilog operations are signed.
+ def op_stream(doprim: DoPrim): Seq[Any] = {
+ def parenthesize(e: Expression, isFirst: Boolean): Any = doprim.op match {
+ // these PrimOps emit either {..., a0, ...} or a0 so they never need parentheses
+ case Shl | Cat | Cvt | AsUInt | AsSInt | AsClock | AsAsyncReset => e
+ case _ =>
+ e match {
+ case e: DoPrim =>
+ op_stream(e) match {
+ /** DoPrims like AsUInt simply emit Seq(a0), so we need to
+ * recursively check whether a0 needs to be parenthesized
+ */
+ case Seq(passthrough: Expression) => parenthesize(passthrough, isFirst)
+
+ /* Parentheses are never needed if precedence is greater
+ * Otherwise, the first expression does not need parentheses if
+ * - it's precedence is equal AND
+ * - the ops are not unary operations (which all have equal precedence)
+ */
+ case other =>
+ val noParens =
+ precedenceGt(e.op, doprim.op) ||
+ (isFirst && precedenceEq(e.op, doprim.op) && !isUnaryOp(e.op))
+ if (noParens) other else Seq("(", other, ")")
+ }
+
+ /** Mux args should always have parens because Mux has the lowest precedence
+ */
+ case _: Mux => Seq("(", e, ")")
+ case _ => e
+ }
+ }
+
+ // Cast to SInt, don't cast multiple times
+ def doCast(e: Expression): Any = e match {
+ case DoPrim(AsSInt, Seq(arg), _, _) => doCast(arg)
+ case slit: SIntLiteral => slit
+ case other => Seq("$signed(", other, ")")
+ }
+ def castIf(e: Expression, isFirst: Boolean = false): Any = {
+ if (doprim.args.exists(_.tpe.isInstanceOf[SIntType])) {
+ e.tpe match {
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected non-SInt type for $e in $doprim")
+ }
+ } else {
+ parenthesize(e, isFirst)
+ }
+ }
+ def cast(e: Expression, isFirst: Boolean = false): Any = doprim.tpe match {
+ case _: UIntType => parenthesize(e, isFirst)
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected type for $e in $doprim")
+ }
+ def castAs(e: Expression, isFirst: Boolean = false): Any = e.tpe match {
+ case _: UIntType => parenthesize(e, isFirst)
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected type for $e in $doprim")
+ }
+ def a0: Expression = doprim.args.head
+ def a1: Expression = doprim.args(1)
+ def c0: Int = doprim.consts.head.toInt
+ def c1: Int = doprim.consts(1).toInt
+
+ def castCatArgs(a0: Expression, a1: Expression): Seq[Any] = {
+ val a0Seq = a0 match {
+ case cat @ DoPrim(PrimOps.Cat, args, _, _) => castCatArgs(args.head, args(1))
+ case _ => Seq(cast(a0))
+ }
+ val a1Seq = a1 match {
+ case cat @ DoPrim(PrimOps.Cat, args, _, _) => castCatArgs(args.head, args(1))
+ case _ => Seq(cast(a1))
+ }
+ a0Seq ++ Seq(",") ++ a1Seq
+ }
+
+ doprim.op match {
+ case Add => Seq(castIf(a0, true), " + ", castIf(a1))
+ case Addw => Seq(castIf(a0, true), " + ", castIf(a1))
+ case Sub => Seq(castIf(a0, true), " - ", castIf(a1))
+ case Subw => Seq(castIf(a0, true), " - ", castIf(a1))
+ case Mul => Seq(castIf(a0, true), " * ", castIf(a1))
+ case Div => Seq(castIf(a0, true), " / ", castIf(a1))
+ case Rem => Seq(castIf(a0, true), " % ", castIf(a1))
+ case Lt => Seq(castIf(a0, true), " < ", castIf(a1))
+ case Leq => Seq(castIf(a0, true), " <= ", castIf(a1))
+ case Gt => Seq(castIf(a0, true), " > ", castIf(a1))
+ case Geq => Seq(castIf(a0, true), " >= ", castIf(a1))
+ case Eq => Seq(castIf(a0, true), " == ", castIf(a1))
+ case Neq => Seq(castIf(a0, true), " != ", castIf(a1))
+ case Pad =>
+ val w = bitWidth(a0.tpe)
+ val diff = c0 - w
+ if (w == BigInt(0) || diff <= 0) Seq(a0)
+ else
+ doprim.tpe match {
+ // Either sign extend or zero extend.
+ // If width == BigInt(1), don't extract bit
+ case (_: SIntType) if w == BigInt(1) => Seq("{", c0, "{", a0, "}}")
+ case (_: SIntType) => Seq("{{", diff, "{", parenthesize(a0, true), "[", w - 1, "]}},", a0, "}")
+ case (_) => Seq("{{", diff, "'d0}, ", a0, "}")
+ }
+ // Because we don't support complex Expressions, all casts are ignored
+ // This simplifies handling of assignment of a signed expression to an unsigned LHS value
+ // which does not require a cast in Verilog
+ case AsUInt | AsSInt | AsClock | AsAsyncReset => Seq(a0)
+ case Dshlw => Seq(cast(a0), " << ", parenthesize(a1, false))
+ case Dshl => Seq(cast(a0), " << ", parenthesize(a1, false))
+ case Dshr =>
+ doprim.tpe match {
+ case (_: SIntType) => Seq(cast(a0), " >>> ", parenthesize(a1, false))
+ case (_) => Seq(cast(a0), " >> ", parenthesize(a1, false))
+ }
+ case Shl => if (c0 > 0) Seq("{", cast(a0), s", $c0'h0}") else Seq(cast(a0))
+ case Shr if c0 >= bitWidth(a0.tpe) =>
+ error("Verilog emitter does not support SHIFT_RIGHT >= arg width")
+ case Shr if c0 == (bitWidth(a0.tpe) - 1) => Seq(parenthesize(a0, true), "[", bitWidth(a0.tpe) - 1, "]")
+ case Shr => Seq(parenthesize(a0, true), "[", bitWidth(a0.tpe) - 1, ":", c0, "]")
+ case Neg => Seq("-", cast(a0, true))
+ case Cvt =>
+ a0.tpe match {
+ case (_: UIntType) => Seq("{1'b0,", cast(a0), "}")
+ case (_: SIntType) => Seq(cast(a0))
+ }
+ case Not => Seq("~", parenthesize(a0, true))
+ case And => Seq(castAs(a0, true), " & ", castAs(a1))
+ case Or => Seq(castAs(a0, true), " | ", castAs(a1))
+ case Xor => Seq(castAs(a0, true), " ^ ", castAs(a1))
+ case Andr => Seq("&", cast(a0, true))
+ case Orr => Seq("|", cast(a0, true))
+ case Xorr => Seq("^", cast(a0, true))
+ case Cat => "{" +: (castCatArgs(a0, a1) :+ "}")
+ // If selecting zeroth bit and single-bit wire, just emit the wire
+ case Bits if c0 == 0 && c1 == 0 && bitWidth(a0.tpe) == BigInt(1) => Seq(a0)
+ case Bits if c0 == c1 => Seq(parenthesize(a0, true), "[", c0, "]")
+ case Bits => Seq(parenthesize(a0, true), "[", c0, ":", c1, "]")
+ // If selecting zeroth bit and single-bit wire, just emit the wire
+ case Head if c0 == 1 && bitWidth(a0.tpe) == BigInt(1) => Seq(a0)
+ case Head if c0 == 1 => Seq(parenthesize(a0, true), "[", bitWidth(a0.tpe) - 1, "]")
+ case Head =>
+ val msb = bitWidth(a0.tpe) - 1
+ val lsb = bitWidth(a0.tpe) - c0
+ Seq(parenthesize(a0, true), "[", msb, ":", lsb, "]")
+ case Tail if c0 == (bitWidth(a0.tpe) - 1) => Seq(parenthesize(a0, true), "[0]")
+ case Tail => Seq(parenthesize(a0, true), "[", bitWidth(a0.tpe) - c0 - 1, ":0]")
+ }
+ }
+
+ /**
+ * 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
+ * @return the render reference
+ */
+ def getRenderer(m: Module, moduleMap: Map[String, DefModule])(implicit writer: Writer): VerilogRender = {
+ new VerilogRender(m, moduleMap)(writer)
+ }
+
+ /**
+ * 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, "", new EmissionOptions(Seq.empty))(writer)
+ case m: Module => new VerilogRender(m, moduleMap)(writer)
+ }
+ }
+
+ def addFormalStatement(
+ formals: mutable.Map[Expression, ArrayBuffer[Seq[Any]]],
+ clk: Expression,
+ en: Expression,
+ stmt: Seq[Any],
+ info: Info,
+ msg: StringLit
+ ): Unit = {
+ throw EmitterException(
+ "Cannot emit verification statements in Verilog" +
+ "(2001). Use the SystemVerilog emitter instead."
+ )
+ }
+
+ /**
+ * Store Emission option per Target
+ * Guarantee only one emission option per Target
+ */
+ private[firrtl] class EmissionOptionMap[V <: EmissionOption](val df: V) {
+ private val m = collection.mutable.HashMap[ReferenceTarget, V]().withDefaultValue(df)
+ def +=(elem: (ReferenceTarget, V)): EmissionOptionMap.this.type = {
+ if (m.contains(elem._1))
+ throw EmitterException(s"Multiple EmissionOption for the target ${elem._1} (${m(elem._1)} ; ${elem._2})")
+ m += (elem)
+ this
+ }
+ def apply(key: ReferenceTarget): V = m.apply(key)
+ }
+
+ /** Provide API to retrieve EmissionOptions based on the provided [[AnnotationSeq]]
+ *
+ * @param annotations : AnnotationSeq to be searched for EmissionOptions
+ */
+ private[firrtl] class EmissionOptions(annotations: AnnotationSeq) {
+ // Private so that we can present an immutable API
+ private val memoryEmissionOption = new EmissionOptionMap[MemoryEmissionOption](MemoryEmissionOptionDefault)
+ private val registerEmissionOption = new EmissionOptionMap[RegisterEmissionOption](RegisterEmissionOptionDefault)
+ private val wireEmissionOption = new EmissionOptionMap[WireEmissionOption](WireEmissionOptionDefault)
+ private val portEmissionOption = new EmissionOptionMap[PortEmissionOption](PortEmissionOptionDefault)
+ private val nodeEmissionOption = new EmissionOptionMap[NodeEmissionOption](NodeEmissionOptionDefault)
+ private val connectEmissionOption = new EmissionOptionMap[ConnectEmissionOption](ConnectEmissionOptionDefault)
+
+ def getMemoryEmissionOption(target: ReferenceTarget): MemoryEmissionOption =
+ memoryEmissionOption(target)
+
+ def getRegisterEmissionOption(target: ReferenceTarget): RegisterEmissionOption =
+ registerEmissionOption(target)
+
+ def getWireEmissionOption(target: ReferenceTarget): WireEmissionOption =
+ wireEmissionOption(target)
+
+ def getPortEmissionOption(target: ReferenceTarget): PortEmissionOption =
+ portEmissionOption(target)
+
+ def getNodeEmissionOption(target: ReferenceTarget): NodeEmissionOption =
+ nodeEmissionOption(target)
+
+ def getConnectEmissionOption(target: ReferenceTarget): ConnectEmissionOption =
+ connectEmissionOption(target)
+
+ private val emissionAnnos = annotations.collect {
+ case m: SingleTargetAnnotation[ReferenceTarget] @unchecked with EmissionOption => m
+ }
+ // using multiple foreach instead of a single partial function as an Annotation can gather multiple EmissionOptions for simplicity
+ emissionAnnos.foreach {
+ case a: MemoryEmissionOption => memoryEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ emissionAnnos.foreach {
+ case a: RegisterEmissionOption => registerEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ emissionAnnos.foreach {
+ case a: WireEmissionOption => wireEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ emissionAnnos.foreach {
+ case a: PortEmissionOption => portEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ emissionAnnos.foreach {
+ case a: NodeEmissionOption => nodeEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ emissionAnnos.foreach {
+ case a: ConnectEmissionOption => connectEmissionOption += ((a.target, a))
+ case _ =>
+ }
+ }
+
+ /**
+ * 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 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(
+ description: Seq[Description],
+ portDescriptions: Map[String, Seq[Description]],
+ m: Module,
+ moduleMap: Map[String, DefModule],
+ circuitName: String,
+ emissionOptions: EmissionOptions
+ )(
+ implicit writer: Writer) {
+
+ def this(
+ m: Module,
+ moduleMap: Map[String, DefModule],
+ circuitName: String,
+ emissionOptions: EmissionOptions
+ )(
+ implicit writer: Writer
+ ) = {
+ this(Seq(), Map.empty, m, moduleMap, circuitName, emissionOptions)(writer)
+ }
+ def this(m: Module, moduleMap: Map[String, DefModule])(implicit writer: Writer) = {
+ this(Seq(), Map.empty, m, moduleMap, "", new EmissionOptions(Seq.empty))(writer)
+ }
+
+ val netlist = mutable.LinkedHashMap[WrappedExpression, InfoExpr]()
+ val namespace = Namespace(m)
+ namespace.newName("_RAND") // Start rand names at _RAND_0
+ def build_netlist(s: Statement): Unit = {
+ s.foreach(build_netlist)
+ s match {
+ case sx: Connect => netlist(sx.loc) = InfoExpr(sx.info, sx.expr)
+ case sx: IsInvalid => error("Should have removed these!")
+ // TODO Since only register update and memories use the netlist anymore, I think nodes are
+ // unnecessary
+ case sx: DefNode =>
+ val e = WRef(sx.name, sx.value.tpe, NodeKind, SourceFlow)
+ netlist(e) = InfoExpr(sx.info, sx.value)
+ case _ =>
+ }
+ }
+
+ val portdefs = ArrayBuffer[Seq[Any]]()
+ // maps ifdef guard to declaration blocks
+ val ifdefDeclares: mutable.Map[String, ArrayBuffer[Seq[Any]]] = mutable.Map().withDefault { key =>
+ val value = ArrayBuffer[Seq[Any]]()
+ ifdefDeclares(key) = value
+ value
+ }
+ val declares = ArrayBuffer[Seq[Any]]()
+ val instdeclares = ArrayBuffer[Seq[Any]]()
+ val assigns = ArrayBuffer[Seq[Any]]()
+ val attachSynAssigns = ArrayBuffer.empty[Seq[Any]]
+ val attachAliases = ArrayBuffer.empty[Seq[Any]]
+ // No (aka synchronous) always blocks, keyed by clock
+ val noResetAlwaysBlocks = mutable.LinkedHashMap[Expression, ArrayBuffer[Seq[Any]]]()
+ // One always block per async reset register, (Clock, Reset, Content)
+ // An alternative approach is to have one always block per combination of clock and async reset,
+ // but Formality doesn't allow more than 1 statement inside async reset always blocks
+ val asyncResetAlwaysBlocks = mutable.ArrayBuffer[(Expression, Expression, Seq[Any])]()
+ // Used to determine type of initvar for initializing memories
+ var maxMemSize: BigInt = BigInt(0)
+ // maps ifdef guard to initial blocks
+ val ifdefInitials: mutable.Map[String, ArrayBuffer[Seq[Any]]] = mutable.Map().withDefault { key =>
+ val value = ArrayBuffer[Seq[Any]]()
+ ifdefInitials(key) = value
+ value
+ }
+ val initials = ArrayBuffer[Seq[Any]]()
+ // In Verilog, async reset registers are expressed using always blocks of the form:
+ // always @(posedge clock or posedge reset) begin
+ // if (reset) ...
+ // There is a fundamental mismatch between this representation which treats async reset
+ // registers as edge-triggered when in reality they are level-triggered.
+ // When not randomized, there is no mismatch because the async reset transition at the start
+ // of simulation from X to 1 triggers the posedge block for async reset.
+ // When randomized, this can result in silicon-simulation mismatch when async reset is held high
+ // upon power on with no clock, then async reset is dropped before the clock starts. In this
+ // circumstance, the async reset register will be randomized in simulation instead of being
+ // reset. To fix this, we need extra initial block logic to reset async reset registers again
+ // post-randomize.
+ val asyncInitials = ArrayBuffer[Seq[Any]]()
+ // memories need to be initialized even when randomization is disabled
+ val memoryInitials = ArrayBuffer[Seq[Any]]()
+ val simulates = ArrayBuffer[Seq[Any]]()
+ val formals = mutable.LinkedHashMap[Expression, ArrayBuffer[Seq[Any]]]()
+
+ def bigIntToVLit(bi: BigInt): String =
+ if (bi.isValidInt) bi.toString else s"${bi.bitLength}'d$bi"
+
+ // declare vector type with no preset and optionally with an ifdef guard
+ private def declareVectorType(
+ b: String,
+ n: String,
+ tpe: Type,
+ size: BigInt,
+ info: Info,
+ ifdefOpt: Option[String]
+ ): Unit = {
+ val decl = Seq(b, " ", tpe, " ", n, " [0:", bigIntToVLit(size - 1), "];", info)
+ if (ifdefOpt.isDefined) {
+ ifdefDeclares(ifdefOpt.get) += decl
+ } else {
+ declares += decl
+ }
+ }
+
+ // original vector type declare without initial value
+ def declareVectorType(b: String, n: String, tpe: Type, size: BigInt, info: Info): Unit =
+ declareVectorType(b, n, tpe, size, info, None)
+
+ // declare vector type with initial value
+ def declareVectorType(b: String, n: String, tpe: Type, size: BigInt, info: Info, preset: Expression): Unit = {
+ declares += Seq(b, " ", tpe, " ", n, " [0:", bigIntToVLit(size - 1), "] = ", preset, ";", info)
+ }
+
+ val moduleTarget = CircuitTarget(circuitName).module(m.name)
+
+ // declare with initial value
+ def declare(b: String, n: String, t: Type, info: Info, preset: Expression) = t match {
+ case tx: VectorType =>
+ declareVectorType(b, n, tx.tpe, tx.size, info, preset)
+ case tx =>
+ declares += Seq(b, " ", tx, " ", n, " = ", preset, ";", info)
+ }
+
+ // original declare without initial value and optinally with an ifdef guard
+ private def declare(b: String, n: String, t: Type, info: Info, ifdefOpt: Option[String]): Unit = t match {
+ case tx: VectorType =>
+ declareVectorType(b, n, tx.tpe, tx.size, info, ifdefOpt)
+ case tx =>
+ val decl = Seq(b, " ", tx, " ", n, ";", info)
+ if (ifdefOpt.isDefined) {
+ ifdefDeclares(ifdefOpt.get) += decl
+ } else {
+ declares += decl
+ }
+ }
+
+ // original declare without initial value and with an ifdef guard
+ private def declare(b: String, n: String, t: Type, info: Info, ifdef: String): Unit =
+ declare(b, n, t, info, Some(ifdef))
+
+ // original declare without initial value
+ def declare(b: String, n: String, t: Type, info: Info): Unit =
+ declare(b, n, t, info, None)
+
+ def assign(e: Expression, infoExpr: InfoExpr): Unit =
+ assign(e, infoExpr.expr, infoExpr.info)
+
+ def assign(e: Expression, value: Expression, info: Info): Unit = {
+ assigns += Seq("assign ", e, " = ", value, ";", info)
+ }
+
+ // In simulation, assign garbage under a predicate
+ def garbageAssign(e: Expression, syn: Expression, garbageCond: Expression, info: Info) = {
+ assigns += Seq("`ifndef RANDOMIZE_GARBAGE_ASSIGN")
+ assigns += Seq("assign ", e, " = ", syn, ";", info)
+ assigns += Seq("`else")
+ assigns += Seq(
+ "assign ",
+ e,
+ " = ",
+ garbageCond,
+ " ? ",
+ rand_string(syn.tpe, "RANDOMIZE_GARBAGE_ASSIGN"),
+ " : ",
+ syn,
+ ";",
+ info
+ )
+ assigns += Seq("`endif // RANDOMIZE_GARBAGE_ASSIGN")
+ }
+
+ def invalidAssign(e: Expression) = {
+ assigns += Seq("`ifdef RANDOMIZE_INVALID_ASSIGN")
+ assigns += Seq("assign ", e, " = ", rand_string(e.tpe, "RANDOMIZE_INVALID_ASSIGN"), ";")
+ assigns += Seq("`endif // RANDOMIZE_INVALID_ASSIGN")
+ }
+
+ def regUpdate(r: Expression, clk: Expression, reset: Expression, init: Expression) = {
+ def addUpdate(info: Info, expr: Expression, tabs: Seq[String]): Seq[Seq[Any]] = expr match {
+ case m: Mux =>
+ if (m.tpe == ClockType) throw EmitterException("Cannot emit clock muxes directly")
+ if (m.tpe == AsyncResetType) throw EmitterException("Cannot emit async reset muxes directly")
+
+ val (eninfo, tinfo, finfo) = MultiInfo.demux(info)
+ lazy val _if = Seq(tabs, "if (", m.cond, ") begin", eninfo)
+ lazy val _else = Seq(tabs, "end else begin")
+ lazy val _ifNot = Seq(tabs, "if (!(", m.cond, ")) begin", eninfo)
+ lazy val _end = Seq(tabs, "end")
+ lazy val _true = addUpdate(tinfo, m.tval, tab +: tabs)
+ lazy val _false = addUpdate(finfo, m.fval, tab +: tabs)
+ lazy val _elseIfFalse = {
+ val _falsex = addUpdate(finfo, m.fval, tabs) // _false, but without an additional tab
+ Seq(tabs, "end else ", _falsex.head.tail) +: _falsex.tail
+ }
+
+ /* For a Mux assignment, there are five possibilities, with one subcase for asynchronous reset:
+ * 1. Both the true and false condition are self-assignments; do nothing
+ * 2. The true condition is a self-assignment; invert the false condition and use that only
+ * 3. The false condition is a self-assignment
+ * a) The reset is asynchronous; emit both 'if' and a trivial 'else' to avoid latches
+ * b) The reset is synchronous; skip the false condition
+ * 4. The false condition is a Mux; use the true condition and use 'else if' for the false condition
+ * 5. Default; use both the true and false conditions
+ */
+ (m.tval, m.fval) match {
+ case (t, f) if weq(t, r) && weq(f, r) => Nil
+ case (t, _) if weq(t, r) => _ifNot +: _false :+ _end
+ case (_, f) if weq(f, r) =>
+ m.cond.tpe match {
+ case AsyncResetType => (_if +: _true :+ _else) ++ _true :+ _end
+ case _ => _if +: _true :+ _end
+ }
+ case (_, _: Mux) => (_if +: _true) ++ _elseIfFalse
+ case _ => (_if +: _true :+ _else) ++ _false :+ _end
+ }
+ case e => Seq(Seq(tabs, r, " <= ", e, ";", info))
+ }
+ if (weq(init, r)) { // Synchronous Reset
+ val InfoExpr(info, e) = netlist(r)
+ noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= addUpdate(info, e, Seq.empty)
+ } else { // Asynchronous Reset
+ assert(reset.tpe == AsyncResetType, "Error! Synchronous reset should have been removed!")
+ val tv = init
+ val InfoExpr(finfo, fv) = netlist(r)
+ // TODO add register info argument and build a MultiInfo to pass
+ asyncResetAlwaysBlocks += (
+ (
+ clk,
+ reset,
+ addUpdate(NoInfo, Mux(reset, tv, fv, mux_type_and_widths(tv, fv)), Seq.empty)
+ )
+ )
+ }
+ }
+
+ def update(e: Expression, value: Expression, clk: Expression, en: Expression, info: Info) = {
+ val lines = noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]())
+ if (weq(en, one)) lines += Seq(e, " <= ", value, ";")
+ else {
+ lines += Seq("if(", en, ") begin")
+ lines += Seq(tab, e, " <= ", value, ";", info)
+ lines += Seq("end")
+ }
+ }
+
+ // Declares an intermediate wire to hold a large enough random number.
+ // Then, return the correct number of bits selected from the random value
+ def rand_string(t: Type, ifdefOpt: Option[String]): Seq[Any] = {
+ val nx = namespace.newName("_RAND")
+ val rand = VRandom(bitWidth(t))
+ val tx = SIntType(IntWidth(rand.realWidth))
+ declare("reg", nx, tx, NoInfo, ifdefOpt)
+ val initial = Seq(wref(nx, tx), " = ", VRandom(bitWidth(t)), ";")
+ if (ifdefOpt.isDefined) {
+ ifdefInitials(ifdefOpt.get) += initial
+ } else {
+ initials += initial
+ }
+ Seq(nx, "[", bitWidth(t) - 1, ":0]")
+ }
+
+ def rand_string(t: Type, ifdef: String): Seq[Any] = rand_string(t, Some(ifdef))
+
+ def rand_string(t: Type): Seq[Any] = rand_string(t, None)
+
+ def initialize(e: Expression, reset: Expression, init: Expression) = {
+ val randString = rand_string(e.tpe, "RANDOMIZE_REG_INIT")
+ ifdefInitials("RANDOMIZE_REG_INIT") += Seq(e, " = ", randString, ";")
+ reset.tpe match {
+ case AsyncResetType =>
+ asyncInitials += Seq("if (", reset, ") begin")
+ asyncInitials += Seq(tab, e, " = ", init, ";")
+ asyncInitials += Seq("end")
+ case _ => // do nothing
+ }
+ }
+
+ def initialize_mem(s: DefMemory, opt: MemoryEmissionOption): Unit = {
+ if (s.depth > maxMemSize) {
+ maxMemSize = s.depth
+ }
+
+ val dataWidth = bitWidth(s.dataType)
+ val maxDataValue = (BigInt(1) << dataWidth.toInt) - 1
+
+ def checkValueRange(value: BigInt, at: String): Unit = {
+ if (value < 0) throw EmitterException(s"Memory ${at} cannot be initialized with negative value: $value")
+ if (value > maxDataValue)
+ throw EmitterException(s"Memory ${at} cannot be initialized with value: $value. Too large (> $maxDataValue)!")
+ }
+
+ opt.initValue match {
+ case MemoryArrayInit(values) =>
+ if (values.length != s.depth)
+ throw EmitterException(
+ s"Memory ${s.name} of depth ${s.depth} cannot be initialized with an array of length ${values.length}!"
+ )
+ val memName = LowerTypes.loweredName(wref(s.name, s.dataType))
+ values.zipWithIndex.foreach {
+ case (value, addr) =>
+ checkValueRange(value, s"${s.name}[$addr]")
+ val access = s"$memName[${bigIntToVLit(addr)}]"
+ memoryInitials += Seq(access, " = ", bigIntToVLit(value), ";")
+ }
+ case MemoryScalarInit(value) =>
+ checkValueRange(value, s.name)
+ // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization
+ val index = wref("initvar", s.dataType)
+ memoryInitials += Seq("for (initvar = 0; initvar < ", bigIntToVLit(s.depth), "; initvar = initvar+1)")
+ memoryInitials += Seq(
+ tab,
+ WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow),
+ " = ",
+ bigIntToVLit(value),
+ ";"
+ )
+ case MemoryRandomInit =>
+ // note: s.dataType is the incorrect type for initvar, but it is ignored in the serialization
+ val index = wref("initvar", s.dataType)
+ val rstring = rand_string(s.dataType, "RANDOMIZE_MEM_INIT")
+ ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(
+ "for (initvar = 0; initvar < ",
+ bigIntToVLit(s.depth),
+ "; initvar = initvar+1)"
+ )
+ ifdefInitials("RANDOMIZE_MEM_INIT") += Seq(
+ tab,
+ WSubAccess(wref(s.name, s.dataType), index, s.dataType, SinkFlow),
+ " = ",
+ rstring,
+ ";"
+ )
+ }
+ }
+
+ def simulate(clk: Expression, en: Expression, s: Seq[Any], cond: Option[String], info: Info) = {
+ val lines = noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]())
+ lines += Seq("`ifndef SYNTHESIS")
+ if (cond.nonEmpty) {
+ lines += Seq(s"`ifdef ${cond.get}")
+ lines += Seq(tab, s"if (`${cond.get}) begin")
+ lines += Seq("`endif")
+ }
+ lines += Seq(tab, tab, "if (", en, ") begin")
+ lines += Seq(tab, tab, tab, s, info)
+ lines += Seq(tab, tab, "end")
+ if (cond.nonEmpty) {
+ lines += Seq(s"`ifdef ${cond.get}")
+ lines += Seq(tab, "end")
+ lines += Seq("`endif")
+ }
+ lines += Seq("`endif // SYNTHESIS")
+ }
+
+ def addFormal(clk: Expression, en: Expression, stmt: Seq[Any], info: Info, msg: StringLit): Unit = {
+ addFormalStatement(formals, clk, en, stmt, info, msg)
+ }
+
+ def formalStatement(op: Formal.Value, cond: Expression): Seq[Any] = {
+ Seq(op.toString, "(", cond, ");")
+ }
+
+ def stop(ret: Int): Seq[Any] = Seq(if (ret == 0) "$finish;" else "$fatal;")
+
+ def printf(str: StringLit, args: Seq[Expression]): Seq[Any] = {
+ val strx = str.verilogEscape +: args.flatMap(Seq(",", _))
+ 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)))
+ }
+ }
+
+ def build_attribute(attrs: String): Seq[Seq[String]] = {
+ Seq(Seq("(* ") ++ Seq(attrs) ++ Seq(" *)"))
+ }
+
+ // Turn ports into Seq[String] and add to portdefs
+ def build_ports(): Unit = {
+ def padToMax(strs: Seq[String]): Seq[String] = {
+ val len = if (strs.nonEmpty) strs.map(_.length).max else 0
+ strs.map(_.padTo(len, ' '))
+ }
+
+ // Turn directions into strings (and AnalogType into inout)
+ val dirs = m.ports.map {
+ case Port(_, name, dir, tpe) =>
+ (dir, tpe) match {
+ case (_, AnalogType(_)) => "inout " // padded to length of output
+ case (Input, _) => "input "
+ case (Output, _) => "output"
+ }
+ }
+ // Turn types into strings, all ports must be GroundTypes
+ val tpes = m.ports.map {
+ case Port(_, _, _, tpe: GroundType) => stringify(tpe)
+ case port: Port => error(s"Trying to emit non-GroundType Port $port")
+ }
+
+ // dirs are already padded
+ (dirs, padToMax(tpes), m.ports).zipped.toSeq.zipWithIndex.foreach {
+ case ((dir, tpe, Port(info, name, _, _)), i) =>
+ portDescriptions.get(name).map {
+ case d =>
+ portdefs += Seq("")
+ portdefs ++= build_description(d)
+ }
+
+ if (i != m.ports.size - 1) {
+ portdefs += Seq(dir, " ", tpe, " ", name, ",", info)
+ } else {
+ portdefs += Seq(dir, " ", tpe, " ", name, info)
+ }
+ }
+ }
+
+ def build_description(d: Seq[Description]): Seq[Seq[String]] = d.flatMap {
+ case DocString(desc) => build_comment(desc.string)
+ case Attribute(attr) => build_attribute(attr.string)
+ }
+
+ def build_streams(s: Statement): Unit = {
+ val withoutDescription = s match {
+ case DescribedStmt(d, stmt) =>
+ stmt match {
+ case sx: IsDeclaration =>
+ declares ++= build_description(d)
+ case _ =>
+ }
+ stmt
+ case stmt => stmt
+ }
+ withoutDescription.foreach(build_streams)
+ withoutDescription match {
+ case sx @ Connect(info, loc @ WRef(_, _, PortKind | WireKind | InstanceKind, _), expr) =>
+ assign(loc, expr, info)
+ case sx: DefWire =>
+ declare("wire", sx.name, sx.tpe, sx.info)
+ case sx: DefRegister =>
+ val options = emissionOptions.getRegisterEmissionOption(moduleTarget.ref(sx.name))
+ val e = wref(sx.name, sx.tpe)
+ if (options.useInitAsPreset) {
+ declare("reg", sx.name, sx.tpe, sx.info, sx.init)
+ regUpdate(e, sx.clock, sx.reset, e)
+ } else {
+ declare("reg", sx.name, sx.tpe, sx.info)
+ regUpdate(e, sx.clock, sx.reset, sx.init)
+ }
+ if (!options.disableRandomization)
+ initialize(e, sx.reset, sx.init)
+ case sx: DefNode =>
+ declare("wire", sx.name, sx.value.tpe, sx.info, sx.value)
+ case sx: Stop =>
+ simulate(sx.clk, sx.en, stop(sx.ret), Some("STOP_COND"), sx.info)
+ case sx: Print =>
+ simulate(sx.clk, sx.en, printf(sx.string, sx.args), Some("PRINTF_COND"), sx.info)
+ case sx: Verification =>
+ addFormal(sx.clk, sx.en, formalStatement(sx.op, sx.pred), sx.info, sx.msg)
+ // 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)
+ 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(");")
+ case sx: DefMemory =>
+ val options = emissionOptions.getMemoryEmissionOption(moduleTarget.ref(sx.name))
+ val fullSize = sx.depth * (sx.dataType match {
+ case GroundType(IntWidth(width)) => width
+ })
+ val decl = if (fullSize > (1 << 29)) "reg /* sparse */" else "reg"
+ declareVectorType(decl, sx.name, sx.dataType, sx.depth, sx.info)
+ initialize_mem(sx, options)
+ 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")
+ // Ports should share an always@posedge, so can't have intermediary wire
+
+ 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))
+ // assign(en, netlist(en)) //;Connects value to m.r.en
+ val mem = WRef(sx.name, memType(sx), MemKind, UnknownFlow)
+ val memPort = WSubAccess(mem, addr, sx.dataType, UnknownFlow)
+ val depthValue = UIntLiteral(sx.depth, IntWidth(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
+ // TODO should we use the info here for anything?
+ val InfoExpr(_, 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
+ assign(data, netlist(data))
+ assign(addr, netlist(addr))
+ assign(mask, netlist(mask))
+ assign(en, netlist(en))
+
+ val mem = WRef(sx.name, memType(sx), MemKind, UnknownFlow)
+ val memPort = WSubAccess(mem, addr, sx.dataType, UnknownFlow)
+ 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"
+ )
+ case _ =>
+ }
+ }
+
+ def emit_streams(): Unit = {
+ build_description(description).foreach(emit(_))
+ emit(Seq("module ", m.name, "(", m.info))
+ for (x <- portdefs) emit(Seq(tab, x))
+ emit(Seq(");"))
+
+ ifdefDeclares.toSeq.sortWith(_._1 < _._1).foreach {
+ case (ifdef, declares) =>
+ emit(Seq("`ifdef " + ifdef))
+ for (x <- declares) emit(Seq(tab, x))
+ emit(Seq("`endif // " + ifdef))
+ }
+ for (x <- declares) emit(Seq(tab, x))
+ for (x <- instdeclares) emit(Seq(tab, x))
+ for (x <- assigns) emit(Seq(tab, x))
+ if (attachAliases.nonEmpty) {
+ emit(Seq("`ifdef SYNTHESIS"))
+ for (x <- attachSynAssigns) emit(Seq(tab, x))
+ emit(Seq("`elsif verilator"))
+ emit(
+ Seq(
+ tab,
+ "`error \"Verilator does not support alias and thus cannot arbirarily connect bidirectional wires and ports\""
+ )
+ )
+ emit(Seq("`else"))
+ for (x <- attachAliases) emit(Seq(tab, x))
+ emit(Seq("`endif"))
+ }
+
+ for ((clk, content) <- noResetAlwaysBlocks if content.nonEmpty) {
+ emit(Seq(tab, "always @(posedge ", clk, ") begin"))
+ for (line <- content) emit(Seq(tab, tab, line))
+ emit(Seq(tab, "end"))
+ }
+
+ for ((clk, reset, content) <- asyncResetAlwaysBlocks if content.nonEmpty) {
+ emit(Seq(tab, "always @(posedge ", clk, " or posedge ", reset, ") begin"))
+ for (line <- content) emit(Seq(tab, tab, line))
+ emit(Seq(tab, "end"))
+ }
+
+ if (initials.nonEmpty || ifdefInitials.nonEmpty || memoryInitials.nonEmpty) {
+ emit(Seq("// Register and memory initialization"))
+ emit(Seq("`ifdef RANDOMIZE_GARBAGE_ASSIGN"))
+ emit(Seq("`define RANDOMIZE"))
+ emit(Seq("`endif"))
+ emit(Seq("`ifdef RANDOMIZE_INVALID_ASSIGN"))
+ emit(Seq("`define RANDOMIZE"))
+ emit(Seq("`endif"))
+ emit(Seq("`ifdef RANDOMIZE_REG_INIT"))
+ emit(Seq("`define RANDOMIZE"))
+ emit(Seq("`endif"))
+ emit(Seq("`ifdef RANDOMIZE_MEM_INIT"))
+ emit(Seq("`define RANDOMIZE"))
+ emit(Seq("`endif"))
+ emit(Seq("`ifndef RANDOM"))
+ emit(Seq("`define RANDOM $random"))
+ emit(Seq("`endif"))
+ // the initvar is also used to initialize memories to constants
+ if (memoryInitials.isEmpty) emit(Seq("`ifdef RANDOMIZE_MEM_INIT"))
+ // Since simulators don't actually support memories larger than 2^31 - 1, there is no reason
+ // to change Verilog emission in the common case. Instead, we only emit a larger initvar
+ // where necessary
+ if (maxMemSize.isValidInt) {
+ emit(Seq(" integer initvar;"))
+ } else {
+ // Width must be able to represent maxMemSize because that's the upper bound in init loop
+ val width = maxMemSize.bitLength - 1 // minus one because [width-1:0] has a width of "width"
+ emit(Seq(s" reg [$width:0] initvar;"))
+ }
+ if (memoryInitials.isEmpty) emit(Seq("`endif"))
+ emit(Seq("`ifndef SYNTHESIS"))
+ // User-defined macro of code to run before an initial block
+ emit(Seq("`ifdef FIRRTL_BEFORE_INITIAL"))
+ emit(Seq("`FIRRTL_BEFORE_INITIAL"))
+ emit(Seq("`endif"))
+ emit(Seq("initial begin"))
+ emit(Seq(" `ifdef RANDOMIZE"))
+ emit(Seq(" `ifdef INIT_RANDOM"))
+ emit(Seq(" `INIT_RANDOM"))
+ emit(Seq(" `endif"))
+ // This enables testbenches to seed the random values at some time
+ // before `RANDOMIZE_DELAY (or the legacy value 0.002 if
+ // `RANDOMIZE_DELAY is not defined).
+ // Verilator does not support delay statements, so they are omitted.
+ emit(Seq(" `ifndef VERILATOR"))
+ emit(Seq(" `ifdef RANDOMIZE_DELAY"))
+ emit(Seq(" #`RANDOMIZE_DELAY begin end"))
+ emit(Seq(" `else"))
+ emit(Seq(" #0.002 begin end"))
+ emit(Seq(" `endif"))
+ emit(Seq(" `endif"))
+ ifdefInitials.toSeq.sortWith(_._1 < _._1).foreach {
+ case (ifdef, initials) =>
+ emit(Seq("`ifdef " + ifdef))
+ for (x <- initials) emit(Seq(tab, x))
+ emit(Seq("`endif // " + ifdef))
+ }
+ for (x <- initials) emit(Seq(tab, x))
+ for (x <- asyncInitials) emit(Seq(tab, x))
+ emit(Seq(" `endif // RANDOMIZE"))
+ for (x <- memoryInitials) emit(Seq(tab, x))
+ emit(Seq("end // initial"))
+ // User-defined macro of code to run after an initial block
+ emit(Seq("`ifdef FIRRTL_AFTER_INITIAL"))
+ emit(Seq("`FIRRTL_AFTER_INITIAL"))
+ emit(Seq("`endif"))
+ emit(Seq("`endif // SYNTHESIS"))
+ }
+
+ if (formals.keys.nonEmpty) {
+ for ((clk, content) <- formals if content.nonEmpty) {
+ emit(Seq(tab, "always @(posedge ", clk, ") begin"))
+ for (line <- content) emit(Seq(tab, tab, line))
+ emit(Seq(tab, "end"))
+ }
+ }
+
+ emit(Seq("endmodule"))
+ }
+
+ /**
+ * The standard verilog emitter, wraps up everything into the
+ * verilog
+ * @return
+ */
+ def emit_verilog(): DefModule = {
+
+ build_netlist(m.body)
+ build_ports()
+ build_streams(m.body)
+ emit_streams()
+ m
+ }
+
+ /**
+ * This emits a verilog module that can be bound to a module defined in chisel.
+ * It uses the same machinery as the general emitter in order to insure that
+ * parameters signature is exactly the same as the module being bound to
+ * @param overrideName Override the module name
+ * @param body the body of the bind module
+ * @return A module constructed from the body
+ */
+ def emitVerilogBind(overrideName: String, body: String): DefModule = {
+ build_netlist(m.body)
+ build_ports()
+
+ build_description(description).foreach(emit(_))
+
+ emit(Seq("module ", overrideName, "(", m.info))
+ for (x <- portdefs) emit(Seq(tab, x))
+
+ emit(Seq(");"))
+ emit(body)
+ emit(Seq("endmodule"), top = 0)
+ m
+ }
+ }
+
+ /** Preamble for every emitted Verilog file */
+ def transforms = new TransformManager(firrtl.stage.Forms.VerilogOptimized, prerequisites).flattenedTransformOrder
+
+ def emit(state: CircuitState, writer: Writer): Unit = {
+ val cs = runTransforms(state)
+ val emissionOptions = new EmissionOptions(cs.annotations)
+ val moduleMap = cs.circuit.modules.map(m => m.name -> m).toMap
+ cs.circuit.modules.foreach {
+ case dm @ DescribedMod(d, pds, m: Module) =>
+ val renderer = new VerilogRender(d, pds, m, moduleMap, cs.circuit.main, emissionOptions)(writer)
+ renderer.emit_verilog()
+ case m: Module =>
+ val renderer = new VerilogRender(m, moduleMap, cs.circuit.main, emissionOptions)(writer)
+ renderer.emit_verilog()
+ case _ => // do nothing
+ }
+ }
+
+ override def execute(state: CircuitState): CircuitState = {
+ val writerToString =
+ (writer: java.io.StringWriter) => writer.toString.replaceAll("""(?m) +$""", "") // trim trailing whitespace
+
+ val newAnnos = state.annotations.flatMap {
+ case EmitCircuitAnnotation(a) if this.getClass == a =>
+ val writer = new java.io.StringWriter
+ emit(state, writer)
+ Seq(
+ EmittedVerilogCircuitAnnotation(
+ EmittedVerilogCircuit(state.circuit.main, writerToString(writer), outputSuffix)
+ )
+ )
+
+ case EmitAllModulesAnnotation(a) if this.getClass == a =>
+ val cs = runTransforms(state)
+ val emissionOptions = new EmissionOptions(cs.annotations)
+ val moduleMap = cs.circuit.modules.map(m => m.name -> m).toMap
+
+ cs.circuit.modules.flatMap {
+ case dm @ DescribedMod(d, pds, module: Module) =>
+ val writer = new java.io.StringWriter
+ val renderer = new VerilogRender(d, pds, module, moduleMap, cs.circuit.main, emissionOptions)(writer)
+ renderer.emit_verilog()
+ Some(
+ EmittedVerilogModuleAnnotation(EmittedVerilogModule(module.name, writerToString(writer), outputSuffix))
+ )
+ case module: Module =>
+ val writer = new java.io.StringWriter
+ val renderer = new VerilogRender(module, moduleMap, cs.circuit.main, emissionOptions)(writer)
+ renderer.emit_verilog()
+ Some(
+ EmittedVerilogModuleAnnotation(EmittedVerilogModule(module.name, writerToString(writer), outputSuffix))
+ )
+ case _ => None
+ }
+ case _ => Seq()
+ }
+ state.copy(annotations = newAnnos ++ state.annotations)
+ }
+}
+
+case class VRandom(width: BigInt) extends Expression {
+ def tpe = UIntType(IntWidth(width))
+ def nWords = (width + 31) / 32
+ def realWidth = nWords * 32
+ override def serialize: String = "RANDOM"
+ def mapExpr(f: Expression => Expression): Expression = this
+ def mapType(f: Type => Type): Expression = this
+ def mapWidth(f: Width => Width): Expression = this
+ def foreachExpr(f: Expression => Unit): Unit = ()
+ def foreachType(f: Type => Unit): Unit = ()
+ def foreachWidth(f: Width => Unit): Unit = ()
+}