diff options
| author | Jack Koenig | 2019-02-14 15:08:35 -0800 |
|---|---|---|
| committer | GitHub | 2019-02-14 15:08:35 -0800 |
| commit | 2272044c6ab46b5148c39c124e66e1a8e9073a24 (patch) | |
| tree | 83ad2141b1a3c54707dd9b33073f9217b0ae16c8 /src | |
| parent | d487b4cb6726e7e8d1a18f894021652594125221 (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')
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) + } } |
