aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJack Koenig2019-02-14 15:08:35 -0800
committerGitHub2019-02-14 15:08:35 -0800
commit2272044c6ab46b5148c39c124e66e1a8e9073a24 (patch)
tree83ad2141b1a3c54707dd9b33073f9217b0ae16c8 /src
parentd487b4cb6726e7e8d1a18f894021652594125221 (diff)
Asynchronous Reset (#1011)
Fixes #219 * Adds AsyncResetType (similar to ClockType) * Registers with reset signal of type AsyncResetType are async reset registers * Registers with async reset can only be reset to literal values * Add initialization logic for async reset registers
Diffstat (limited to 'src')
-rw-r--r--src/main/antlr4/FIRRTL.g42
-rw-r--r--src/main/proto/firrtl.proto6
-rw-r--r--src/main/scala/firrtl/Emitter.scala103
-rw-r--r--src/main/scala/firrtl/PrimOps.scala19
-rw-r--r--src/main/scala/firrtl/Utils.scala2
-rw-r--r--src/main/scala/firrtl/Visitor.scala1
-rw-r--r--src/main/scala/firrtl/WIR.scala1
-rw-r--r--src/main/scala/firrtl/ir/IR.scala6
-rw-r--r--src/main/scala/firrtl/passes/CheckWidths.scala1
-rw-r--r--src/main/scala/firrtl/passes/Checks.scala54
-rw-r--r--src/main/scala/firrtl/passes/InferWidths.scala12
-rw-r--r--src/main/scala/firrtl/proto/FromProto.scala1
-rw-r--r--src/main/scala/firrtl/proto/ToProto.scala4
-rw-r--r--src/main/scala/firrtl/transforms/ConstantPropagation.scala6
-rw-r--r--src/main/scala/firrtl/transforms/FlattenRegUpdate.scala3
-rw-r--r--src/main/scala/firrtl/transforms/RemoveReset.scala3
-rw-r--r--src/main/scala/firrtl/transforms/RemoveWires.scala36
-rw-r--r--src/test/resources/features/AsyncResetTester.fir43
-rw-r--r--src/test/scala/firrtlTests/AsyncResetSpec.scala169
-rw-r--r--src/test/scala/firrtlTests/ConstantPropagationTests.scala26
-rw-r--r--src/test/scala/firrtlTests/FirrtlSpec.scala13
-rw-r--r--src/test/scala/firrtlTests/ProtoBufSpec.scala8
-rw-r--r--src/test/scala/firrtlTests/RemoveWiresSpec.scala17
23 files changed, 454 insertions, 82 deletions
diff --git a/src/main/antlr4/FIRRTL.g4 b/src/main/antlr4/FIRRTL.g4
index cc5d0a16..8bdbbea3 100644
--- a/src/main/antlr4/FIRRTL.g4
+++ b/src/main/antlr4/FIRRTL.g4
@@ -56,6 +56,7 @@ type
| 'SInt' ('<' intLit '>')?
| 'Fixed' ('<' intLit '>')? ('<' '<' intLit '>' '>')?
| 'Clock'
+ | 'AsyncReset'
| 'Analog' ('<' intLit '>')?
| '{' field* '}' // Bundle
| type '[' intLit ']' // Vector
@@ -253,6 +254,7 @@ primop
| 'neq('
| 'pad('
| 'asUInt('
+ | 'asAsyncReset('
| 'asSInt('
| 'asClock('
| 'shl('
diff --git a/src/main/proto/firrtl.proto b/src/main/proto/firrtl.proto
index 7be042ab..8f115c5e 100644
--- a/src/main/proto/firrtl.proto
+++ b/src/main/proto/firrtl.proto
@@ -262,6 +262,10 @@ message Firrtl {
// Empty.
}
+ message AsyncResetType {
+ // Empty.
+ }
+
message BundleType {
message Field {
// Required.
@@ -299,6 +303,7 @@ message Firrtl {
VectorType vector_type = 6;
FixedType fixed_type = 7;
AnalogType analog_type = 8;
+ AsyncResetType async_reset_type = 9;
}
}
@@ -425,6 +430,7 @@ message Firrtl {
OP_SHIFT_BINARY_POINT_LEFT = 35;
OP_SHIFT_BINARY_POINT_RIGHT = 36;
OP_SET_BINARY_POINT = 37;
+ OP_AS_ASYNC_RESET = 38;
}
// Required.
diff --git a/src/main/scala/firrtl/Emitter.scala b/src/main/scala/firrtl/Emitter.scala
index 8049e33c..edbe9df6 100644
--- a/src/main/scala/firrtl/Emitter.scala
+++ b/src/main/scala/firrtl/Emitter.scala
@@ -156,7 +156,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
case (_: UIntType | _: SIntType | _: AnalogType) =>
val wx = bitWidth(tpe) - 1
if (wx > 0) s"[$wx:0]" else ""
- case ClockType => ""
+ case ClockType | AsyncResetType => ""
case _ => throwInternalError(s"trying to write unsupported type in the Verilog Emitter: $tpe")
}
def emit(x: Any)(implicit w: Writer) { emit(x, 0) }
@@ -297,6 +297,7 @@ class VerilogEmitter extends SeqTransform with Emitter {
case AsUInt => Seq("$unsigned(", a0, ")")
case AsSInt => Seq("$signed(", a0, ")")
case AsClock => Seq(a0)
+ case AsAsyncReset => Seq(a0)
case Dshlw => Seq(cast(a0), " << ", a1)
case Dshl => Seq(cast(a0), " << ", a1)
case Dshr => doprim.tpe match {
@@ -412,8 +413,23 @@ class VerilogEmitter extends SeqTransform with Emitter {
val assigns = ArrayBuffer[Seq[Any]]()
val attachSynAssigns = ArrayBuffer.empty[Seq[Any]]
val attachAliases = ArrayBuffer.empty[Seq[Any]]
- val at_clock = mutable.LinkedHashMap[Expression, ArrayBuffer[Seq[Any]]]()
+ // No (aka synchronous) always blocks, keyed by clock
+ val noResetAlwaysBlocks = mutable.LinkedHashMap[Expression, ArrayBuffer[Seq[Any]]]()
+ // One always block per async reset register, (Clock, Reset, Content)
+ // An alternative approach is to have one always block per combination of clock and async reset,
+ // but Formality doesn't allow more than 1 statement inside async reset always blocks
+ val asyncResetAlwaysBlocks = mutable.ArrayBuffer[(Expression, Expression, Seq[Any])]()
val initials = ArrayBuffer[Seq[Any]]()
+ // In Verilog, async reset registers are expressed using always blocks of the form:
+ // always @(posedge clock or posedge reset) begin
+ // if (reset) ...
+ // There is a fundamental mismatch between this representation which treats async reset
+ // registers as edge-triggered when in reality they are level-triggered.
+ // This can result in silicon-simulation mismatch in the case where async reset is held high
+ // upon power on with no clock, then async reset is dropped before the clock starts. In this
+ // circumstance, the async reset register will be randomized in simulation instead of being
+ // reset. To fix this, we need extra initial block logic for async reset registers
+ val asyncInitials = ArrayBuffer[Seq[Any]]()
val simulates = ArrayBuffer[Seq[Any]]()
def declare(b: String, n: String, t: Type, info: Info) = t match {
@@ -443,12 +459,13 @@ class VerilogEmitter extends SeqTransform with Emitter {
assigns += Seq("`endif // RANDOMIZE_INVALID_ASSIGN")
}
- def regUpdate(r: Expression, clk: Expression) = {
+ 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")
@@ -471,17 +488,23 @@ class VerilogEmitter extends SeqTransform with Emitter {
case e => Seq(Seq(tabs, r, " <= ", e, ";"))
}
}
-
- at_clock.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= addUpdate(netlist(r), "")
+ if (weq(init, r)) { // Synchronous Reset
+ noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]()) ++= addUpdate(netlist(r), "")
+ } else { // Asynchronous Reset
+ assert(reset.tpe == AsyncResetType, "Error! Synchronous reset should have been removed!")
+ val tv = init
+ val fv = netlist(r)
+ asyncResetAlwaysBlocks += ((clk, reset, addUpdate(Mux(reset, tv, fv, mux_type_and_widths(tv, fv)), "")))
+ }
}
def update(e: Expression, value: Expression, clk: Expression, en: Expression, info: Info) = {
- if (!at_clock.contains(clk)) at_clock(clk) = ArrayBuffer[Seq[Any]]()
- if (weq(en, one)) at_clock(clk) += Seq(e, " <= ", value, ";")
+ val lines = noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]())
+ if (weq(en, one)) lines += Seq(e, " <= ", value, ";")
else {
- at_clock(clk) += Seq("if(", en, ") begin")
- at_clock(clk) += Seq(tab, e, " <= ", value, ";", info)
- at_clock(clk) += Seq("end")
+ lines += Seq("if(", en, ") begin")
+ lines += Seq(tab, e, " <= ", value, ";", info)
+ lines += Seq("end")
}
}
@@ -496,10 +519,17 @@ class VerilogEmitter extends SeqTransform with Emitter {
Seq(nx, "[", bitWidth(t) - 1, ":0]")
}
- def initialize(e: Expression) = {
+ def initialize(e: Expression, reset: Expression, init: Expression) = {
initials += Seq("`ifdef RANDOMIZE_REG_INIT")
initials += Seq(e, " = ", rand_string(e.tpe), ";")
initials += Seq("`endif // RANDOMIZE_REG_INIT")
+ reset.tpe match {
+ case AsyncResetType =>
+ asyncInitials += Seq("if (", reset, ") begin")
+ asyncInitials += Seq(tab, e, " = ", init, ";")
+ asyncInitials += Seq("end")
+ case _ => // do nothing
+ }
}
def initialize_mem(s: DefMemory) {
@@ -513,22 +543,22 @@ class VerilogEmitter extends SeqTransform with Emitter {
}
def simulate(clk: Expression, en: Expression, s: Seq[Any], cond: Option[String], info: Info) = {
- if (!at_clock.contains(clk)) at_clock(clk) = ArrayBuffer[Seq[Any]]()
- at_clock(clk) += Seq("`ifndef SYNTHESIS")
+ val lines = noResetAlwaysBlocks.getOrElseUpdate(clk, ArrayBuffer[Seq[Any]]())
+ lines += Seq("`ifndef SYNTHESIS")
if (cond.nonEmpty) {
- at_clock(clk) += Seq(s"`ifdef ${cond.get}")
- at_clock(clk) += Seq(tab, s"if (`${cond.get}) begin")
- at_clock(clk) += Seq("`endif")
+ lines += Seq(s"`ifdef ${cond.get}")
+ lines += Seq(tab, s"if (`${cond.get}) begin")
+ lines += Seq("`endif")
}
- at_clock(clk) += Seq(tab, tab, "if (", en, ") begin")
- at_clock(clk) += Seq(tab, tab, tab, s, info)
- at_clock(clk) += Seq(tab, tab, "end")
+ lines += Seq(tab, tab, "if (", en, ") begin")
+ lines += Seq(tab, tab, tab, s, info)
+ lines += Seq(tab, tab, "end")
if (cond.nonEmpty) {
- at_clock(clk) += Seq(s"`ifdef ${cond.get}")
- at_clock(clk) += Seq(tab, "end")
- at_clock(clk) += Seq("`endif")
+ lines += Seq(s"`ifdef ${cond.get}")
+ lines += Seq(tab, "end")
+ lines += Seq("`endif")
}
- at_clock(clk) += Seq("`endif // SYNTHESIS")
+ lines += Seq("`endif // SYNTHESIS")
}
def stop(ret: Int): Seq[Any] = Seq(if (ret == 0) "$finish;" else "$fatal;")
@@ -614,8 +644,8 @@ class VerilogEmitter extends SeqTransform with Emitter {
case sx: DefRegister =>
declare("reg", sx.name, sx.tpe, sx.info)
val e = wref(sx.name, sx.tpe)
- regUpdate(e, sx.clock)
- initialize(e)
+ regUpdate(e, sx.clock, sx.reset, sx.init)
+ initialize(e, sx.reset, sx.init)
case sx: DefNode =>
declare("wire", sx.name, sx.value.tpe, sx.info)
assign(WRef(sx.name, sx.value.tpe, NodeKind, MALE), sx.value, sx.info)
@@ -757,9 +787,11 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq("`ifndef RANDOM"))
emit(Seq("`define RANDOM $random"))
emit(Seq("`endif"))
- emit(Seq("`ifdef RANDOMIZE"))
+ emit(Seq("`ifdef RANDOMIZE_MEM_INIT"))
emit(Seq(" integer initvar;"))
- emit(Seq(" initial begin"))
+ emit(Seq("`endif"))
+ emit(Seq("initial begin"))
+ emit(Seq(" `ifdef RANDOMIZE"))
emit(Seq(" `ifdef INIT_RANDOM"))
emit(Seq(" `INIT_RANDOM"))
emit(Seq(" `endif"))
@@ -775,13 +807,20 @@ class VerilogEmitter extends SeqTransform with Emitter {
emit(Seq(" `endif"))
emit(Seq(" `endif"))
for (x <- initials) emit(Seq(tab, x))
- emit(Seq(" end"))
- emit(Seq("`endif // RANDOMIZE"))
+ emit(Seq(" `endif // RANDOMIZE"))
+ for (x <- asyncInitials) emit(Seq(tab, x))
+ emit(Seq("end"))
+ }
+
+ for ((clk, content) <- noResetAlwaysBlocks if content.nonEmpty) {
+ emit(Seq(tab, "always @(posedge ", clk, ") begin"))
+ for (line <- content) emit(Seq(tab, tab, line))
+ emit(Seq(tab, "end"))
}
- for (clk_stream <- at_clock if clk_stream._2.nonEmpty) {
- emit(Seq(tab, "always @(posedge ", clk_stream._1, ") begin"))
- for (x <- clk_stream._2) emit(Seq(tab, tab, x))
+ for ((clk, reset, content) <- asyncResetAlwaysBlocks if content.nonEmpty) {
+ emit(Seq(tab, "always @(posedge ", clk, " or posedge ", reset, ") begin"))
+ for (line <- content) emit(Seq(tab, tab, line))
emit(Seq(tab, "end"))
}
emit(Seq("endmodule"))
diff --git a/src/main/scala/firrtl/PrimOps.scala b/src/main/scala/firrtl/PrimOps.scala
index 0e88ff45..4d60bae7 100644
--- a/src/main/scala/firrtl/PrimOps.scala
+++ b/src/main/scala/firrtl/PrimOps.scala
@@ -39,6 +39,8 @@ object PrimOps extends LazyLogging {
case object AsSInt extends PrimOp { override def toString = "asSInt" }
/** Interpret As Clock */
case object AsClock extends PrimOp { override def toString = "asClock" }
+ /** Interpret As AsyncReset */
+ case object AsAsyncReset extends PrimOp { override def toString = "asAsyncReset" }
/** Static Shift Left */
case object Shl extends PrimOp { override def toString = "shl" }
/** Static Shift Right */
@@ -83,8 +85,9 @@ object PrimOps extends LazyLogging {
case object BPSet extends PrimOp { override def toString = "bpset" }
private lazy val builtinPrimOps: Seq[PrimOp] =
- Seq(Add, Sub, Mul, Div, Rem, Lt, Leq, Gt, Geq, Eq, Neq, Pad, AsUInt, AsSInt, AsClock, Shl, Shr,
- Dshl, Dshr, Neg, Cvt, Not, And, Or, Xor, Andr, Orr, Xorr, Cat, Bits, Head, Tail, AsFixedPoint, BPShl, BPShr, BPSet)
+ Seq(Add, Sub, Mul, Div, Rem, Lt, Leq, Gt, Geq, Eq, Neq, Pad, AsUInt, AsSInt, AsClock,
+ AsAsyncReset, Shl, Shr, Dshl, Dshr, Neg, Cvt, Not, And, Or, Xor, Andr, Orr, Xorr, Cat, Bits,
+ Head, Tail, AsFixedPoint, BPShl, BPShr, BPSet)
private lazy val strToPrimOp: Map[String, PrimOp] = builtinPrimOps.map { case op : PrimOp=> op.toString -> op }.toMap
/** Seq of String representations of [[ir.PrimOp]]s */
@@ -203,6 +206,7 @@ object PrimOps extends LazyLogging {
case _: FixedType => UIntType(w1)
case ClockType => UIntType(IntWidth(1))
case AnalogType(w) => UIntType(w1)
+ case AsyncResetType => UIntType(IntWidth(1))
case _ => UnknownType
}
case AsSInt => t1 match {
@@ -211,6 +215,7 @@ object PrimOps extends LazyLogging {
case _: FixedType => SIntType(w1)
case ClockType => SIntType(IntWidth(1))
case _: AnalogType => SIntType(w1)
+ case AsyncResetType => SIntType(IntWidth(1))
case _ => UnknownType
}
case AsFixedPoint => t1 match {
@@ -219,6 +224,7 @@ object PrimOps extends LazyLogging {
case _: FixedType => FixedType(w1, c1)
case ClockType => FixedType(IntWidth(1), c1)
case _: AnalogType => FixedType(w1, c1)
+ case AsyncResetType => FixedType(IntWidth(1), c1)
case _ => UnknownType
}
case AsClock => t1 match {
@@ -226,6 +232,15 @@ object PrimOps extends LazyLogging {
case _: SIntType => ClockType
case ClockType => ClockType
case _: AnalogType => ClockType
+ case AsyncResetType => ClockType
+ case _ => UnknownType
+ }
+ case AsAsyncReset => t1 match {
+ case _: UIntType => AsyncResetType
+ case _: SIntType => AsyncResetType
+ case ClockType => AsyncResetType
+ case _: AnalogType => AsyncResetType
+ case AsyncResetType => AsyncResetType
case _ => UnknownType
}
case Shl => t1 match {
diff --git a/src/main/scala/firrtl/Utils.scala b/src/main/scala/firrtl/Utils.scala
index f60d68b1..ae2835de 100644
--- a/src/main/scala/firrtl/Utils.scala
+++ b/src/main/scala/firrtl/Utils.scala
@@ -388,6 +388,7 @@ object Utils extends LazyLogging {
def mux_type(e1: Expression, e2: Expression): Type = mux_type(e1.tpe, e2.tpe)
def mux_type(t1: Type, t2: Type): Type = (t1, t2) match {
case (ClockType, ClockType) => ClockType
+ case (AsyncResetType, AsyncResetType) => AsyncResetType
case (t1: UIntType, t2: UIntType) => UIntType(UnknownWidth)
case (t1: SIntType, t2: SIntType) => SIntType(UnknownWidth)
case (t1: FixedType, t2: FixedType) => FixedType(UnknownWidth, UnknownWidth)
@@ -406,6 +407,7 @@ object Utils extends LazyLogging {
}
(t1, t2) match {
case (ClockType, ClockType) => ClockType
+ case (AsyncResetType, AsyncResetType) => AsyncResetType
case (t1x: UIntType, t2x: UIntType) => UIntType(wmax(t1x.width, t2x.width))
case (t1x: SIntType, t2x: SIntType) => SIntType(wmax(t1x.width, t2x.width))
case (FixedType(w1, p1), FixedType(w2, p2)) =>
diff --git a/src/main/scala/firrtl/Visitor.scala b/src/main/scala/firrtl/Visitor.scala
index 7b965bff..cd42207c 100644
--- a/src/main/scala/firrtl/Visitor.scala
+++ b/src/main/scala/firrtl/Visitor.scala
@@ -130,6 +130,7 @@ class Visitor(infoMode: InfoMode) extends FIRRTLBaseVisitor[FirrtlNode] {
case 2 => FixedType(getWidth(ctx.intLit(0)), getWidth(ctx.intLit(1)))
}
case "Clock" => ClockType
+ case "AsyncReset" => AsyncResetType
case "Analog" => if (ctx.getChildCount > 1) AnalogType(IntWidth(string2BigInt(ctx.intLit(0).getText)))
else AnalogType(UnknownWidth)
case "{" => BundleType(ctx.field.asScala.map(visitField))
diff --git a/src/main/scala/firrtl/WIR.scala b/src/main/scala/firrtl/WIR.scala
index 7e873c08..0ec37d34 100644
--- a/src/main/scala/firrtl/WIR.scala
+++ b/src/main/scala/firrtl/WIR.scala
@@ -223,6 +223,7 @@ class WrappedType(val t: Type) {
case (_: UIntType, _: UIntType) => true
case (_: SIntType, _: SIntType) => true
case (ClockType, ClockType) => true
+ case (AsyncResetType, AsyncResetType) => true
case (_: FixedType, _: FixedType) => true
// Analog totally skips out of the Firrtl type system.
// The only way Analog can play with another Analog component is through Attach.
diff --git a/src/main/scala/firrtl/ir/IR.scala b/src/main/scala/firrtl/ir/IR.scala
index 4f647c0c..4c00bdd1 100644
--- a/src/main/scala/firrtl/ir/IR.scala
+++ b/src/main/scala/firrtl/ir/IR.scala
@@ -591,6 +591,12 @@ case object ClockType extends GroundType {
def mapWidth(f: Width => Width): Type = this
def foreachWidth(f: Width => Unit): Unit = Unit
}
+case object AsyncResetType extends GroundType {
+ val width = IntWidth(1)
+ def serialize: String = "AsyncReset"
+ def mapWidth(f: Width => Width): Type = this
+ def foreachWidth(f: Width => Unit): Unit = Unit
+}
case class AnalogType(width: Width) extends GroundType {
def serialize: String = "Analog" + width.serialize
def mapWidth(f: Width => Width): Type = AnalogType(f(width))
diff --git a/src/main/scala/firrtl/passes/CheckWidths.scala b/src/main/scala/firrtl/passes/CheckWidths.scala
index 061c6b16..e1e9fc5f 100644
--- a/src/main/scala/firrtl/passes/CheckWidths.scala
+++ b/src/main/scala/firrtl/passes/CheckWidths.scala
@@ -107,6 +107,7 @@ object CheckWidths extends Pass {
case sx: DefRegister =>
sx.reset.tpe match {
case UIntType(IntWidth(w)) if w == 1 =>
+ case AsyncResetType =>
case _ => errors.append(new CheckTypes.IllegalResetType(info, target.serialize, sx.name))
}
case _ =>
diff --git a/src/main/scala/firrtl/passes/Checks.scala b/src/main/scala/firrtl/passes/Checks.scala
index 4b996f5d..b6855d75 100644
--- a/src/main/scala/firrtl/passes/Checks.scala
+++ b/src/main/scala/firrtl/passes/Checks.scala
@@ -53,6 +53,8 @@ object CheckHighForm extends Pass {
s"$info: [module $mname] Primop $op argument $value < 0.")
class LsbLargerThanMsbException(info: Info, mname: String, op: String, lsb: Int, msb: Int) extends PassException(
s"$info: [module $mname] Primop $op lsb $lsb > $msb.")
+ class NonLiteralAsyncResetValueException(info: Info, mname: String, reg: String, init: String) extends PassException(
+ s"$info: [module $mname] AsyncReset Reg '$reg' reset to non-literal '$init'")
def run(c: Circuit): Circuit = {
val errors = new Errors()
@@ -74,7 +76,7 @@ object CheckHighForm extends Pass {
case Add | Sub | Mul | Div | Rem | Lt | Leq | Gt | Geq |
Eq | Neq | Dshl | Dshr | And | Or | Xor | Cat =>
correctNum(Option(2), 0)
- case AsUInt | AsSInt | AsClock | Cvt | Neq | Not =>
+ case AsUInt | AsSInt | AsClock | AsAsyncReset | Cvt | Neq | Not =>
correctNum(Option(1), 0)
case AsFixedPoint | Pad | Head | Tail | BPShl | BPShr | BPSet =>
correctNum(Option(1), 1)
@@ -164,6 +166,13 @@ object CheckHighForm extends Pass {
val info = get_info(s) match {case NoInfo => minfo case x => x}
s foreach checkName(info, mname, names)
s match {
+ case DefRegister(info, name, _,_, reset, init) if reset.tpe == AsyncResetType =>
+ init match {
+ case _: Literal => // okay
+ case nonlit =>
+ val e = new NonLiteralAsyncResetValueException(info, mname, name, nonlit.serialize)
+ errors.append(e)
+ }
case sx: DefMemory =>
if (hasFlip(sx.dataType))
errors.append(new MemWithFlipException(info, mname, sx.name))
@@ -291,41 +300,43 @@ object CheckTypes extends Pass {
case tx => true
}
def check_types_primop(info: Info, mname: String, e: DoPrim): Unit = {
- def checkAllTypes(exprs: Seq[Expression], okUInt: Boolean, okSInt: Boolean, okClock: Boolean, okFix: Boolean): Unit = {
- exprs.foldLeft((false, false, false, false)) {
- case ((isUInt, isSInt, isClock, isFix), expr) => expr.tpe match {
- case u: UIntType => (true, isSInt, isClock, isFix)
- case s: SIntType => (isUInt, true, isClock, isFix)
- case ClockType => (isUInt, isSInt, true, isFix)
- case f: FixedType => (isUInt, isSInt, isClock, true)
- case UnknownType =>
+ def checkAllTypes(exprs: Seq[Expression], okUInt: Boolean, okSInt: Boolean, okClock: Boolean, okFix: Boolean, okAsync: Boolean): Unit = {
+ exprs.foldLeft((false, false, false, false, false)) {
+ case ((isUInt, isSInt, isClock, isFix, isAsync), expr) => expr.tpe match {
+ case u: UIntType => (true, isSInt, isClock, isFix, isAsync)
+ case s: SIntType => (isUInt, true, isClock, isFix, isAsync)
+ case ClockType => (isUInt, isSInt, true, isFix, isAsync)
+ case f: FixedType => (isUInt, isSInt, isClock, true, isAsync)
+ case AsyncResetType => (isUInt, isSInt, isClock, isFix, true)
+ case UnknownType =>
errors.append(new IllegalUnknownType(info, mname, e.serialize))
- (isUInt, isSInt, isClock, isFix)
+ (isUInt, isSInt, isClock, isFix, isAsync)
case other => throwInternalError(s"Illegal Type: ${other.serialize}")
}
} match {
// (UInt, SInt, Clock, Fixed)
- case (isAll, false, false, false) if isAll == okUInt =>
- case (false, isAll, false, false) if isAll == okSInt =>
- case (false, false, isAll, false) if isAll == okClock =>
- case (false, false, false, isAll) if isAll == okFix =>
+ case (isAll, false, false, false, false) if isAll == okUInt =>
+ case (false, isAll, false, false, false) if isAll == okSInt =>
+ case (false, false, isAll, false, false) if isAll == okClock =>
+ case (false, false, false, isAll, false) if isAll == okFix =>
+ case (false, false, false, false, isAll) if isAll == okAsync =>
case x => errors.append(new OpNotCorrectType(info, mname, e.op.serialize, exprs.map(_.tpe.serialize)))
}
}
e.op match {
- case AsUInt | AsSInt | AsClock | AsFixedPoint =>
+ case AsUInt | AsSInt | AsClock | AsFixedPoint | AsAsyncReset =>
// All types are ok
case Dshl | Dshr =>
- checkAllTypes(Seq(e.args.head), okUInt=true, okSInt=true, okClock=false, okFix=true)
- checkAllTypes(Seq(e.args(1)), okUInt=true, okSInt=false, okClock=false, okFix=false)
+ checkAllTypes(Seq(e.args.head), okUInt=true, okSInt=true, okClock=false, okFix=true, okAsync=false)
+ checkAllTypes(Seq(e.args(1)), okUInt=true, okSInt=false, okClock=false, okFix=false, okAsync=false)
case Add | Sub | Mul | Lt | Leq | Gt | Geq | Eq | Neq =>
- checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=true)
+ checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=true, okAsync=false)
case Pad | Shl | Shr | Cat | Bits | Head | Tail =>
- checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=true)
+ checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=true, okAsync=false)
case BPShl | BPShr | BPSet =>
- checkAllTypes(e.args, okUInt=false, okSInt=false, okClock=false, okFix=true)
+ checkAllTypes(e.args, okUInt=false, okSInt=false, okClock=false, okFix=true, okAsync=false)
case _ =>
- checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=false)
+ checkAllTypes(e.args, okUInt=true, okSInt=true, okClock=false, okFix=false, okAsync=false)
}
}
@@ -415,6 +426,7 @@ object CheckTypes extends Pass {
}
sx.reset.tpe match {
case UIntType(IntWidth(w)) if w == 1 =>
+ case AsyncResetType =>
case UIntType(UnknownWidth) => // cannot catch here, though width may ultimately be wrong
case _ => errors.append(new IllegalResetType(info, mname, sx.name))
}
diff --git a/src/main/scala/firrtl/passes/InferWidths.scala b/src/main/scala/firrtl/passes/InferWidths.scala
index 6420c3a2..6652c1fe 100644
--- a/src/main/scala/firrtl/passes/InferWidths.scala
+++ b/src/main/scala/firrtl/passes/InferWidths.scala
@@ -228,6 +228,7 @@ object InferWidths extends Pass {
case (t1: UIntType, t2: UIntType) => Seq(WGeq(t1.width, t2.width))
case (t1: SIntType, t2: SIntType) => Seq(WGeq(t1.width, t2.width))
case (ClockType, ClockType) => Nil
+ case (AsyncResetType, AsyncResetType) => Nil
case (FixedType(w1, p1), FixedType(w2, p2)) => Seq(WGeq(w1,w2), WGeq(p1,p2))
case (AnalogType(w1), AnalogType(w2)) => Seq(WGeq(w1,w2), WGeq(w2,w1))
case (t1: BundleType, t2: BundleType) =>
@@ -282,10 +283,13 @@ object InferWidths extends Pass {
case Flip => get_constraints_t(expx.tpe, locx.tpe)//WGeq(getWidth(expx), getWidth(locx))
}
})
- case (s: DefRegister) => v ++= (
- get_constraints_t(s.reset.tpe, UIntType(IntWidth(1))) ++
- get_constraints_t(UIntType(IntWidth(1)), s.reset.tpe) ++
- get_constraints_t(s.tpe, s.init.tpe))
+ case (s: DefRegister) =>
+ if (s.reset.tpe != AsyncResetType ) {
+ v ++= (
+ get_constraints_t(s.reset.tpe, UIntType(IntWidth(1))) ++
+ get_constraints_t(UIntType(IntWidth(1)), s.reset.tpe))
+ }
+ v ++= get_constraints_t(s.tpe, s.init.tpe)
case (s:Conditionally) => v ++=
get_constraints_t(s.pred.tpe, UIntType(IntWidth(1))) ++
get_constraints_t(UIntType(IntWidth(1)), s.pred.tpe)
diff --git a/src/main/scala/firrtl/proto/FromProto.scala b/src/main/scala/firrtl/proto/FromProto.scala
index dda2099c..e7d415cb 100644
--- a/src/main/scala/firrtl/proto/FromProto.scala
+++ b/src/main/scala/firrtl/proto/FromProto.scala
@@ -240,6 +240,7 @@ object FromProto {
case SINT_TYPE_FIELD_NUMBER => convert(tpe.getSintType)
case FIXED_TYPE_FIELD_NUMBER => convert(tpe.getFixedType)
case CLOCK_TYPE_FIELD_NUMBER => ir.ClockType
+ case ASYNC_RESET_TYPE_FIELD_NUMBER => ir.AsyncResetType
case ANALOG_TYPE_FIELD_NUMBER => convert(tpe.getAnalogType)
case BUNDLE_TYPE_FIELD_NUMBER =>
ir.BundleType(tpe.getBundleType.getFieldList.asScala.map(convert(_)))
diff --git a/src/main/scala/firrtl/proto/ToProto.scala b/src/main/scala/firrtl/proto/ToProto.scala
index b3fb9a0c..b0b59e06 100644
--- a/src/main/scala/firrtl/proto/ToProto.scala
+++ b/src/main/scala/firrtl/proto/ToProto.scala
@@ -80,6 +80,7 @@ object ToProto {
AsSInt -> Op.OP_AS_SINT,
AsClock -> Op.OP_AS_CLOCK,
AsFixedPoint -> Op.OP_AS_FIXED_POINT,
+ AsAsyncReset -> Op.OP_AS_ASYNC_RESET,
Shl -> Op.OP_SHIFT_LEFT,
Shr -> Op.OP_SHIFT_RIGHT,
Dshl -> Op.OP_DYNAMIC_SHIFT_LEFT,
@@ -335,6 +336,9 @@ object ToProto {
case ir.ClockType =>
val ct = Firrtl.Type.ClockType.newBuilder()
tb.setClockType(ct)
+ case ir.AsyncResetType =>
+ val at = Firrtl.Type.AsyncResetType.newBuilder()
+ tb.setAsyncResetType(at)
case ir.AnalogType(width) =>
val at = Firrtl.Type.AnalogType.newBuilder()
convert(width).foreach(at.setWidth)
diff --git a/src/main/scala/firrtl/transforms/ConstantPropagation.scala b/src/main/scala/firrtl/transforms/ConstantPropagation.scala
index 6618312a..fdaa7112 100644
--- a/src/main/scala/firrtl/transforms/ConstantPropagation.scala
+++ b/src/main/scala/firrtl/transforms/ConstantPropagation.scala
@@ -350,6 +350,8 @@ class ConstantPropagation extends Transform with ResolvedAnnotationPaths {
// Keep track of any submodule inputs we drive with a constant
// (can have more than 1 of the same submodule)
val constSubInputs = mutable.HashMap.empty[String, mutable.HashMap[String, Seq[Literal]]]
+ // AsyncReset registers don't have reset turned into a mux so we must be careful
+ val asyncResetRegs = mutable.HashSet.empty[String]
// Copy constant mapping for constant inputs (except ones marked dontTouch!)
nodeMap ++= constInputs.filterNot { case (pname, _) => dontTouches.contains(pname) }
@@ -405,6 +407,8 @@ class ConstantPropagation extends Transform with ResolvedAnnotationPaths {
// Record things that should be propagated
stmtx match {
case x: DefNode if !dontTouches.contains(x.name) => propagateRef(x.name, x.value)
+ case reg: DefRegister if reg.reset.tpe == AsyncResetType =>
+ asyncResetRegs += reg.name
case Connect(_, WRef(wname, wtpe, WireKind, _), expr: Literal) if !dontTouches.contains(wname) =>
val exprx = constPropExpression(nodeMap, instMap, constSubOutputs)(pad(expr, wtpe))
propagateRef(wname, exprx)
@@ -414,7 +418,7 @@ class ConstantPropagation extends Transform with ResolvedAnnotationPaths {
constOutputs(pname) = paddedLit
// Const prop registers that are driven by a mux tree containing only instances of one constant or self-assigns
// This requires that reset has been made explicit
- case Connect(_, lref @ WRef(lname, ltpe, RegKind, _), rhs) if !dontTouches.contains(lname) =>
+ case Connect(_, lref @ WRef(lname, ltpe, RegKind, _), rhs) if !dontTouches(lname) && !asyncResetRegs(lname) =>
/** Checks if an RHS expression e of a register assignment is convertible to a constant assignment.
* Here, this means that e must be 1) a literal, 2) a self-connect, or 3) a mux tree of cases (1) and (2).
* In case (3), it also recursively checks that the two mux cases are convertible to constants and
diff --git a/src/main/scala/firrtl/transforms/FlattenRegUpdate.scala b/src/main/scala/firrtl/transforms/FlattenRegUpdate.scala
index f21e6b18..2bce124c 100644
--- a/src/main/scala/firrtl/transforms/FlattenRegUpdate.scala
+++ b/src/main/scala/firrtl/transforms/FlattenRegUpdate.scala
@@ -81,7 +81,8 @@ object FlattenRegUpdate {
def onStmt(stmt: Statement): Statement = stmt.map(onStmt) match {
case reg @ DefRegister(_, rname, _,_, resetCond, _) =>
- assert(resetCond == Utils.zero, "Register reset should have already been made explicit!")
+ assert(resetCond.tpe == AsyncResetType || resetCond == Utils.zero,
+ "Synchronous reset should have already been made explicit!")
val ref = WRef(reg)
val update = Connect(NoInfo, ref, constructRegUpdate(netlist.getOrElse(ref, ref)))
regUpdates += update
diff --git a/src/main/scala/firrtl/transforms/RemoveReset.scala b/src/main/scala/firrtl/transforms/RemoveReset.scala
index bfec76a2..0b8b907d 100644
--- a/src/main/scala/firrtl/transforms/RemoveReset.scala
+++ b/src/main/scala/firrtl/transforms/RemoveReset.scala
@@ -22,7 +22,8 @@ class RemoveReset extends Transform {
val resets = mutable.HashMap.empty[String, Reset]
def onStmt(stmt: Statement): Statement = {
stmt match {
- case reg @ DefRegister(_, rname, _, _, reset, init) if reset != Utils.zero =>
+ case reg @ DefRegister(_, rname, _, _, reset, init)
+ if reset != Utils.zero && reset.tpe != AsyncResetType =>
// Add register reset to map
resets(rname) = Reset(reset, init)
reg.copy(reset = Utils.zero, init = WRef(reg))
diff --git a/src/main/scala/firrtl/transforms/RemoveWires.scala b/src/main/scala/firrtl/transforms/RemoveWires.scala
index 1b5b3e5f..da79be8e 100644
--- a/src/main/scala/firrtl/transforms/RemoveWires.scala
+++ b/src/main/scala/firrtl/transforms/RemoveWires.scala
@@ -6,6 +6,7 @@ package transforms
import firrtl.ir._
import firrtl.Utils._
import firrtl.Mappers._
+import firrtl.traversals.Foreachers._
import firrtl.WrappedExpression._
import firrtl.graph.{DiGraph, MutableDiGraph, CyclicException}
@@ -29,7 +30,7 @@ class RemoveWires extends Transform {
def rec(e: Expression): Expression = {
e match {
case ref @ WRef(_,_, WireKind | NodeKind | RegKind, _) => refs += ref
- case nested @ (_: Mux | _: DoPrim | _: ValidIf) => nested map rec
+ case nested @ (_: Mux | _: DoPrim | _: ValidIf) => nested.foreach(rec)
case _ => // Do nothing
}
e
@@ -40,13 +41,15 @@ class RemoveWires extends Transform {
// Transform netlist into DefNodes
private def getOrderedNodes(
- netlist: mutable.LinkedHashMap[WrappedExpression, (Expression, Info)],
+ netlist: mutable.LinkedHashMap[WrappedExpression, (Seq[Expression], Info)],
regInfo: mutable.Map[WrappedExpression, DefRegister]): Try[Seq[Statement]] = {
val digraph = new MutableDiGraph[WrappedExpression]
- for ((sink, (expr, _)) <- netlist) {
+ for ((sink, (exprs, _)) <- netlist) {
digraph.addVertex(sink)
- for (source <- extractNodeWireRegRefs(expr)) {
- digraph.addPairWithEdge(sink, source)
+ for (expr <- exprs) {
+ for (source <- extractNodeWireRegRefs(expr)) {
+ digraph.addPairWithEdge(sink, source)
+ }
}
}
@@ -57,10 +60,11 @@ class RemoveWires extends Transform {
val ordered = digraph.linearize.reverse
ordered.map { key =>
val WRef(name, _, kind, _) = key.e1
- val (rhs, info) = netlist(key)
kind match {
case RegKind => regInfo(key)
- case WireKind | NodeKind => DefNode(info, name, rhs)
+ case WireKind | NodeKind =>
+ val (Seq(rhs), info) = netlist(key)
+ DefNode(info, name, rhs)
}
}
}
@@ -72,7 +76,7 @@ class RemoveWires extends Transform {
// Store all "other" statements here, non-wire, non-node connections, printfs, etc.
val otherStmts = mutable.ArrayBuffer.empty[Statement]
// Add nodes and wire connection here
- val netlist = mutable.LinkedHashMap.empty[WrappedExpression, (Expression, Info)]
+ val netlist = mutable.LinkedHashMap.empty[WrappedExpression, (Seq[Expression], Info)]
// Info at definition of wires for combining into node
val wireInfo = mutable.HashMap.empty[WrappedExpression, Info]
// Additional info about registers
@@ -81,12 +85,16 @@ class RemoveWires extends Transform {
def onStmt(stmt: Statement): Statement = {
stmt match {
case node: DefNode =>
- netlist(we(WRef(node))) = (node.value, node.info)
+ netlist(we(WRef(node))) = (Seq(node.value), node.info)
case wire: DefWire if !wire.tpe.isInstanceOf[AnalogType] => // Remove all non-Analog wires
wireInfo(WRef(wire)) = wire.info
case reg: DefRegister =>
+ val resetDep = reg.reset.tpe match {
+ case AsyncResetType => reg.reset :: Nil
+ case _ => Nil
+ }
regInfo(we(WRef(reg))) = reg
- netlist(we(WRef(reg))) = (reg.clock, reg.info)
+ netlist(we(WRef(reg))) = (reg.clock :: resetDep, reg.info)
case decl: IsDeclaration => // Keep all declarations except for nodes and non-Analog wires
decls += decl
case con @ Connect(cinfo, lhs, rhs) => kind(lhs) match {
@@ -94,20 +102,20 @@ class RemoveWires extends Transform {
// Be sure to pad the rhs since nodes get their type from the rhs
val paddedRhs = ConstantPropagation.pad(rhs, lhs.tpe)
val dinfo = wireInfo(lhs)
- netlist(we(lhs)) = (paddedRhs, MultiInfo(dinfo, cinfo))
+ netlist(we(lhs)) = (Seq(paddedRhs), MultiInfo(dinfo, cinfo))
case _ => otherStmts += con // Other connections just pass through
}
case invalid @ IsInvalid(info, expr) =>
kind(expr) match {
case WireKind =>
val width = expr.tpe match { case GroundType(width) => width } // LowFirrtl
- netlist(we(expr)) = (ValidIf(Utils.zero, UIntLiteral(BigInt(0), width), expr.tpe), info)
+ netlist(we(expr)) = (Seq(ValidIf(Utils.zero, UIntLiteral(BigInt(0), width), expr.tpe)), info)
case _ => otherStmts += invalid
}
case other @ (_: Print | _: Stop | _: Attach) =>
otherStmts += other
case EmptyStmt => // Dont bother keeping EmptyStmts around
- case block: Block => block map onStmt
+ case block: Block => block.foreach(onStmt)
case _ => throwInternalError()
}
stmt
@@ -136,7 +144,7 @@ class RemoveWires extends Transform {
)
def execute(state: CircuitState): CircuitState = {
- val result = state.copy(circuit = state.circuit map onModule)
+ val result = state.copy(circuit = state.circuit.map(onModule))
cleanup.foldLeft(result) { case (in, xform) => xform.execute(in) }
}
}
diff --git a/src/test/resources/features/AsyncResetTester.fir b/src/test/resources/features/AsyncResetTester.fir
new file mode 100644
index 00000000..15efbd8d
--- /dev/null
+++ b/src/test/resources/features/AsyncResetTester.fir
@@ -0,0 +1,43 @@
+
+circuit AsyncResetTester :
+ module AsyncResetTester :
+ input clock : Clock
+ input reset : UInt<1>
+
+ reg div : UInt<2>, clock with : (reset => (reset, UInt(0)))
+ div <= tail(add(div, UInt(1)), 1)
+
+ reg slowClkReg : UInt<1>, clock with : (reset => (reset, UInt(0)))
+ slowClkReg <= eq(div, UInt(0))
+ node slowClk = asClock(slowClkReg)
+
+ reg counter : UInt<4>, clock with : (reset => (reset, UInt(0)))
+ counter <= tail(add(counter, UInt(1)), 1)
+
+ reg asyncResetReg : UInt<1>, clock with : (reset => (reset, UInt(0)))
+ asyncResetReg <= eq(counter, UInt(2))
+ node asyncReset = asAsyncReset(asyncResetReg)
+
+ reg r : UInt<8>, slowClk with : (reset => (asyncReset, UInt("h55")))
+ ; We always set the register on slowClk
+ when UInt(1) :
+ r <= UInt("hff")
+
+ when and(leq(counter, UInt(2)), neq(counter, UInt(0))) :
+ when neq(r, UInt("hff")) :
+ printf(clock, UInt(1), "Assertion 1 failed!\n")
+ stop(clock, UInt(1), 1)
+ ; Do the async reset
+ when eq(counter, UInt(3)) :
+ when neq(r, UInt("h55")) :
+ printf(clock, UInt(1), "Assertion 2 failed!\n")
+ stop(clock, UInt(1), 1)
+ ; Back to normal value
+ when eq(counter, UInt(5)) :
+ when neq(r, UInt("hff")) :
+ printf(clock, UInt(1), "Assertion 3 failed!\n")
+ stop(clock, UInt(1), 1)
+ ; Success!
+ when eq(counter, UInt(6)) :
+ stop(clock, UInt(1), 0)
+
diff --git a/src/test/scala/firrtlTests/AsyncResetSpec.scala b/src/test/scala/firrtlTests/AsyncResetSpec.scala
new file mode 100644
index 00000000..c1078a03
--- /dev/null
+++ b/src/test/scala/firrtlTests/AsyncResetSpec.scala
@@ -0,0 +1,169 @@
+// See LICENSE for license details.
+
+package firrtlTests
+
+import firrtl._
+import firrtl.ir._
+import FirrtlCheckers._
+
+class AsyncResetSpec extends FirrtlFlatSpec {
+ def compile(input: String): CircuitState =
+ (new VerilogCompiler).compileAndEmit(CircuitState(parse(input), ChirrtlForm), List.empty)
+ def compileBody(body: String) = {
+ val str = """
+ |circuit Test :
+ | module Test :
+ |""".stripMargin + body.split("\n").mkString(" ", "\n ", "")
+ compile(str)
+ }
+
+ "AsyncReset" should "generate async-reset always blocks" in {
+ val result = compileBody(s"""
+ |input clock : Clock
+ |input reset : AsyncReset
+ |input x : UInt<8>
+ |output z : UInt<8>
+ |reg r : UInt<8>, clock with : (reset => (reset, UInt(123)))
+ |r <= x
+ |z <= r""".stripMargin
+ )
+ result should containLine ("always @(posedge clock or posedge reset) begin")
+ }
+
+ it should "support casting to other types" in {
+ val result = compileBody(s"""
+ |input a : AsyncReset
+ |output v : UInt<1>
+ |output w : SInt<1>
+ |output x : Clock
+ |output y : Fixed<1><<0>>
+ |output z : AsyncReset
+ |v <= asUInt(a)
+ |w <= asSInt(a)
+ |x <= asClock(a)
+ |y <= asFixedPoint(a, 0)
+ |z <= asAsyncReset(a)""".stripMargin
+ )
+ result should containLine ("assign v = $unsigned(a);")
+ result should containLine ("assign w = $signed(a);")
+ result should containLine ("assign x = a;")
+ result should containLine ("assign y = $signed(a);")
+ result should containLine ("assign z = a;")
+ }
+
+ "Other types" should "support casting to AsyncReset" in {
+ val result = compileBody(s"""
+ |input a : UInt<1>
+ |input b : SInt<1>
+ |input c : Clock
+ |input d : Fixed<1><<0>>
+ |input e : AsyncReset
+ |output v : AsyncReset
+ |output w : AsyncReset
+ |output x : AsyncReset
+ |output y : AsyncReset
+ |output z : AsyncReset
+ |v <= asAsyncReset(a)
+ |w <= asAsyncReset(a)
+ |x <= asAsyncReset(a)
+ |y <= asAsyncReset(a)
+ |z <= asAsyncReset(a)""".stripMargin
+ )
+ result should containLine ("assign v = a;")
+ result should containLine ("assign w = a;")
+ result should containLine ("assign x = a;")
+ result should containLine ("assign y = a;")
+ result should containLine ("assign z = a;")
+ }
+
+ "Non-literals" should "NOT be allowed as reset values for AsyncReset" in {
+ an [passes.CheckHighForm.NonLiteralAsyncResetValueException] shouldBe thrownBy {
+ compileBody(s"""
+ |input clock : Clock
+ |input reset : AsyncReset
+ |input x : UInt<8>
+ |input y : UInt<8>
+ |output z : UInt<8>
+ |reg r : UInt<8>, clock with : (reset => (reset, y))
+ |r <= x
+ |z <= r""".stripMargin
+ )
+ }
+ }
+
+
+ "Every async reset reg" should "generate its own always block" in {
+ val result = compileBody(s"""
+ |input clock0 : Clock
+ |input clock1 : Clock
+ |input syncReset : UInt<1>
+ |input asyncReset : AsyncReset
+ |input x : UInt<8>[5]
+ |output z : UInt<8>[5]
+ |reg r0 : UInt<8>, clock0 with : (reset => (syncReset, UInt(123)))
+ |reg r1 : UInt<8>, clock1 with : (reset => (syncReset, UInt(123)))
+ |reg r2 : UInt<8>, clock0 with : (reset => (asyncReset, UInt(123)))
+ |reg r3 : UInt<8>, clock0 with : (reset => (asyncReset, UInt(123)))
+ |reg r4 : UInt<8>, clock1 with : (reset => (asyncReset, UInt(123)))
+ |r0 <= x[0]
+ |r1 <= x[1]
+ |r2 <= x[2]
+ |r3 <= x[3]
+ |r4 <= x[4]
+ |z[0] <= r0
+ |z[1] <= r1
+ |z[2] <= r2
+ |z[3] <= r3
+ |z[4] <= r4""".stripMargin
+ )
+ result should containLines (
+ "always @(posedge clock0) begin",
+ "if (syncReset) begin",
+ "r0 <= 8'h7b;",
+ "end else begin",
+ "r0 <= x_0;",
+ "end",
+ "end"
+ )
+ result should containLines (
+ "always @(posedge clock1) begin",
+ "if (syncReset) begin",
+ "r1 <= 8'h7b;",
+ "end else begin",
+ "r1 <= x_1;",
+ "end",
+ "end"
+ )
+ result should containLines (
+ "always @(posedge clock0 or posedge asyncReset) begin",
+ "if (asyncReset) begin",
+ "r2 <= 8'h7b;",
+ "end else begin",
+ "r2 <= x_2;",
+ "end",
+ "end"
+ )
+ result should containLines (
+ "always @(posedge clock0 or posedge asyncReset) begin",
+ "if (asyncReset) begin",
+ "r3 <= 8'h7b;",
+ "end else begin",
+ "r3 <= x_3;",
+ "end",
+ "end"
+ )
+ result should containLines (
+ "always @(posedge clock1 or posedge asyncReset) begin",
+ "if (asyncReset) begin",
+ "r4 <= 8'h7b;",
+ "end else begin",
+ "r4 <= x_4;",
+ "end",
+ "end"
+ )
+ }
+
+}
+
+class AsyncResetExecutionTest extends ExecutionTest("AsyncResetTester", "/features")
+
diff --git a/src/test/scala/firrtlTests/ConstantPropagationTests.scala b/src/test/scala/firrtlTests/ConstantPropagationTests.scala
index ee2540e0..6fb2ab8d 100644
--- a/src/test/scala/firrtlTests/ConstantPropagationTests.scala
+++ b/src/test/scala/firrtlTests/ConstantPropagationTests.scala
@@ -1005,6 +1005,32 @@ class ConstantPropagationIntegrationSpec extends LowTransformSpec {
execute(input, check, Seq.empty)
}
+ "Registers async reset and a constant connection" should "NOT be removed" in {
+ val input =
+ """circuit Top :
+ | module Top :
+ | input clock : Clock
+ | input reset : AsyncReset
+ | input en : UInt<1>
+ | output z : UInt<8>
+ | reg r : UInt<8>, clock with : (reset => (reset, UInt<4>("hb")))
+ | when en :
+ | r <= UInt<4>("h0")
+ | z <= r""".stripMargin
+ val check =
+ """circuit Top :
+ | module Top :
+ | input clock : Clock
+ | input reset : AsyncReset
+ | input en : UInt<1>
+ | output z : UInt<8>
+ | reg r : UInt<8>, clock with :
+ | reset => (reset, UInt<8>("hb"))
+ | z <= r
+ | r <= mux(en, UInt<8>("h0"), r)""".stripMargin
+ execute(input, check, Seq.empty)
+ }
+
"Registers with constant reset and connection to the same constant" should "be replaced with that constant" in {
val input =
"""circuit Top :
diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala
index 88238785..b3729f96 100644
--- a/src/test/scala/firrtlTests/FirrtlSpec.scala
+++ b/src/test/scala/firrtlTests/FirrtlSpec.scala
@@ -216,15 +216,18 @@ object FirrtlCheckers extends FirrtlMatchers {
}
/** Checks that the emitted circuit has the expected line, both will be normalized */
- def containLine(expectedLine: String) = new CircuitStateStringMatcher(expectedLine)
+ def containLine(expectedLine: String) = containLines(expectedLine)
- class CircuitStateStringMatcher(expectedLine: String) extends Matcher[CircuitState] {
+ /** Checks that the emitted circuit has the expected lines in order, all lines will be normalized */
+ def containLines(expectedLines: String*) = new CircuitStateStringsMatcher(expectedLines)
+
+ class CircuitStateStringsMatcher(expectedLines: Seq[String]) extends Matcher[CircuitState] {
override def apply(state: CircuitState): MatchResult = {
val emitted = state.getEmittedCircuit.value
MatchResult(
- emitted.split("\n").map(normalized).contains(normalized(expectedLine)),
- emitted + "\n did not contain \"" + expectedLine + "\"",
- s"${state.circuit.main} contained $expectedLine"
+ emitted.split("\n").map(normalized).containsSlice(expectedLines.map(normalized)),
+ emitted + "\n did not contain \"" + expectedLines + "\"",
+ s"${state.circuit.main} contained $expectedLines"
)
}
}
diff --git a/src/test/scala/firrtlTests/ProtoBufSpec.scala b/src/test/scala/firrtlTests/ProtoBufSpec.scala
index ff266f1f..090a7fea 100644
--- a/src/test/scala/firrtlTests/ProtoBufSpec.scala
+++ b/src/test/scala/firrtlTests/ProtoBufSpec.scala
@@ -24,7 +24,8 @@ class ProtoBufSpec extends FirrtlFlatSpec {
FirrtlResourceTest("Rob", "/regress"),
FirrtlResourceTest("RocketCore", "/regress"),
FirrtlResourceTest("ICache", "/regress"),
- FirrtlResourceTest("FPU", "/regress")
+ FirrtlResourceTest("FPU", "/regress"),
+ FirrtlResourceTest("AsyncResetTester", "/features")
)
for (FirrtlResourceTest(name, dir) <- firrtlResourceTests) {
@@ -136,4 +137,9 @@ class ProtoBufSpec extends FirrtlFlatSpec {
val slit = ir.SIntLiteral(-123)
FromProto.convert(ToProto.convert(slit).build) should equal (slit)
}
+
+ it should "support AsyncResetTypes" in {
+ val port = ir.Port(ir.NoInfo, "reset", ir.Input, ir.AsyncResetType)
+ FromProto.convert(ToProto.convert(port).build) should equal (port)
+ }
}
diff --git a/src/test/scala/firrtlTests/RemoveWiresSpec.scala b/src/test/scala/firrtlTests/RemoveWiresSpec.scala
index d15e6908..e40a770b 100644
--- a/src/test/scala/firrtlTests/RemoveWiresSpec.scala
+++ b/src/test/scala/firrtlTests/RemoveWiresSpec.scala
@@ -165,4 +165,21 @@ class RemoveWiresSpec extends FirrtlFlatSpec {
// Check declaration before use is maintained
passes.CheckHighForm.execute(result)
}
+
+ it should "order registers with async reset correctly" in {
+ val result = compileBody(s"""
+ |input clock : Clock
+ |input reset : UInt<1>
+ |input in : UInt<8>
+ |output out : UInt<8>
+ |wire areset : AsyncReset
+ |reg r : UInt<8>, clock with : (reset => (areset, UInt(0)))
+ |areset <= asAsyncReset(reset)
+ |r <= in
+ |out <= r
+ |""".stripMargin
+ )
+ // Check declaration before use is maintained
+ passes.CheckHighForm.execute(result)
+ }
}