// See LICENSE for license details.

package firrtlTests
package transforms

import org.scalatest.FlatSpec
import org.scalatest.Matchers
import org.scalatest.junit.JUnitRunner
import java.io._

import firrtl._
import firrtl.ir.{Circuit, Type, GroundType, IntWidth}
import firrtl.Parser
import firrtl.passes.PassExceptions
import firrtl.annotations.{
   Named,
   CircuitName,
   ModuleName,
   ComponentName,
   Annotation,
   Target
}
import firrtl.transforms.TopWiring._


trait TopWiringTestsCommon extends FirrtlRunners {

   val testDir = createTestDirectory("TopWiringTests")
   val testDirName = testDir.getPath
   def transform = new TopWiringTransform

   def topWiringDummyOutputFilesFunction(dir: String,
                                         mapping: Seq[((ComponentName, Type, Boolean, Seq[String], String), Int)],
                                         state: CircuitState): CircuitState = {
     state
   }

   def topWiringTestOutputFilesFunction(dir: String,
                                        mapping: Seq[((ComponentName, Type, Boolean, Seq[String], String), Int)],
                                        state: CircuitState): CircuitState = {
     val testOutputFile = new PrintWriter(new File(dir, "TopWiringOutputTest.txt" ))
     mapping map {
          case ((_, tpe, _, path, prefix), index) => {
            val portwidth = tpe match { case GroundType(IntWidth(w)) => w }
            val portnum = index
            val portname = prefix + path.mkString("_")
            testOutputFile.append(s"new top level port $portnum : $portname, with width $portwidth \n")
          }
     }
     testOutputFile.close()
     state
   }
}

/**
 * Tests TopWiring transformation
 */
class TopWiringTests extends MiddleTransformSpec with TopWiringTestsCommon  {

