aboutsummaryrefslogtreecommitdiff
path: root/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'src/test')
-rw-r--r--src/test/scala/firrtlTests/transforms/MustDedupSpec.scala267
1 files changed, 267 insertions, 0 deletions
diff --git a/src/test/scala/firrtlTests/transforms/MustDedupSpec.scala b/src/test/scala/firrtlTests/transforms/MustDedupSpec.scala
new file mode 100644
index 00000000..2f633e0e
--- /dev/null
+++ b/src/test/scala/firrtlTests/transforms/MustDedupSpec.scala
@@ -0,0 +1,267 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package firrtlTests.transforms
+
+import org.scalatest.featurespec.AnyFeatureSpec
+import org.scalatest.GivenWhenThen
+import firrtl.testutils.FirrtlMatchers
+import java.io.File
+
+import firrtl.graph.DiGraph
+import firrtl.analyses.InstanceKeyGraph
+import firrtl.annotations.CircuitTarget
+import firrtl.annotations.TargetToken.OfModule
+import firrtl.transforms._
+import firrtl.transforms.MustDeduplicateTransform._
+import firrtl.transforms.MustDeduplicateTransform.DisjointChildren._
+import firrtl.util.BackendCompilationUtilities.createTestDirectory
+import firrtl.stage.{FirrtlSourceAnnotation, RunFirrtlTransformAnnotation}
+import firrtl.options.{TargetDirAnnotation}
+import logger.{LogLevel, LogLevelAnnotation, Logger}
+
+class MustDedupSpec extends AnyFeatureSpec with FirrtlMatchers with GivenWhenThen {
+
+ Feature("When you have a simple non-deduping hierarcy") {
+ val text = """
+ |circuit A :
+ | module C :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | io.out <= io.in
+ | module C_1 :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | io.out <= and(io.in, UInt("hff"))
+ | module B :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst c of C
+ | io <= c.io
+ | module B_1 :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst c of C_1
+ | io <= c.io
+ | module A :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst b of B
+ | inst b_1 of B_1
+ | io.out <= and(b.io.out, b_1.io.out)
+ | b.io.in <= io.in
+ | b_1.io.in <= io.in
+ """.stripMargin
+ val top = CircuitTarget("A")
+ val bdedup = MustDeduplicateAnnotation(Seq(top.module("B"), top.module("B_1")))
+ val igraph = InstanceKeyGraph(parse(text))
+
+ Scenario("Full compilation should fail and dump reports to disk") {
+ val testDir = createTestDirectory("must_dedup")
+ val reportDir = new File(testDir, "reports")
+ val annos = Seq(
+ TargetDirAnnotation(testDir.toString),
+ FirrtlSourceAnnotation(text),
+ RunFirrtlTransformAnnotation(new MustDeduplicateTransform),
+ MustDeduplicateReportDirectory(reportDir.toString),
+ bdedup
+ )
+
+ a[DeduplicationFailureException] shouldBe thrownBy {
+ (new firrtl.stage.FirrtlPhase).transform(annos)
+ }
+
+ reportDir should exist
+
+ val report0 = new File(reportDir, "report_0.rpt")
+ report0 should exist
+
+ val expectedModules = Seq("B", "B_1", "C", "C_1")
+ for (mod <- expectedModules) {
+ new File(reportDir, s"modules/$mod.fir") should exist
+ }
+ }
+
+ Scenario("Non-deduping children should give actionable debug information") {
+ When("Finding dedup failures")
+ val failure = findDedupFailures(Seq(OfModule("B"), OfModule("B_1")), igraph)
+
+ Then("The children should appear as a failure candidate")
+ failure.candidates should be(Seq(LikelyShouldMatch(OfModule("C"), OfModule("C_1"))))
+
+ And("There should be a pretty DiGraph showing context")
+ val got = makeDedupFailureDiGraph(failure, igraph.graph.transformNodes(_.module))
+ val expected = DiGraph("A" -> "(B)", "A" -> "(B_1)", "(B)" -> "C [0]", "(B_1)" -> "C_1 [0]")
+ // DiGraph uses referential equality so compare serialized form
+ got.prettyTree() should be(expected.prettyTree())
+ }
+
+ Scenario("Unrelated hierarchies should give actionable debug information") {
+ When("Finding dedup failures")
+ val failure = findDedupFailures(Seq(OfModule("B"), OfModule("C_1")), igraph)
+
+ Then("The failure should note the hierarchies don't match")
+ failure.candidates should be(Seq(DisjointChildren(OfModule("B"), OfModule("C_1"), Left)))
+
+ And("There should be a pretty DiGraph showing context")
+ val got = makeDedupFailureDiGraph(failure, igraph.graph.transformNodes(_.module))
+ val expected = DiGraph("A" -> "(B) [0]", "(B) [0]" -> "C", "B_1" -> "(C_1) [0]")
+ // DiGraph uses referential equality so compare serialized form
+ got.prettyTree() should be(expected.prettyTree())
+ }
+ }
+
+ Feature("When you have a deep, non-deduping hierarchy") {
+ // Shadow hierarchy just to get an InstanceKeyGraph which can only be made from a circuit
+ val text = parse("""
+ |circuit A :
+ | module E:
+ | skip
+ | module F :
+ | skip
+ | module F_1 :
+ | inst e of E
+ | module D :
+ | skip
+ | module D_1 :
+ | skip
+ | module C :
+ | inst d of D
+ | inst f of F
+ | module C_1 :
+ | inst d of D_1
+ | inst f of F_1
+ | module B :
+ | inst c of C
+ | inst e of E
+ | module B_1 :
+ | inst c of C_1
+ | inst e of E
+ | module A :
+ | inst b of B
+ | inst b_1 of B_1
+ |""".stripMargin)
+ val igraph = InstanceKeyGraph(text)
+
+ Scenario("Non-deduping children should give actionable debug information") {
+ When("Finding dedup failures")
+ val failure = findDedupFailures(Seq(OfModule("B"), OfModule("B_1")), igraph)
+
+ Then("The children should appear as a failure candidate")
+ failure.candidates should be(
+ Seq(LikelyShouldMatch(OfModule("D"), OfModule("D_1")), DisjointChildren(OfModule("F"), OfModule("F_1"), Right))
+ )
+
+ And("There should be a pretty DiGraph showing context")
+ val got = makeDedupFailureDiGraph(failure, igraph.graph.transformNodes(_.module))
+ val expected = DiGraph(
+ "A" -> "(B)",
+ "A" -> "(B_1)",
+ "(B)" -> "C",
+ "C" -> "D [0]",
+ "C" -> "F [1]",
+ "(B_1)" -> "C_1",
+ "C_1" -> "D_1 [0]",
+ "C_1" -> "F_1 [1]",
+ "F_1 [1]" -> "E",
+ // These last 2 are undesirable but E is included because it's a submodule of disjoint F and F_1
+ "(B)" -> "E",
+ "(B_1)" -> "E"
+ )
+ // DiGraph uses referential equality so compare serialized form
+ got.prettyTree() should be(expected.prettyTree())
+ }
+ }
+
+ Feature("When you have multiple modules that should dedup, but don't") {
+ // Shadow hierarchy just to get an InstanceKeyGraph which can only be made from a circuit
+ val text = parse("""
+ |circuit A :
+ | module D :
+ | skip
+ | module D_1 :
+ | skip
+ | module C :
+ | skip
+ | module C_1 :
+ | skip
+ | module B :
+ | inst c of C
+ | inst d of D
+ | module B_1 :
+ | inst c of C_1
+ | inst d of D
+ | module B_2 :
+ | inst c of C
+ | inst d of D_1
+ | module A :
+ | inst b of B
+ | inst b_1 of B_1
+ | inst b_2 of B_2
+ |""".stripMargin)
+ val igraph = InstanceKeyGraph(text)
+
+ Scenario("Non-deduping children should give actionable debug information") {
+ When("Finding dedup failures")
+ val failure = findDedupFailures(Seq(OfModule("B"), OfModule("B_1"), OfModule("B_2")), igraph)
+
+ Then("The children should appear as a failure candidate")
+ failure.candidates should be(
+ Seq(LikelyShouldMatch(OfModule("C"), OfModule("C_1")), LikelyShouldMatch(OfModule("D"), OfModule("D_1")))
+ )
+
+ And("There should be a pretty DiGraph showing context")
+ val got = makeDedupFailureDiGraph(failure, igraph.graph.transformNodes(_.module))
+ val expected = DiGraph(
+ "A" -> "(B)",
+ "A" -> "(B_1)",
+ "A" -> "(B_2)",
+ "(B)" -> "C [0]",
+ "(B)" -> "D [1]",
+ "(B_1)" -> "C_1 [0]",
+ "(B_1)" -> "D [1]",
+ "(B_2)" -> "C [0]",
+ "(B_2)" -> "D_1 [1]"
+ )
+ // DiGraph uses referential equality so compare serialized form
+ got.prettyTree() should be(expected.prettyTree())
+ }
+ }
+
+ Feature("When you have modules that should dedup, and they do") {
+ val text = """
+ |circuit A :
+ | module C :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | io.out <= io.in
+ | module C_1 :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | io.out <= io.in
+ | module B :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst c of C
+ | io <= c.io
+ | module B_1 :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst c of C_1
+ | io <= c.io
+ | module A :
+ | output io : { flip in : UInt<8>, out : UInt<8> }
+ | inst b of B
+ | inst b_1 of B_1
+ | io.out <= and(b.io.out, b_1.io.out)
+ | b.io.in <= io.in
+ | b_1.io.in <= io.in
+ """.stripMargin
+ val top = CircuitTarget("A")
+ val bdedup = MustDeduplicateAnnotation(Seq(top.module("B"), top.module("B_1")))
+
+ Scenario("Full compilation should succeed") {
+ val testDir = createTestDirectory("must_dedup")
+ val reportDir = new File(testDir, "reports")
+ val annos = Seq(
+ TargetDirAnnotation(testDir.toString),
+ FirrtlSourceAnnotation(text),
+ RunFirrtlTransformAnnotation(new MustDeduplicateTransform),
+ MustDeduplicateReportDirectory(reportDir.toString),
+ bdedup
+ )
+
+ (new firrtl.stage.FirrtlPhase).transform(annos)
+ }
+ }
+}