diff --git a/README.md b/README.md index 5efe975e1f..d547e2aeeb 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ A Firrtl compiler is constructed by chaining together these transformations, then writing the final circuit to a file. - For a detailed description of Firrtl's intermediate representation, see the document "Specification of the Firrtl Language" located in [spec/spec.pdf](https://github.com/freechipsproject/firrtl/blob/master/spec/spec.pdf). + For a detailed description of Firrtl's intermediate representation, see the [FIRRTL Language Specification](https://github.com/chipsalliance/firrtl-spec/releases/latest/download/spec.pdf) ([source](https://github.com/chipsalliance/firrtl-spec)). #### Wiki Pages and Tutorials diff --git a/src/main/scala/firrtl/backends/verilog/LegalizeVerilog.scala b/src/main/scala/firrtl/backends/verilog/LegalizeVerilog.scala index f063f39529..a76377673e 100644 --- a/src/main/scala/firrtl/backends/verilog/LegalizeVerilog.scala +++ b/src/main/scala/firrtl/backends/verilog/LegalizeVerilog.scala @@ -9,6 +9,7 @@ import firrtl.transforms.ConstantPropagation import firrtl.{bitWidth, Dshlw, Transform} import firrtl.Mappers._ import firrtl.passes.{Pass, SplitExpressions} +import firrtl.passes.PadWidths.forceWidth /** Rewrites some expressions for valid/better Verilog emission. * - solves shift right overflows by replacing the shift with 0 for UInts and MSB for SInts @@ -47,16 +48,44 @@ object LegalizeVerilog extends Pass { } } - import firrtl.passes.PadWidths.forceWidth private def getWidth(e: Expression): Int = bitWidth(e.tpe).toInt + /* 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 function pads the arguments to be the same [max] width (to avoid lint issues) + * and then performs a bit extraction back down to the correct [min] width + */ + private def legalizeRem(e: Expression): Expression = e match { + case rem @ DoPrim(Rem, Seq(a, b), _, tpe) => + val awidth = getWidth(a) + val bwidth = getWidth(b) + // Do nothing if the widths are the same + if (awidth == bwidth) { + rem + } else { + // First pad the arguments to fix lint warnings because Verilog width is max of arguments + val maxWidth = awidth.max(bwidth) + val newType = tpe.mapWidth(_ => IntWidth(maxWidth)) + val paddedRem = + rem + .map(forceWidth(maxWidth)(_)) // Pad the input arguments + .mapType(_ => newType) // Also make the width for this op correct + // Second, bit extract back down to the min width of original arguments to match FIRRTL semantics + val minWidth = awidth.min(bwidth) + forceWidth(minWidth)(paddedRem) + } + case _ => e + } + private def onExpr(expr: Expression): Expression = expr.map(onExpr) match { case prim: DoPrim => prim.op match { case Shr => ConstantPropagation.foldShiftRight(prim) case Bits | Head | Tail => legalizeBitExtract(prim) case Neg => legalizeNeg(prim) - case Rem => prim.map(forceWidth(prim.args.map(getWidth).max)) + case Rem => legalizeRem(prim) case Dshl => // special case as args aren't all same width prim.copy(op = Dshlw, args = Seq(forceWidth(getWidth(prim))(prim.args.head), prim.args(1))) diff --git a/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala index 3ca862b95b..d05824784e 100644 --- a/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala +++ b/src/main/scala/firrtl/passes/VerilogModulusCleanup.scala @@ -4,9 +4,6 @@ package firrtl package passes import firrtl.ir._ -import firrtl.Mappers._ -import firrtl.PrimOps.{Bits, Rem} -import firrtl.Utils._ import firrtl.options.Dependency import scala.collection.mutable @@ -24,6 +21,7 @@ import scala.collection.mutable * This is technically incorrect firrtl, but allows the verilog emitter * to emit correct verilog without needing to add temporary nodes */ +@deprecated("This pass's functionality has been moved to LegalizeVerilog", "FIRRTL 1.5.2") object VerilogModulusCleanup extends Pass { override def prerequisites = firrtl.stage.Forms.LowFormMinimumOptimized ++ @@ -43,62 +41,5 @@ object VerilogModulusCleanup extends Pass { override def invalidates(a: Transform) = false - 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), flow(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) - } + def run(c: Circuit): Circuit = c } diff --git a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala index 3ec19e770d..cc4e0cfe40 100644 --- a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala +++ b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala @@ -241,4 +241,149 @@ class VerilogEquivalenceSpec extends FirrtlFlatSpec { firrtlEquivalenceWithVerilog(firrtlFalse, verilogFalse) } + "unsigned modulus" should "be handled correctly" in { + val input1 = + s""" + |circuit Modulus : + | module Modulus : + | input x : UInt<8> + | input y : UInt<4> + | input z : UInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected1 = + """ + |module ModulusRef( + | input [7:0] x, + | input [3:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = x % y; + | wire [3:0] ext = mod[3:0]; + | assign out = ext == z; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input1, expected1) + + val input2 = + s""" + |circuit Modulus : + | module Modulus : + | input x : UInt<4> + | input y : UInt<8> + | input z : UInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected2 = + """ + |module ModulusRef( + | input [3:0] x, + | input [7:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = x % y; + | wire [3:0] ext = mod[3:0]; + | assign out = ext == z; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input2, expected2) + + val input3 = + s""" + |circuit Modulus : + | module Modulus : + | input x : UInt<8> + | input y : UInt<8> + | input z : UInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected3 = + """ + |module ModulusRef( + | input [7:0] x, + | input [7:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = x % y; + | assign out = mod == z; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input3, expected3) + } + + "signed modulus" should "be handled correctly" in { + val input1 = + s""" + |circuit Modulus : + | module Modulus : + | input x : SInt<8> + | input y : SInt<4> + | input z : SInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected1 = + """ + |module ModulusRef( + | input [7:0] x, + | input [3:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = $signed(x) % $signed(y); + | wire [3:0] ext = mod[3:0]; + | assign out = ext == z; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input1, expected1) + + val input2 = + s""" + |circuit Modulus : + | module Modulus : + | input x : SInt<4> + | input y : SInt<8> + | input z : SInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected2 = + """ + |module ModulusRef( + | input [3:0] x, + | input [7:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = $signed(x) % $signed(y); + | wire [3:0] ext = mod[3:0]; + | assign out = ext == z; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input2, expected2) + + val input3 = + s""" + |circuit Modulus : + | module Modulus : + | input x : SInt<8> + | input y : SInt<8> + | input z : SInt<4> + | output out : UInt<1> + | out <= eq(rem(x, y), z) + |""".stripMargin + val expected3 = + """ + |module ModulusRef( + | input [7:0] x, + | input [7:0] y, + | input [3:0] z, + | output out + |); + | wire [7:0] mod = $signed(x) % $signed(y); + | assign out = mod == {{4{z[3]}}, z}; + |endmodule""".stripMargin + firrtlEquivalenceWithVerilog(input3, expected3) + } }