diff options
| author | Jack Koenig | 2017-06-27 17:25:57 -0700 |
|---|---|---|
| committer | Jack Koenig | 2017-06-27 18:50:15 -0700 |
| commit | 6f55a30b201716b6a0e72b65f6e5777b6b5d4b81 (patch) | |
| tree | 61f5f7f7f278afbcd4fe1f5d6f7c4b4e536ce117 | |
| parent | f8572ba6532359e8a0f1bc34f3eb8241a29129ab (diff) | |
Emitting reg update mux tree, only walk netlist for wires and nodes
Fixes bug where the Verilog emitter could pull the next value for a
register that feeds a second register, removing the first register from
the second register's update.
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 52 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/IntegrationSpec.scala | 1 | ||||
| -rw-r--r-- | test/integration/PipeTester.fir | 51 |
3 files changed, 78 insertions, 26 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index 00b5137e..744de96d 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -355,16 +355,11 @@ class VerilogEmitter extends SeqTransform with Emitter { def emit_verilog(m: Module, moduleMap: Map[String, DefModule])(implicit w: Writer): DefModule = { val netlist = mutable.LinkedHashMap[WrappedExpression, Expression]() - val addrRegs = mutable.HashSet[WrappedExpression]() val namespace = Namespace(m) namespace.newName("_RAND") // Start rand names at _RAND_0 def build_netlist(s: Statement): Statement = s map build_netlist match { case sx: Connect => netlist(sx.loc) = sx.expr - (kind(sx.loc), kind(sx.expr)) match { - case (MemKind, RegKind) => addrRegs += sx.expr - case _ => - } sx case sx: IsInvalid => netlist(sx.expr) = wref(namespace.newTemp, sx.expr.tpe) @@ -423,27 +418,32 @@ class VerilogEmitter extends SeqTransform with Emitter { } def addUpdate(e: Expression, tabs: String): Seq[Seq[Any]] = { - if (weq(e, r)) Nil else netlist getOrElse (e, e) match { - case m: Mux if canFlatten(m) => - val ifStatement = Seq(tabs, "if (", m.cond, ") begin") - val trueCase = - // Don't generate mux trees for mem addr pipes - if (addrRegs(r)) Seq(Seq(tabs + tab, r, " <= ", m.tval, ";")) - else addUpdate(m.tval, tabs + tab) - val elseStatement = Seq(tabs, "end else begin") - val 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 _ => Seq(Seq(tabs, r, " <= ", e, ";")) + if (weq(e, r)) Nil // Don't bother emitting connection of register to itself + else { + // Only walk netlist for nodes and wires, NOT registers or other state + val expr = kind(e) match { + case NodeKind | WireKind => netlist.getOrElse(e, e) + case _ => e + } + expr match { + case m: Mux if canFlatten(m) => + val ifStatement = Seq(tabs, "if (", m.cond, ") begin") + val trueCase = addUpdate(m.tval, tabs + tab) + val elseStatement = Seq(tabs, "end else begin") + val 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 _ => Seq(Seq(tabs, r, " <= ", e, ";")) + } } } diff --git a/src/test/scala/firrtlTests/IntegrationSpec.scala b/src/test/scala/firrtlTests/IntegrationSpec.scala index 6ac45b6d..647aa91b 100644 --- a/src/test/scala/firrtlTests/IntegrationSpec.scala +++ b/src/test/scala/firrtlTests/IntegrationSpec.scala @@ -11,6 +11,7 @@ import java.io.File class GCDExecutionTest extends ExecutionTest("GCDTester", "/integration") class RightShiftExecutionTest extends ExecutionTest("RightShiftTester", "/integration") class MemExecutionTest extends ExecutionTest("MemTester", "/integration") +class PipeExecutionTest extends ExecutionTest("PipeTester", "/integration") // This is a bit custom some kind of one off class GCDSplitEmissionExecutionTest extends FirrtlFlatSpec { diff --git a/test/integration/PipeTester.fir b/test/integration/PipeTester.fir new file mode 100644 index 00000000..3ca2f001 --- /dev/null +++ b/test/integration/PipeTester.fir @@ -0,0 +1,51 @@ + +circuit PipeTester : + ; This module should simply delay a signal by 2 cycles + ; Internal registers reset to 0 + module Pipe : + input clock : Clock + input reset : UInt<1> + input in : UInt<4> + output out : UInt<4> + + ;reg r : UInt<4>, clock with : (reset => (reset, UInt(0))) + ;r <= in + ; This is equivalent to the above + + reg r : UInt<4>, clock + r <= mux(reset, UInt(0), in) + + reg s : UInt<4>, clock with : (reset => (reset, UInt(0))) + s <= r + out <= s + + module PipeTester : + input clock : Clock + input reset : UInt<1> + + inst pipe of Pipe + pipe.clock <= clock + pipe.reset <= reset + pipe.in <= UInt(3) + + reg cycle : UInt<4>, clock with : (reset => (reset, UInt<4>(0))) + cycle <= tail(add(cycle, UInt(1)), 1) + + wire fail : UInt<1> + fail <= UInt(0) + + when fail : + printf(clock, not(reset), "Assertion failed!\n") + stop(clock, not(reset), 1) + + when not(reset) : + when lt(cycle, UInt(2)) : + when neq(pipe.out, UInt(0)) : + fail <= UInt(1) + when eq(cycle, UInt(2)) : + when neq(pipe.out, UInt(3)) : + fail <= UInt(1) + when eq(cycle, UInt(3)) : + printf(clock, UInt(1), "Success!\n") + stop(clock, UInt(1), 0) + |
