diff options
| author | Kevin Laeufer | 2021-02-17 12:16:52 -0800 |
|---|---|---|
| committer | GitHub | 2021-02-17 20:16:52 +0000 |
| commit | 5a89fca6090948d0a99c217a09c692e58a20d1df (patch) | |
| tree | 7996829e3589205607862cbbf578a4e9a9d6e623 /src/test/scala | |
| parent | 856226416cfa2d770c7205efad5331297c2e3a32 (diff) | |
Allow Side Effecting Statement to have Names (#2057)
* firrtl: add optional statement labels for stop, printf, assert, assume and cover
* test: parsing of statement labels
* ir: ensure that name is properly retained
* SymbolTable: add support for labled statements
* test: parsing statement labels
* test: lower types name collisions with named statements
* ignore empty names
* Inline: deal with named and unnamed statements
* RemoveWires: treat stop, printf and verification statements as "others"
* test: fix InlineInstance tests
* DeadCodeEliminations: statements are now als declarations
* CheckHighForm: ensure that statement names are not used as references
* CheckSpec: throw error if statement name collides
* add pass to automatically add missing statement names
* check: make sure that two statements cannot have the same name
* stmtLabel -> stmtName
* scalafmt
* add statement names to spec
* spec: meta data -> metadata
* EnsureStatementNames: explain naming algorithm
* remove returns
* better namespace use
* ir: add CanBeReferenced trait
* ir: add newline as jack requested
Diffstat (limited to 'src/test/scala')
| -rw-r--r-- | src/test/scala/firrtl/testutils/FirrtlSpec.scala | 11 | ||||
| -rw-r--r-- | src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala | 39 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/CheckSpec.scala | 44 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/InlineInstancesTests.scala | 54 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/LowerTypesSpec.scala | 19 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/ParserSpec.scala | 21 | ||||
| -rw-r--r-- | src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala | 160 |
7 files changed, 262 insertions, 86 deletions
diff --git a/src/test/scala/firrtl/testutils/FirrtlSpec.scala b/src/test/scala/firrtl/testutils/FirrtlSpec.scala index 6de2af1e..24793437 100644 --- a/src/test/scala/firrtl/testutils/FirrtlSpec.scala +++ b/src/test/scala/firrtl/testutils/FirrtlSpec.scala @@ -123,21 +123,21 @@ trait FirrtlRunners extends BackendCompilationUtilities { } /** Check equivalence of Firrtl with reference Verilog - * + * * @note the name of the reference Verilog module is grabbed via regex * @param inputFirrtl string containing Firrtl source * @param referenceVerilog Verilog that will be used as reference for LEC * @param timesteps the maximum number of timesteps to consider */ def firrtlEquivalenceWithVerilog( - inputFirrtl: String, - referenceVerilog: String, - timesteps: Int = 1 + inputFirrtl: String, + referenceVerilog: String, + timesteps: Int = 1 ): Unit = { val VerilogModule = """(?s).*module\s(\w+).*""".r val refName = referenceVerilog match { case VerilogModule(name) => name - case _ => throw new Exception(s"Reference Verilog must match simple regex! $VerilogModule") + case _ => throw new Exception(s"Reference Verilog must match simple regex! $VerilogModule") } val circuit = Parser.parse(inputFirrtl.split("\n").toIterator) val inputName = circuit.main @@ -163,7 +163,6 @@ trait FirrtlRunners extends BackendCompilationUtilities { assert(BackendCompilationUtilities.yosysExpectSuccess(inputName, refName, testDir, timesteps)) } - /** Compiles input Firrtl to Verilog */ def compileToVerilog(input: String, annotations: AnnotationSeq = Seq.empty): String = { val circuit = Parser.parse(input.split("\n").toIterator) diff --git a/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala b/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala new file mode 100644 index 00000000..4c993994 --- /dev/null +++ b/src/test/scala/firrtl/transforms/EnsureNamedStatementsSpec.scala @@ -0,0 +1,39 @@ +// SPDX-License-Identifier: Apache-2.0 + +package firrtl.transforms + +import firrtl.options.Dependency +import firrtl.testutils.LeanTransformSpec + +class EnsureNamedStatementsSpec extends LeanTransformSpec(Seq(Dependency(EnsureNamedStatements))) { + behavior.of("EnsureNamedStatements") + + it should "automatically name statements that do not have a name yet" in { + val src = """circuit test : + | module test : + | input clock : Clock + | input stop_ : UInt<1> + | assert(clock, UInt(1), not(UInt(0)), "") + | stop(clock, UInt(1), 1) : stop_123 + | stop(clock, UInt(1), 1) + | assert(clock, UInt(0), UInt(0), "") + | assume(clock, UInt(0), UInt(0), "") + | cover(clock, UInt(0), UInt(0), "") + | cover(clock, UInt(0), UInt(0), "") + | + |""".stripMargin + + val result = compile(src, List()).circuit.serialize.split('\n').map(_.trim) + + val expected = List( + """assert(clock, UInt<1>("h1"), not(UInt<1>("h0")), "") : assert_0""", + """stop(clock, UInt<1>("h1"), 1) : stop_123""", + """stop(clock, UInt<1>("h1"), 1) : stop_0""", + """assert(clock, UInt<1>("h0"), UInt<1>("h0"), "") : assert_1""", + """assume(clock, UInt<1>("h0"), UInt<1>("h0"), "") : assume_0""", + """cover(clock, UInt<1>("h0"), UInt<1>("h0"), "") : cover_0""", + """cover(clock, UInt<1>("h0"), UInt<1>("h0"), "") : cover_1""" + ) + expected.foreach(e => assert(result.contains(e))) + } +} diff --git a/src/test/scala/firrtlTests/CheckSpec.scala b/src/test/scala/firrtlTests/CheckSpec.scala index 1137f8cd..a3efc784 100644 --- a/src/test/scala/firrtlTests/CheckSpec.scala +++ b/src/test/scala/firrtlTests/CheckSpec.scala @@ -384,6 +384,36 @@ class CheckSpec extends AnyFlatSpec with Matchers { } } + "Attempting to shadow a statement name" should "throw an error" in { + val input = + s"""|circuit scopes: + | module scopes: + | input c: Clock + | input i: UInt<1> + | output o: UInt<1> + | wire x: UInt<1> + | when i: + | stop(c, UInt(1), 1) : x + | o <= and(x, i) + |""".stripMargin + assertThrows[CheckHighForm.NotUniqueException] { + checkHighInput(input) + } + } + + "Colliding statement names" should "throw an error" in { + val input = + s"""|circuit test: + | module test: + | input c: Clock + | stop(c, UInt(1), 1) : x + | stop(c, UInt(1), 1) : x + |""".stripMargin + assertThrows[CheckHighForm.NotUniqueException] { + checkHighInput(input) + } + } + "Conditionally statements" should "create separate consequent and alternate scopes" in { val input = s"""|circuit scopes: @@ -536,6 +566,20 @@ class CheckSpec extends AnyFlatSpec with Matchers { } } + it should "throw an exception if a statement name is used as a reference" in { + val src = """ + |circuit test: + | module test: + | input clock: Clock + | output a: UInt<2> + | stop(clock, UInt(1), 1) : hello + | a <= hello + |""".stripMargin + assertThrows[CheckHighForm.UndeclaredReferenceException] { + checkHighInput(src) + } + } + } object CheckSpec { diff --git a/src/test/scala/firrtlTests/InlineInstancesTests.scala b/src/test/scala/firrtlTests/InlineInstancesTests.scala index 6bee2b77..cc7257d2 100644 --- a/src/test/scala/firrtlTests/InlineInstancesTests.scala +++ b/src/test/scala/firrtlTests/InlineInstancesTests.scala @@ -460,6 +460,60 @@ class InlineInstancesTests extends LowTransformSpec { ) } + "inlining named statements" should "work" in { + val input = + """circuit Top : + | module Top : + | input clock : Clock + | input a : UInt<32> + | output b : UInt<32> + | inst i of Inline + | i.clock <= clock + | i.a <= a + | b <= i.b + | module Inline : + | input clock : Clock + | input a : UInt<32> + | output b : UInt<32> + | b <= a + | assert(clock, UInt(1), eq(a,b), "a == b") : assert1 + | assert(clock, UInt(1), not(eq(a,b)), "a != b") + | stop(clock, UInt(0), 0) + |""".stripMargin + val check = + """circuit Top : + | module Top : + | input clock : Clock + | input a : UInt<32> + | output b : UInt<32> + | wire i_clock : Clock + | wire i_a : UInt<32> + | wire i_b : UInt<32> + | i_b <= i_a + | assert(i_clock, UInt(1), eq(i_a, i_b), "a == b") : i_assert1 + | assert(i_clock, UInt(1), not(eq(i_a, i_b)), "a != b") + | stop(i_clock, UInt(0), 0) + | b <= i_b + | i_clock <= clock + | i_a <= a + |""".stripMargin + val top = CircuitTarget("Top").module("Top") + val inlined = top.instOf("i", "Inline") + + executeWithAnnos( + input, + check, + Seq( + inline("Inline"), + NoCircuitDedupAnnotation, + DummyAnno(inlined.ref("assert1")) + ), + Seq( + DummyAnno(top.ref("i_assert1")) + ) + ) + } + "inlining both grandparent and grandchild" should "should work" in { val input = """circuit Top : diff --git a/src/test/scala/firrtlTests/LowerTypesSpec.scala b/src/test/scala/firrtlTests/LowerTypesSpec.scala index da84b362..78d03e68 100644 --- a/src/test/scala/firrtlTests/LowerTypesSpec.scala +++ b/src/test/scala/firrtlTests/LowerTypesSpec.scala @@ -321,6 +321,25 @@ class LowerTypesUniquifySpec extends FirrtlFlatSpec { executeTest(input, expected) } + it should "rename nodes colliding with labled statements" in { + val input = + """circuit Test : + | module Test : + | input clock : Clock + | reg x : { b : UInt<1>, c : { d : UInt<2>, e : UInt<3>}[2], c_1_e : UInt<4>}[2], clock + | node a = x + | printf(clock, UInt(1), "") : a_0_c_ + | assert(clock, UInt(1), UInt(1), "") : a__0 + """.stripMargin + val expected = Seq( + "node a___0_b = x_0_b", + "node a___1_c__1_e = x_1_c__1_e", + "node a___1_c_1_e = x_1_c_1_e" + ) + + executeTest(input, expected) + } + it should "rename DefRegister expressions: clock, reset, and init" in { val input = """circuit Test : diff --git a/src/test/scala/firrtlTests/ParserSpec.scala b/src/test/scala/firrtlTests/ParserSpec.scala index 373b960c..ba61b134 100644 --- a/src/test/scala/firrtlTests/ParserSpec.scala +++ b/src/test/scala/firrtlTests/ParserSpec.scala @@ -147,6 +147,27 @@ class ParserSpec extends FirrtlFlatSpec { } } + // ********** Statement labels ********** + it should "allow certain statement to have a label" in { + val prelude = Seq("circuit top :", " module top :", " input c : Clock") + val statements = Seq("stop(c, UInt(1), 0)", "printf(c, UInt(1), \"\")") ++ + Seq("assert", "assume", "cover").map(_ + "(c, UInt(1), UInt(1), \"\")") + val validLabels = Seq(":test" -> "test", " :test" -> "test", " : test" -> "test", " : test01" -> "test01") + statements.foreach { stmt => + validLabels.foreach { + case (lbl, expected) => + val line = " " + stmt + lbl + val src = (prelude :+ line).mkString("\n") + "\n" + val res = firrtl.Parser.parse(src) + CircuitState(res, Nil) should containTree { + case s: Stop => s.name == expected + case s: Print => s.name == expected + case s: Verification => s.name == expected + } + } + } + } + // ********** Keywords ********** "Keywords" should "be allowed as Ids" in { import KeywordTests._ diff --git a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala index d88309ce..747f6689 100644 --- a/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala +++ b/src/test/scala/firrtlTests/VerilogEquivalenceSpec.scala @@ -7,116 +7,116 @@ import firrtl.testutils._ class VerilogEquivalenceSpec extends FirrtlFlatSpec { "mul followed by cat" should "be correct" in { val header = s""" - |circuit Multiply : - | module Multiply : - | input x : UInt<4> - | input y : UInt<2> - | input z : UInt<2> - | output out : UInt<8> - |""".stripMargin + |circuit Multiply : + | module Multiply : + | input x : UInt<4> + | input y : UInt<2> + | input z : UInt<2> + | output out : UInt<8> + |""".stripMargin val input1 = header + """ - | out <= cat(z, mul(x, y))""".stripMargin + | out <= cat(z, mul(x, y))""".stripMargin val input2 = header + """ - | node n = mul(x, y) - | node m = cat(z, n) - | out <= m""".stripMargin + | node n = mul(x, y) + | node m = cat(z, n) + | out <= m""".stripMargin val expected = s""" - |module MultiplyRef( - | input [3:0] x, - | input [1:0] y, - | input [1:0] z, - | output [7:0] out - |); - | wire [5:0] w = x * y; - | assign out = {z, w}; - |endmodule""".stripMargin + |module MultiplyRef( + | input [3:0] x, + | input [1:0] y, + | input [1:0] z, + | output [7:0] out + |); + | wire [5:0] w = x * y; + | assign out = {z, w}; + |endmodule""".stripMargin firrtlEquivalenceWithVerilog(input1, expected) firrtlEquivalenceWithVerilog(input2, expected) } "div followed by cat" should "be correct" in { val header = s""" - |circuit Divide : - | module Divide : - | input x : UInt<4> - | input y : UInt<2> - | input z : UInt<2> - | output out : UInt<6> - |""".stripMargin + |circuit Divide : + | module Divide : + | input x : UInt<4> + | input y : UInt<2> + | input z : UInt<2> + | output out : UInt<6> + |""".stripMargin val input1 = header + """ - | out <= cat(z, div(x, y))""".stripMargin + | out <= cat(z, div(x, y))""".stripMargin val input2 = header + """ - | node n = div(x, y) - | node m = cat(z, n) - | out <= m""".stripMargin + | node n = div(x, y) + | node m = cat(z, n) + | out <= m""".stripMargin val expected = s""" - |module DivideRef( - | input [3:0] x, - | input [1:0] y, - | input [1:0] z, - | output [5:0] out - |); - | wire [3:0] w = x / y; - | assign out = {z, w}; - |endmodule""".stripMargin + |module DivideRef( + | input [3:0] x, + | input [1:0] y, + | input [1:0] z, + | output [5:0] out + |); + | wire [3:0] w = x / y; + | assign out = {z, w}; + |endmodule""".stripMargin firrtlEquivalenceWithVerilog(input1, expected) firrtlEquivalenceWithVerilog(input2, expected) } "signed mul followed by cat" should "be correct" in { val header = s""" - |circuit SignedMultiply : - | module SignedMultiply : - | input x : SInt<4> - | input y : SInt<2> - | input z : SInt<2> - | output out : UInt<8> - |""".stripMargin + |circuit SignedMultiply : + | module SignedMultiply : + | input x : SInt<4> + | input y : SInt<2> + | input z : SInt<2> + | output out : UInt<8> + |""".stripMargin val input1 = header + """ - | out <= cat(z, mul(x, y))""".stripMargin + | out <= cat(z, mul(x, y))""".stripMargin val input2 = header + """ - | node n = mul(x, y) - | node m = cat(z, n) - | out <= m""".stripMargin + | node n = mul(x, y) + | node m = cat(z, n) + | out <= m""".stripMargin val expected = s""" - |module SignedMultiplyRef( - | input signed [3:0] x, - | input signed [1:0] y, - | input signed [1:0] z, - | output [7:0] out - |); - | wire [5:0] w = x * y; - | assign out = {z, w}; - |endmodule""".stripMargin + |module SignedMultiplyRef( + | input signed [3:0] x, + | input signed [1:0] y, + | input signed [1:0] z, + | output [7:0] out + |); + | wire [5:0] w = x * y; + | assign out = {z, w}; + |endmodule""".stripMargin firrtlEquivalenceWithVerilog(input1, expected) firrtlEquivalenceWithVerilog(input2, expected) } "signed div followed by cat" should "be correct" in { val header = s""" - |circuit SignedDivide : - | module SignedDivide : - | input x : SInt<4> - | input y : SInt<2> - | input z : SInt<2> - | output out : UInt<7> - |""".stripMargin + |circuit SignedDivide : + | module SignedDivide : + | input x : SInt<4> + | input y : SInt<2> + | input z : SInt<2> + | output out : UInt<7> + |""".stripMargin val input1 = header + """ - | out <= cat(z, div(x, y))""".stripMargin + | out <= cat(z, div(x, y))""".stripMargin val input2 = header + """ - | node n = div(x, y) - | node m = cat(z, n) - | out <= m""".stripMargin + | node n = div(x, y) + | node m = cat(z, n) + | out <= m""".stripMargin val expected = s""" - |module SignedDivideRef( - | input signed [3:0] x, - | input signed [1:0] y, - | input signed [1:0] z, - | output [6:0] out - |); - | wire [4:0] w = x / y; - | assign out = {z, w}; - |endmodule""".stripMargin + |module SignedDivideRef( + | input signed [3:0] x, + | input signed [1:0] y, + | input signed [1:0] z, + | output [6:0] out + |); + | wire [4:0] w = x / y; + | assign out = {z, w}; + |endmodule""".stripMargin firrtlEquivalenceWithVerilog(input1, expected) firrtlEquivalenceWithVerilog(input2, expected) } |
