aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJack Koenig2020-01-07 19:34:29 -0800
committerGitHub2020-01-07 19:34:29 -0800
commitd5dd427c0267dc143d4297d5fd0716f19cd7634b (patch)
tree589e59ef4e2563ca67e695f476ed67a8f8ef5aa5 /src
parent66f354558a21cd0d339968b3665b44c17c2c16e8 (diff)
parente27bb38cf5b3ee8135bf416c2532b2abc2fc5ae4 (diff)
Merge pull request #1264 from freechipsproject/cleanup-verilog-emitter-casts
Cleanup verilog emitter casts
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Emitter.scala85
-rw-r--r--src/main/scala/firrtl/Utils.scala11
-rw-r--r--src/main/scala/firrtl/transforms/ConstantPropagation.scala31
-rw-r--r--src/main/scala/firrtl/transforms/InlineCasts.scala70
-rw-r--r--src/main/scala/firrtl/transforms/LegalizeClocks.scala69
-rw-r--r--src/test/scala/firrtlTests/AsyncResetSpec.scala6
-rw-r--r--src/test/scala/firrtlTests/ConstantPropagationTests.scala26
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala99
-rw-r--r--src/test/scala/firrtlTests/transforms/LegalizeClocks.scala67
9 files changed, 414 insertions, 50 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 95c762ae..2d98cf04 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -281,29 +281,35 @@ class VerilogEmitter extends SeqTransform with Emitter {
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 cast_if(e: Expression): Any = {
- doprim.args find (_.tpe match {
- case (_: SIntType) => true
- case (_) => false
- }) match {
- case None => e
- case Some(_) => e.tpe match {
- case (_: SIntType) => Seq("$signed(", e, ")")
- case (_: UIntType) => Seq("$signed({1'b0,", e, "})")
- case _ => throwInternalError(s"unrecognized type: $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): 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 {
+ e
}
}
def cast(e: Expression): Any = doprim.tpe match {
- case (t: UIntType) => e
- case (t: SIntType) => Seq("$signed(",e,")")
- case _ => throwInternalError(s"cast - unrecognized type: $e")
+ case _: UIntType => e
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected type for $e in $doprim")
}
- def cast_as(e: Expression): Any = e.tpe match {
- case (t: UIntType) => e
- case (t: SIntType) => Seq("$signed(",e,")")
- case _ => throwInternalError(s"cast_as - unrecognized type: $e")
+ def castAs(e: Expression): Any = e.tpe match {
+ case _: UIntType => e
+ 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)
@@ -313,12 +319,14 @@ class VerilogEmitter extends SeqTransform with Emitter {
def checkArgumentLegality(e: Expression): Unit = e match {
case _: UIntLiteral | _: SIntLiteral | _: WRef | _: WSubField =>
case DoPrim(Not, args, _,_) => args.foreach(checkArgumentLegality)
+ case DoPrim(op, args, _,_) if isCast(op) => args.foreach(checkArgumentLegality)
case _ => throw EmitterException(s"Can't emit ${e.getClass.getName} as PrimOp argument")
}
def checkCatArgumentLegality(e: Expression): Unit = e match {
case _: UIntLiteral | _: SIntLiteral | _: WRef | _: WSubField =>
case DoPrim(Not, args, _,_) => args.foreach(checkArgumentLegality)
+ case DoPrim(op, args, _,_) if isCast(op) => args.foreach(checkArgumentLegality)
case DoPrim(Cat, args, _, _) => args foreach(checkCatArgumentLegality)
case _ => throw EmitterException(s"Can't emit ${e.getClass.getName} as PrimOp argument")
}
@@ -337,22 +345,23 @@ class VerilogEmitter extends SeqTransform with Emitter {
doprim.op match {
case Cat => doprim.args foreach(checkCatArgumentLegality)
+ case cast if isCast(cast) => // Casts are allowed to wrap any Expression
case other => doprim.args foreach checkArgumentLegality
}
doprim.op match {
- case Add => Seq(cast_if(a0), " + ", cast_if(a1))
- case Addw => Seq(cast_if(a0), " + ", cast_if(a1))
- case Sub => Seq(cast_if(a0), " - ", cast_if(a1))
- case Subw => Seq(cast_if(a0), " - ", cast_if(a1))
- case Mul => Seq(cast_if(a0), " * ", cast_if(a1))
- case Div => Seq(cast_if(a0), " / ", cast_if(a1))
- case Rem => Seq(cast_if(a0), " % ", cast_if(a1))
- case Lt => Seq(cast_if(a0), " < ", cast_if(a1))
- case Leq => Seq(cast_if(a0), " <= ", cast_if(a1))
- case Gt => Seq(cast_if(a0), " > ", cast_if(a1))
- case Geq => Seq(cast_if(a0), " >= ", cast_if(a1))
- case Eq => Seq(cast_if(a0), " == ", cast_if(a1))
- case Neq => Seq(cast_if(a0), " != ", cast_if(a1))
+ case Add => Seq(castIf(a0), " + ", castIf(a1))
+ case Addw => Seq(castIf(a0), " + ", castIf(a1))
+ case Sub => Seq(castIf(a0), " - ", castIf(a1))
+ case Subw => Seq(castIf(a0), " - ", castIf(a1))
+ case Mul => Seq(castIf(a0), " * ", castIf(a1))
+ case Div => Seq(castIf(a0), " / ", castIf(a1))
+ case Rem => Seq(castIf(a0), " % ", castIf(a1))
+ case Lt => Seq(castIf(a0), " < ", castIf(a1))
+ case Leq => Seq(castIf(a0), " <= ", castIf(a1))
+ case Gt => Seq(castIf(a0), " > ", castIf(a1))
+ case Geq => Seq(castIf(a0), " >= ", castIf(a1))
+ case Eq => Seq(castIf(a0), " == ", castIf(a1))
+ case Neq => Seq(castIf(a0), " != ", castIf(a1))
case Pad =>
val w = bitWidth(a0.tpe)
val diff = c0 - w
@@ -364,10 +373,10 @@ class VerilogEmitter extends SeqTransform with Emitter {
case (_: SIntType) => Seq("{{", diff, "{", a0, "[", w - 1, "]}},", a0, "}")
case (_) => Seq("{{", diff, "'d0}, ", a0, "}")
}
- case AsUInt => Seq("$unsigned(", a0, ")")
- case AsSInt => Seq("$signed(", a0, ")")
- case AsClock => Seq(a0)
- case AsAsyncReset => Seq(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), " << ", a1)
case Dshl => Seq(cast(a0), " << ", a1)
case Dshr => doprim.tpe match {
@@ -384,9 +393,9 @@ class VerilogEmitter extends SeqTransform with Emitter {
case (_: SIntType) => Seq(cast(a0))
}
case Not => Seq("~", a0)
- case And => Seq(cast_as(a0), " & ", cast_as(a1))
- case Or => Seq(cast_as(a0), " | ", cast_as(a1))
- case Xor => Seq(cast_as(a0), " ^ ", cast_as(a1))
+ case And => Seq(castAs(a0), " & ", castAs(a1))
+ case Or => Seq(castAs(a0), " | ", castAs(a1))
+ case Xor => Seq(castAs(a0), " ^ ", castAs(a1))
case Andr => Seq("&", cast(a0))
case Orr => Seq("|", cast(a0))
case Xorr => Seq("^", cast(a0))
@@ -967,6 +976,8 @@ class VerilogEmitter extends SeqTransform with Emitter {
new BlackBoxSourceHelper,
new ReplaceTruncatingArithmetic,
new InlineNotsTransform,
+ new InlineCastsTransform,
+ new LegalizeClocksTransform,
new FlattenRegUpdate,
new DeadCodeElimination,
passes.VerilogModulusCleanup,
diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala
index 8a76aca6..6cb309b3 100644
--- a/src/main/scala/firrtl/Utils.scala
+++ b/src/main/scala/firrtl/Utils.scala
@@ -188,6 +188,17 @@ object Utils extends LazyLogging {
case sx => sx
}
+ /** Returns true if PrimOp is a cast, false otherwise */
+ def isCast(op: PrimOp): Boolean = op match {
+ case AsUInt | AsSInt | AsClock | AsAsyncReset | AsFixedPoint => true
+ case _ => false
+ }
+ /** Returns true if Expression is a casting PrimOp, false otherwise */
+ def isCast(expr: Expression): Boolean = expr match {
+ case DoPrim(op, _,_,_) if isCast(op) => true
+ case _ => false
+ }
+
/** Provide a nice name to create a temporary **/
def niceName(e: Expression): String = niceName(1)(e)
def niceName(depth: Int)(e: Expression): String = {
diff --git a/src/main/scala/firrtl/transforms/ConstantPropagation.scala b/src/main/scala/firrtl/transforms/ConstantPropagation.scala
index a008a4d3..20b24e60 100644
--- a/src/main/scala/firrtl/transforms/ConstantPropagation.scala
+++ b/src/main/scala/firrtl/transforms/ConstantPropagation.scala
@@ -286,16 +286,33 @@ class ConstantPropagation extends Transform with ResolvedAnnotationPaths {
case UIntLiteral(v, IntWidth(w)) => UIntLiteral(v ^ ((BigInt(1) << w.toInt) - 1), IntWidth(w))
case _ => e
}
- case AsUInt => e.args.head match {
- case SIntLiteral(v, IntWidth(w)) => UIntLiteral(v + (if (v < 0) BigInt(1) << w.toInt else 0), IntWidth(w))
- case u: UIntLiteral => u
- case _ => e
- }
+ case AsUInt =>
+ e.args.head match {
+ case SIntLiteral(v, IntWidth(w)) => UIntLiteral(v + (if (v < 0) BigInt(1) << w.toInt else 0), IntWidth(w))
+ case arg => arg.tpe match {
+ case _: UIntType => arg
+ case _ => e
+ }
+ }
case AsSInt => e.args.head match {
case UIntLiteral(v, IntWidth(w)) => SIntLiteral(v - ((v >> (w.toInt-1)) << w.toInt), IntWidth(w))
- case s: SIntLiteral => s
- case _ => e
+ case arg => arg.tpe match {
+ case _: SIntType => arg
+ case _ => e
+ }
}
+ case AsClock =>
+ val arg = e.args.head
+ arg.tpe match {
+ case ClockType => arg
+ case _ => e
+ }
+ case AsAsyncReset =>
+ val arg = e.args.head
+ arg.tpe match {
+ case AsyncResetType => arg
+ case _ => e
+ }
case Pad => e.args.head match {
case UIntLiteral(v, IntWidth(w)) => UIntLiteral(v, IntWidth(e.consts.head max w))
case SIntLiteral(v, IntWidth(w)) => SIntLiteral(v, IntWidth(e.consts.head max w))
diff --git a/src/main/scala/firrtl/transforms/InlineCasts.scala b/src/main/scala/firrtl/transforms/InlineCasts.scala
new file mode 100644
index 00000000..e504eb70
--- /dev/null
+++ b/src/main/scala/firrtl/transforms/InlineCasts.scala
@@ -0,0 +1,70 @@
+package firrtl
+package transforms
+
+import firrtl.ir._
+import firrtl.Mappers._
+
+import firrtl.Utils.{isCast, NodeMap}
+
+object InlineCastsTransform {
+
+ // Checks if an Expression is made up of only casts terminated by a Literal or Reference
+ // There must be at least one cast
+ // Note that this can have false negatives but MUST NOT have false positives
+ private def isSimpleCast(castSeen: Boolean)(expr: Expression): Boolean = expr match {
+ case _: WRef | _: Literal | _: WSubField => castSeen
+ case DoPrim(op, args, _,_) if isCast(op) => args.forall(isSimpleCast(true))
+ case _ => false
+ }
+
+ /** Recursively replace [[WRef]]s with new [[Expression]]s
+ *
+ * @param replace a '''mutable''' HashMap mapping [[WRef]]s to values with which the [[WRef]]
+ * will be replaced. It is '''not''' mutated in this function
+ * @param expr the Expression being transformed
+ * @return Returns expr with [[WRef]]s replaced by values found in replace
+ */
+ def onExpr(replace: NodeMap)(expr: Expression): Expression = {
+ expr.map(onExpr(replace)) match {
+ case e @ WRef(name, _,_,_) =>
+ replace.get(name)
+ .filter(isSimpleCast(castSeen=false))
+ .getOrElse(e)
+ case e @ DoPrim(op, Seq(WRef(name, _,_,_)), _,_) if isCast(op) =>
+ replace.get(name)
+ .map(value => e.copy(args = Seq(value)))
+ .getOrElse(e)
+ case other => other // Not a candidate
+ }
+ }
+
+ /** Inline casts in a Statement
+ *
+ * @param netlist a '''mutable''' HashMap mapping references to [[firrtl.ir.DefNode DefNode]]s to their connected
+ * [[firrtl.ir.Expression Expression]]s. This function '''will''' mutate it if stmt is a [[firrtl.ir.DefNode
+ * DefNode]] with a value that is a cast [[PrimOp]]
+ * @param stmt the Statement being searched for nodes and transformed
+ * @return Returns stmt with casts inlined
+ */
+ def onStmt(netlist: NodeMap)(stmt: Statement): Statement =
+ stmt.map(onStmt(netlist)).map(onExpr(netlist)) match {
+ case node @ DefNode(_, name, value) =>
+ netlist(name) = value
+ node
+ case other => other
+ }
+
+ /** Replaces truncating arithmetic in a Module */
+ def onMod(mod: DefModule): DefModule = mod.map(onStmt(new NodeMap))
+}
+
+/** Inline nodes that are simple casts */
+class InlineCastsTransform extends Transform {
+ def inputForm = LowForm
+ def outputForm = LowForm
+
+ def execute(state: CircuitState): CircuitState = {
+ val modulesx = state.circuit.modules.map(InlineCastsTransform.onMod(_))
+ state.copy(circuit = state.circuit.copy(modules = modulesx))
+ }
+}
diff --git a/src/main/scala/firrtl/transforms/LegalizeClocks.scala b/src/main/scala/firrtl/transforms/LegalizeClocks.scala
new file mode 100644
index 00000000..1c2fc045
--- /dev/null
+++ b/src/main/scala/firrtl/transforms/LegalizeClocks.scala
@@ -0,0 +1,69 @@
+package firrtl
+package transforms
+
+import firrtl.ir._
+import firrtl.Mappers._
+import firrtl.Utils.isCast
+
+// Fixup otherwise legal Verilog that lint tools and other tools don't like
+// Currently:
+// - don't emit "always @(posedge <literal>)"
+// Hitting this case is rare, but legal FIRRTL
+// TODO This should be unified with all Verilog legalization transforms
+object LegalizeClocksTransform {
+
+ // Checks if an Expression is illegal in use in a @(posedge <Expression>) construct
+ // Legality is defined here by what standard lint tools accept
+ // Currently only looks for literals nested within casts
+ private def illegalClockExpr(expr: Expression): Boolean = expr match {
+ case _: Literal => true
+ case DoPrim(op, args, _,_) if isCast(op) => args.exists(illegalClockExpr)
+ case _ => false
+ }
+
+ /** Legalize Clocks in a Statement
+ *
+ * Enforces legal Verilog semantics on all Clock Expressions.
+ * Legal is defined as what standard lint tools accept.
+ * Currently only Literal Expressions (guarded by casts) are handled.
+ *
+ * @note namespace is lazy because it should not typically be needed
+ */
+ def onStmt(namespace: => Namespace)(stmt: Statement): Statement =
+ stmt.map(onStmt(namespace)) match {
+ // Proper union types would deduplicate this code
+ case r: DefRegister if illegalClockExpr(r.clock) =>
+ val node = DefNode(r.info, namespace.newTemp, r.clock)
+ val rx = r.copy(clock = WRef(node))
+ Block(Seq(node, rx))
+ case p: Print if illegalClockExpr(p.clk) =>
+ val node = DefNode(p.info, namespace.newTemp, p.clk)
+ val px = p.copy(clk = WRef(node))
+ Block(Seq(node, px))
+ case s: Stop if illegalClockExpr(s.clk) =>
+ val node = DefNode(s.info, namespace.newTemp, s.clk)
+ val sx = s.copy(clk = WRef(node))
+ Block(Seq(node, sx))
+ case other => other
+ }
+
+ def onMod(mod: DefModule): DefModule = {
+ // It's actually *extremely* important that this Namespace is a lazy val
+ // onStmt accepts it lazily so that we don't perform the namespacing traversal unless necessary
+ // If we were to inline the declaration, it would create a Namespace for every problem, causing
+ // name collisions
+ lazy val namespace = Namespace(mod)
+ mod.map(onStmt(namespace))
+ }
+}
+
+/** Ensure Clocks to be emitted are legal Verilog */
+class LegalizeClocksTransform extends Transform {
+ def inputForm = LowForm
+ def outputForm = LowForm
+
+ def execute(state: CircuitState): CircuitState = {
+ val modulesx = state.circuit.modules.map(LegalizeClocksTransform.onMod(_))
+ state.copy(circuit = state.circuit.copy(modules = modulesx))
+ }
+}
diff --git a/src/test/scala/firrtlTests/AsyncResetSpec.scala b/src/test/scala/firrtlTests/AsyncResetSpec.scala
index ed90954b..f347ec14 100644
--- a/src/test/scala/firrtlTests/AsyncResetSpec.scala
+++ b/src/test/scala/firrtlTests/AsyncResetSpec.scala
@@ -65,10 +65,10 @@ class AsyncResetSpec extends FirrtlFlatSpec {
|z <= asAsyncReset(a)
|""".stripMargin
)
- result should containLine ("assign v = $unsigned(a);")
- result should containLine ("assign w = $signed(a);")
+ result should containLine ("assign v = a;")
+ result should containLine ("assign w = a;")
result should containLine ("assign x = a;")
- result should containLine ("assign y = $signed(a);")
+ result should containLine ("assign y = a;")
result should containLine ("assign z = a;")
}
diff --git a/src/test/scala/firrtlTests/ConstantPropagationTests.scala b/src/test/scala/firrtlTests/ConstantPropagationTests.scala
index 71709255..af186cda 100644
--- a/src/test/scala/firrtlTests/ConstantPropagationTests.scala
+++ b/src/test/scala/firrtlTests/ConstantPropagationTests.scala
@@ -3,8 +3,6 @@
package firrtlTests
import firrtl._
-import firrtl.ir.Circuit
-import firrtl.Parser.IgnoreInfo
import firrtl.passes._
import firrtl.transforms._
@@ -824,6 +822,30 @@ class ConstantPropagationSingleModule extends ConstantPropagationSpec {
""".stripMargin
(parse(exec(input))) should be(parse(check))
}
+
+ def castCheck(tpe: String, cast: String): Unit = {
+ val input =
+ s"""circuit Top :
+ | module Top :
+ | input x : $tpe
+ | output z : $tpe
+ | z <= $cast(x)
+ """.stripMargin
+ val check =
+ s"""circuit Top :
+ | module Top :
+ | input x : $tpe
+ | output z : $tpe
+ | z <= x
+ """.stripMargin
+ (parse(exec(input)).serialize) should be (parse(check).serialize)
+ }
+ it should "optimize unnecessary casts" in {
+ castCheck("UInt<4>", "asUInt")
+ castCheck("SInt<4>", "asSInt")
+ castCheck("Clock", "asClock")
+ castCheck("AsyncReset", "asAsyncReset")
+ }
}
// More sophisticated tests of the full compiler
diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
index 0376a830..bb7659e9 100644
--- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala
+++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
@@ -182,6 +182,16 @@ class DoPrimVerilog extends FirrtlFlatSpec {
}
class VerilogEmitterSpec extends FirrtlFlatSpec {
+ private def compile(input: String): CircuitState =
+ (new VerilogCompiler).compileAndEmit(CircuitState(parse(input), ChirrtlForm), List.empty)
+ private def compileBody(body: String): CircuitState = {
+ val str = """
+ |circuit Test :
+ | module Test :
+ |""".stripMargin + body.split("\n").mkString(" ", "\n ", "")
+ compile(str)
+ }
+
"Ports" should "emit with widths aligned and names aligned" in {
val compiler = new VerilogCompiler
val input =
@@ -378,7 +388,6 @@ class VerilogEmitterSpec extends FirrtlFlatSpec {
output.circuit.serialize should be (parse(check_firrtl).serialize)
}
-
behavior of "Register Updates"
they should "emit using 'else if' constructs" in {
@@ -520,6 +529,94 @@ class VerilogEmitterSpec extends FirrtlFlatSpec {
result should containLine ("tmp <= in_9;")
}
+ "SInt addition" should "have casts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : SInt<4>
+ |input y : SInt<4>
+ |output z : SInt
+ |z <= add(x, y)
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + $signed(y);")
+ }
+
+ it should "NOT cast SInt literals" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : SInt<4>
+ |output z : SInt
+ |z <= add(x, SInt(-1))
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + -4'sh1;")
+ }
+
+ it should "inline asSInt casts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<4>
+ |input y : UInt<4>
+ |output z : SInt
+ |node _T_1 = asSInt(x)
+ |z <= add(_T_1, asSInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + $signed(y);")
+ }
+
+ "Verilog Emitter" should "drop asUInt casts on Clocks" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : Clock
+ |input y : Clock
+ |output z : UInt<1>
+ |node _T_1 = asUInt(x)
+ |z <= eq(_T_1, asUInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asClock casts on UInts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<1>
+ |input y : UInt<1>
+ |output z : Clock
+ |node _T_1 = eq(x, y)
+ |z <= asClock(_T_1)
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asUInt casts on AsyncResets" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : AsyncReset
+ |input y : AsyncReset
+ |output z : UInt<1>
+ |node _T_1 = asUInt(x)
+ |z <= eq(_T_1, asUInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asAsyncReset casts on UInts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<1>
+ |input y : UInt<1>
+ |output z : AsyncReset
+ |node _T_1 = eq(x, y)
+ |z <= asAsyncReset(_T_1)
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
}
class VerilogDescriptionEmitterSpec extends FirrtlFlatSpec {
diff --git a/src/test/scala/firrtlTests/transforms/LegalizeClocks.scala b/src/test/scala/firrtlTests/transforms/LegalizeClocks.scala
new file mode 100644
index 00000000..5c2412ae
--- /dev/null
+++ b/src/test/scala/firrtlTests/transforms/LegalizeClocks.scala
@@ -0,0 +1,67 @@
+// See LICENSE for license details.
+
+package firrtlTests.transforms
+
+import firrtl._
+import firrtlTests.FirrtlFlatSpec
+import firrtlTests.FirrtlCheckers._
+
+class LegalizeClocksTransformSpec extends FirrtlFlatSpec {
+ def compile(input: String): CircuitState =
+ (new MinimumVerilogCompiler).compileAndEmit(CircuitState(parse(input), ChirrtlForm), Nil)
+
+ behavior of "LegalizeClocksTransform"
+
+ it should "not emit @(posedge 1'h0) for stop" in {
+ val input =
+ """circuit test :
+ | module test :
+ | stop(asClock(UInt(1)), UInt(1), 1)
+ |""".stripMargin
+ val result = compile(input)
+ result should containLine (s"always @(posedge _GEN_0) begin")
+ result.getEmittedCircuit.value shouldNot include ("always @(posedge 1")
+ }
+
+ it should "not emit @(posedge 1'h0) for printf" in {
+ val input =
+ """circuit test :
+ | module test :
+ | printf(asClock(UInt(1)), UInt(1), "hi")
+ |""".stripMargin
+ val result = compile(input)
+ result should containLine (s"always @(posedge _GEN_0) begin")
+ result.getEmittedCircuit.value shouldNot include ("always @(posedge 1")
+ }
+
+ it should "not emit @(posedge 1'h0) for reg" in {
+ val input =
+ """circuit test :
+ | module test :
+ | output out : UInt<8>
+ | input in : UInt<8>
+ | reg r : UInt<8>, asClock(UInt(0))
+ | r <= in
+ | out <= r
+ |""".stripMargin
+ val result = compile(input)
+ result should containLine (s"always @(posedge _GEN_0) begin")
+ result.getEmittedCircuit.value shouldNot include ("always @(posedge 1")
+ }
+
+ it should "deduplicate injected nodes for literal clocks" in {
+ val input =
+ """circuit test :
+ | module test :
+ | printf(asClock(UInt(1)), UInt(1), "hi")
+ | stop(asClock(UInt(1)), UInt(1), 1)
+ |""".stripMargin
+ val result = compile(input)
+ result should containLine (s"wire _GEN_0;")
+ // Check that there's only 1 _GEN_0 instantiation
+ val verilog = result.getEmittedCircuit.value
+ val matches = "wire\\s+_GEN_0;".r.findAllIn(verilog)
+ matches.size should be (1)
+
+ }
+}