   "The signal x in module C" should s"be connected to Top port with topwiring prefix and outputfile in $testDirName" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                         TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_x <= a1.topwiring_b1_c1_x
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x: UInt<1>
           |    inst b1 of B
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= b1.topwiring_c1_x
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    inst c1 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal x in module C inst c1 and c2" should
    s"be connected to Top port with topwiring prefix and outfile in $testDirName" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |    inst c2 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                    ModuleName(s"C", CircuitName(s"Top"))), s"topwiring_"),
                               TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x: UInt<1>
           |    output topwiring_a1_b1_c2_x: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_x <= a1.topwiring_b1_c1_x
           |    topwiring_a1_b1_c2_x <= a1.topwiring_b1_c2_x
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x: UInt<1>
           |    output topwiring_b1_c2_x: UInt<1>
           |    inst b1 of B
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= b1.topwiring_c1_x
           |    topwiring_b1_c2_x <= b1.topwiring_c2_x
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    output topwiring_c2_x: UInt<1>
           |    inst c1 of C
           |    inst c2 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |    topwiring_c2_x <= c2.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal x in module C" should
   s"be connected to Top port with topwiring prefix and outputfile in $testDirName, after name colission" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |    wire topwiring_a1_b1_c1_x : UInt<1>
           |    topwiring_a1_b1_c1_x <= UInt(0)
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |    wire topwiring_b1_c1_x : UInt<1>
           |    topwiring_b1_c1_x <= UInt(0)
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                                 s"topwiring_"),
                               TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x_0: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    wire topwiring_a1_b1_c1_x : UInt<1>
           |    topwiring_a1_b1_c1_x <= UInt<1>("h0")
           |    topwiring_a1_b1_c1_x_0 <= a1.topwiring_b1_c1_x_0
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x_0: UInt<1>
           |    inst b1 of B
           |    wire topwiring_b1_c1_x : UInt<1>
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= UInt<1>("h0")
           |    topwiring_b1_c1_x_0 <= b1.topwiring_c1_x
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    inst c1 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal x in module C" should
   "be connected to Top port with topwiring prefix and no output function" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_x <= a1.topwiring_b1_c1_x
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x: UInt<1>
           |    inst b1 of B
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= b1.topwiring_c1_x
           |  module A_ :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    inst c1 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal x in module C inst c1 and c2 and signal y in module A_" should
   s"be connected to Top port with topwiring prefix and outfile in $testDirName" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output x: UInt<1>
           |    wire y : UInt<1>
           |    y <= UInt(1)
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |    inst c2 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                               TopWiringAnnotation(ComponentName(s"y",
                                                                 ModuleName(s"A_", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                         TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x: UInt<1>
           |    output topwiring_a1_b1_c2_x: UInt<1>
           |    output topwiring_a2_y: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_x <= a1.topwiring_b1_c1_x
           |    topwiring_a1_b1_c2_x <= a1.topwiring_b1_c2_x
           |    topwiring_a2_y <= a2.topwiring_y
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x: UInt<1>
           |    output topwiring_b1_c2_x: UInt<1>
           |    inst b1 of B
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= b1.topwiring_c1_x
           |    topwiring_b1_c2_x <= b1.topwiring_c2_x
           |  module A_ :
           |    output x: UInt<1>
           |    output topwiring_y: UInt<1>
           |    wire y : UInt<1>
           |    x <= UInt(1)
           |    y <= UInt<1>("h1")
           |    topwiring_y <= y
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    output topwiring_c2_x: UInt<1>
           |    inst c1 of C
           |    inst c2 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |    topwiring_c2_x <= c2.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal x in module C inst c1 and c2 and signal y in module A_" should
   s"be connected to Top port with topwiring and top2wiring prefix and outfile in $testDirName" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output x: UInt<1>
           |    wire y : UInt<1>
           |    y <= UInt(1)
           |    x <= UInt(1)
           |  module B :
           |    output x: UInt<1>
           |    x <= UInt(1)
           |    inst c1 of C
           |    inst c2 of C
           |  module C:
           |    output x: UInt<1>
           |    x <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"x",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                               TopWiringAnnotation(ComponentName(s"y",
                                                                 ModuleName(s"A_", CircuitName(s"Top"))),
                                                   s"top2wiring_"),
                         TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_x: UInt<1>
           |    output topwiring_a1_b1_c2_x: UInt<1>
           |    output top2wiring_a2_y: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_x <= a1.topwiring_b1_c1_x
           |    topwiring_a1_b1_c2_x <= a1.topwiring_b1_c2_x
           |    top2wiring_a2_y <= a2.top2wiring_y
           |  module A :
           |    output x: UInt<1>
           |    output topwiring_b1_c1_x: UInt<1>
           |    output topwiring_b1_c2_x: UInt<1>
           |    inst b1 of B
           |    x <= UInt(1)
           |    topwiring_b1_c1_x <= b1.topwiring_c1_x
           |    topwiring_b1_c2_x <= b1.topwiring_c2_x
           |  module A_ :
           |    output x: UInt<1>
           |    output top2wiring_y: UInt<1>
           |    wire y : UInt<1>
           |    x <= UInt(1)
           |    y <= UInt<1>("h1")
           |    top2wiring_y <= y
           |  module B :
           |    output x: UInt<1>
           |    output topwiring_c1_x: UInt<1>
           |    output topwiring_c2_x: UInt<1>
           |    inst c1 of C
           |    inst c2 of C
           |    x <= UInt(1)
           |    topwiring_c1_x <= c1.topwiring_x
           |    topwiring_c2_x <= c2.topwiring_x
           |  module C:
           |    output x: UInt<1>
           |    output topwiring_x: UInt<1>
           |    x <= UInt(0)
           |    topwiring_x <= x
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "The signal fullword in module C inst c1 and c2 and signal y in module A_" should
   s"be connected to Top port with topwiring and top2wiring prefix and outfile in $testDirName" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output fullword: UInt<1>
           |    fullword <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output fullword: UInt<1>
           |    wire y : UInt<1>
           |    y <= UInt(1)
           |    fullword <= UInt(1)
           |  module B :
           |    output fullword: UInt<1>
           |    fullword <= UInt(1)
           |    inst c1 of C
           |    inst c2 of C
           |  module C:
           |    output fullword: UInt<1>
           |    fullword <= UInt(0)
           """.stripMargin
      val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"fullword",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                               TopWiringAnnotation(ComponentName(s"y",
                                                                 ModuleName(s"A_", CircuitName(s"Top"))),
                                                   s"top2wiring_"),
                         TopWiringOutputFilesAnnotation(testDirName, topWiringTestOutputFilesFunction))
      val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_c1_fullword: UInt<1>
           |    output topwiring_a1_b1_c2_fullword: UInt<1>
           |    output top2wiring_a2_y: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_c1_fullword <= a1.topwiring_b1_c1_fullword
           |    topwiring_a1_b1_c2_fullword <= a1.topwiring_b1_c2_fullword
           |    top2wiring_a2_y <= a2.top2wiring_y
           |  module A :
           |    output fullword: UInt<1>
           |    output topwiring_b1_c1_fullword: UInt<1>
           |    output topwiring_b1_c2_fullword: UInt<1>
           |    inst b1 of B
           |    fullword <= UInt(1)
           |    topwiring_b1_c1_fullword <= b1.topwiring_c1_fullword
           |    topwiring_b1_c2_fullword <= b1.topwiring_c2_fullword
           |  module A_ :
           |    output fullword: UInt<1>
           |    output top2wiring_y: UInt<1>
           |    wire y : UInt<1>
           |    fullword <= UInt(1)
           |    y <= UInt<1>("h1")
           |    top2wiring_y <= y
           |  module B :
           |    output fullword: UInt<1>
           |    output topwiring_c1_fullword: UInt<1>
           |    output topwiring_c2_fullword: UInt<1>
           |    inst c1 of C
           |    inst c2 of C
           |    fullword <= UInt(1)
           |    topwiring_c1_fullword <= c1.topwiring_fullword
           |    topwiring_c2_fullword <= c2.topwiring_fullword
           |  module C:
           |    output fullword: UInt<1>
           |    output topwiring_fullword: UInt<1>
           |    fullword <= UInt(0)
           |    topwiring_fullword <= fullword
           """.stripMargin
      execute(input, check, topwiringannos)
   }


   "The signal fullword in module C inst c1 and c2 and signal fullword in module B" should
   s"be connected to Top port with topwiring prefix" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst a2 of A_
           |  module A :
           |    output fullword: UInt<1>
           |    fullword <= UInt(1)
           |    inst b1 of B
           |  module A_ :
           |    output fullword: UInt<1>
           |    wire y : UInt<1>
           |    y <= UInt(1)
           |    fullword <= UInt(1)
           |  module B :
           |    output fullword: UInt<1>
           |    fullword <= UInt(1)
           |    inst c1 of C
           |    inst c2 of C
           |  module C:
           |    output fullword: UInt<1>
           |    fullword <= UInt(0)
           """.stripMargin
     val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"fullword",
                                                                 ModuleName(s"C", CircuitName(s"Top"))),
                                                   s"topwiring_"),
                               TopWiringAnnotation(ComponentName(s"fullword",
                                                                 ModuleName(s"B", CircuitName(s"Top"))),
                                                   s"topwiring_"))
     val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_b1_fullword: UInt<1>
           |    output topwiring_a1_b1_c1_fullword: UInt<1>
           |    output topwiring_a1_b1_c2_fullword: UInt<1>
           |    inst a1 of A
           |    inst a2 of A_
           |    topwiring_a1_b1_fullword <= a1.topwiring_b1_fullword
           |    topwiring_a1_b1_c1_fullword <= a1.topwiring_b1_c1_fullword
           |    topwiring_a1_b1_c2_fullword <= a1.topwiring_b1_c2_fullword
           |  module A :
           |    output fullword: UInt<1>
           |    output topwiring_b1_fullword: UInt<1>
           |    output topwiring_b1_c1_fullword: UInt<1>
           |    output topwiring_b1_c2_fullword: UInt<1>
           |    inst b1 of B
           |    fullword <= UInt(1)
           |    topwiring_b1_fullword <= b1.topwiring_fullword
           |    topwiring_b1_c1_fullword <= b1.topwiring_c1_fullword
           |    topwiring_b1_c2_fullword <= b1.topwiring_c2_fullword
           |  module A_ :
           |    output fullword: UInt<1>
           |    wire y : UInt<1>
           |    fullword <= UInt(1)
           |    y <= UInt<1>("h1")
           |  module B :
           |    output fullword: UInt<1>
           |    output topwiring_fullword: UInt<1>
           |    output topwiring_c1_fullword: UInt<1>
           |    output topwiring_c2_fullword: UInt<1>
           |    inst c1 of C
           |    inst c2 of C
           |    fullword <= UInt(1)
           |    topwiring_fullword <= fullword
           |    topwiring_c1_fullword <= c1.topwiring_fullword
           |    topwiring_c2_fullword <= c2.topwiring_fullword
           |  module C:
           |    output fullword: UInt<1>
           |    output topwiring_fullword: UInt<1>
           |    fullword <= UInt(0)
           |    topwiring_fullword <= fullword
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "TopWiringTransform" should "do nothing if run without TopWiring* annotations" in {
     val input = """|circuit Top :
                    |  module Top :
                    |    input foo : UInt<1>""".stripMargin
     val inputFile = {
       val fileName = s"${testDir.getAbsolutePath}/input-no-sources.fir"
       val w = new PrintWriter(fileName)
       w.write(input)
       w.close()
       fileName
     }
     val args = Array(
       "--custom-transforms", "firrtl.transforms.TopWiring.TopWiringTransform",
       "--input-file", inputFile,
       "--top-name", "Top",
       "--compiler", "low",
       "--info-mode", "ignore"
     )
     firrtl.Driver.execute(args) match {
       case FirrtlExecutionSuccess(_, emitted) => parse(emitted) should be (parse(input))
       case _ => fail
     }
   }

   "TopWiringTransform" should "remove TopWiringAnnotations" in {
     val input =
       """|circuit Top:
          |  module Top:
          |    wire foo: UInt<1>""".stripMargin

     val bar =
       Target
         .deserialize("~Top|Top>foo")
         .toNamed match { case a: ComponentName => a }

     val annotations = Seq(TopWiringAnnotation(bar, "bar_"))
     val outputState = (new TopWiringTransform).execute(CircuitState(Parser.parse(input), MidForm, annotations, None))

     outputState.circuit.serialize should include ("output bar_foo")
     outputState.annotations.toSeq should be (empty)
   }
}

class AggregateTopWiringTests extends MiddleTransformSpec with TopWiringTestsCommon {

   "An aggregate wire named myAgg in A" should s"be wired to Top's IO as topwiring_a1_myAgg" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |  module A:
           |    wire myAgg: { a: UInt<1>, b: SInt<8> }
           |    myAgg.a <= UInt(0)
           |    myAgg.b <= SInt(-1)
           """.stripMargin
     val topwiringannos = Seq(TopWiringAnnotation(ComponentName(s"myAgg", ModuleName(s"A", CircuitName(s"Top"))),
                                                   s"topwiring_"))
     val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_myAgg: { a: UInt<1>, b: SInt<8> }
           |    inst a1 of A
           |    topwiring_a1_myAgg.a <= a1.topwiring_myAgg.a
           |    topwiring_a1_myAgg.b <= a1.topwiring_myAgg.b
           |  module A :
           |    output topwiring_myAgg: { a: UInt<1>, b: SInt<8> }
           |    wire myAgg: { a: UInt<1>, b: SInt<8> }
           |    myAgg.a <= UInt(0)
           |    myAgg.b <= SInt(-1)
           |    topwiring_myAgg.a <= myAgg.a
           |    topwiring_myAgg.b <= myAgg.b
           """.stripMargin
      execute(input, check, topwiringannos)
   }

   "Aggregate wires myAgg in Top.a1, Top.b.a1 and Top.b.a2" should
   s"be wired to Top's IO as topwiring_a1_myAgg, topwiring_b_a1_myAgg, and topwiring_b_a2_myAgg" in {
      val input =
         """circuit Top :
           |  module Top :
           |    inst a1 of A
           |    inst b of B
           |  module B:
           |    inst a1 of A
           |    inst a2 of A
           |  module A:
           |    wire myAgg: { a: UInt<1>, b: SInt<8> }
           |    myAgg.a <= UInt(0)
           |    myAgg.b <= SInt(-1)
           """.stripMargin
     val topwiringannos = Seq(
        TopWiringAnnotation(ComponentName(s"myAgg", ModuleName(s"A", CircuitName(s"Top"))), s"topwiring_"))

     val check =
         """circuit Top :
           |  module Top :
           |    output topwiring_a1_myAgg: { a: UInt<1>, b: SInt<8> }
           |    output topwiring_b_a1_myAgg: { a: UInt<1>, b: SInt<8> }
           |    output topwiring_b_a2_myAgg: { a: UInt<1>, b: SInt<8> }
           |    inst a1 of A
           |    inst b of B
           |    topwiring_a1_myAgg.a <= a1.topwiring_myAgg.a
           |    topwiring_a1_myAgg.b <= a1.topwiring_myAgg.b
           |    topwiring_b_a1_myAgg.a <= b.topwiring_a1_myAgg.a
           |    topwiring_b_a1_myAgg.b <= b.topwiring_a1_myAgg.b
           |    topwiring_b_a2_myAgg.a <= b.topwiring_a2_myAgg.a
           |    topwiring_b_a2_myAgg.b <= b.topwiring_a2_myAgg.b
           |  module B:
           |    output topwiring_a1_myAgg: { a: UInt<1>, b: SInt<8> }
           |    output topwiring_a2_myAgg: { a: UInt<1>, b: SInt<8> }
           |    inst a1 of A
           |    inst a2 of A
           |    topwiring_a1_myAgg.a <= a1.topwiring_myAgg.a
           |    topwiring_a1_myAgg.b <= a1.topwiring_myAgg.b
           |    topwiring_a2_myAgg.a <= a2.topwiring_myAgg.a
           |    topwiring_a2_myAgg.b <= a2.topwiring_myAgg.b
           |  module A :
           |    output topwiring_myAgg: { a: UInt<1>, b: SInt<8> }
           |    wire myAgg: { a: UInt<1>, b: SInt<8> }
           |    myAgg.a <= UInt(0)
           |    myAgg.b <= SInt(-1)
           |    topwiring_myAgg.a <= myAgg.a
           |    topwiring_myAgg.b <= myAgg.b
           """.stripMargin
      execute(input, check, topwiringannos)
   }
}
