diff options
| author | Jim Lawson | 2017-01-19 09:27:07 -0800 |
|---|---|---|
| committer | GitHub | 2017-01-19 09:27:07 -0800 |
| commit | e53ad11ee3dadb584cd12117235f0034f56d64dc (patch) | |
| tree | 5dc8ac74bad95f393dad4c71dd9dabf9fd380484 | |
| parent | 60da9bd691bad4561451b317fba5e98cc023c5ba (diff) | |
| parent | b0623fe1856caeba11cda1ccaf68f094489169a7 (diff) | |
Merge branch 'master' into addmiddlefirrtlcompiler
| -rw-r--r-- | src/main/scala/firrtl/Emitter.scala | 1 | ||||
| -rw-r--r-- | src/main/scala/firrtl/passes/VerilogModulusCleanup.scala | 84 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/VerilogEmitterTests.scala | 21 |
3 files changed, 106 insertions, 0 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala index e40a18a4..a290ced4 100644 --- a/src/main/scala/firrtl/Emitter.scala +++ b/src/main/scala/firrtl/Emitter.scala @@ -562,6 +562,7 @@ class VerilogEmitter extends Emitter with PassBased { } def passSeq = Seq( + passes.VerilogModulusCleanup, passes.VerilogWrap, passes.VerilogRename, passes.VerilogPrep) diff --git a/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala new file mode 100644 index 00000000..b4df534f --- /dev/null +++ b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala @@ -0,0 +1,84 @@ +// See LICENSE for license details. + +package firrtl +package passes + +import firrtl.ir._ +import firrtl.Mappers._ +import firrtl.PrimOps.{Bits, Rem} +import firrtl.Utils._ + +import scala.collection.mutable + +/** + * Verilog has the width of (a % b) = Max(W(a), W(b)) + * FIRRTL has the width of (a % b) = Min(W(a), W(b)), which makes more sense, + * but nevertheless is a problem when emitting verilog + * + * This pass finds every instance of (a % b) and: + * 1) adds a temporary node equal to (a % b) with width Max(W(a), W(b)) + * 2) replaces the reference to (a % b) with a bitslice of the temporary node + * to get back down to width Min(W(a), W(b)) + * + * This is technically incorrect firrtl, but allows the verilog emitter + * to emit correct verilog without needing to add temporary nodes + */ +object VerilogModulusCleanup extends Pass { + def name = "Add temporary nodes with verilog widths for modulus" + + private def onModule(m: Module): Module = { + val namespace = Namespace(m) + def onStmt(s: Statement): Statement = { + val v = mutable.ArrayBuffer[Statement]() + + def getWidth(e: Expression): Width = e.tpe match { + case t: GroundType => t.width + case t => UnknownWidth + } + + def maxWidth(ws: Seq[Width]): Width = ws reduceLeft { (x,y) => (x,y) match { + case (IntWidth(x), IntWidth(y)) => IntWidth(x max y) + case (x, y) => UnknownWidth + }} + + def verilogRemWidth(e: DoPrim)(tpe: Type): Type = { + val newWidth = maxWidth(e.args.map(exp => getWidth(exp))) + tpe mapWidth (w => newWidth) + } + + def removeRem(e: Expression): Expression = e match { + case e: DoPrim => e.op match { + case Rem => + val name = namespace.newTemp + val newType = e mapType verilogRemWidth(e) + v += DefNode(get_info(s), name, e mapType verilogRemWidth(e)) + val remRef = WRef(name, newType.tpe, kind(e), gender(e)) + val remWidth = bitWidth(e.tpe) + DoPrim(Bits, Seq(remRef), Seq(remWidth - 1, BigInt(0)), e.tpe) + case _ => e + } + case _ => e + } + + s map removeRem match { + case x: Block => x map onStmt + case EmptyStmt => EmptyStmt + case x => + v += x + v.size match { + case 1 => v.head + case _ => Block(v.toSeq) + } + } + } + Module(m.info, m.name, m.ports, onStmt(m.body)) + } + + def run(c: Circuit): Circuit = { + val modules = c.modules map { + case m: Module => onModule(m) + case m: ExtModule => m + } + Circuit(c.info, modules, c.main) + } +} diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala index 39592269..41fd6e41 100644 --- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala +++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala @@ -74,4 +74,25 @@ class DoPrimVerilog extends FirrtlFlatSpec { |""".stripMargin.split("\n") map normalized executeTest(input, check, compiler) } + "Rem" should "emit correctly" in { + val compiler = new VerilogCompiler + val input = + """circuit Test : + | module Test : + | input in : UInt<8> + | output out : UInt<1> + | out <= rem(in, UInt<1>("h1")) + |""".stripMargin + val check = + """module Test( + | input [7:0] in, + | output out + |); + | wire [7:0] _GEN_0; + | assign out = _GEN_0[0]; + | assign _GEN_0 = in % 8'h1; + |endmodule + |""".stripMargin.split("\n") map normalized + executeTest(input, check, compiler) + } } |
