diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 58 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/VerilogEmitterTests.scala | 142 |
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 { |
