aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSchuyler Eldridge2019-10-22 00:48:59 -0400
committerGitHub2019-10-22 00:48:59 -0400
commitf5e6e2bc201c6206a705244d8977bda4b83e6efd (patch)
tree3e42e57b110beefd6e13457db199d7c3a7d0bd4c /src
parentb43288d588d04775230456ca85fa231a8cf397fe (diff)
parenta4cb228161410de4c5ab2029d7756870f32d28b8 (diff)
Merge pull request #1204 from freechipsproject/else-if
Emit Verilog else-if for Register Updates
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Emitter.scala58
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala142
2 files changed, 173 insertions, 27 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index d7c73e39..c427e0fc 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -534,33 +534,37 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
def regUpdate(r: Expression, clk: Expression, reset: Expression, init: Expression) = {
- def addUpdate(expr: Expression, tabs: String): Seq[Seq[Any]] = {
- if (weq(expr, r)) Nil // Don't bother emitting connection of register to itself
- else 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")
-
- def ifStatement = Seq(tabs, "if (", m.cond, ") begin")
-
- val trueCase = addUpdate(m.tval, tabs + tab)
- val elseStatement = Seq(tabs, "end else begin")
-
- def ifNotStatement = Seq(tabs, "if (!(", m.cond, ")) begin")
-
- val falseCase = addUpdate(m.fval, tabs + tab)
- val endStatement = Seq(tabs, "end")
-
- ((trueCase.nonEmpty, falseCase.nonEmpty): @unchecked) match {
- case (true, true) =>
- ifStatement +: trueCase ++: elseStatement +: falseCase :+ endStatement
- case (true, false) =>
- ifStatement +: trueCase :+ endStatement
- case (false, true) =>
- ifNotStatement +: falseCase :+ endStatement
- }
- case e => Seq(Seq(tabs, r, " <= ", e, ";"))
- }
+ def addUpdate(expr: Expression, tabs: 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")
+
+ lazy val _if = Seq(tabs, "if (", m.cond, ") begin")
+ lazy val _else = Seq(tabs, "end else begin")
+ lazy val _ifNot = Seq(tabs, "if (!(", m.cond, ")) begin")
+ lazy val _end = Seq(tabs, "end")
+ lazy val _true = addUpdate(m.tval, tabs + tab)
+ lazy val _false = addUpdate(m.fval, tabs + tab)
+ lazy val _elseIfFalse = {
+ val _falsex = addUpdate(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:
+ * 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; 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) => _if +: _true :+ _end
+ case (_, _: Mux) => (_if +: _true) ++ _elseIfFalse
+ case _ => (_if +: _true :+ _else) ++ _false :+ _end
+ }
+ case e => Seq(Seq(tabs, r, " <= ", e, ";"))
}
if (weq(init, r)) { // Synchronous Reset
noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= addUpdate(netlist(r), "")
diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
index b712b46d..7ea2f03a 100644
--- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala
+++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
@@ -304,6 +304,148 @@ 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 {
+ val input =
+ s"""|circuit Foo:
+ | module Foo:
+ | input clock: Clock
+ | input sel: UInt<2>
+ | input in_0: UInt<1>
+ | input in_1: UInt<1>
+ | input in_2: UInt<1>
+ | input in_3: UInt<1>
+ | output out: UInt<1>
+ | reg tmp: UInt<1>, clock
+ | node _GEN_0 = mux(eq(sel, UInt<2>(2)), in_2, in_3)
+ | node _GEN_1 = mux(eq(sel, UInt<2>(1)), in_1, _GEN_0)
+ | tmp <= mux(eq(sel, UInt<2>(0)), in_0, _GEN_1)
+ | out <= tmp
+ |""".stripMargin
+ val circuit = Seq(ToWorkingIR, ResolveKinds, InferTypes).foldLeft(parse(input)) { case (c, p) => p.run(c) }
+ val state = CircuitState(circuit, LowForm, Seq(EmitCircuitAnnotation(classOf[VerilogEmitter])))
+ val result = (new VerilogEmitter).execute(state)
+ result should containLine ("if (sel == 2'h0) begin")
+ result should containLine ("end else if (sel == 2'h1) begin" )
+ result should containLine ("end else if (sel == 2'h2) begin")
+ result should containLine ("end else begin")
+ }
+
+ they should "ignore self assignments in false conditions" in {
+ val input =
+ s"""|circuit Foo:
+ | module Foo:
+ | input clock: Clock
+ | input sel: UInt<1>
+ | input in: UInt<1>
+ | output out: UInt<1>
+ | reg tmp: UInt<1>, clock
+ | tmp <= mux(eq(sel, UInt<1>(0)), in, tmp)
+ | out <= tmp
+ |""".stripMargin
+ val circuit = Seq(ToWorkingIR, ResolveKinds, InferTypes).foldLeft(parse(input)) { case (c, p) => p.run(c) }
+ val state = CircuitState(circuit, LowForm, Seq(EmitCircuitAnnotation(classOf[VerilogEmitter])))
+ val result = (new VerilogEmitter).execute(state)
+ result should not (containLine ("tmp <= tmp"))
+ }
+
+ they should "ignore self assignments in true conditions and invert condition" in {
+ val input =
+ s"""|circuit Foo:
+ | module Foo:
+ | input clock: Clock
+ | input sel: UInt<1>
+ | input in: UInt<1>
+ | output out: UInt<1>
+ | reg tmp: UInt<1>, clock
+ | tmp <= mux(eq(sel, UInt<1>(0)), tmp, in)
+ | out <= tmp
+ |""".stripMargin
+ val circuit = Seq(ToWorkingIR, ResolveKinds, InferTypes).foldLeft(parse(input)) { case (c, p) => p.run(c) }
+ val state = CircuitState(circuit, LowForm, Seq(EmitCircuitAnnotation(classOf[VerilogEmitter])))
+ val result = (new VerilogEmitter).execute(state)
+ result should containLine ("if (!(sel == 1'h0)) begin")
+ result should not (containLine ("tmp <= tmp"))
+ }
+
+ they should "ignore self assignments in both true and false conditions" in {
+ val input =
+ s"""|circuit Foo:
+ | module Foo:
+ | input clock: Clock
+ | input sel: UInt<1>
+ | input in: UInt<1>
+ | output out: UInt<1>
+ | reg tmp: UInt<1>, clock
+ | tmp <= mux(eq(sel, UInt<1>(0)), tmp, tmp)
+ | out <= tmp
+ |""".stripMargin
+ val circuit = Seq(ToWorkingIR, ResolveKinds, InferTypes).foldLeft(parse(input)) { case (c, p) => p.run(c) }
+ val state = CircuitState(circuit, LowForm, Seq(EmitCircuitAnnotation(classOf[VerilogEmitter])))
+ val result = (new VerilogEmitter).execute(state)
+ result should not (containLine ("tmp <= tmp"))
+ result should not (containLine ("always @(posedge clock) begin"))
+ }
+
+ they should "properly indent muxes in either the true or false condition" in {
+ val input =
+ s"""|circuit Foo:
+ | module Foo:
+ | input clock: Clock
+ | input reset: UInt<1>
+ | input sel: UInt<3>
+ | input in_0: UInt<1>
+ | input in_1: UInt<1>
+ | input in_2: UInt<1>
+ | input in_3: UInt<1>
+ | input in_4: UInt<1>
+ | input in_5: UInt<1>
+ | input in_6: UInt<1>
+ | input in_7: UInt<1>
+ | input in_8: UInt<1>
+ | input in_9: UInt<1>
+ | input in_10: UInt<1>
+ | input in_11: UInt<1>
+ | output out: UInt<1>
+ | reg tmp: UInt<0>, clock
+ | node m7 = mux(eq(sel, UInt<3>(7)), in_8, in_9)
+ | node m6 = mux(eq(sel, UInt<3>(6)), in_6, in_7)
+ | node m5 = mux(eq(sel, UInt<3>(5)), in_4, in_5)
+ | node m4 = mux(eq(sel, UInt<3>(4)), in_2, in_3)
+ | node m3 = mux(eq(sel, UInt<3>(3)), in_0, in_1)
+ | node m2 = mux(eq(sel, UInt<3>(2)), m6, m7)
+ | node m1 = mux(eq(sel, UInt<3>(1)), m4, m5)
+ | node m0 = mux(eq(sel, UInt<3>(0)), m2, m3)
+ | tmp <= mux(reset, m0, m1)
+ | out <= tmp
+ |""".stripMargin
+ val circuit = Seq(ToWorkingIR, ResolveKinds, InferTypes).foldLeft(parse(input)) { case (c, p) => p.run(c) }
+ val state = CircuitState(circuit, LowForm, Seq(EmitCircuitAnnotation(classOf[VerilogEmitter])))
+ val result = (new VerilogEmitter).execute(state)
+ /* The Verilog string is used to check for no whitespace between "else" and "if". */
+ val verilogString = result.getEmittedCircuit.value
+ result should containLine ("if (sel == 3'h0) begin")
+ verilogString should include ("end else if (sel == 3'h1) begin")
+ result should containLine ("if (sel == 3'h2) begin")
+ verilogString should include ("end else if (sel == 3'h3) begin")
+ result should containLine ("if (sel == 3'h4) begin")
+ verilogString should include ("end else if (sel == 3'h5) begin")
+ result should containLine ("if (sel == 3'h6) begin")
+ verilogString should include ("end else if (sel == 3'h7) begin")
+ result should containLine ("tmp <= in_0;")
+ result should containLine ("tmp <= in_1;")
+ result should containLine ("tmp <= in_2;")
+ result should containLine ("tmp <= in_3;")
+ result should containLine ("tmp <= in_4;")
+ result should containLine ("tmp <= in_5;")
+ result should containLine ("tmp <= in_6;")
+ result should containLine ("tmp <= in_7;")
+ result should containLine ("tmp <= in_8;")
+ result should containLine ("tmp <= in_9;")
+ }
+
}
class VerilogDescriptionEmitterSpec extends FirrtlFlatSpec {