diff --git a/core/src/main/scala/chisel3/domain/Type.scala b/core/src/main/scala/chisel3/domain/Type.scala index 6842f331327..54ef8c8b15a 100644 --- a/core/src/main/scala/chisel3/domain/Type.scala +++ b/core/src/main/scala/chisel3/domain/Type.scala @@ -61,6 +61,8 @@ final class Type private[domain] (val domain: Domain) extends Element { self => override def cloneType: this.type = new Type(domain).asInstanceOf[this.type] + override def toString: String = s"${domain.name}.Type" + override def toPrintable: Printable = throwException(s"'domain.Type' does not support hardware printing" + this._errorContext) diff --git a/core/src/main/scala/chisel3/domain/package.scala b/core/src/main/scala/chisel3/domain/package.scala index e10f2613ceb..9834e9b97db 100644 --- a/core/src/main/scala/chisel3/domain/package.scala +++ b/core/src/main/scala/chisel3/domain/package.scala @@ -23,7 +23,7 @@ package object domain { * @param source the source of the forward */ def define[A <: domain.Type](sink: A, source: A)(implicit sourceInfo: SourceInfo): Unit = { - Builder.pushCommand(ir.DomainDefine(sourceInfo, sink.lref, source.ref)) + chisel3.internal.MonoConnect.domainDefine(sourceInfo, sink, source, Builder.referenceUserContainer) } /** Unsafe cast to a variadic list of domains. diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala index 30f205943c6..5f2d8b65f56 100644 --- a/core/src/main/scala/chisel3/internal/BiConnect.scala +++ b/core/src/main/scala/chisel3/internal/BiConnect.scala @@ -116,6 +116,22 @@ private[chisel3] object BiConnect { } pushCommand(DefInvalid(sourceInfo, left_a.lref(sourceInfo))) case (DontCare, right_a: Analog) => connect(sourceInfo, right, left, context_mod) + // Two domains are bi-connected at the root. `domain_define` has no + // direction, so :<=/<> are simply lowered to the same node as `domain.define`. + case (left_d: chisel3.domain.Type, right_d: chisel3.domain.Type) => + // Pick the directional sense using the same rule as Records: if the + // left-hand side can't be a sink, flip so that the sink is writable. + val (sink, source) = + if (!MonoConnect.canBeSink(left_d, context_mod) && MonoConnect.canBeSink(right_d, context_mod)) { + (right_d, left_d) + } else { + (left_d, right_d) + } + try { + MonoConnect.domainDefine(sourceInfo, sink, source, context_mod) + } catch { + case MonoConnectException(message) => throw BiConnectException(message) + } case (left_e: Element, right_e: Element) => { elemConnect(sourceInfo, left_e, right_e, context_mod) // TODO(twigg): Verify the element-level classes are connectable diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala index 51bd4114243..dd83a2b03b1 100644 --- a/core/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -7,7 +7,7 @@ import chisel3._ import chisel3.experimental.{Analog, BaseModule, SourceInfo} import chisel3.internal.binding._ import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl.ir.{Block, Connect, DefInvalid, ProbeDefine, PropAssign} +import chisel3.internal.firrtl.ir.{Block, Connect, DefInvalid, DomainDefine, ProbeDefine, PropAssign} import chisel3.internal.firrtl.Converter import chisel3.experimental.dataview.{isView, reify, reifyIdentityView} import chisel3.properties.{Class, Property} @@ -80,6 +80,10 @@ private[chisel3] object MonoConnect { MonoConnectException(s"Source ${formatName(source)} of Probed type cannot participate in a mono connection (:=)") def SinkProbeMonoConnectionException(sink: Data) = MonoConnectException(s"Sink ${formatName(sink)} of Probed type cannot participate in a mono connection (:=)") + def MismatchedDomainException(sink: chisel3.domain.Type, source: chisel3.domain.Type) = + MonoConnectException( + s"Sink (${sink.domain.name}) and Source (${source.domain.name}) are different kinds of domains." + ) /** Check if the argument is visible from current block scope * @@ -155,6 +159,9 @@ private[chisel3] object MonoConnect { elemConnect(sourceInfo, sink_e, source_e, context_mod) case (sink_p: Property[_], source_p: Property[_]) => propConnect(sourceInfo, sink_p, source_p, context_mod) + // Two domains are connected at the root. + case (sink_d: chisel3.domain.Type, source_d: chisel3.domain.Type) => + domainDefine(sourceInfo, sink_d, source_d, context_mod) // Handle Vec case case (sink_v: Vec[Data @unchecked], source_v: Vec[Data @unchecked]) => @@ -456,6 +463,24 @@ private[chisel3] object MonoConnect { } } + def domainDefine( + sourceInfo: SourceInfo, + sink: chisel3.domain.Type, + source: chisel3.domain.Type, + context: BaseModule + ): Unit = { + implicit val info: SourceInfo = sourceInfo + if (sink.domain != source.domain) { + throw MismatchedDomainException(sink, source) + } + checkConnect.checkConnection(sourceInfo, sink, source, context) + context match { + case rm: RawModule => + rm.addCommand(DomainDefine(sourceInfo, sink.lref, source.ref)) + case _ => throwException("Internal Error! Domain connection can only occur within RawModule.") + } + } + def probeDefine( sourceInfo: SourceInfo, sinkProbe: Data, diff --git a/src/test/scala/chiselTests/DomainSpec.scala b/src/test/scala/chiselTests/DomainSpec.scala index 8a96cccfdec..f7b6d5863db 100644 --- a/src/test/scala/chiselTests/DomainSpec.scala +++ b/src/test/scala/chiselTests/DomainSpec.scala @@ -110,23 +110,46 @@ class DomainSpec extends AnyFlatSpec with Matchers with FileCheck { } - they should "be capable of being forwarded with the domain define operation" in { + they should "be capable of being forwarded with the domain define operation or connects" in { class Foo extends RawModule { val a = IO(Input(ClockDomain.Type())) val b = IO(Output(ClockDomain.Type())) domain.define(b, a) + b := a + b <> a + b :<= a + b :<>= a } ChiselStage.emitCHIRRTL(new Foo).fileCheck() { - """|CHECK: module Foo : - |CHECK: domain_define b = a + """|CHECK: module Foo : + |CHECK-COUNT-5: domain_define b = a + |CHECK-NOT: domain_define |""".stripMargin } } + they should "error if connecting two different kinds of domains with :=" in { + + object DomainA extends Domain + object DomainB extends Domain + + class Foo extends RawModule { + val a = IO(Input(DomainA.Type())) + val b = IO(Output(DomainB.Type())) + b := a + } + + val exception = intercept[ChiselException] { + ChiselStage.elaborate(new Foo, Array("--throw-on-first-error")) + } + exception.getMessage should include("are different kinds of domains") + + } + behavior of "The associate method" it should "error if given zero arguments" in {