diff options
| author | Jiuyang Liu | 2020-11-10 00:12:06 +0000 |
|---|---|---|
| committer | GitHub | 2020-11-10 00:12:06 +0000 |
| commit | 92af63c599fc480f6480ee22f23763f54881085f (patch) | |
| tree | b4fde701d44a0d5a0d44d3a05489a61481762a7f | |
| parent | fe95544d573fff9bb114b3302986aa746e1f4763 (diff) | |
Refactor emiter (#1879)
* split big Emitter to submodules.
* fix all deprecated warning.
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
18 files changed, 1493 insertions, 1468 deletions
diff --git a/src/main/scala/firrtl/Compiler.scala b/src/main/scala/firrtl/Compiler.scala index fc852208..b4629a2a 100644 --- a/src/main/scala/firrtl/Compiler.scala +++ b/src/main/scala/firrtl/Compiler.scala @@ -8,7 +8,6 @@ import java.io.Writer import scala.collection.mutable import scala.util.Try import scala.util.control.NonFatal - import firrtl.annotations._ import firrtl.ir.Circuit import firrtl.Utils.throwInternalError @@ -420,7 +419,7 @@ trait Emitter extends Transform { override def invalidates(a: Transform) = false - @deprecated("Use emission annotations instead", "firrtl 1.0") + @deprecated("Use emission annotations instead", "FIRRTL 1.0") def emit(state: CircuitState, writer: Writer): Unit /** An output suffix to use if the output of this [[Emitter]] was written to a file */ diff --git a/src/main/scala/firrtl/Driver.scala b/src/main/scala/firrtl/Driver.scala index 52ea122e..03683fda 100644 --- a/src/main/scala/firrtl/Driver.scala +++ b/src/main/scala/firrtl/Driver.scala @@ -34,14 +34,14 @@ import firrtl.options.phases.DeletedWrapper * @see firrtlTests/DriverSpec.scala in the test directory for a lot more examples * @see [[CompilerUtils.mergeTransforms]] to see how customTransformations are inserted */ -@deprecated("Use firrtl.stage.FirrtlStage", "1.2") +@deprecated("Use firrtl.stage.FirrtlStage", "FIRRTL 1.2") object Driver { /** Print a warning message * * @param message error message */ - @deprecated("Use firrtl.options.StageUtils.dramaticWarning", "1.2") + @deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2") def dramaticWarning(message: String): Unit = StageUtils.dramaticWarning(message) /** @@ -49,14 +49,14 @@ object Driver { * * @param message error message */ - @deprecated("Use firrtl.options.StageUtils.dramaticWarning", "1.2") + @deprecated("Use firrtl.options.StageUtils.dramaticWarning", "FIRRTL 1.2") def dramaticError(message: String): Unit = StageUtils.dramaticError(message) /** Load annotation file based on options * @param optionsManager use optionsManager config to load annotation file if it exists * update the firrtlOptions with new annotations if it does */ - @deprecated("Use side-effect free getAnnotation instead", "1.1") + @deprecated("Use side-effect free getAnnotation instead", "FIRRTL 1.1") def loadAnnotations(optionsManager: ExecutionOptionsManager with HasFirrtlOptions): Unit = { val msg = "Driver.loadAnnotations is deprecated, use Driver.getAnnotations instead" Driver.dramaticWarning(msg) diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index c0be071e..7fdf0bfc 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -2,26 +2,14 @@ package firrtl -import java.io.Writer +import java.io.File -import scala.collection.mutable -import firrtl.ir._ -import firrtl.passes._ -import firrtl.transforms.FixAddingNegativeLiterals -import firrtl.annotations._ -import firrtl.traversals.Foreachers._ -import firrtl.PrimOps._ -import firrtl.WrappedExpression._ -import Utils._ -import MemPortUtils.{memPortField, memType} +import firrtl.annotations.NoTargetAnnotation import firrtl.backends.experimental.smt.{Btor2Emitter, SMTLibEmitter} -import firrtl.options.{CustomFileEmission, HasShellOptions, PhaseException, ShellOption} import firrtl.options.Viewer.view -import firrtl.stage.{FirrtlFileAnnotation, FirrtlOptions, RunFirrtlTransformAnnotation, TransformManager} -// Datastructures -import scala.collection.mutable.ArrayBuffer - -import java.io.File +import firrtl.options.{CustomFileEmission, HasShellOptions, PhaseException, ShellOption} +import firrtl.passes.PassException +import firrtl.stage.{FirrtlFileAnnotation, FirrtlOptions, RunFirrtlTransformAnnotation} case class EmitterException(message: String) extends PassException(message) @@ -29,11 +17,12 @@ case class EmitterException(message: String) extends PassException(message) sealed trait EmitAnnotation extends NoTargetAnnotation { val emitter: Class[_ <: Emitter] } + case class EmitCircuitAnnotation(emitter: Class[_ <: Emitter]) extends EmitAnnotation + case class EmitAllModulesAnnotation(emitter: Class[_ <: Emitter]) extends EmitAnnotation object EmitCircuitAnnotation extends HasShellOptions { - val options = Seq( new ShellOption[String]( longOption = "emit-circuit", @@ -69,7 +58,6 @@ object EmitCircuitAnnotation extends HasShellOptions { helpValueName = Some("<chirrtl|high|middle|low|verilog|mverilog|sverilog>") ) ) - } object EmitAllModulesAnnotation extends HasShellOptions { @@ -112,19 +100,19 @@ object EmitAllModulesAnnotation extends HasShellOptions { // ***** Annotations for results of emission ***** sealed abstract class EmittedComponent { - def name: String - def value: String + def name: String + + def value: String + def outputSuffix: String } + sealed abstract class EmittedCircuit extends EmittedComponent -final case class EmittedFirrtlCircuit(name: String, value: String, outputSuffix: String) extends EmittedCircuit -final case class EmittedVerilogCircuit(name: String, value: String, outputSuffix: String) extends EmittedCircuit + sealed abstract class EmittedModule extends EmittedComponent -final case class EmittedFirrtlModule(name: String, value: String, outputSuffix: String) extends EmittedModule -final case class EmittedVerilogModule(name: String, value: String, outputSuffix: String) extends EmittedModule /** Traits for Annotations containing emitted components */ -sealed trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation with CustomFileEmission { +trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation with CustomFileEmission { val value: T override protected def baseFileName(annotations: AnnotationSeq): String = { @@ -132,1425 +120,30 @@ sealed trait EmittedAnnotation[T <: EmittedComponent] extends NoTargetAnnotation } override protected val suffix: Option[String] = Some(value.outputSuffix) - } -sealed trait EmittedCircuitAnnotation[T <: EmittedCircuit] extends EmittedAnnotation[T] { +sealed trait EmittedCircuitAnnotation[T <: EmittedCircuit] extends EmittedAnnotation[T] { override def getBytes = value.value.getBytes - } -sealed trait EmittedModuleAnnotation[T <: EmittedModule] extends EmittedAnnotation[T] { +sealed trait EmittedModuleAnnotation[T <: EmittedModule] extends EmittedAnnotation[T] { override def getBytes = value.value.getBytes - } +case class EmittedFirrtlModuleAnnotation(value: EmittedFirrtlModule) + extends EmittedModuleAnnotation[EmittedFirrtlModule] case class EmittedFirrtlCircuitAnnotation(value: EmittedFirrtlCircuit) extends EmittedCircuitAnnotation[EmittedFirrtlCircuit] { override def replacements(file: File): AnnotationSeq = Seq(FirrtlFileAnnotation(file.toString)) - } + +final case class EmittedFirrtlCircuit(name: String, value: String, outputSuffix: String) extends EmittedCircuit +final case class EmittedFirrtlModule(name: String, value: String, outputSuffix: String) extends EmittedModule + +final case class EmittedVerilogCircuit(name: String, value: String, outputSuffix: String) extends EmittedCircuit +final case class EmittedVerilogModule(name: String, value: String, outputSuffix: String) extends EmittedModule case class EmittedVerilogCircuitAnnotation(value: EmittedVerilogCircuit) extends EmittedCircuitAnnotation[EmittedVerilogCircuit] -case class EmittedFirrtlModuleAnnotation(value: EmittedFirrtlModule) - extends EmittedModuleAnnotation[EmittedFirrtlModule] case class EmittedVerilogModuleAnnotation(value: EmittedVerilogModule) extends EmittedModuleAnnotation[EmittedVerilogModule] - -sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Emitter { - def inputForm = form - def outputForm = form - - val outputSuffix: String = form.outputSuffix - - private def emitAllModules(circuit: Circuit): Seq[EmittedFirrtlModule] = { - // For a given module, returns a Seq of all modules instantited inside of it - def collectInstantiatedModules(mod: Module, map: Map[String, DefModule]): Seq[DefModule] = { - // Use list instead of set to maintain order - val modules = mutable.ArrayBuffer.empty[DefModule] - def onStmt(stmt: Statement): Unit = stmt match { - case DefInstance(_, _, name, _) => modules += map(name) - case WDefInstance(_, _, name, _) => modules += map(name) - case _: WDefInstanceConnector => throwInternalError(s"unrecognized statement: $stmt") - case other => other.foreach(onStmt) - } - onStmt(mod.body) - modules.distinct.toSeq - } - val modMap = circuit.modules.map(m => m.name -> m).toMap - // Turn each module into it's own circuit with it as the top and all instantied modules as ExtModules - circuit.modules.collect { - case m: Module => - val instModules = collectInstantiatedModules(m, modMap) - val extModules = instModules.map { - case Module(info, name, ports, _) => ExtModule(info, name, ports, name, Seq.empty) - case ext: ExtModule => ext - } - val newCircuit = Circuit(m.info, extModules :+ m, m.name) - EmittedFirrtlModule(m.name, newCircuit.serialize, outputSuffix) - } - } - - override def execute(state: CircuitState): CircuitState = { - val newAnnos = state.annotations.flatMap { - case EmitCircuitAnnotation(a) if this.getClass == a => - Seq( - EmittedFirrtlCircuitAnnotation( - EmittedFirrtlCircuit(state.circuit.main, state.circuit.serialize, outputSuffix) - ) - ) - case EmitAllModulesAnnotation(a) if this.getClass == a => - emitAllModules(state.circuit).map(EmittedFirrtlModuleAnnotation(_)) - case _ => Seq() - } - state.copy(annotations = newAnnos ++ state.annotations) - } - - // Old style, deprecated - def emit(state: CircuitState, writer: Writer): Unit = writer.write(state.circuit.serialize) -} - -// ***** Start actual Emitters ***** -class ChirrtlEmitter extends FirrtlEmitter(ChirrtlForm) -class HighFirrtlEmitter extends FirrtlEmitter(HighForm) -class MiddleFirrtlEmitter extends FirrtlEmitter(MidForm) -class LowFirrtlEmitter extends FirrtlEmitter(LowForm) - -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 = () -} - -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 [[PrimOp.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) - } -} - -class MinimumVerilogEmitter extends VerilogEmitter with Emitter { - - override def prerequisites = firrtl.stage.Forms.AssertsRemoved ++ - firrtl.stage.Forms.LowFormMinimumOptimized - - override def transforms = - new TransformManager(firrtl.stage.Forms.VerilogMinimumOptimized, prerequisites).flattenedTransformOrder - -} - -class SystemVerilogEmitter extends VerilogEmitter { - override val outputSuffix: String = ".sv" - - override def prerequisites = firrtl.stage.Forms.LowFormOptimized - - override def addFormalStatement( - formals: mutable.Map[Expression, ArrayBuffer[Seq[Any]]], - clk: Expression, - en: Expression, - stmt: Seq[Any], - info: Info, - msg: StringLit - ): Unit = { - val lines = formals.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) - lines += Seq("// ", msg.serialize) - lines += Seq("if (", en, ") begin") - lines += Seq(tab, stmt, info) - lines += Seq("end") - } - - override def execute(state: CircuitState): CircuitState = { - super.execute(state) - } -} diff --git a/src/main/scala/firrtl/ExecutionOptionsManager.scala b/src/main/scala/firrtl/ExecutionOptionsManager.scala index 8602b984..b8c0dedd 100644 --- a/src/main/scala/firrtl/ExecutionOptionsManager.scala +++ b/src/main/scala/firrtl/ExecutionOptionsManager.scala @@ -32,10 +32,10 @@ import scala.collection.Seq * '''NOTE''' In all derived trait/classes, if you intend on maintaining backwards compatibility, * be sure to add new options at the end of the current ones and don't remove any existing ones. */ -@deprecated("Use firrtl.options.HasScoptOptions and/or library/transform registration", "1.2") +@deprecated("Use firrtl.options.HasScoptOptions and/or library/transform registration", "FIRRTL 1.2") trait ComposableOptions -@deprecated("Use firrtl.options.{ExecutionOptionsManager, TerminateOnExit, DuplicateHandling}", "1.2") +@deprecated("Use firrtl.options.{ExecutionOptionsManager, TerminateOnExit, DuplicateHandling}", "FIRRTL 1.2") abstract class HasParser(applicationName: String) { final val parser = new OptionParser[Unit](applicationName) { var terminateOnExit = true @@ -66,7 +66,7 @@ abstract class HasParser(applicationName: String) { * For example, in chisel, by deferring this it is possible for the execute there to first elaborate the * circuit and then set the topName from that if it has not already been set. */ -@deprecated("Use a FirrtlOptionsView, LoggerOptionsView, or construct your own view of an AnnotationSeq", "1.2") +@deprecated("Use a FirrtlOptionsView, LoggerOptionsView, or construct your own view of an AnnotationSeq", "FIRRTL 1.2") case class CommonOptions( topName: String = "", targetDirName: String = ".", @@ -96,7 +96,7 @@ case class CommonOptions( programArgs.map(a => ProgramArgsAnnotation(a)) } -@deprecated("Specify command line arguments in an Annotation mixing in HasScoptOptions", "1.2") +@deprecated("Specify command line arguments in an Annotation mixing in HasScoptOptions", "FIRRTL 1.2") trait HasCommonOptions { self: ExecutionOptionsManager => var commonOptions = CommonOptions() @@ -210,7 +210,7 @@ final case class OneFilePerModule(targetDir: String) extends OutputConfig * @param compilerName which compiler to use * @param annotations annotations to pass to compiler */ -@deprecated("Use a FirrtlOptionsView or construct your own view of an AnnotationSeq", "1.2") +@deprecated("Use a FirrtlOptionsView or construct your own view of an AnnotationSeq", "FIRRTL 1.2") case class FirrtlExecutionOptions( inputFileNameOverride: String = "", outputFileNameOverride: String = "", @@ -330,7 +330,7 @@ case class FirrtlExecutionOptions( * @param optionsManager this is needed to access build function and its common options * @return */ - @deprecated("Use FirrtlOptions.annotationFileNames instead", "1.1") + @deprecated("Use FirrtlOptions.annotationFileNames instead", "FIRRTL 1.1") def getAnnotationFileName(optionsManager: ExecutionOptionsManager): String = { optionsManager.getBuildFileName("anno", annotationFileNameOverride) } @@ -363,7 +363,7 @@ case class FirrtlExecutionOptions( } } -@deprecated("Specify command line arguments in an Annotation mixing in HasScoptOptions", "1.2") +@deprecated("Specify command line arguments in an Annotation mixing in HasScoptOptions", "FIRRTL 1.2") trait HasFirrtlOptions { self: ExecutionOptionsManager => var firrtlOptions = FirrtlExecutionOptions() @@ -589,10 +589,10 @@ trait HasFirrtlOptions { parser.note("") } -@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "1.2") +@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "FIRRTL 1.2") sealed trait FirrtlExecutionResult -@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "1.2") +@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "FIRRTL 1.2") object FirrtlExecutionSuccess { def apply( emitType: String, @@ -613,7 +613,7 @@ object FirrtlExecutionSuccess { * "sverilog" * @param emitted The emitted result of compilation */ -@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "1.2") +@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "FIRRTL 1.2") class FirrtlExecutionSuccess( val emitType: String, val emitted: String, @@ -625,13 +625,13 @@ class FirrtlExecutionSuccess( * * @param message Some kind of hint as to what went wrong. */ -@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "1.2") +@deprecated("Use FirrtlStage and examine the output AnnotationSeq directly", "FIRRTL 1.2") case class FirrtlExecutionFailure(message: String) extends FirrtlExecutionResult /** * @param applicationName The name shown in the usage */ -@deprecated("Use new FirrtlStage infrastructure", "1.2") +@deprecated("Use new FirrtlStage infrastructure", "FIRRTL 1.2") class ExecutionOptionsManager(val applicationName: String) extends HasParser(applicationName) with HasCommonOptions { def parse(args: Array[String]): Boolean = { diff --git a/src/main/scala/firrtl/FirrtlException.scala b/src/main/scala/firrtl/FirrtlException.scala index 91d5350e..685718cd 100644 --- a/src/main/scala/firrtl/FirrtlException.scala +++ b/src/main/scala/firrtl/FirrtlException.scala @@ -4,7 +4,7 @@ package firrtl import scala.util.control.NoStackTrace -@deprecated("External users should use either FirrtlUserException or their own hierarchy", "1.2") +@deprecated("External users should use either FirrtlUserException or their own hierarchy", "FIRRTL 1.2") object FIRRTLException { def defaultMessage(message: String, cause: Throwable) = { if (message != null) { @@ -16,7 +16,7 @@ object FIRRTLException { } } } -@deprecated("External users should use either FirrtlUserException or their own hierarchy", "1.2") +@deprecated("External users should use either FirrtlUserException or their own hierarchy", "FIRRTL 1.2") class FIRRTLException(val str: String, cause: Throwable = null) extends RuntimeException(FIRRTLException.defaultMessage(str, cause), cause) diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala index ac987456..a52e451f 100644 --- a/src/main/scala/firrtl/Utils.scala +++ b/src/main/scala/firrtl/Utils.scala @@ -307,7 +307,7 @@ object Utils extends LazyLogging { onExp(expression) ReferenceTarget(main, module, Nil, ref, tokens.toSeq) } - @deprecated("get_flip is fundamentally slow, use to_flip(flow(expr))", "1.2") + @deprecated("get_flip is fundamentally slow, use to_flip(flow(expr))", "FIRRTL 1.2") def get_flip(t: Type, i: Int, f: Orientation): Orientation = { if (i >= get_size(t)) throwInternalError(s"get_flip: shouldn't be here - $i >= get_size($t)") t match { diff --git a/src/main/scala/firrtl/backends/firrtl/FirrtlEmitter.scala b/src/main/scala/firrtl/backends/firrtl/FirrtlEmitter.scala new file mode 100644 index 00000000..26e03633 --- /dev/null +++ b/src/main/scala/firrtl/backends/firrtl/FirrtlEmitter.scala @@ -0,0 +1,67 @@ +package firrtl + +import java.io.Writer + +import firrtl.Utils._ +import firrtl.ir._ +import firrtl.traversals.Foreachers._ + +import scala.collection.mutable + +sealed abstract class FirrtlEmitter(form: CircuitForm) extends Transform with Emitter { + def inputForm = form + def outputForm = form + + val outputSuffix: String = form.outputSuffix + + private def emitAllModules(circuit: Circuit): Seq[EmittedFirrtlModule] = { + // For a given module, returns a Seq of all modules instantited inside of it + def collectInstantiatedModules(mod: Module, map: Map[String, DefModule]): Seq[DefModule] = { + // Use list instead of set to maintain order + val modules = mutable.ArrayBuffer.empty[DefModule] + def onStmt(stmt: Statement): Unit = stmt match { + case DefInstance(_, _, name, _) => modules += map(name) + case WDefInstance(_, _, name, _) => modules += map(name) + case _: WDefInstanceConnector => throwInternalError(s"unrecognized statement: $stmt") + case other => other.foreach(onStmt) + } + onStmt(mod.body) + modules.distinct.toSeq + } + val modMap = circuit.modules.map(m => m.name -> m).toMap + // Turn each module into it's own circuit with it as the top and all instantied modules as ExtModules + circuit.modules.collect { + case m: Module => + val instModules = collectInstantiatedModules(m, modMap) + val extModules = instModules.map { + case Module(info, name, ports, _) => ExtModule(info, name, ports, name, Seq.empty) + case ext: ExtModule => ext + } + val newCircuit = Circuit(m.info, extModules :+ m, m.name) + EmittedFirrtlModule(m.name, newCircuit.serialize, outputSuffix) + } + } + + override def execute(state: CircuitState): CircuitState = { + val newAnnos = state.annotations.flatMap { + case EmitCircuitAnnotation(a) if this.getClass == a => + Seq( + EmittedFirrtlCircuitAnnotation( + EmittedFirrtlCircuit(state.circuit.main, state.circuit.serialize, outputSuffix) + ) + ) + case EmitAllModulesAnnotation(a) if this.getClass == a => + emitAllModules(state.circuit).map(EmittedFirrtlModuleAnnotation(_)) + case _ => Seq() + } + state.copy(annotations = newAnnos ++ state.annotations) + } + + // Old style, deprecated + def emit(state: CircuitState, writer: Writer): Unit = writer.write(state.circuit.serialize) +} + +class ChirrtlEmitter extends FirrtlEmitter(ChirrtlForm) +class HighFirrtlEmitter extends FirrtlEmitter(HighForm) +class MiddleFirrtlEmitter extends FirrtlEmitter(MidForm) +class LowFirrtlEmitter extends FirrtlEmitter(LowForm) diff --git a/src/main/scala/firrtl/backends/verilog/MinimumVerilogEmitter.scala b/src/main/scala/firrtl/backends/verilog/MinimumVerilogEmitter.scala new file mode 100644 index 00000000..4d8bade6 --- /dev/null +++ b/src/main/scala/firrtl/backends/verilog/MinimumVerilogEmitter.scala @@ -0,0 +1,13 @@ +package firrtl + +import firrtl.stage.TransformManager + +class MinimumVerilogEmitter extends VerilogEmitter with Emitter { + + override def prerequisites = firrtl.stage.Forms.AssertsRemoved ++ + firrtl.stage.Forms.LowFormMinimumOptimized + + override def transforms = + new TransformManager(firrtl.stage.Forms.VerilogMinimumOptimized, prerequisites).flattenedTransformOrder + +} diff --git a/src/main/scala/firrtl/backends/verilog/SystemVerilogEmitter.scala b/src/main/scala/firrtl/backends/verilog/SystemVerilogEmitter.scala new file mode 100644 index 00000000..2d8e3089 --- /dev/null +++ b/src/main/scala/firrtl/backends/verilog/SystemVerilogEmitter.scala @@ -0,0 +1,31 @@ +package firrtl + +import firrtl.ir.{Expression, Info, StringLit} + +import scala.collection.mutable +import scala.collection.mutable.ArrayBuffer + +class SystemVerilogEmitter extends VerilogEmitter { + override val outputSuffix: String = ".sv" + + override def prerequisites = firrtl.stage.Forms.LowFormOptimized + + override def addFormalStatement( + formals: mutable.Map[Expression, ArrayBuffer[Seq[Any]]], + clk: Expression, + en: Expression, + stmt: Seq[Any], + info: Info, + msg: StringLit + ): Unit = { + val lines = formals.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) + lines += Seq("// ", msg.serialize) + lines += Seq("if (", en, ") begin") + lines += Seq(tab, stmt, info) + lines += Seq("end") + } + + override def execute(state: CircuitState): CircuitState = { + super.execute(state) + } +} 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 = () +} diff --git a/src/main/scala/firrtl/package.scala b/src/main/scala/firrtl/package.scala index 999b123d..adaddeda 100644 --- a/src/main/scala/firrtl/package.scala +++ b/src/main/scala/firrtl/package.scala @@ -10,10 +10,10 @@ package object firrtl { implicit def annoSeqToSeq(as: AnnotationSeq): Seq[Annotation] = as.underlying /* Options as annotations compatibility items */ - @deprecated("Use firrtl.stage.TargetDirAnnotation", "1.2") + @deprecated("Use firrtl.stage.TargetDirAnnotation", "FIRRTL 1.2") type TargetDirAnnotation = firrtl.options.TargetDirAnnotation - @deprecated("Use firrtl.stage.TargetDirAnnotation", "1.2") + @deprecated("Use firrtl.stage.TargetDirAnnotation", "FIRRTL 1.2") val TargetDirAnnotation = firrtl.options.TargetDirAnnotation type WRef = ir.Reference diff --git a/src/main/scala/firrtl/passes/InferTypes.scala b/src/main/scala/firrtl/passes/InferTypes.scala index bbc1ef6d..01f0b823 100644 --- a/src/main/scala/firrtl/passes/InferTypes.scala +++ b/src/main/scala/firrtl/passes/InferTypes.scala @@ -13,7 +13,7 @@ object InferTypes extends Pass { override def prerequisites = Dependency(ResolveKinds) +: firrtl.stage.Forms.WorkingIR override def invalidates(a: Transform) = false - @deprecated("This should never have been public", "1.3.2") + @deprecated("This should never have been public", "FIRRTL 1.3.2") type TypeMap = collection.mutable.LinkedHashMap[String, Type] private type TypeLookup = collection.mutable.HashMap[String, Type] @@ -102,7 +102,7 @@ object CInferTypes extends Pass { override def prerequisites = firrtl.stage.Forms.ChirrtlForm override def invalidates(a: Transform) = false - @deprecated("This should never have been public", "1.3.2") + @deprecated("This should never have been public", "FIRRTL 1.3.2") type TypeMap = collection.mutable.LinkedHashMap[String, Type] private type TypeLookup = collection.mutable.HashMap[String, Type] diff --git a/src/main/scala/firrtl/passes/wiring/WiringUtils.scala b/src/main/scala/firrtl/passes/wiring/WiringUtils.scala index 0b121cc0..d926f6a9 100644 --- a/src/main/scala/firrtl/passes/wiring/WiringUtils.scala +++ b/src/main/scala/firrtl/passes/wiring/WiringUtils.scala @@ -37,7 +37,7 @@ case class Modifications( /** A lineage tree representing the instance hierarchy in a design */ -@deprecated("Use DiGraph/InstanceGraph", "1.1.1") +@deprecated("Use DiGraph/InstanceGraph", "FIRRTL 1.1.1") case class Lineage( name: String, children: Seq[(String, Lineage)] = Seq.empty, @@ -80,13 +80,13 @@ case class Lineage( } object WiringUtils { - @deprecated("Use DiGraph/InstanceGraph", "1.1.1") + @deprecated("Use DiGraph/InstanceGraph", "FIRRTL 1.1.1") type ChildrenMap = mutable.HashMap[String, Seq[(String, String)]] /** Given a circuit, returns a map from module name to children * instance/module names */ - @deprecated("Use DiGraph/InstanceGraph", "1.1.1") + @deprecated("Use DiGraph/InstanceGraph", "FIRRTL 1.1.1") def getChildrenMap(c: Circuit): ChildrenMap = { val childrenMap = new ChildrenMap() def getChildren(mname: String)(s: Statement): Unit = s match { @@ -105,7 +105,7 @@ object WiringUtils { /** Returns a module's lineage, containing all children lineages as well */ - @deprecated("Use DiGraph/InstanceGraph", "1.1.1") + @deprecated("Use DiGraph/InstanceGraph", "FIRRTL 1.1.1") def getLineage(childrenMap: ChildrenMap, module: String): Lineage = Lineage(module, childrenMap(module).map { case (i, m) => (i, getLineage(childrenMap, m)) }) diff --git a/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala b/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala index 12f33183..d1c0a134 100644 --- a/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala +++ b/src/main/scala/firrtl/transforms/BlackBoxSourceHelper.scala @@ -182,7 +182,10 @@ object BlackBoxSourceHelper { val defaultFileListName = "firrtl_black_box_resource_files.f" - @deprecated("Renamed to defaultFileListName, as the file list name may now be changed with an annotation", "1.2") + @deprecated( + "Renamed to defaultFileListName, as the file list name may now be changed with an annotation", + "FIRRTL 1.2" + ) def fileListName = defaultFileListName def writeTextToFile(text: String, file: File): Unit = { diff --git a/src/main/scala/firrtl/transforms/InferResets.scala b/src/main/scala/firrtl/transforms/InferResets.scala index 0f5fb608..4c6ffde2 100644 --- a/src/main/scala/firrtl/transforms/InferResets.scala +++ b/src/main/scala/firrtl/transforms/InferResets.scala @@ -16,9 +16,9 @@ import scala.collection.mutable import scala.util.Try object InferResets { - @deprecated("This is no longer in use and will be removed", "1.3") + @deprecated("This is no longer in use and will be removed", "FIRRTL 1.3") final class DifferingDriverTypesException private (msg: String) extends PassException(msg) - @deprecated("This is no longer in use and will be removed", "1.3") + @deprecated("This is no longer in use and will be removed", "FIRRTL 1.3") object DifferingDriverTypesException { def apply(target: ReferenceTarget, tpes: Seq[(Type, Seq[TypeDriver])]): DifferingDriverTypesException = { val xs = tpes.map { case (t, ds) => s"${ds.map(_.target().serialize).mkString(", ")} of type ${t.serialize}" } diff --git a/src/main/scala/firrtl/util/BackendCompilationUtilities.scala b/src/main/scala/firrtl/util/BackendCompilationUtilities.scala index 8d2d8be9..7d5fec1d 100644 --- a/src/main/scala/firrtl/util/BackendCompilationUtilities.scala +++ b/src/main/scala/firrtl/util/BackendCompilationUtilities.scala @@ -247,7 +247,7 @@ object BackendCompilationUtilities extends LazyLogging { } } -@deprecated("use object BackendCompilationUtilities", "1.3") +@deprecated("use object BackendCompilationUtilities", "FIRRTL 1.3") trait BackendCompilationUtilities extends LazyLogging { lazy val TestDirectory = BackendCompilationUtilities.TestDirectory def timeStamp: String = BackendCompilationUtilities.timeStamp diff --git a/src/main/scala/logger/Logger.scala b/src/main/scala/logger/Logger.scala index fe4b8111..80c024fb 100644 --- a/src/main/scala/logger/Logger.scala +++ b/src/main/scala/logger/Logger.scala @@ -122,7 +122,7 @@ object Logger { * @tparam A The return type of codeBlock * @return Whatever block returns */ - @deprecated("Use makeScope(opts: FirrtlOptions)", "1.2") + @deprecated("Use makeScope(opts: FirrtlOptions)", "FIRRTL 1.2") def makeScope[A](manager: ExecutionOptionsManager)(codeBlock: => A): A = makeScope(manager.commonOptions.toAnnotations)(codeBlock) @@ -134,7 +134,7 @@ object Logger { * @tparam A return type of codeBlock * @return */ - @deprecated("Use makescope(opts: FirrtlOptions)", "1.2") + @deprecated("Use makescope(opts: FirrtlOptions)", "FIRRTL 1.2") def makeScope[A](args: Array[String] = Array.empty)(codeBlock: => A): A = { val executionOptionsManager = new ExecutionOptionsManager("logger") if (executionOptionsManager.parse(args)) { @@ -353,7 +353,7 @@ object Logger { * from the command line via an options manager * @param optionsManager manager */ - @deprecated("Use setOptions(annotations: AnnotationSeq)", "1.2") + @deprecated("Use setOptions(annotations: AnnotationSeq)", "FIRRTL 1.2") def setOptions(optionsManager: ExecutionOptionsManager): Unit = setOptions(optionsManager.commonOptions.toAnnotations) diff --git a/src/main/scala/logger/LoggerOptions.scala b/src/main/scala/logger/LoggerOptions.scala index c0b5d35c..683d6741 100644 --- a/src/main/scala/logger/LoggerOptions.scala +++ b/src/main/scala/logger/LoggerOptions.scala @@ -35,6 +35,6 @@ class LoggerOptions private[logger] ( def getLogFileName(): Option[String] = if (!logToFile) None else logFileName.orElse(Some("a.log")) /** True if a [[Logger]] should be writing to a file */ - @deprecated("logToFile was removed, use logFileName.nonEmpty", "1.2") + @deprecated("logToFile was removed, use logFileName.nonEmpty", "FIRRTL 1.2") def logToFile(): Boolean = logFileName.nonEmpty } |
