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/test | |
| 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/test')
| -rw-r--r-- | src/test/resources/features/AsyncResetTester.fir | 43 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/AsyncResetSpec.scala | 169 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ConstantPropagationTests.scala | 26 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FirrtlSpec.scala | 13 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ProtoBufSpec.scala | 8 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/RemoveWiresSpec.scala | 17 |
6 files changed, 270 insertions, 6 deletions
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) + } } |
