aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/main/scala/firrtl/Emitter.scala84
-rw-r--r--src/main/scala/firrtl/Utils.scala11
-rw-r--r--src/main/scala/firrtl/transforms/InlineCasts.scala70
-rw-r--r--src/test/scala/firrtlTests/AsyncResetSpec.scala6
-rw-r--r--src/test/scala/firrtlTests/VerilogEmitterTests.scala99
5 files changed, 229 insertions, 41 deletions
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 95c762ae..67921a42 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -281,29 +281,35 @@ class VerilogEmitter extends SeqTransform with Emitter {
case _ => throwInternalError(s"attempt to print unrecognized expression: $e")
}
+ // NOTE: We emit SInts as regular Verilog unsigned wires/regs so the real type of any SInt
+ // reference is actually unsigned in the emitted Verilog. Thus we must cast refs as necessary
+ // to ensure Verilog operations are signed.
def op_stream(doprim: DoPrim): Seq[Any] = {
- def cast_if(e: Expression): Any = {
- doprim.args find (_.tpe match {
- case (_: SIntType) => true
- case (_) => false
- }) match {
- case None => e
- case Some(_) => e.tpe match {
- case (_: SIntType) => Seq("$signed(", e, ")")
- case (_: UIntType) => Seq("$signed({1'b0,", e, "})")
- case _ => throwInternalError(s"unrecognized type: $e")
+ // Cast to SInt, don't cast multiple times
+ def doCast(e: Expression): Any = e match {
+ case DoPrim(AsSInt, Seq(arg), _,_) => doCast(arg)
+ case slit: SIntLiteral => slit
+ case other => Seq("$signed(", other, ")")
+ }
+ def castIf(e: Expression): Any = {
+ if (doprim.args.exists(_.tpe.isInstanceOf[SIntType])) {
+ e.tpe match {
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected non-SInt type for $e in $doprim")
}
+ } else {
+ e
}
}
def cast(e: Expression): Any = doprim.tpe match {
- case (t: UIntType) => e
- case (t: SIntType) => Seq("$signed(",e,")")
- case _ => throwInternalError(s"cast - unrecognized type: $e")
+ case _: UIntType => e
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected type for $e in $doprim")
}
- def cast_as(e: Expression): Any = e.tpe match {
- case (t: UIntType) => e
- case (t: SIntType) => Seq("$signed(",e,")")
- case _ => throwInternalError(s"cast_as - unrecognized type: $e")
+ def castAs(e: Expression): Any = e.tpe match {
+ case _: UIntType => e
+ case _: SIntType => doCast(e)
+ case _ => throwInternalError(s"Unexpected type for $e in $doprim")
}
def a0: Expression = doprim.args.head
def a1: Expression = doprim.args(1)
@@ -313,12 +319,14 @@ class VerilogEmitter extends SeqTransform with Emitter {
def checkArgumentLegality(e: Expression): Unit = e match {
case _: UIntLiteral | _: SIntLiteral | _: WRef | _: WSubField =>
case DoPrim(Not, args, _,_) => args.foreach(checkArgumentLegality)
+ case DoPrim(op, args, _,_) if isCast(op) => args.foreach(checkArgumentLegality)
case _ => throw EmitterException(s"Can't emit ${e.getClass.getName} as PrimOp argument")
}
def checkCatArgumentLegality(e: Expression): Unit = e match {
case _: UIntLiteral | _: SIntLiteral | _: WRef | _: WSubField =>
case DoPrim(Not, args, _,_) => args.foreach(checkArgumentLegality)
+ case DoPrim(op, args, _,_) if isCast(op) => args.foreach(checkArgumentLegality)
case DoPrim(Cat, args, _, _) => args foreach(checkCatArgumentLegality)
case _ => throw EmitterException(s"Can't emit ${e.getClass.getName} as PrimOp argument")
}
@@ -337,22 +345,23 @@ class VerilogEmitter extends SeqTransform with Emitter {
doprim.op match {
case Cat => doprim.args foreach(checkCatArgumentLegality)
+ case cast if isCast(cast) => // Casts are allowed to wrap any Expression
case other => doprim.args foreach checkArgumentLegality
}
doprim.op match {
- case Add => Seq(cast_if(a0), " + ", cast_if(a1))
- case Addw => Seq(cast_if(a0), " + ", cast_if(a1))
- case Sub => Seq(cast_if(a0), " - ", cast_if(a1))
- case Subw => Seq(cast_if(a0), " - ", cast_if(a1))
- case Mul => Seq(cast_if(a0), " * ", cast_if(a1))
- case Div => Seq(cast_if(a0), " / ", cast_if(a1))
- case Rem => Seq(cast_if(a0), " % ", cast_if(a1))
- case Lt => Seq(cast_if(a0), " < ", cast_if(a1))
- case Leq => Seq(cast_if(a0), " <= ", cast_if(a1))
- case Gt => Seq(cast_if(a0), " > ", cast_if(a1))
- case Geq => Seq(cast_if(a0), " >= ", cast_if(a1))
- case Eq => Seq(cast_if(a0), " == ", cast_if(a1))
- case Neq => Seq(cast_if(a0), " != ", cast_if(a1))
+ case Add => Seq(castIf(a0), " + ", castIf(a1))
+ case Addw => Seq(castIf(a0), " + ", castIf(a1))
+ case Sub => Seq(castIf(a0), " - ", castIf(a1))
+ case Subw => Seq(castIf(a0), " - ", castIf(a1))
+ case Mul => Seq(castIf(a0), " * ", castIf(a1))
+ case Div => Seq(castIf(a0), " / ", castIf(a1))
+ case Rem => Seq(castIf(a0), " % ", castIf(a1))
+ case Lt => Seq(castIf(a0), " < ", castIf(a1))
+ case Leq => Seq(castIf(a0), " <= ", castIf(a1))
+ case Gt => Seq(castIf(a0), " > ", castIf(a1))
+ case Geq => Seq(castIf(a0), " >= ", castIf(a1))
+ case Eq => Seq(castIf(a0), " == ", castIf(a1))
+ case Neq => Seq(castIf(a0), " != ", castIf(a1))
case Pad =>
val w = bitWidth(a0.tpe)
val diff = c0 - w
@@ -364,10 +373,10 @@ class VerilogEmitter extends SeqTransform with Emitter {
case (_: SIntType) => Seq("{{", diff, "{", a0, "[", w - 1, "]}},", a0, "}")
case (_) => Seq("{{", diff, "'d0}, ", a0, "}")
}
- case AsUInt => Seq("$unsigned(", a0, ")")
- case AsSInt => Seq("$signed(", a0, ")")
- case AsClock => Seq(a0)
- case AsAsyncReset => Seq(a0)
+ // Because we don't support complex Expressions, all casts are ignored
+ // This simplifies handling of assignment of a signed expression to an unsigned LHS value
+ // which does not require a cast in Verilog
+ case AsUInt | AsSInt | AsClock | AsAsyncReset => Seq(a0)
case Dshlw => Seq(cast(a0), " << ", a1)
case Dshl => Seq(cast(a0), " << ", a1)
case Dshr => doprim.tpe match {
@@ -384,9 +393,9 @@ class VerilogEmitter extends SeqTransform with Emitter {
case (_: SIntType) => Seq(cast(a0))
}
case Not => Seq("~", a0)
- case And => Seq(cast_as(a0), " & ", cast_as(a1))
- case Or => Seq(cast_as(a0), " | ", cast_as(a1))
- case Xor => Seq(cast_as(a0), " ^ ", cast_as(a1))
+ case And => Seq(castAs(a0), " & ", castAs(a1))
+ case Or => Seq(castAs(a0), " | ", castAs(a1))
+ case Xor => Seq(castAs(a0), " ^ ", castAs(a1))
case Andr => Seq("&", cast(a0))
case Orr => Seq("|", cast(a0))
case Xorr => Seq("^", cast(a0))
@@ -967,6 +976,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
new BlackBoxSourceHelper,
new ReplaceTruncatingArithmetic,
new InlineNotsTransform,
+ new InlineCastsTransform,
new FlattenRegUpdate,
new DeadCodeElimination,
passes.VerilogModulusCleanup,
diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala
index 8a76aca6..6cb309b3 100644
--- a/src/main/scala/firrtl/Utils.scala
+++ b/src/main/scala/firrtl/Utils.scala
@@ -188,6 +188,17 @@ object Utils extends LazyLogging {
case sx => sx
}
+ /** Returns true if PrimOp is a cast, false otherwise */
+ def isCast(op: PrimOp): Boolean = op match {
+ case AsUInt | AsSInt | AsClock | AsAsyncReset | AsFixedPoint => true
+ case _ => false
+ }
+ /** Returns true if Expression is a casting PrimOp, false otherwise */
+ def isCast(expr: Expression): Boolean = expr match {
+ case DoPrim(op, _,_,_) if isCast(op) => true
+ case _ => false
+ }
+
/** Provide a nice name to create a temporary **/
def niceName(e: Expression): String = niceName(1)(e)
def niceName(depth: Int)(e: Expression): String = {
diff --git a/src/main/scala/firrtl/transforms/InlineCasts.scala b/src/main/scala/firrtl/transforms/InlineCasts.scala
new file mode 100644
index 00000000..e504eb70
--- /dev/null
+++ b/src/main/scala/firrtl/transforms/InlineCasts.scala
@@ -0,0 +1,70 @@
+package firrtl
+package transforms
+
+import firrtl.ir._
+import firrtl.Mappers._
+
+import firrtl.Utils.{isCast, NodeMap}
+
+object InlineCastsTransform {
+
+ // Checks if an Expression is made up of only casts terminated by a Literal or Reference
+ // There must be at least one cast
+ // Note that this can have false negatives but MUST NOT have false positives
+ private def isSimpleCast(castSeen: Boolean)(expr: Expression): Boolean = expr match {
+ case _: WRef | _: Literal | _: WSubField => castSeen
+ case DoPrim(op, args, _,_) if isCast(op) => args.forall(isSimpleCast(true))
+ case _ => false
+ }
+
+ /** Recursively replace [[WRef]]s with new [[Expression]]s
+ *
+ * @param replace a '''mutable''' HashMap mapping [[WRef]]s to values with which the [[WRef]]
+ * will be replaced. It is '''not''' mutated in this function
+ * @param expr the Expression being transformed
+ * @return Returns expr with [[WRef]]s replaced by values found in replace
+ */
+ def onExpr(replace: NodeMap)(expr: Expression): Expression = {
+ expr.map(onExpr(replace)) match {
+ case e @ WRef(name, _,_,_) =>
+ replace.get(name)
+ .filter(isSimpleCast(castSeen=false))
+ .getOrElse(e)
+ case e @ DoPrim(op, Seq(WRef(name, _,_,_)), _,_) if isCast(op) =>
+ replace.get(name)
+ .map(value => e.copy(args = Seq(value)))
+ .getOrElse(e)
+ case other => other // Not a candidate
+ }
+ }
+
+ /** Inline casts in a Statement
+ *
+ * @param netlist a '''mutable''' HashMap mapping references to [[firrtl.ir.DefNode DefNode]]s to their connected
+ * [[firrtl.ir.Expression Expression]]s. This function '''will''' mutate it if stmt is a [[firrtl.ir.DefNode
+ * DefNode]] with a value that is a cast [[PrimOp]]
+ * @param stmt the Statement being searched for nodes and transformed
+ * @return Returns stmt with casts inlined
+ */
+ def onStmt(netlist: NodeMap)(stmt: Statement): Statement =
+ stmt.map(onStmt(netlist)).map(onExpr(netlist)) match {
+ case node @ DefNode(_, name, value) =>
+ netlist(name) = value
+ node
+ case other => other
+ }
+
+ /** Replaces truncating arithmetic in a Module */
+ def onMod(mod: DefModule): DefModule = mod.map(onStmt(new NodeMap))
+}
+
+/** Inline nodes that are simple casts */
+class InlineCastsTransform extends Transform {
+ def inputForm = LowForm
+ def outputForm = LowForm
+
+ def execute(state: CircuitState): CircuitState = {
+ val modulesx = state.circuit.modules.map(InlineCastsTransform.onMod(_))
+ state.copy(circuit = state.circuit.copy(modules = modulesx))
+ }
+}
diff --git a/src/test/scala/firrtlTests/AsyncResetSpec.scala b/src/test/scala/firrtlTests/AsyncResetSpec.scala
index ed90954b..f347ec14 100644
--- a/src/test/scala/firrtlTests/AsyncResetSpec.scala
+++ b/src/test/scala/firrtlTests/AsyncResetSpec.scala
@@ -65,10 +65,10 @@ class AsyncResetSpec extends FirrtlFlatSpec {
|z <= asAsyncReset(a)
|""".stripMargin
)
- result should containLine ("assign v = $unsigned(a);")
- result should containLine ("assign w = $signed(a);")
+ result should containLine ("assign v = a;")
+ result should containLine ("assign w = a;")
result should containLine ("assign x = a;")
- result should containLine ("assign y = $signed(a);")
+ result should containLine ("assign y = a;")
result should containLine ("assign z = a;")
}
diff --git a/src/test/scala/firrtlTests/VerilogEmitterTests.scala b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
index 0376a830..bb7659e9 100644
--- a/src/test/scala/firrtlTests/VerilogEmitterTests.scala
+++ b/src/test/scala/firrtlTests/VerilogEmitterTests.scala
@@ -182,6 +182,16 @@ class DoPrimVerilog extends FirrtlFlatSpec {
}
class VerilogEmitterSpec extends FirrtlFlatSpec {
+ private def compile(input: String): CircuitState =
+ (new VerilogCompiler).compileAndEmit(CircuitState(parse(input), ChirrtlForm), List.empty)
+ private def compileBody(body: String): CircuitState = {
+ val str = """
+ |circuit Test :
+ | module Test :
+ |""".stripMargin + body.split("\n").mkString(" ", "\n ", "")
+ compile(str)
+ }
+
"Ports" should "emit with widths aligned and names aligned" in {
val compiler = new VerilogCompiler
val input =
@@ -378,7 +388,6 @@ 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 {
@@ -520,6 +529,94 @@ class VerilogEmitterSpec extends FirrtlFlatSpec {
result should containLine ("tmp <= in_9;")
}
+ "SInt addition" should "have casts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : SInt<4>
+ |input y : SInt<4>
+ |output z : SInt
+ |z <= add(x, y)
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + $signed(y);")
+ }
+
+ it should "NOT cast SInt literals" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : SInt<4>
+ |output z : SInt
+ |z <= add(x, SInt(-1))
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + -4'sh1;")
+ }
+
+ it should "inline asSInt casts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<4>
+ |input y : UInt<4>
+ |output z : SInt
+ |node _T_1 = asSInt(x)
+ |z <= add(_T_1, asSInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = $signed(x) + $signed(y);")
+ }
+
+ "Verilog Emitter" should "drop asUInt casts on Clocks" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : Clock
+ |input y : Clock
+ |output z : UInt<1>
+ |node _T_1 = asUInt(x)
+ |z <= eq(_T_1, asUInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asClock casts on UInts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<1>
+ |input y : UInt<1>
+ |output z : Clock
+ |node _T_1 = eq(x, y)
+ |z <= asClock(_T_1)
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asUInt casts on AsyncResets" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : AsyncReset
+ |input y : AsyncReset
+ |output z : UInt<1>
+ |node _T_1 = asUInt(x)
+ |z <= eq(_T_1, asUInt(y))
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
+ it should "drop asAsyncReset casts on UInts" in {
+ val compiler = new VerilogCompiler
+ val result = compileBody(
+ """input x : UInt<1>
+ |input y : UInt<1>
+ |output z : AsyncReset
+ |node _T_1 = eq(x, y)
+ |z <= asAsyncReset(_T_1)
+ |""".stripMargin
+ )
+ result should containLine("assign z = x == y;")
+ }
+
}
class VerilogDescriptionEmitterSpec extends FirrtlFlatSpec {