From 17f7bc04491397e8839a4dab8756c7fa8ac2ce13 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 16 Oct 2019 12:40:08 -0400 Subject: Emit Verilog "else if" in register updates Modifies the Verilog emitter to emit "else if" blocks as opposed to more deeply nested "else begin if" blocks. This improves the output Verilog readability. Signed-off-by: Schuyler Eldridge --- src/main/scala/firrtl/Emitter.scala | 58 ++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 27 deletions(-) (limited to 'src') 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), "") -- cgit v1.2.3 From a4cb228161410de4c5ab2029d7756870f32d28b8 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 17 Oct 2019 11:15:59 -0400 Subject: Add Register Updates/else-if Verilog Emitter tests Signed-off-by: Schuyler Eldridge --- .../scala/firrtlTests/VerilogEmitterTests.scala | 142 +++++++++++++++++++++ 1 file changed, 142 insertions(+) (limited to 'src') 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 { -- cgit v1.2.3