// SPDX-License-Identifier: Apache-2.0 package firrtlTests.transforms import firrtl.testutils.FirrtlFlatSpec import firrtl._ import firrtl.passes._ import firrtl.passes.wiring.{SinkAnnotation, SourceAnnotation, WiringTransform} import firrtl.annotations._ import firrtl.annotations.TargetToken.{Field, Index} class InferWidthsWithAnnosSpec extends FirrtlFlatSpec { private def executeTest(input: String, check: String, transforms: Seq[Transform], annotations: Seq[Annotation]) = { val start = CircuitState(parse(input), ChirrtlForm, annotations) val end = transforms.foldLeft(start) { (c: CircuitState, t: Transform) => t.runTransform(c) } val resLines = end.circuit.serialize.split("\n").map(normalized) val checkLines = parse(check).serialize.split("\n").map(normalized) resLines should be(checkLines) } "CheckWidths on wires with unknown widths" should "result in an error" in { val transforms = Seq(ToWorkingIR, CheckHighForm, ResolveKinds, InferTypes, CheckTypes, ResolveFlows, new InferWidths, CheckWidths) val input = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire x: UInt<3> | | module A : | wire y: UInt""".stripMargin // A.y should have uninferred width intercept[CheckWidths.UninferredWidth] { executeTest(input, "", transforms, Seq.empty) } } "InferWidthsWithAnnos" should "infer widths using WidthGeqConstraintAnnotation" in { val transforms = Seq(ToWorkingIR, CheckHighForm, ResolveKinds, InferTypes, CheckTypes, ResolveFlows, new InferWidths, CheckWidths) val annos = Seq( WidthGeqConstraintAnnotation( ReferenceTarget("Top", "A", Nil, "y", Nil), ReferenceTarget("Top", "B", Nil, "x", Nil) ) ) val input = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire x: UInt<3> | | module A : | wire y: UInt""".stripMargin val output = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire x: UInt<3> | | module A : | wire y: UInt<3>""".stripMargin // A.y should have same width as B.x executeTest(input, output, transforms, annos) } "InferWidthsWithAnnos" should "work with token paths" in { val transforms = Seq(ToWorkingIR, CheckHighForm, ResolveKinds, InferTypes, CheckTypes, ResolveFlows, new InferWidths, CheckWidths) val tokenLists = Seq( Seq(Field("x")), Seq(Field("y"), Index(0), Field("yy")), Seq(Field("y"), Index(1), Field("yy")) ) val annos = tokenLists.map { tokens => WidthGeqConstraintAnnotation( ReferenceTarget("Top", "A", Nil, "bundle", tokens), ReferenceTarget("Top", "B", Nil, "bundle", tokens) ) } val input = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | | module A : | wire bundle : {x : UInt, y: {yy : UInt}[2] }""".stripMargin val output = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | | module A : | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] }""".stripMargin // elements of A.bundle should have same width as B.bundle executeTest(input, output, transforms, annos) } "InferWidthsWithAnnos" should "work with WiringTransform" in { def transforms() = Seq( ToWorkingIR, ResolveKinds, InferTypes, ResolveFlows, new InferWidths, CheckWidths, new WiringTransform, new ResolveAndCheck ) val sourceTarget = ComponentName("bundle", ModuleName("A", CircuitName("Top"))) val source = SourceAnnotation(sourceTarget, "pin") val sinkTarget = ComponentName("bundle", ModuleName("B", CircuitName("Top"))) val sink = SinkAnnotation(sinkTarget, "pin") val tokenLists = Seq( Seq(Field("x")), Seq(Field("y"), Index(0), Field("yy")), Seq(Field("y"), Index(1), Field("yy")) ) val wgeqAnnos = tokenLists.map { tokens => WidthGeqConstraintAnnotation( ReferenceTarget("Top", "A", Nil, "bundle", tokens), ReferenceTarget("Top", "B", Nil, "bundle", tokens) ) } val failAnnos = Seq(source, sink) val successAnnos = wgeqAnnos ++: failAnnos val input = """circuit Top : | module Top : | inst b of B | inst a of A | | module B : | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | | module A : | wire bundle : {x : UInt, y: {yy : UInt}[2] }""".stripMargin val output = """circuit Top : | module Top : | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | inst b of B | inst a of A | b.pin <= bundle | bundle <= a.bundle_0 | | module B : | input pin : {x : UInt<1>, y: {yy : UInt<3>}[2] } | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | bundle <= pin | | module A : | output bundle_0 : {x : UInt<1>, y: {yy : UInt<3>}[2] } | wire bundle : {x : UInt<1>, y: {yy : UInt<3>}[2] } | bundle_0 <= bundle""".stripMargin // should fail without extra constraint annos due to UninferredWidths val exceptions = intercept[PassExceptions] { executeTest(input, "", transforms, failAnnos) }.exceptions.reverse val msg = exceptions.head.toString assert(msg.contains(s"2 errors detected!")) assert(exceptions.tail.forall(_.isInstanceOf[CheckWidths.UninferredWidth])) // should pass with extra constraints executeTest(input, output, transforms, successAnnos) } }