diff options
| author | John's Brew | 2020-03-13 02:35:10 +0100 |
|---|---|---|
| committer | GitHub | 2020-03-12 18:35:10 -0700 |
| commit | 5c0c0018d812d57270035a9d3bd82e2289acf4ec (patch) | |
| tree | 3e9c319c0e98566b42540a5f31d043d5d0287c17 /src/test | |
| parent | 7e8d21e7f5fe3469eada53e6a6c60e38c134c403 (diff) | |
Add Support for FPGA Bitstream Preset-registers (#1050)
Introduce Preset Register Specialized Emission
- Introduce EmissionOption trait
- Introduce PresetAnnotation & PresetRegAnnotation
- Enable the collection of Annotations in the Emitter
- Introduce collection mechanism for EmissionOptions in the Emitter
- Add PropagatePresetAnnotation transform to annotate register for emission and clean-up the useless reset tree (no DCE involved)
- Add corresponding tests spec and tester
Co-authored-by: Jack Koenig <koenig@sifive.com>
Diffstat (limited to 'src/test')
| -rw-r--r-- | src/test/resources/features/PresetTester.fir | 51 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/FirrtlSpec.scala | 4 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/LoweringCompilersSpec.scala | 2 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/PresetSpec.scala | 239 |
4 files changed, 294 insertions, 2 deletions
diff --git a/src/test/resources/features/PresetTester.fir b/src/test/resources/features/PresetTester.fir new file mode 100644 index 00000000..a2395c99 --- /dev/null +++ b/src/test/resources/features/PresetTester.fir @@ -0,0 +1,51 @@ + +circuit PresetTester : + + module Test : + input clock : Clock + input reset : AsyncReset + input x : UInt<4> + output z : UInt<4> + reg r : UInt<4>, clock with : (reset => (reset, UInt(12))) + r <= x + z <= r + + module PresetTester : + 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 x : UInt<5>, slowClk with : (reset => (reset, UInt(9))) + wire z : UInt<5> + + wire preset : AsyncReset + preset <= asAsyncReset(UInt(0)) ; should be annotated as Preset + + inst i of Test + i.clock <= slowClk + i.reset <= preset + i.x <= x + z <= i.z + + when eq(counter, UInt(0)) : + when neq(z, UInt(12)) : + printf(clock, UInt(1), "Assertion 1 failed! z=%d \n",z) + stop(clock, UInt(1), 1) + ; Do the async reset + when eq(counter, UInt(1)) : + when neq(z, UInt(9)) : + printf(clock, UInt(1), "Assertion 2 failed! z=%d \n",z) + stop(clock, UInt(1), 1) + ; Success! + when eq(counter, UInt(3)) : + stop(clock, UInt(1), 0) + diff --git a/src/test/scala/firrtlTests/FirrtlSpec.scala b/src/test/scala/firrtlTests/FirrtlSpec.scala index fe94a643..1eea3671 100644 --- a/src/test/scala/firrtlTests/FirrtlSpec.scala +++ b/src/test/scala/firrtlTests/FirrtlSpec.scala @@ -310,9 +310,9 @@ class TestFirrtlFlatSpec extends FirrtlFlatSpec { } /** Super class for execution driven Firrtl tests */ -abstract class ExecutionTest(name: String, dir: String, vFiles: Seq[String] = Seq.empty) extends FirrtlPropSpec { +abstract class ExecutionTest(name: String, dir: String, vFiles: Seq[String] = Seq.empty, annotations: AnnotationSeq = Seq.empty) extends FirrtlPropSpec { property(s"$name should execute correctly") { - runFirrtlTest(name, dir, vFiles) + runFirrtlTest(name, dir, vFiles, annotations = annotations) } } /** Super class for compilation driven Firrtl tests */ diff --git a/src/test/scala/firrtlTests/LoweringCompilersSpec.scala b/src/test/scala/firrtlTests/LoweringCompilersSpec.scala index f3183599..dcc4e48d 100644 --- a/src/test/scala/firrtlTests/LoweringCompilersSpec.scala +++ b/src/test/scala/firrtlTests/LoweringCompilersSpec.scala @@ -203,6 +203,7 @@ class LoweringCompilersSpec extends FlatSpec with Matchers { new firrtl.transforms.FixAddingNegativeLiterals, new firrtl.transforms.ReplaceTruncatingArithmetic, new firrtl.transforms.InlineBitExtractionsTransform, + new firrtl.transforms.PropagatePresetAnnotations, new firrtl.transforms.InlineCastsTransform, new firrtl.transforms.LegalizeClocksTransform, new firrtl.transforms.FlattenRegUpdate, @@ -222,6 +223,7 @@ class LoweringCompilersSpec extends FlatSpec with Matchers { new firrtl.transforms.FixAddingNegativeLiterals, new firrtl.transforms.ReplaceTruncatingArithmetic, new firrtl.transforms.InlineBitExtractionsTransform, + new firrtl.transforms.PropagatePresetAnnotations, new firrtl.transforms.InlineCastsTransform, new firrtl.transforms.LegalizeClocksTransform, new firrtl.transforms.FlattenRegUpdate, diff --git a/src/test/scala/firrtlTests/PresetSpec.scala b/src/test/scala/firrtlTests/PresetSpec.scala new file mode 100644 index 00000000..d35aa69f --- /dev/null +++ b/src/test/scala/firrtlTests/PresetSpec.scala @@ -0,0 +1,239 @@ +// See LICENSE for license details. + +package firrtlTests + +import firrtl._ +import FirrtlCheckers._ +import firrtl.annotations._ + +class PresetSpec extends FirrtlFlatSpec { + type Mod = Seq[String] + type ModuleSeq = Seq[Mod] + def compile(input: String, annos: AnnotationSeq): CircuitState = + (new VerilogCompiler).compileAndEmit(CircuitState(parse(input), ChirrtlForm, annos), List.empty) + def compileBody(modules: ModuleSeq) = { + val annos = Seq(new PresetAnnotation(CircuitTarget("Test").module("Test").ref("reset")), firrtl.transforms.NoDCEAnnotation) + var str = """ + |circuit Test : + |""".stripMargin + modules foreach ((m: Mod) => { + val header = "|module " + m(0) + " :" + str += header.stripMargin.stripMargin.split("\n").mkString(" ", "\n ", "") + str += m(1).split("\n").mkString(" ", "\n ", "") + str += """ + |""".stripMargin + }) + compile(str,annos) + } + + "Preset" should """behave properly given a `Preset` annotated `AsyncReset` INPUT reset: + - replace AsyncReset specific blocks by standard Register blocks + - add inline declaration of all registers connected to reset + - remove the useless input port""" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |reg r : UInt<1>, clock with : (reset => (reset, UInt(0))) + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result shouldNot containLine ("input reset,") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + + it should """behave properly given a `Preset` annotated `AsyncReset` WIRE reset: + - replace AsyncReset specific blocks by standard Register blocks + - add inline declaration of all registers connected to reset + - remove the useless wire declaration and assignation""" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input x : UInt<1> + |output z : UInt<1> + |wire reset : AsyncReset + |reset <= asAsyncReset(UInt(0)) + |reg r : UInt<1>, clock with : (reset => (reset, UInt(0))) + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + // it should also remove useless asyncReset signal, all along the path down to registers + result shouldNot containLine ("wire reset;") + result shouldNot containLine ("assign reset = 1'h0;") + } + it should "raise TreeCleanUpOrphantException on cast of annotated AsyncReset" in { + an [firrtl.transforms.PropagatePresetAnnotations.TreeCleanUpOrphanException] shouldBe thrownBy { + compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input x : UInt<1> + |output z : UInt<1> + |output sz : UInt<1> + |wire reset : AsyncReset + |reset <= asAsyncReset(UInt(0)) + |reg r : UInt<1>, clock with : (reset => (reset, UInt(0))) + |wire sreset : UInt<1> + |sreset <= asUInt(reset) ; this is FORBIDDEN + |reg s : UInt<1>, clock with : (reset => (sreset, UInt(0))) + |r <= x + |s <= x + |z <= r + |sz <= s""".stripMargin)) + ) + } + } + + it should "propagate through bundles" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |wire bundle : {in_rst: AsyncReset, out_rst:AsyncReset} + |bundle.in_rst <= reset + |bundle.out_rst <= bundle.in_rst + |reg r : UInt<1>, clock with : (reset => (bundle.out_rst, UInt(0))) + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("input reset,") + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + it should "propagate through vectors" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |wire vector : AsyncReset[2] + |vector[0] <= reset + |vector[1] <= vector[0] + |reg r : UInt<1>, clock with : (reset => (vector[1], UInt(0))) + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("input reset,") + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + + it should "propagate through bundles of vectors" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |wire bundle : {in_rst: AsyncReset[2], out_rst:AsyncReset} + |bundle.in_rst[0] <= reset + |bundle.in_rst[1] <= bundle.in_rst[0] + |bundle.out_rst <= bundle.in_rst[1] + |reg r : UInt<1>, clock with : (reset => (bundle.out_rst, UInt(0))) + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("input reset,") + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + it should """propagate properly accross modules: + - replace AsyncReset specific blocks by standard Register blocks + - add inline declaration of all registers connected to reset + - remove the useless input port of instanciated module + - remove the useless instance connections + - remove wires and assignations used in instance connections + """ in { + val result = compileBody(Seq( + Seq("TestA",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |reg r : UInt<1>, clock with : (reset => (reset, UInt(0))) + |r <= x + |z <= r + |""".stripMargin), + Seq("Test",s""" + |input clock : Clock + |input x : UInt<1> + |output z : UInt<1> + |wire reset : AsyncReset + |reset <= asAsyncReset(UInt(0)) + |inst i of TestA + |i.clock <= clock + |i.reset <= reset + |i.x <= x + |z <= i.z""".stripMargin) + )) + // assess that all useless connections are not emitted + result shouldNot containLine ("wire i_reset;") + result shouldNot containLine (".reset(i_reset),") + result shouldNot containLine ("assign i_reset = reset;") + result shouldNot containLine ("input reset,") + + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + + it should "propagate even through disordonned statements" in { + val result = compileBody(Seq(Seq("Test",s""" + |input clock : Clock + |input reset : AsyncReset + |input x : UInt<1> + |output z : UInt<1> + |wire bundle : {in_rst: AsyncReset, out_rst:AsyncReset} + |reg r : UInt<1>, clock with : (reset => (bundle.out_rst, UInt(0))) + |bundle.out_rst <= bundle.in_rst + |bundle.in_rst <= reset + |r <= x + |z <= r""".stripMargin)) + ) + result shouldNot containLine ("input reset,") + result shouldNot containLine ("always @(posedge clock or posedge reset) begin") + result shouldNot containLines ( + "if (reset) begin", + "r = 1'h0;", + "end") + result should containLine ("always @(posedge clock) begin") + result should containLine ("reg r = 1'h0;") + } + +} + +class PresetExecutionTest extends ExecutionTest( + "PresetTester", + "/features", + annotations = Seq(new PresetAnnotation(CircuitTarget("PresetTester").module("PresetTester").ref("preset"))) +) |
