diff options
| author | Jack Koenig | 2019-08-13 12:09:27 +0530 |
|---|---|---|
| committer | GitHub | 2019-08-13 12:09:27 +0530 |
| commit | f08f8dbb3c480220f92923a7f3242fcbb644b65e (patch) | |
| tree | 45cdb7543f6252ad2feb5aaf4e0e0580d3d27565 /src/test | |
| parent | 63e88b6e1696e2c8d6da91f6f5eb128a9d0395ae (diff) | |
Infer reset (#1068)
* Add abstract "Reset" which can be inferred to AsyncReset or UInt<1>
* Enhance async reset initial value literal check to support aggregates
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/scala/firrtlTests/AsyncResetSpec.scala | 137 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/InferResetsSpec.scala | 355 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ProtoBufSpec.scala | 5 |
3 files changed, 495 insertions, 2 deletions
diff --git a/src/test/scala/firrtlTests/AsyncResetSpec.scala b/src/test/scala/firrtlTests/AsyncResetSpec.scala index c1078a03..6fcb647a 100644 --- a/src/test/scala/firrtlTests/AsyncResetSpec.scala +++ b/src/test/scala/firrtlTests/AsyncResetSpec.scala @@ -3,7 +3,6 @@ package firrtlTests import firrtl._ -import firrtl.ir._ import FirrtlCheckers._ class AsyncResetSpec extends FirrtlFlatSpec { @@ -30,6 +29,25 @@ class AsyncResetSpec extends FirrtlFlatSpec { result should containLine ("always @(posedge clock or posedge reset) begin") } + it should "work in nested and flipped aggregates with regular and partial connect" in { + val result = compileBody(s""" + |output fizz : { flip foo : { a : AsyncReset, flip b: AsyncReset }[2], bar : { a : AsyncReset, flip b: AsyncReset }[2] } + |output buzz : { flip foo : { a : AsyncReset, flip b: AsyncReset }[2], bar : { a : AsyncReset, flip b: AsyncReset }[2] } + |fizz.bar <= fizz.foo + |buzz.bar <- buzz.foo + |""".stripMargin + ) + + result should containLine ("assign fizz_foo_0_b = fizz_bar_0_b;") + result should containLine ("assign fizz_foo_1_b = fizz_bar_1_b;") + result should containLine ("assign fizz_bar_0_a = fizz_foo_0_a;") + result should containLine ("assign fizz_bar_1_a = fizz_foo_1_a;") + result should containLine ("assign buzz_foo_0_b = buzz_bar_0_b;") + result should containLine ("assign buzz_foo_1_b = buzz_bar_1_b;") + result should containLine ("assign buzz_bar_0_a = buzz_foo_0_a;") + result should containLine ("assign buzz_bar_1_a = buzz_foo_1_a;") + } + it should "support casting to other types" in { val result = compileBody(s""" |input a : AsyncReset @@ -77,7 +95,7 @@ class AsyncResetSpec extends FirrtlFlatSpec { } "Non-literals" should "NOT be allowed as reset values for AsyncReset" in { - an [passes.CheckHighForm.NonLiteralAsyncResetValueException] shouldBe thrownBy { + an [checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy { compileBody(s""" |input clock : Clock |input reset : AsyncReset @@ -91,6 +109,121 @@ class AsyncResetSpec extends FirrtlFlatSpec { } } + "Late non-literals connections" should "NOT be allowed as reset values for AsyncReset" in { + an [checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy { + compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<8> + |input y : UInt<8> + |output z : UInt<8> + |wire a : UInt<8> + |reg r : UInt<8>, clock with : (reset => (reset, a)) + |a <= y + |r <= x + |z <= r""".stripMargin + ) + } + } + + "Hidden Non-literals" should "NOT be allowed as reset values for AsyncReset" in { + an [checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy { + compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1>[4] + |input y : UInt<1> + |output z : UInt<1>[4] + |wire literal : UInt<1>[4] + |literal[0] <= UInt<1>("h00") + |literal[1] <= y + |literal[2] <= UInt<1>("h00") + |literal[3] <= UInt<1>("h00") + |reg r : UInt<1>[4], clock with : (reset => (reset, literal)) + |r <= x + |z <= r""".stripMargin + ) + } + } + "Wire connected to non-literal" should "NOT be allowed as reset values for AsyncReset" in { + an [checks.CheckResets.NonLiteralAsyncResetValueException] shouldBe thrownBy { + compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |input y : UInt<1> + |input cond : UInt<1> + |output z : UInt<1> + |wire w : UInt<1> + |w <= UInt(1) + |when cond : + | w <= y + |reg r : UInt<1>, clock with : (reset => (reset, w)) + |r <= x + |z <= r""".stripMargin + ) + } + } + + "Complex literals" should "be allowed as reset values for AsyncReset" in { + val result = compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1>[4] + |output z : UInt<1>[4] + |wire literal : UInt<1>[4] + |literal[0] <= UInt<1>("h00") + |literal[1] <= UInt<1>("h00") + |literal[2] <= UInt<1>("h00") + |literal[3] <= UInt<1>("h00") + |reg r : UInt<1>[4], clock with : (reset => (reset, literal)) + |r <= x + |z <= r""".stripMargin + ) + result should containLine ("always @(posedge clock or posedge reset) begin") + } + + "Complex literals of complex literals" should "be allowed as reset values for AsyncReset" in { + val result = compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1>[4] + |output z : UInt<1>[4] + |wire literal : UInt<1>[2] + |literal[0] <= UInt<1>("h01") + |literal[1] <= UInt<1>("h01") + |wire complex_literal : UInt<1>[4] + |complex_literal[0] <= literal[0] + |complex_literal[1] <= literal[1] + |complex_literal[2] <= UInt<1>("h00") + |complex_literal[3] <= UInt<1>("h00") + |reg r : UInt<1>[4], clock with : (reset => (reset, complex_literal)) + |r <= x + |z <= r""".stripMargin + ) + result should containLine ("always @(posedge clock or posedge reset) begin") + } + "Literals of bundle literals" should "be allowed as reset values for AsyncReset" in { + val result = compileBody(s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1>[4] + |output z : UInt<1>[4] + |wire bundle : {a: UInt<1>, b: UInt<1>} + |bundle.a <= UInt<1>("h01") + |bundle.b <= UInt<1>("h01") + |wire complex_literal : UInt<1>[4] + |complex_literal[0] <= bundle.a + |complex_literal[1] <= bundle.b + |complex_literal[2] <= UInt<1>("h00") + |complex_literal[3] <= UInt<1>("h00") + |reg r : UInt<1>[4], clock with : (reset => (reset, complex_literal)) + |r <= x + |z <= r""".stripMargin + ) + result should containLine ("always @(posedge clock or posedge reset) begin") + } + "Every async reset reg" should "generate its own always block" in { val result = compileBody(s""" diff --git a/src/test/scala/firrtlTests/InferResetsSpec.scala b/src/test/scala/firrtlTests/InferResetsSpec.scala new file mode 100644 index 00000000..ac13033a --- /dev/null +++ b/src/test/scala/firrtlTests/InferResetsSpec.scala @@ -0,0 +1,355 @@ +// See LICENSE for license details. + +package firrtlTests + +import firrtl._ +import firrtl.ir._ +import firrtl.passes.{CheckHighForm, CheckTypes} +import firrtl.transforms.InferResets +import FirrtlCheckers._ + +// TODO +// - Test nodes in the connection +// - Test with whens (is this allowed?) +class InferResetsSpec extends FirrtlFlatSpec { + def compile(input: String, compiler: Compiler = new MiddleFirrtlCompiler): CircuitState = + compiler.compileAndEmit(CircuitState(parse(input), ChirrtlForm), List.empty) + + behavior of "ResetType" + + val BoolType = UIntType(IntWidth(1)) + + it should "support casting to other types" in { + val result = compile(s""" + |circuit top: + | module top: + | input a : UInt<1> + | output v : UInt<1> + | output w : SInt<1> + | output x : Clock + | output y : Fixed<1><<0>> + | output z : AsyncReset + | wire r : Reset + | r <= a + | v <= asUInt(r) + | w <= asSInt(r) + | x <= asClock(r) + | y <= asFixedPoint(r, 0) + | z <= asAsyncReset(r)""".stripMargin + ) + println(result.getEmittedCircuit) + result should containLine ("wire r : UInt<1>") + result should containLine ("r <= a") + result should containLine ("v <= asUInt(r)") + result should containLine ("w <= asSInt(r)") + result should containLine ("x <= asClock(r)") + result should containLine ("y <= asSInt(r)") + result should containLine ("z <= asAsyncReset(r)") + } + + it should "work across Module boundaries" in { + val result = compile(s""" + |circuit top : + | module child : + | input clock : Clock + | input childReset : Reset + | input x : UInt<8> + | output z : UInt<8> + | reg r : UInt<8>, clock with : (reset => (childReset, UInt(123))) + | r <= x + | z <= r + | module top : + | input clock : Clock + | input reset : UInt<1> + | input x : UInt<8> + | output z : UInt<8> + | inst c of child + | c.clock <= clock + | c.childReset <= reset + | c.x <= x + | z <= c.z + |""".stripMargin + ) + result should containTree { case Port(_, "childReset", Input, BoolType) => true } + } + + it should "work across multiple Module boundaries" in { + val result = compile(s""" + |circuit top : + | module child : + | input resetIn : Reset + | output resetOut : Reset + | resetOut <= resetIn + | module top : + | input clock : Clock + | input reset : UInt<1> + | input x : UInt<8> + | output z : UInt<8> + | inst c of child + | c.resetIn <= reset + | reg r : UInt<8>, clock with : (reset => (c.resetOut, UInt(123))) + | r <= x + | z <= r + |""".stripMargin + ) + result should containTree { case Port(_, "resetIn", Input, BoolType) => true } + result should containTree { case Port(_, "resetOut", Output, BoolType) => true } + } + + it should "work in nested and flipped aggregates with regular and partial connect" in { + val result = compile(s""" + |circuit top : + | module top : + | output fizz : { flip foo : { a : AsyncReset, flip b: Reset }[2], bar : { a : Reset, flip b: AsyncReset }[2] } + | output buzz : { flip foo : { a : AsyncReset, c: UInt<1>, flip b: Reset }[2], bar : { a : Reset, flip b: AsyncReset, c: UInt<8> }[2] } + | fizz.bar <= fizz.foo + | buzz.bar <- buzz.foo + |""".stripMargin, + new LowFirrtlCompiler + ) + result should containTree { case Port(_, "fizz_foo_0_a", Input, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_foo_0_b", Output, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_foo_1_a", Input, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_foo_1_b", Output, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_bar_0_a", Output, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_bar_0_b", Input, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_bar_1_a", Output, AsyncResetType) => true } + result should containTree { case Port(_, "fizz_bar_1_b", Input, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_foo_0_a", Input, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_foo_0_b", Output, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_foo_1_a", Input, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_foo_1_b", Output, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_bar_0_a", Output, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_bar_0_b", Input, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_bar_1_a", Output, AsyncResetType) => true } + result should containTree { case Port(_, "buzz_bar_1_b", Input, AsyncResetType) => true } + } + + it should "NOT allow last connect semantics to pick the right type for Reset" in { + an [InferResets.DifferingDriverTypesException] shouldBe thrownBy { + compile(s""" + |circuit top : + | module top : + | input reset0 : AsyncReset + | input reset1 : UInt<1> + | output out : Reset + | wire w1 : Reset + | wire w2 : Reset + | w1 <= reset0 + | w2 <= reset1 + | out <= w1 + | out <= w2 + |""".stripMargin + ) + } + } + + it should "NOT support last connect semantics across whens" in { + an [InferResets.DifferingDriverTypesException] shouldBe thrownBy { + compile(s""" + |circuit top : + | module top : + | input reset0 : AsyncReset + | input reset1 : UInt<1> + | input en0 : UInt<1> + | output out : Reset + | wire w1 : Reset + | wire w2 : Reset + | w1 <= reset0 + | w2 <= reset1 + | out <= w1 + | when en0 : + | out <= w2 + |""".stripMargin + ) + } + } + + it should "not allow different Reset Types to drive a single Reset" in { + an [InferResets.DifferingDriverTypesException] shouldBe thrownBy { + val result = compile(s""" + |circuit top : + | module top : + | input reset0 : AsyncReset + | input reset1 : UInt<1> + | input en : UInt<1> + | output out : Reset + | wire w1 : Reset + | wire w2 : Reset + | w1 <= reset0 + | w2 <= reset1 + | out <= w1 + | when en : + | out <= w2 + |""".stripMargin + ) + } + } + + it should "allow ResetType to drive AsyncResets or UInt<1>" in { + val result1 = compile(s""" + |circuit top : + | module top : + | input in : UInt<1> + | output out : UInt<1> + | wire w : Reset + | w <= in + | out <= w + |""".stripMargin + ) + result1 should containTree { case DefWire(_, "w", BoolType) => true } + val result2 = compile(s""" + |circuit top : + | module top : + | output foo : { flip a : UInt<1> } + | input bar : { flip a : UInt<1> } + | wire w : { flip a : Reset } + | foo <= w + | w <= bar + |""".stripMargin + ) + val AggType = BundleType(Seq(Field("a", Flip, BoolType))) + result2 should containTree { case DefWire(_, "w", AggType) => true } + val result3 = compile(s""" + |circuit top : + | module top : + | input in : UInt<1> + | output out : UInt<1> + | wire w : Reset + | w <- in + | out <- w + |""".stripMargin + ) + result3 should containTree { case DefWire(_, "w", BoolType) => true } + } + + it should "error if a ResetType driving UInt<1> infers to AsyncReset" in { + an [Exception] shouldBe thrownBy { + compile(s""" + |circuit top : + | module top : + | input in : AsyncReset + | output out : UInt<1> + | wire w : Reset + | w <= in + | out <= w + |""".stripMargin + ) + } + } + + it should "error if a ResetType driving AsyncReset infers to UInt<1>" in { + an [Exception] shouldBe thrownBy { + compile(s""" + |circuit top : + | module top : + | input in : UInt<1> + | output out : AsyncReset + | wire w : Reset + | w <= in + | out <= w + |""".stripMargin + ) + } + } + + it should "not allow ResetType as an Input or ExtModule output" in { + // TODO what exception should be thrown here? + an [CheckHighForm.ResetInputException] shouldBe thrownBy { + val result = compile(s""" + |circuit top : + | module top : + | input in : { foo : Reset } + | output out : Reset + | out <= in.foo + |""".stripMargin + ) + } + an [CheckHighForm.ResetExtModuleOutputException] shouldBe thrownBy { + val result = compile(s""" + |circuit top : + | extmodule ext : + | output out : { foo : Reset } + | module top : + | output out : Reset + | inst e of ext + | out <= e.out.foo + |""".stripMargin + ) + } + } + + it should "not allow Vecs to infer different Reset Types" in { + an [CheckTypes.InvalidConnect] shouldBe thrownBy { + val result = compile(s""" + |circuit top : + | module top : + | input reset0 : AsyncReset + | input reset1 : UInt<1> + | output out : Reset[2] + | out[0] <= reset0 + | out[1] <= reset1 + |""".stripMargin + ) + } + } + + // Or is this actually an error? The behavior is that out is inferred as AsyncReset[2] + ignore should "not allow Vecs only be partially inferred" in { + // Some exception should be thrown, TODO figure out which one + an [Exception] shouldBe thrownBy { + val result = compile(s""" + |circuit top : + | module top : + | input reset : AsyncReset + | output out : Reset[2] + | out is invalid + | out[0] <= reset + |""".stripMargin + ) + } + } + + + it should "support inferring modules that would dedup differently" in { + val result = compile(s""" + |circuit top : + | module child : + | input clock : Clock + | input childReset : Reset + | input x : UInt<8> + | output z : UInt<8> + | reg r : UInt<8>, clock with : (reset => (childReset, UInt(123))) + | r <= x + | z <= r + | module child_1 : + | input clock : Clock + | input childReset : Reset + | input x : UInt<8> + | output z : UInt<8> + | reg r : UInt<8>, clock with : (reset => (childReset, UInt(123))) + | r <= x + | z <= r + | module top : + | input clock : Clock + | input reset1 : UInt<1> + | input reset2 : AsyncReset + | input x : UInt<8>[2] + | output z : UInt<8>[2] + | inst c of child + | c.clock <= clock + | c.childReset <= reset1 + | c.x <= x[0] + | z[0] <= c.z + | inst c2 of child_1 + | c2.clock <= clock + | c2.childReset <= reset2 + | c2.x <= x[1] + | z[1] <= c2.z + |""".stripMargin + ) + result should containTree { case Port(_, "childReset", Input, BoolType) => true } + result should containTree { case Port(_, "childReset", Input, AsyncResetType) => true } + } +} + diff --git a/src/test/scala/firrtlTests/ProtoBufSpec.scala b/src/test/scala/firrtlTests/ProtoBufSpec.scala index 526a194c..2f347c6d 100644 --- a/src/test/scala/firrtlTests/ProtoBufSpec.scala +++ b/src/test/scala/firrtlTests/ProtoBufSpec.scala @@ -180,4 +180,9 @@ class ProtoBufSpec extends FirrtlFlatSpec { val port = ir.Port(ir.NoInfo, "reset", ir.Input, ir.AsyncResetType) FromProto.convert(ToProto.convert(port).build) should equal (port) } + + it should "support ResetTypes" in { + val port = ir.Port(ir.NoInfo, "reset", ir.Input, ir.ResetType) + FromProto.convert(ToProto.convert(port).build) should equal (port) + } } |
