diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala index c9d78d4b2f7..1679e354b33 100644 --- a/core/src/main/scala/chisel3/BlackBox.scala +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -126,8 +126,8 @@ package experimental { // Ports are named in the same way as regular Modules namePorts() - val firrtlPorts = getModulePorts.map { case port => - Port(port, port.specifiedDirection, UnlocatableSourceInfo) + val firrtlPorts = getModulePortsAndLocators.map { case (port, _, associations) => + Port(port, port.specifiedDirection, associations, UnlocatableSourceInfo) } val component = DefBlackBox(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params, getKnownLayers) _component = Some(component) @@ -217,8 +217,8 @@ abstract class BlackBox( port.setRef(ModuleIO(this, _namespace.name(name)), force = true) } - val firrtlPorts = namedPorts.map { namedPort => - Port(namedPort._2, namedPort._2.specifiedDirection, UnlocatableSourceInfo) + val firrtlPorts = getModulePortsAndLocators.map { case (port, _, associations) => + Port(port, port.specifiedDirection, associations, UnlocatableSourceInfo) } val component = DefBlackBox(this, name, firrtlPorts, io.specifiedDirection, params, getKnownLayers) _component = Some(component) diff --git a/core/src/main/scala/chisel3/IO.scala b/core/src/main/scala/chisel3/IO.scala index 9a997684132..c4749e3c9a1 100644 --- a/core/src/main/scala/chisel3/IO.scala +++ b/core/src/main/scala/chisel3/IO.scala @@ -1,5 +1,6 @@ package chisel3 +import chisel3.domain import chisel3.internal.{throwException, Builder} import chisel3.experimental.{noPrefix, requireIsChiselType, SourceInfo} import chisel3.properties.{Class, Property} diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 328501315af..b23dd491beb 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -576,6 +576,8 @@ package experimental { private val _ports = new ArrayBuffer[(Data, SourceInfo)]() + private val _associations = new HashMap[Data, LinkedHashSet[Data]]() + // getPorts unfortunately already used for tester compatibility protected[chisel3] def getModulePorts: Seq[Data] = { require(_closed, "Can't get ports before module close") @@ -583,9 +585,11 @@ package experimental { } // gets Ports along with there source locators - private[chisel3] def getModulePortsAndLocators: Seq[(Data, SourceInfo)] = { + private[chisel3] def getModulePortsAndLocators: Seq[(Data, SourceInfo, Seq[Data])] = { require(_closed, "Can't get ports before module close") - _ports.toSeq + _ports.toSeq.map { case (port, info) => + (port, info, _associations.get(port).map(_.toSeq).getOrElse(Seq.empty[Data])) + } } /** Get IOs that are currently bound to this module. @@ -608,6 +612,21 @@ package experimental { protected def portsSize: Int = _ports.size + /* Associate a port of this module with one or more domains. */ + final def associate(port: Data, domain: Data, domains: Data*)(implicit si: SourceInfo): Unit = { + val allDomains = Seq(domain) ++ domains + if (!portsContains(port)) { + Builder.error(s"""Unable to associate port '$port' to domains '${allDomains.mkString( + ", " + )}' because the port does not exist in this module""")(si) + return + } + _associations.updateWith(port) { + case Some(acc) => Some(acc ++= allDomains) + case None => Some(LinkedHashSet.empty[Data] ++= allDomains) + } + } + /** Generates the FIRRTL Component (Module or Blackbox) of this Module. * Also closes the module so no more construction can happen inside. */ @@ -618,7 +637,7 @@ package experimental { private[chisel3] def initializeInParent(): Unit private[chisel3] def namePorts(): Unit = { - for ((port, source) <- getModulePortsAndLocators) { + for ((port, source, _) <- getModulePortsAndLocators) { port._computeName(None) match { case Some(name) => if (_namespace.contains(name)) { @@ -882,7 +901,7 @@ package experimental { private[chisel3] def addSecretIO[A <: Data](iodef: A)(implicit sourceInfo: SourceInfo): A = { val name = iodef._computeName(None).getOrElse("secret") iodef.setRef(ModuleIO(this, _namespace.name(name))) - val newPort = new Port(iodef, iodef.specifiedDirection, sourceInfo) + val newPort = new Port(iodef, iodef.specifiedDirection, Seq.empty, sourceInfo) if (_closed) { _component.get.secretPorts += newPort } else secretPorts += newPort @@ -905,7 +924,9 @@ package experimental { * TODO(twigg): Specifically walk the Data definition to call out which nodes * are problematic. */ - protected def IO[T <: Data](iodef: => T)(implicit sourceInfo: SourceInfo): T = { + protected def IO[T <: Data](iodef: => T)( + implicit sourceInfo: SourceInfo + ): T = { chisel3.IO.apply(iodef) } diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index dae51bb3a46..59cd5063e06 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -91,7 +91,7 @@ abstract class RawModule extends BaseModule { private var _firrtlPorts: Option[Seq[Port]] = None private[chisel3] def checkPorts(): Unit = { - for ((port, source) <- getModulePortsAndLocators) { + for ((port, source, _) <- getModulePortsAndLocators) { if (port._computeName(None).isEmpty) { Builder.error( s"Unable to name port $port in $this, " + @@ -175,8 +175,8 @@ abstract class RawModule extends BaseModule { nameId(id) } - val firrtlPorts = getModulePortsAndLocators.map { case (port, sourceInfo) => - Port(port, port.specifiedDirection, sourceInfo) + val firrtlPorts = getModulePortsAndLocators.map { case (port, sourceInfo, associations) => + Port(port, port.specifiedDirection, associations, sourceInfo) } _firrtlPorts = Some(firrtlPorts) diff --git a/core/src/main/scala/chisel3/domain/Domain.scala b/core/src/main/scala/chisel3/domain/Domain.scala new file mode 100644 index 00000000000..8588a0fa916 --- /dev/null +++ b/core/src/main/scala/chisel3/domain/Domain.scala @@ -0,0 +1,20 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.domain + +import chisel3.experimental.SourceInfo +import chisel3.util.simpleClassName + +object Field { + sealed trait Type + + object String extends Type +} + +abstract class Domain()(implicit val sourceInfo: SourceInfo) { self: Singleton => + + private[chisel3] def name: String = simpleClassName(this.getClass()) + + def fields: Seq[(String, Field.Type)] = Seq.empty + +} diff --git a/core/src/main/scala/chisel3/domain/Type.scala b/core/src/main/scala/chisel3/domain/Type.scala new file mode 100644 index 00000000000..cf8144128dc --- /dev/null +++ b/core/src/main/scala/chisel3/domain/Type.scala @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.domain + +import chisel3.{fromIntToLiteral, Data, Element, Printable, UInt, UnknownWidth, Width} +import chisel3.experimental.SourceInfo +import chisel3.internal.{throwException, Builder} + +final class Type private (val domain: Domain) extends Element { self => + + private[chisel3] def _asUIntImpl(first: Boolean)(implicit sourceInfo: SourceInfo): chisel3.UInt = { + Builder.error(s"${this._localErrorContext} does not support .asUInt.") + 0.U + } + + override protected def _fromUInt(that: UInt)(implicit sourceInfo: SourceInfo): Data = { + Builder.exception(s"${this._localErrorContext} cannot be driven by UInt") + } + + override def cloneType: this.type = new Type(domain).asInstanceOf[this.type] + + override def toPrintable: Printable = + throwException(s"'domain.Type' does not support hardware printing" + this._errorContext) + + private[chisel3] def width: Width = UnknownWidth + + addDomain(domain) + +} + +object Type { + + def apply(domain: Domain) = new Type(domain) + +} diff --git a/core/src/main/scala/chisel3/domain/package.scala b/core/src/main/scala/chisel3/domain/package.scala new file mode 100644 index 00000000000..de79890db14 --- /dev/null +++ b/core/src/main/scala/chisel3/domain/package.scala @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3 + +import chisel3.internal.Builder + +package object domain { + + def addDomain(domain: Domain) = { + Builder.domains += domain + } + +} diff --git a/core/src/main/scala/chisel3/domains/ClockDomain.scala b/core/src/main/scala/chisel3/domains/ClockDomain.scala new file mode 100644 index 00000000000..bd41176401c --- /dev/null +++ b/core/src/main/scala/chisel3/domains/ClockDomain.scala @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.domains + +import chisel3.domain.{Domain, Field} +import chisel3.experimental.UnlocatableSourceInfo + +object ClockDomain extends Domain()(sourceInfo = UnlocatableSourceInfo) { + + override def fields: Seq[(String, Field.Type)] = Seq( + "name_pattern" -> Field.String + ) + +} diff --git a/core/src/main/scala/chisel3/experimental/IntrinsicModule.scala b/core/src/main/scala/chisel3/experimental/IntrinsicModule.scala index f98b8e2ad2a..4fefd064b09 100644 --- a/core/src/main/scala/chisel3/experimental/IntrinsicModule.scala +++ b/core/src/main/scala/chisel3/experimental/IntrinsicModule.scala @@ -23,8 +23,8 @@ abstract class IntrinsicModule(intrinsicName: String, val params: Map[String, Pa // Ports are named in the same way as regular Modules namePorts() - val firrtlPorts = getModulePorts.map { case port => - Port(port, port.specifiedDirection, UnlocatableSourceInfo) + val firrtlPorts = getModulePortsAndLocators.map { case (port, _, associations) => + Port(port, port.specifiedDirection, associations, UnlocatableSourceInfo) } val component = DefIntrinsicModule(this, name, firrtlPorts, SpecifiedDirection.Unspecified, params) _component = Some(component) diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/core/Instance.scala b/core/src/main/scala/chisel3/experimental/hierarchy/core/Instance.scala index 6f14117d07c..8518c97b862 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/core/Instance.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/core/Instance.scala @@ -133,8 +133,14 @@ object Instance extends SourceInfoDoc { require(!_closed, s"Can't generate $desiredName module more than once") evaluateAtModuleBodyEnd() _closed = true - val firrtlPorts = importedDefinition.proto.getModulePortsAndLocators.map { case (port, sourceInfo) => - Port(port, port.specifiedDirection, sourceInfo): @nowarn // Deprecated code allowed for internal use + val firrtlPorts = importedDefinition.proto.getModulePortsAndLocators.map { + case (port, sourceInfo, associations) => + Port( + port, + port.specifiedDirection, + associations, + sourceInfo + ): @nowarn // Deprecated code allowed for internal use } val component = DefBlackBox( diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 8821a66da03..996f31575ae 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -531,6 +531,7 @@ private[chisel3] class DynamicContext( val annotations = ArrayBuffer[() => Seq[Annotation]]() val layers = mutable.LinkedHashSet[layer.Layer]() val options = mutable.LinkedHashSet[choice.Case]() + val domains = mutable.LinkedHashSet[domain.Domain]() var currentModule: Option[BaseModule] = None // Views that do not correspond to a single ReferenceTarget and thus require renaming @@ -602,6 +603,7 @@ private[chisel3] object Builder extends LazyLogging { def layers: mutable.LinkedHashSet[layer.Layer] = dynamicContext.layers def options: mutable.LinkedHashSet[choice.Case] = dynamicContext.options + def domains: mutable.LinkedHashSet[domain.Domain] = dynamicContext.domains def contextCache: BuilderContextCache = dynamicContext.contextCache @@ -1122,7 +1124,8 @@ private[chisel3] object Builder extends LazyLogging { makeViewRenameMap(circuitName = components.last.name), typeAliases, layerAdjacencyList(layer.Layer.Root).map(foldLayers).toSeq, - optionDefs + optionDefs, + domains.toSeq.map(d => Domain(d.sourceInfo, d.name)) ) (ElaboratedCircuit(circuit, dynamicContext.annotationSeq.toSeq), mod) } diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index ed027523159..e03887a371f 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -397,6 +397,7 @@ private[chisel3] object Converter { extractType(t._elements.head._2, childClearDir, info, checkProbe, true, typeAliases) } case t: Property[_] => t.getPropertyType + case t: domain.Type => fir.DomainType(t.domain.name) } def convert(name: String, param: Param): fir.Param = param match { @@ -441,7 +442,13 @@ private[chisel3] object Converter { case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false } val tpe = extractType(port.id, clearDir, port.sourceInfo, true, true, typeAliases) - fir.Port(convert(port.sourceInfo), getRef(port.id, port.sourceInfo).name, dir, tpe) + fir.Port( + convert(port.sourceInfo), + getRef(port.id, port.sourceInfo).name, + dir, + tpe, + port.associations.map(getRef(_, UnlocatableSourceInfo).name) + ) } def convert(component: Component, typeAliases: Seq[String]): fir.DefModule = component match { diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 927bfe94aa0..f71a7a54091 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -481,6 +481,11 @@ private[chisel3] object ir { chiselLayer: layer.Layer ) + final case class Domain( + sourceInfo: SourceInfo, + name: String + ) + class LayerBlock(val sourceInfo: SourceInfo, val layer: chisel3.layer.Layer) extends Command { val region = new Block(sourceInfo) } @@ -498,7 +503,7 @@ private[chisel3] object ir { case class DefOption(sourceInfo: SourceInfo, name: String, cases: Seq[DefOptionCase]) case class DefOptionCase(sourceInfo: SourceInfo, name: String) - case class Port(id: Data, dir: SpecifiedDirection, sourceInfo: SourceInfo) + case class Port(id: Data, dir: SpecifiedDirection, associations: Seq[Data], sourceInfo: SourceInfo) case class Printf( id: printf.Printf, @@ -606,7 +611,8 @@ private[chisel3] object ir { renames: RenameMap, typeAliases: Seq[DefTypeAlias], layers: Seq[Layer], - options: Seq[DefOption] + options: Seq[DefOption], + domains: Seq[Domain] ) { def firrtlAnnotations: Iterable[Annotation] = annotations.flatMap(_().flatMap(_.update(renames))) } diff --git a/core/src/main/scala/chisel3/internal/firrtl/Serializer.scala b/core/src/main/scala/chisel3/internal/firrtl/Serializer.scala index 5dc2dd2bccc..5c4c1e94c6d 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Serializer.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Serializer.scala @@ -507,6 +507,7 @@ private[chisel3] object Serializer { case t: Property[_] => // TODO can we not use FIRRTL types here? b ++= fir.Serializer.serialize(t.getPropertyType) + case t: domain.Type => b ++= "Domain of "; b ++= t.domain.name; } private def serialize(name: String, param: Param)(implicit b: StringBuilder): Unit = param match { @@ -561,6 +562,14 @@ private[chisel3] object Serializer { b ++= legalize(getRef(port.id, port.sourceInfo).name) b ++= " : " val tpe = serializeType(port.id, clearDir, port.sourceInfo, true, true, typeAliases) + if (port.associations.nonEmpty) { + b ++= " domains [" + port.associations.zipWithIndex.foreach { case (assoc, i) => + if (i > 0) b ++= ", " + b ++= legalize(getRef(assoc, UnlocatableSourceInfo).name) + } + b ++= "]" + } serialize(port.sourceInfo) } @@ -665,6 +674,23 @@ private[chisel3] object Serializer { // serialize(ta.sourceInfo) TODO: Uncomment once firtool accepts infos for type aliases } + private def serialize(domain: Domain)(implicit b: StringBuilder, indent: Int): Unit = { + newLineAndIndent() + b ++= "domain " + b ++= domain.name + b ++= " :" + } + + private def serializeDomains(domains: Seq[Domain])(implicit indent: Int): Iterator[String] = { + if (domains.isEmpty) + return Iterator.empty + + implicit val b = new StringBuilder + domains.foreach(serialize) + newLineNoIndent() + Iterator(b.toString) + } + // TODO make Annotation serialization lazy private def serialize(circuit: Circuit, annotations: Seq[Annotation]): Iterator[String] = { implicit val indent: Int = 0 @@ -699,12 +725,14 @@ private[chisel3] object Serializer { Iterator(b.toString) } else Iterator.empty val layers = serialize(circuit.layers)(indent + 1) + val domains = serializeDomains(circuit.domains)(indent + 1) // TODO what is typeAliases for? Should it be a Set? val typeAliasesSeq: Seq[String] = circuit.typeAliases.map(_.name) prelude ++ options ++ typeAliases ++ layers ++ + domains ++ circuit.components.iterator.zipWithIndex.flatMap { case (m, i) => val newline = Iterator(if (i == 0) s"$NewLine" else s"${NewLine}${NewLine}") newline ++ serialize(m, typeAliasesSeq)(indent + 1) diff --git a/core/src/main/scala/chisel3/properties/Class.scala b/core/src/main/scala/chisel3/properties/Class.scala index 25e6ac532e1..017b32f862a 100644 --- a/core/src/main/scala/chisel3/properties/Class.scala +++ b/core/src/main/scala/chisel3/properties/Class.scala @@ -63,8 +63,8 @@ class Class extends BaseModule { } // Create IR Ports and set the firrtlPorts variable. - val ports = getModulePortsAndLocators.map { case (port, sourceInfo) => - Port(port, port.specifiedDirection, sourceInfo) + val ports = getModulePortsAndLocators.map { case (port, sourceInfo, _) => + Port(port, port.specifiedDirection, Seq.empty, sourceInfo) } // No more commands. diff --git a/firrtl/src/main/scala/firrtl/ir/IR.scala b/firrtl/src/main/scala/firrtl/ir/IR.scala index 5174e8f9fb0..0e06149a48b 100644 --- a/firrtl/src/main/scala/firrtl/ir/IR.scala +++ b/firrtl/src/main/scala/firrtl/ir/IR.scala @@ -507,6 +507,9 @@ case class DefOption(info: Info, name: String, cases: Seq[DefOptionCase]) @deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") case class DefOptionCase(info: Info, name: String) +@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") +case class Domain(info: Info, name: String) + @deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") case class IntrinsicExpr(intrinsic: String, args: Seq[Expression], params: Seq[Param], tpe: Type) extends Expression @@ -707,6 +710,9 @@ case class ClassPropertyType(name: String) extends PropertyType @deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") case object AnyRefPropertyType extends PropertyType +@deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") +case class DomainType(domain: String) extends Type with UseSerializer + @deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") case object UnknownType extends Type with UseSerializer @@ -724,7 +730,7 @@ case object Output extends Direction { /** [[DefModule]] Port */ @deprecated("All APIs in package firrtl are deprecated.", "Chisel 7.0.0") -case class Port(info: Info, name: String, direction: Direction, tpe: Type) +case class Port(info: Info, name: String, direction: Direction, tpe: Type, associations: Seq[String]) extends FirrtlNode with IsDeclaration with UseSerializer @@ -833,7 +839,8 @@ case class Circuit( main: String, typeAliases: Seq[DefTypeAlias] = Seq.empty, layers: Seq[Layer] = Seq.empty, - options: Seq[DefOption] = Seq.empty + options: Seq[DefOption] = Seq.empty, + domains: Seq[Domain] = Seq.empty ) extends FirrtlNode with HasInfo with UseSerializer diff --git a/firrtl/src/main/scala/firrtl/ir/Serializer.scala b/firrtl/src/main/scala/firrtl/ir/Serializer.scala index 49fea6f2dad..484ed791408 100644 --- a/firrtl/src/main/scala/firrtl/ir/Serializer.scala +++ b/firrtl/src/main/scala/firrtl/ir/Serializer.scala @@ -484,6 +484,7 @@ object Serializer { case ClassPropertyType(name) => b ++= "Inst<"; b ++= name; b += '>' case AnyRefPropertyType => b ++= "AnyRef" case AliasType(name) => b ++= name + case DomainType(domain) => b ++= "Domain of "; b ++= (domain); case UnknownType => b += '?' case other => b ++= other.serialize // Handle user-defined nodes } @@ -495,8 +496,14 @@ object Serializer { } private def s(node: Port)(implicit b: StringBuilder, indent: Int): Unit = node match { - case Port(info, name, direction, tpe) => - s(direction); b += ' '; b ++= legalize(name); b ++= " : "; s(tpe); s(info) + case Port(info, name, direction, tpe, associations) => + s(direction); b += ' '; b ++= legalize(name); + if (associations.nonEmpty) { + b ++= " domains ["; + associations.foreach(a => b ++= a) + b++= "]" + } + b ++= " : "; s(tpe); s(info) } private def s(node: Param)(implicit b: StringBuilder, indent: Int): Unit = node match { diff --git a/src/test/scala-2/chisel3/stage/ChiselOptionsViewSpec.scala b/src/test/scala-2/chisel3/stage/ChiselOptionsViewSpec.scala index b11aeea4102..dc116c813e6 100644 --- a/src/test/scala-2/chisel3/stage/ChiselOptionsViewSpec.scala +++ b/src/test/scala-2/chisel3/stage/ChiselOptionsViewSpec.scala @@ -16,7 +16,7 @@ class ChiselOptionsViewSpec extends AnyFlatSpec with Matchers { behavior.of(ChiselOptionsView.getClass.getName) it should "construct a view from an AnnotationSeq" in { - val bar = Circuit("bar", Seq.empty, Seq.empty, RenameMap("bar"), Seq.empty, Seq.empty, Seq.empty) + val bar = Circuit("bar", Seq.empty, Seq.empty, RenameMap("bar"), Seq.empty, Seq.empty, Seq.empty, Seq.empty) val circuit = ElaboratedCircuit(bar, Seq.empty) val annotations = Seq( PrintFullStackTraceAnnotation, diff --git a/src/test/scala-2/chiselTests/DomainSpec.scala b/src/test/scala-2/chiselTests/DomainSpec.scala new file mode 100644 index 00000000000..5412121849c --- /dev/null +++ b/src/test/scala-2/chiselTests/DomainSpec.scala @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.domain.Domain +import chisel3.domains.ClockDomain +import chisel3.testing.FileCheck +import circt.stage.ChiselStage +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class DomainSpec extends AnyFlatSpec with Matchers with FileCheck { + + behavior of "Domains" + + they should "emit FIRRTL for internal and user-defined domains" in { + + object UserDefined extends Domain + + class Foo extends RawModule { + val A = IO(Input(domain.Type(ClockDomain))) + val B = IO(Input(domain.Type(UserDefined))) + val a = IO(Input(Bool())) + val b = IO(Input(Bool())) + + associate(a, A) + associate(b, B) + } + + val chirrtl = ChiselStage.emitCHIRRTL(new Foo, args = Array("--full-stacktrace")) + println(chirrtl) + + chirrtl.fileCheck() { + """|CHECK: circuit Foo : + |CHECK: domain ClockDomain : + |CHECK-NEXT: domain UserDefined : + | + |CHECK: public module Foo : + |CHECK-NEXT: input A : Domain of ClockDomain + |CHECK-NEXT: input B : Domain of UserDefined + |CHECK-NEXT: input a : UInt<1> domains [A] + |CHECK-NEXT: input b : UInt<1> domains [B] + |""".stripMargin + } + + } + +}