summaryrefslogtreecommitdiff
path: root/chiselFrontend
diff options
context:
space:
mode:
Diffstat (limited to 'chiselFrontend')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Attach.scala4
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala10
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Binder.scala10
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Binding.scala96
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Bits.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala137
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala6
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Data.scala6
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Mem.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Module.scala292
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala8
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Printf.scala1
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/Reg.scala4
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/UserModule.scala177
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Builder.scala26
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/Error.scala2
-rw-r--r--chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala10
18 files changed, 508 insertions, 287 deletions
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Attach.scala b/chiselFrontend/src/main/scala/chisel3/core/Attach.scala
index 5e767b84..edc0a7c9 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Attach.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Attach.scala
@@ -14,7 +14,7 @@ object attach { // scalastyle:ignore object.name
AttachException(": Conditional attach is not allowed!")
// Actual implementation
- private[core] def impl(elts: Seq[Analog], contextModule: Module)(implicit sourceInfo: SourceInfo): Unit = {
+ private[core] def impl(elts: Seq[Analog], contextModule: UserModule)(implicit sourceInfo: SourceInfo): Unit = {
if (Builder.whenDepth != 0) throw ConditionalAttachException
// TODO Check that references are valid and can be attached
@@ -35,7 +35,7 @@ object attach { // scalastyle:ignore object.name
*/
def apply(elts: Analog*)(implicit sourceInfo: SourceInfo): Unit = {
try {
- impl(elts, Builder.forcedModule)
+ impl(elts, Builder.forcedUserModule)
} catch {
case AttachException(message) =>
throwException(elts.mkString("Attaching (", ", ", s") failed @$message"))
diff --git a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
index 825dbad7..95ad95ef 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/BiConnect.scala
@@ -54,7 +54,7 @@ object BiConnect {
* during the recursive decent and then rethrow them with extra information added.
* This gives the user a 'path' to where in the connections things went wrong.
*/
- def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: Module): Unit =
+ def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Data, right: Data, context_mod: UserModule): Unit =
(left, right) match {
// Handle element case (root case)
case (left_a: Analog, right_a: Analog) =>
@@ -121,12 +121,12 @@ object BiConnect {
// This function checks if element-level connection operation allowed.
// Then it either issues it or throws the appropriate exception.
- def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: Module): Unit = {
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, left: Element, right: Element, context_mod: UserModule): Unit = {
import Direction.{Input, Output} // Using extensively so import these
// If left or right have no location, assume in context module
// This can occur if one of them is a literal, unbound will error previously
- val left_mod: Module = left.binding.location.getOrElse(context_mod)
- val right_mod: Module = right.binding.location.getOrElse(context_mod)
+ val left_mod: BaseModule = left.binding.location.getOrElse(context_mod)
+ val right_mod: BaseModule = right.binding.location.getOrElse(context_mod)
val left_direction: Option[Direction] = left.binding.direction
val right_direction: Option[Direction] = right.binding.direction
@@ -250,7 +250,7 @@ object BiConnect {
// This function checks if analog element-level attaching is allowed
// Then it either issues it or throws the appropriate exception.
- def analogAttach(implicit sourceInfo: SourceInfo, left: Analog, right: Analog, contextModule: Module): Unit = {
+ def analogAttach(implicit sourceInfo: SourceInfo, left: Analog, right: Analog, contextModule: UserModule): Unit = {
// Error if left or right is BICONNECTED in the current module already
for (elt <- left :: right :: Nil) {
elt.biConnectLocs.get(contextModule) match {
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binder.scala b/chiselFrontend/src/main/scala/chisel3/core/Binder.scala
index c7346dce..d872d7c6 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Binder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Binder.scala
@@ -41,24 +41,24 @@ case object LitBinder extends Binder[LitBinding] {
def apply(in: UnboundBinding) = LitBinding()
}
-case class MemoryPortBinder(enclosure: Module) extends Binder[MemoryPortBinding] {
+case class MemoryPortBinder(enclosure: UserModule) extends Binder[MemoryPortBinding] {
def apply(in: UnboundBinding) = MemoryPortBinding(enclosure)
}
-case class OpBinder(enclosure: Module) extends Binder[OpBinding] {
+case class OpBinder(enclosure: UserModule) extends Binder[OpBinding] {
def apply(in: UnboundBinding) = OpBinding(enclosure)
}
// Notice how PortBinder uses the direction of the UnboundNode
-case class PortBinder(enclosure: Module) extends Binder[PortBinding] {
+case class PortBinder(enclosure: BaseModule) extends Binder[PortBinding] {
def apply(in: UnboundBinding) = PortBinding(enclosure, in.direction)
}
-case class RegBinder(enclosure: Module) extends Binder[RegBinding] {
+case class RegBinder(enclosure: UserModule) extends Binder[RegBinding] {
def apply(in: UnboundBinding) = RegBinding(enclosure)
}
-case class WireBinder(enclosure: Module) extends Binder[WireBinding] {
+case class WireBinder(enclosure: UserModule) extends Binder[WireBinding] {
def apply(in: UnboundBinding) = WireBinding(enclosure)
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
index a857ae85..87e706b7 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Binding.scala
@@ -91,8 +91,6 @@ object Binding {
case unbound @ UnboundBinding(_) => {
element.binding = binder(unbound)
}
- // If autoIOWrap is enabled and we're rebinding a PortBinding, just ignore the rebinding.
- case portBound @ PortBinding(_, _) if (!(forcedModule.compileOptions.requireIOWrap) && binder.isInstanceOf[PortBinder]) =>
case binding => throw AlreadyBoundException(binding.toString)
}
)
@@ -118,68 +116,32 @@ object Binding {
// Excepts if any root element is unbound and thus not on the hardware graph
def checkSynthesizable(target: Data, error_prelude: String): Unit = {
- // This is called if we support autoIOWrap
- def elementOfIO(element: Element): Boolean = {
- element._parent match {
- case None => false
- case Some(x: Module) => {
- // Have we defined the IO ports for this module? If not, do so now.
- if (!x.ioDefined) {
- x.computePorts
- element.binding match {
- case SynthesizableBinding() => true
- case _ => false
- }
- } else {
- // io.flatten eliminates Clock elements, so we need to use io.allElements
- val ports = x.io.allElements
- val isIOElement = ports.contains(element) || element == x.clock || element == x.reset
- isIOElement
- }
- }
- }
- }
-
- // Diagnose a binding error caused by a missing IO() wrapper.
- // element is the element triggering the binding error.
- // Returns true if the element is a member of the module's io but ioDefined is false.
- def isMissingIOWrapper(element: Element): Boolean = {
- element._parent match {
- case None => false
- case Some(x: Module) => {
- // If the IO() wrapper has been executed, it isn't missing.
- if (x.ioDefined) {
- false
- } else {
- // TODO: We should issue the message only once, and if we get here,
- // we know the wrapper is missing, whether or not the element is a member of io.
- // But if it's not an io element, we want to issue the complementary "unbound" error.
- // Revisit this when we collect error messages instead of throwing exceptions.
- // The null test below is due to the fact that we may be evaluating the arguments
- // of the IO() wrapper itself.
- (x.io != null) && x.io.flatten.contains(element)
- }
- }
- }
- }
-
try walkToBinding(
target,
- element => element.binding match {
- case SynthesizableBinding() => {} // OK
- case binding =>
- // The following kludge is an attempt to provide backward compatibility
- // It should be done at at higher level.
- if ((forcedModule.compileOptions.requireIOWrap || !elementOfIO(element))) {
- // Generate a better error message if this is a result of a missing IO() wrapper.
- if (isMissingIOWrapper(element)) {
- throw MissingIOWrapperException
- } else {
- throw NotSynthesizableException
+ element => {
+ // Compatibility mode to automatically wrap ports in IO
+ // TODO: remove me, perhaps by removing Bindings checks from compatibility mode
+ element._parent match {
+ case Some(x: BaseModule) => x._autoWrapPorts
+ case _ =>
+ }
+ // Actual binding check
+ element.binding match {
+ case SynthesizableBinding() => // OK
+ case binding => {
+ // Attempt to diagnose common bindings issues, like forgot to wrap IO(...)
+ element._parent match {
+ case Some(x: LegacyModule) =>
+ // null check in case we try to access io before it is defined
+ if ((x.io != null) && (x.io.flatten contains element)) {
+ throw MissingIOWrapperException
+ }
+ case _ =>
}
- } else {
- Binding.bind(element, PortBinder(element._parent.get), "Error: IO")
+ // Fallback generic exception
+ throw NotSynthesizableException
}
+ }
}
)
catch {
@@ -190,7 +152,7 @@ object Binding {
// Location refers to 'where' in the Module hierarchy this lives
sealed trait Binding {
- def location: Option[Module]
+ def location: Option[BaseModule]
def direction: Option[Direction]
}
@@ -202,7 +164,7 @@ sealed trait UnconstrainedBinding extends Binding {
// A constrained binding can only be read/written by specific modules
// Location will track where this Module is
sealed trait ConstrainedBinding extends Binding {
- def enclosure: Module
+ def enclosure: BaseModule
def location = Some(enclosure)
}
@@ -224,19 +186,19 @@ sealed trait SynthesizableBinding extends Binding
case class LitBinding() // will eventually have literal info
extends SynthesizableBinding with UnconstrainedBinding with UndirectionedBinding
-case class MemoryPortBinding(enclosure: Module)
+case class MemoryPortBinding(enclosure: UserModule)
extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding
// TODO(twigg): Ops between unenclosed nodes can also be unenclosed
// However, Chisel currently binds all op results to a module
-case class OpBinding(enclosure: Module)
+case class OpBinding(enclosure: UserModule)
extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding
-case class PortBinding(enclosure: Module, direction: Option[Direction])
+case class PortBinding(enclosure: BaseModule, direction: Option[Direction])
extends SynthesizableBinding with ConstrainedBinding
-case class RegBinding(enclosure: Module)
+case class RegBinding(enclosure: UserModule)
extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding
-case class WireBinding(enclosure: Module)
+case class WireBinding(enclosure: UserModule)
extends SynthesizableBinding with ConstrainedBinding with UndirectionedBinding
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
index dd3a2e8b..a8a96946 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Bits.scala
@@ -1047,7 +1047,7 @@ final class Analog private (width: Width) extends Element(width) {
// Used to enforce single bulk connect of Analog types, multi-attach is still okay
// Note that this really means 1 bulk connect per Module because a port can
// be connected in the parent module as well
- private[core] val biConnectLocs = mutable.Map.empty[Module, SourceInfo]
+ private[core] val biConnectLocs = mutable.Map.empty[UserModule, SourceInfo]
// Define setter/getter pairing
// Analog can only be bound to Ports and Wires (and Unbound)
diff --git a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala
index fa81a4a5..7a1394bb 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala
@@ -5,7 +5,7 @@ package chisel3.core
import chisel3.internal.Builder.pushCommand
import chisel3.internal.firrtl._
import chisel3.internal.throwException
-import chisel3.internal.sourceinfo.SourceInfo
+import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo}
// TODO: remove this once we have CompileOptions threaded through the macro system.
import chisel3.core.ExplicitCompileOptions.NotStrict
@@ -17,6 +17,86 @@ case class StringParam(value: String) extends Param
/** Unquoted String */
case class RawParam(value: String) extends Param
+abstract class BaseBlackBox extends BaseModule
+
+/** Defines a black box, which is a module that can be referenced from within
+ * Chisel, but is not defined in the emitted Verilog. Useful for connecting
+ * to RTL modules defined outside Chisel.
+ *
+ * A variant of BlackBox, this has a more consistent naming scheme in allowing
+ * multiple top-level IO and does not drop the top prefix.
+ *
+ * @example
+ * Some design require a differential input clock to clock the all design.
+ * With the xilinx FPGA for example, a Verilog template named IBUFDS must be
+ * integrated to use differential input:
+ * {{{
+ * IBUFDS #(.DIFF_TERM("TRUE"),
+ * .IOSTANDARD("DEFAULT")) ibufds (
+ * .IB(ibufds_IB),
+ * .I(ibufds_I),
+ * .O(ibufds_O)
+ * );
+ * }}}
+ *
+ * To instantiate it, a BlackBox can be used like following:
+ * {{{
+ * import chisel3._
+ * import chisel3.experimental._
+ *
+ * // Example with Xilinx differential buffer IBUFDS
+ * class IBUFDS extends ExtModule(Map("DIFF_TERM" -> "TRUE", // Verilog parameters
+ * "IOSTANDARD" -> "DEFAULT"
+ * )) {
+ * val O = IO(Output(Clock()))
+ * val I = IO(Input(Clock()))
+ * val IB = IO(Input(Clock()))
+ * }
+ * }}}
+ * @note The parameters API is experimental and may change
+ */
+abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox {
+ private[core] override def generateComponent(): Component = {
+ require(!_closed, "Can't generate module more than once")
+ _closed = true
+
+ val names = nameIds(classOf[ExtModule])
+
+ // Name ports based on reflection
+ for (port <- getModulePorts) {
+ require(names.contains(port), s"Unable to name port $port in $this")
+ port.setRef(ModuleIO(this, _namespace.name(names(port))))
+ }
+
+ // All suggestions are in, force names to every node.
+ // While BlackBoxes are not supposed to have an implementation, we still need to call
+ // _onModuleClose on all nodes (for example, Aggregates use it for recursive naming).
+ for (id <- getIds) {
+ id.forceName(default="_T", _namespace)
+ id._onModuleClose
+ }
+
+ val firrtlPorts = for (port <- getModulePorts) yield {
+ // Port definitions need to know input or output at top-level.
+ // By FIRRTL semantics, 'flipped' becomes an Input
+ val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output
+ Port(port, direction)
+ }
+
+ val component = DefBlackBox(this, name, firrtlPorts, params)
+ _component = Some(component)
+ component
+ }
+
+ private[core] def initializeInParent() {
+ implicit val sourceInfo = UnlocatableSourceInfo
+
+ for (x <- getModulePorts) {
+ pushCommand(DefInvalid(sourceInfo, x.ref))
+ }
+ }
+}
+
/** Defines a black box, which is a module that can be referenced from within
* Chisel, but is not defined in the emitted Verilog. Useful for connecting
* to RTL modules defined outside Chisel.
@@ -52,45 +132,56 @@ case class RawParam(value: String) extends Param
* }}}
* @note The parameters API is experimental and may change
*/
-abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param]) extends Module {
+abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox {
+ def io: Record
+
+ // Allow access to bindings from the compatibility package
+ protected def _ioPortBound() = portsContains(io)
+
+ private[core] override def generateComponent(): Component = {
+ _autoWrapPorts() // pre-IO(...) compatibility hack
- // The body of a BlackBox is empty, the real logic happens in firrtl/Emitter.scala
- // Bypass standard clock, reset, io port declaration by flattening io
- // TODO(twigg): ? Really, overrides are bad, should extend BaseModule....
- override private[core] def ports = io.elements.toSeq
+ // Restrict IO to just io, clock, and reset
+ require(io != null, "BlackBox must have io")
+ require(portsContains(io), "BlackBox must have io wrapped in IO(...)")
+ require(portsSize == 1, "BlackBox must only have io as IO")
- // Do not do reflective naming of internal signals, just name io
- override private[core] def setRefs(): this.type = {
+ require(!_closed, "Can't generate module more than once")
+ _closed = true
+
+ val namedPorts = io.elements.toSeq
// setRef is not called on the actual io.
// There is a risk of user improperly attempting to connect directly with io
// Long term solution will be to define BlackBox IO differently as part of
// it not descending from the (current) Module
- for ((name, port) <- ports) {
+ for ((name, port) <- namedPorts) {
port.setRef(ModuleIO(this, _namespace.name(name)))
}
+
// We need to call forceName and onModuleClose on all of the sub-elements
// of the io bundle, but NOT on the io bundle itself.
// Doing so would cause the wrong names to be assigned, since their parent
// is now the module itself instead of the io bundle.
- for (id <- _ids; if id ne io) {
+ for (id <- getIds; if id ne io) {
id.forceName(default="_T", _namespace)
id._onModuleClose
}
- this
- }
- // Don't setup clock, reset
- // Cann't invalide io in one bunch, must invalidate each part separately
- override private[core] def setupInParent(implicit sourceInfo: SourceInfo): this.type = _parent match {
- case Some(p) => {
- // Just init instance inputs
- for((_,port) <- ports) pushCommand(DefInvalid(sourceInfo, port.ref))
- this
+ val firrtlPorts = for ((_, port) <- namedPorts) yield {
+ // Port definitions need to know input or output at top-level.
+ // By FIRRTL semantics, 'flipped' becomes an Input
+ val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output
+ Port(port, direction)
}
- case None => this
+
+ val component = DefBlackBox(this, name, firrtlPorts, params)
+ _component = Some(component)
+ component
}
- // Using null is horrible but these signals SHOULD NEVER be used:
- override val clock = null
- override val reset = null
+ private[core] def initializeInParent() {
+ for ((_, port) <- io.elements) {
+ pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref))
+ }
+ }
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala
index 73573bb1..015629e5 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/ChiselAnnotation.scala
@@ -18,7 +18,7 @@ case class ChiselAnnotation(component: InstanceId, transformClass: Class[_ <: Tr
def toFirrtl: Annotation = {
val circuitName = CircuitName(component.pathName.split("""\.""").head)
component match {
- case m: Module =>
+ case m: BaseModule =>
Annotation(
ModuleName(m.name, circuitName), transformClass, value)
case _ =>
diff --git a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
index 482c1566..803e6c0f 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/CompileOptions.scala
@@ -12,8 +12,6 @@ trait CompileOptions {
val connectFieldsMustMatch: Boolean
// When creating an object that takes a type argument, the argument must be unbound (a pure type).
val declaredTypeMustBeUnbound: Boolean
- // Module IOs should be wrapped in an IO() to define their bindings before the reset of the module is defined.
- val requireIOWrap: Boolean
// If a connection operator fails, don't try the connection with the operands (source and sink) reversed.
val dontTryConnectionsSwapped: Boolean
// If connection directionality is not explicit, do not use heuristics to attempt to determine it.
@@ -44,8 +42,6 @@ object ExplicitCompileOptions {
val connectFieldsMustMatch: Boolean,
// When creating an object that takes a type argument, the argument must be unbound (a pure type).
val declaredTypeMustBeUnbound: Boolean,
- // Module IOs should be wrapped in an IO() to define their bindings before the reset of the module is defined.
- val requireIOWrap: Boolean,
// If a connection operator fails, don't try the connection with the operands (source and sink) reversed.
val dontTryConnectionsSwapped: Boolean,
// If connection directionality is not explicit, do not use heuristics to attempt to determine it.
@@ -63,7 +59,6 @@ object ExplicitCompileOptions {
implicit val NotStrict = new CompileOptionsClass (
connectFieldsMustMatch = false,
declaredTypeMustBeUnbound = false,
- requireIOWrap = false,
dontTryConnectionsSwapped = false,
dontAssumeDirectionality = false,
deprecateOldDirectionMethods = false,
@@ -75,7 +70,6 @@ object ExplicitCompileOptions {
implicit val Strict = new CompileOptionsClass (
connectFieldsMustMatch = true,
declaredTypeMustBeUnbound = true,
- requireIOWrap = true,
dontTryConnectionsSwapped = true,
dontAssumeDirectionality = true,
deprecateOldDirectionMethods = true,
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Data.scala b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
index 556f2aeb..c17672ff 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Data.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Data.scala
@@ -200,7 +200,7 @@ abstract class Data extends HasId {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
- MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
+ MonoConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedUserModule)
} catch {
case MonoConnect.MonoConnectException(message) =>
throwException(
@@ -216,7 +216,7 @@ abstract class Data extends HasId {
Binding.checkSynthesizable(this, s"'this' ($this)")
Binding.checkSynthesizable(that, s"'that' ($that)")
try {
- BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedModule)
+ BiConnect.connect(sourceInfo, connectCompileOptions, this, that, Builder.forcedUserModule)
} catch {
case BiConnect.BiConnectException(message) =>
throwException(
@@ -374,7 +374,7 @@ object Wire {
val x = t.chiselCloneType
// Bind each element of x to being a Wire
- Binding.bind(x, WireBinder(Builder.forcedModule), "Error: t")
+ Binding.bind(x, WireBinder(Builder.forcedUserModule), "Error: t")
pushCommand(DefWire(sourceInfo, x))
pushCommand(DefInvalid(sourceInfo, x.ref))
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala
index a48af15a..006670e7 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Mem.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Mem.scala
@@ -95,7 +95,7 @@ sealed abstract class MemBase[T <: Data](t: T, val length: Int) extends HasId wi
t.chiselCloneType, Node(this), dir, i.ref, Node(Builder.forcedClock))
).id
// Bind each element of port to being a MemoryPort
- Binding.bind(port, MemoryPortBinder(Builder.forcedModule), "Error: Fresh t")
+ Binding.bind(port, MemoryPortBinder(Builder.forcedUserModule), "Error: Fresh t")
port
}
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Module.scala b/chiselFrontend/src/main/scala/chisel3/core/Module.scala
index b838eb05..4c4c0c01 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Module.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Module.scala
@@ -2,13 +2,16 @@
package chisel3.core
-import scala.collection.mutable.ArrayBuffer
+import scala.collection.mutable.{ArrayBuffer, HashMap}
+import scala.collection.JavaConversions._
import scala.language.experimental.macros
+
+import java.util.IdentityHashMap
+
import chisel3.internal._
import chisel3.internal.Builder._
import chisel3.internal.firrtl._
-import chisel3.internal.firrtl.{Command => _, _}
-import chisel3.internal.sourceinfo.{InstTransform, SourceInfo, UnlocatableSourceInfo}
+import chisel3.internal.sourceinfo.{InstTransform, SourceInfo}
object Module {
/** A wrapper method that all Module instantiations must be wrapped in
@@ -18,19 +21,16 @@ object Module {
*
* @return the input module `m` with Chisel metadata properly set
*/
- def apply[T <: Module](bc: => T): T = macro InstTransform.apply[T]
-
- def do_apply[T <: Module](bc: => T)(implicit sourceInfo: SourceInfo): T = {
- // Don't generate source info referencing parents inside a module, sincce this interferes with
- // module de-duplication in FIRRTL emission.
- val childSourceInfo = UnlocatableSourceInfo
+ def apply[T <: BaseModule](bc: => T): T = macro InstTransform.apply[T]
+ def do_apply[T <: BaseModule](bc: => T)(implicit sourceInfo: SourceInfo): T = {
if (Builder.readyForModuleConstr) {
throwException("Error: Called Module() twice without instantiating a Module." +
sourceInfo.makeMessage(" See " + _))
}
Builder.readyForModuleConstr = true
- val parent: Option[Module] = Builder.currentModule
+
+ val parent = Builder.currentModule
val whenDepth: Int = Builder.whenDepth
val clockAndReset: Option[ClockAndReset] = Builder.currentClockAndReset
@@ -39,8 +39,7 @@ object Module {
// - unset readyForModuleConstr
// - reset whenDepth to 0
// - set currentClockAndReset
- val m = bc.setRefs()
- m._commands.prepend(DefInvalid(childSourceInfo, m.io.ref)) // init module outputs
+ val module: T = bc // bc is actually evaluated here
if (Builder.whenDepth != 0) {
throwException("Internal Error! When depth is != 0, this should not be possible")
@@ -54,23 +53,15 @@ object Module {
Builder.whenDepth = whenDepth
Builder.currentClockAndReset = clockAndReset // Back to clock and reset scope
- val ports = m.computePorts
- // Blackbox inherits from Module so we have to match on it first TODO fix
- val component = m match {
- case bb: BlackBox =>
- DefBlackBox(bb, bb.name, ports, bb.params)
- case mod: Module =>
- mod._commands.prepend(DefInvalid(childSourceInfo, mod.io.ref)) // init module outputs
- DefModule(mod, mod.name, ports, mod._commands)
- }
- m._component = Some(component)
+ val component = module.generateComponent()
Builder.components += component
- // Avoid referencing 'parent' in top module
+
+ // Handle connections at enclosing scope
if(!Builder.currentModule.isEmpty) {
- pushCommand(DefInstance(sourceInfo, m, ports))
- m.setupInParent(childSourceInfo)
+ pushCommand(DefInstance(sourceInfo, module, component.ports))
+ module.initializeInParent()
}
- m
+ module
}
/** Returns the implicit Clock */
@@ -79,141 +70,94 @@ object Module {
def reset: Bool = Builder.forcedReset
}
-/** Abstract base class for Modules, which behave much like Verilog modules.
- * These may contain both logic and state which are written in the Module
- * body (constructor).
- *
- * @note Module instantiations must be wrapped in a Module() call.
+/** Abstract base class for Modules, an instantiable organizational unit for RTL.
*/
-abstract class Module(
- override_clock: Option[Clock]=None, override_reset: Option[Bool]=None)
- (implicit moduleCompileOptions: CompileOptions)
-extends HasId {
- // _clock and _reset can be clock and reset in these 2ary constructors
- // once chisel2 compatibility issues are resolved
- def this(_clock: Clock)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), None)(moduleCompileOptions)
- def this(_reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(None, Option(_reset))(moduleCompileOptions)
- def this(_clock: Clock, _reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), Option(_reset))(moduleCompileOptions)
-
- // This function binds the iodef as a port in the hardware graph
- private[chisel3] def Port[T<:Data](iodef: T): iodef.type = {
- // Bind each element of the iodef to being a Port
- Binding.bind(iodef, PortBinder(this), "Error: iodef")
- iodef
- }
-
- def annotate(annotation: ChiselAnnotation): Unit = {
- Builder.annotations += annotation
+// TODO: seal this?
+abstract class BaseModule extends HasId {
+ //
+ // Builder Internals - this tracks which Module RTL construction belongs to.
+ //
+ if (!Builder.readyForModuleConstr) {
+ throwException("Error: attempted to instantiate a Module without wrapping it in Module().")
}
+ readyForModuleConstr = false
- private[core] var ioDefined: Boolean = false
-
- /**
- * This must wrap the datatype used to set the io field of any Module.
- * i.e. All concrete modules must have defined io in this form:
- * [lazy] val io[: io type] = IO(...[: io type])
- *
- * Items in [] are optional.
- *
- * The granted iodef WILL NOT be cloned (to allow for more seamless use of
- * anonymous Bundles in the IO) and thus CANNOT have been bound to any logic.
- * This will error if any node is bound (e.g. due to logic in a Bundle
- * constructor, which is considered improper).
- *
- * TODO(twigg): Specifically walk the Data definition to call out which nodes
- * are problematic.
- */
- def IO[T<:Data](iodef: T): iodef.type = {
- require(!ioDefined, "Another IO definition for this module was already declared!")
- ioDefined = true
+ Builder.currentModule = Some(this)
+ Builder.whenDepth = 0
- Port(iodef)
- }
+ //
+ // Module Construction Internals
+ //
+ protected var _closed = false
// Fresh Namespace because in Firrtl, Modules namespaces are disjoint with the global namespace
private[core] val _namespace = Namespace.empty
- private[chisel3] val _commands = ArrayBuffer[Command]()
- private[core] val _ids = ArrayBuffer[HasId]()
- Builder.currentModule = Some(this)
- Builder.whenDepth = 0
- if (!Builder.readyForModuleConstr) {
- throwException("Error: attempted to instantiate a Module without wrapping it in Module().")
+ private val _ids = ArrayBuffer[HasId]()
+ private[chisel3] def addId(d: HasId) {
+ require(!_closed, "Can't write to module after module close")
+ _ids += d
+ }
+ protected def getIds = {
+ require(_closed, "Can't get ids before module close")
+ _ids.toSeq
}
- readyForModuleConstr = false
- /** Desired name of this module. */
+ private val _ports = new ArrayBuffer[Data]()
+ // getPorts unfortunately already used for tester compatibility
+ protected def getModulePorts = {
+ require(_closed, "Can't get ports before module close")
+ _ports.toSeq
+ }
+
+ // These methods allow checking some properties of ports before the module is closed,
+ // mainly for compatibility purposes.
+ protected def portsContains(elem: Data): Boolean = _ports contains elem
+ protected def portsSize: Int = _ports.size
+
+ /** Generates the FIRRTL Component (Module or Blackbox) of this Module.
+ * Also closes the module so no more construction can happen inside.
+ */
+ private[core] def generateComponent(): Component
+
+ /** Sets up this module in the parent context
+ */
+ private[core] def initializeInParent()
+
+ //
+ // Chisel Internals
+ //
+ /** Desired name of this module. Override this to give this module a custom, perhaps parametric,
+ * name.
+ */
def desiredName = this.getClass.getName.split('.').last
/** Legalized name of this module. */
final val name = Builder.globalNamespace.name(desiredName)
- /** Keep component for signal names */
- private[chisel3] var _component: Option[Component] = None
-
- /** Signal name (for simulation). */
- override def instanceName =
- if (_parent == None) name else _component match {
- case None => getRef.name
- case Some(c) => getRef fullName c
- }
-
- /** IO for this Module. At the Scala level (pre-FIRRTL transformations),
- * connections in and out of a Module may only go through `io` elements.
+ /** Called at the Module.apply(...) level after this Module has finished elaborating.
+ * Returns a map of nodes -> names, for named nodes.
+ *
+ * Helper method.
*/
- def io: Record
- val clock = Port(Input(Clock()))
- val reset = Port(Input(Bool()))
-
- // Setup ClockAndReset
- Builder.currentClockAndReset = Some(ClockAndReset(clock, reset))
-
- private[chisel3] def addId(d: HasId) { _ids += d }
-
- private[core] def ports: Seq[(String,Data)] = Vector(
- ("clock", clock), ("reset", reset), ("io", io)
- )
-
- private[core] def computePorts: Seq[firrtl.Port] = {
- // If we're auto-wrapping IO definitions, do so now.
- if (!(compileOptions.requireIOWrap || ioDefined)) {
- IO(io)
- }
- for ((name, port) <- ports) yield {
- // Port definitions need to know input or output at top-level.
- // By FIRRTL semantics, 'flipped' becomes an Input
- val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output
- firrtl.Port(port, direction)
- }
- }
-
- private[core] def setupInParent(implicit sourceInfo: SourceInfo): this.type = {
- _parent match {
- case Some(p) => {
- pushCommand(DefInvalid(sourceInfo, io.ref)) // init instance inputs
- clock := override_clock.getOrElse(Builder.forcedClock)
- reset := override_reset.getOrElse(Builder.forcedReset)
- this
+ protected def nameIds(rootClass: Class[_]): HashMap[HasId, String] = {
+ val names = new HashMap[HasId, String]()
+
+ def name(node: HasId, name: String) {
+ // First name takes priority, like suggestName
+ // TODO: DRYify with suggestName
+ if (!names.contains(node)) {
+ names.put(node, name)
}
- case None => this
- }
- }
-
- private[core] def setRefs(): this.type = {
- for ((name, port) <- ports) {
- port.setRef(ModuleIO(this, _namespace.name(name)))
}
/** Recursively suggests names to supported "container" classes
* Arbitrary nestings of supported classes are allowed so long as the
* innermost element is of type HasId
- * Currently supported:
- * - Iterable
- * - Option
- * (Note that Map is Iterable[Tuple2[_,_]] and thus excluded)
+ * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded)
*/
def nameRecursively(prefix: String, nameMe: Any): Unit =
nameMe match {
- case (id: HasId) => id.suggestName(prefix)
+ case (id: HasId) => name(id, prefix)
case Some(elt) => nameRecursively(prefix, elt)
case (iter: Iterable[_]) if iter.hasDefiniteSize =>
for ((elt, i) <- iter.zipWithIndex) {
@@ -221,27 +165,75 @@ extends HasId {
}
case _ => // Do nothing
}
+
/** Scala generates names like chisel3$util$Queue$$ram for private vals
* This extracts the part after $$ for names like this and leaves names
* without $$ unchanged
*/
def cleanName(name: String): String = name.split("""\$\$""").lastOption.getOrElse(name)
- for (m <- getPublicFields(classOf[Module])) {
+
+ for (m <- getPublicFields(rootClass)) {
nameRecursively(cleanName(m.getName), m.invoke(this))
}
- // For Module instances we haven't named, suggest the name of the Module
- _ids foreach {
- case m: Module => m.suggestName(m.desiredName)
- case _ =>
- }
+ names
+ }
+
+ /** Compatibility function. Allows Chisel2 code which had ports without the IO wrapper to
+ * compile under Bindings checks. Does nothing in non-compatibility mode.
+ *
+ * Should NOT be used elsewhere. This API will NOT last.
+ *
+ * TODO: remove this, perhaps by removing Bindings checks in compatibility mode.
+ */
+ def _autoWrapPorts() {}
- // All suggestions are in, force names to every node.
- _ids.foreach(_.forceName(default="_T", _namespace))
- _ids.foreach(_._onModuleClose)
- this
+ //
+ // BaseModule User API functions
+ //
+ protected def annotate(annotation: ChiselAnnotation): Unit = {
+ Builder.annotations += annotation
}
- // For debuggers/testers
- lazy val getPorts = computePorts
- val compileOptions = moduleCompileOptions
+
+ /**
+ * This must wrap the datatype used to set the io field of any Module.
+ * i.e. All concrete modules must have defined io in this form:
+ * [lazy] val io[: io type] = IO(...[: io type])
+ *
+ * Items in [] are optional.
+ *
+ * The granted iodef WILL NOT be cloned (to allow for more seamless use of
+ * anonymous Bundles in the IO) and thus CANNOT have been bound to any logic.
+ * This will error if any node is bound (e.g. due to logic in a Bundle
+ * constructor, which is considered improper).
+ *
+ * Also registers a Data as a port, also performing bindings. Cannot be called once ports are
+ * requested (so that all calls to ports will return the same information).
+ * Internal API.
+ *
+ * TODO(twigg): Specifically walk the Data definition to call out which nodes
+ * are problematic.
+ */
+ protected def IO[T<:Data](iodef: T): iodef.type = {
+ require(!_closed, "Can't add more ports after module close")
+ // Bind each element of the iodef to being a Port
+ Binding.bind(iodef, PortBinder(this), "Error: iodef")
+ _ports += iodef
+ iodef
+ }
+
+ //
+ // Internal Functions
+ //
+
+ /** Keep component for signal names */
+ private[chisel3] var _component: Option[Component] = None
+
+ /** Signal name (for simulation). */
+ override def instanceName =
+ if (_parent == None) name else _component match {
+ case None => getRef.name
+ case Some(c) => getRef fullName c
+ }
+
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
index 9511fdc9..60f500cd 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/MonoConnect.scala
@@ -55,7 +55,7 @@ object MonoConnect {
* during the recursive decent and then rethrow them with extra information added.
* This gives the user a 'path' to where in the connections things went wrong.
*/
- def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Data, source: Data, context_mod: Module): Unit =
+ def connect(sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Data, source: Data, context_mod: UserModule): Unit =
(sink, source) match {
// Handle element case (root case)
case (sink_e: Element, source_e: Element) => {
@@ -102,12 +102,12 @@ object MonoConnect {
// This function checks if element-level connection operation allowed.
// Then it either issues it or throws the appropriate exception.
- def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: Module): Unit = {
+ def elemConnect(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions, sink: Element, source: Element, context_mod: UserModule): Unit = {
import Direction.{Input, Output} // Using extensively so import these
// If source has no location, assume in context module
// This can occur if is a literal, unbound will error previously
- val sink_mod: Module = sink.binding.location.getOrElse(throw UnwritableSinkException)
- val source_mod: Module = source.binding.location.getOrElse(context_mod)
+ val sink_mod: BaseModule = sink.binding.location.getOrElse(throw UnwritableSinkException)
+ val source_mod: BaseModule = source.binding.location.getOrElse(context_mod)
val sink_direction: Option[Direction] = sink.binding.direction
val source_direction: Option[Direction] = source.binding.direction
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
index 81210f45..2052e95a 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Printf.scala
@@ -64,6 +64,7 @@ object printf { // scalastyle:ignore object.name
val clock = Builder.forcedClock
pushCommand(Printf(sourceInfo, Node(clock), pable))
}
+
private[chisel3] def printfWithoutReset(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo): Unit =
printfWithoutReset(Printable.pack(fmt, data:_*))
}
diff --git a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
index 715bdd70..12d0a939 100644
--- a/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
+++ b/chiselFrontend/src/main/scala/chisel3/core/Reg.scala
@@ -22,7 +22,7 @@ object Reg {
val reg = t.chiselCloneType
val clock = Node(Builder.forcedClock)
- Binding.bind(reg, RegBinder(Builder.forcedModule), "Error: t")
+ Binding.bind(reg, RegBinder(Builder.forcedUserModule), "Error: t")
pushCommand(DefReg(sourceInfo, reg, clock))
reg
}
@@ -90,7 +90,7 @@ object RegInit {
val clock = Node(Builder.forcedClock)
val reset = Node(Builder.forcedReset)
- Binding.bind(reg, RegBinder(Builder.forcedModule), "Error: t")
+ Binding.bind(reg, RegBinder(Builder.forcedUserModule), "Error: t")
Binding.checkSynthesizable(init, s"'init' ($init)")
pushCommand(DefRegInit(sourceInfo, reg, clock, reset, init.ref))
reg
diff --git a/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala
new file mode 100644
index 00000000..916ab119
--- /dev/null
+++ b/chiselFrontend/src/main/scala/chisel3/core/UserModule.scala
@@ -0,0 +1,177 @@
+// See LICENSE for license details.
+
+package chisel3.core
+
+import scala.collection.mutable.{ArrayBuffer, HashMap}
+import scala.collection.JavaConversions._
+import scala.language.experimental.macros
+
+import chisel3.internal._
+import chisel3.internal.Builder._
+import chisel3.internal.firrtl._
+import chisel3.internal.firrtl.{Command => _, _}
+import chisel3.internal.sourceinfo.UnlocatableSourceInfo
+
+/** Abstract base class for Modules that contain Chisel RTL.
+ */
+abstract class UserModule(implicit moduleCompileOptions: CompileOptions)
+ extends BaseModule {
+ //
+ // RTL construction internals
+ //
+ private val _commands = ArrayBuffer[Command]()
+ private[chisel3] def addCommand(c: Command) {
+ require(!_closed, "Can't write to module after module close")
+ _commands += c
+ }
+ protected def getCommands = {
+ require(_closed, "Can't get commands before module close")
+ _commands.toSeq
+ }
+
+ //
+ // Other Internal Functions
+ //
+ // For debuggers/testers, TODO: refactor out into proper public API
+ private var _firrtlPorts: Option[Seq[firrtl.Port]] = None
+ lazy val getPorts = _firrtlPorts.get
+
+ val compileOptions = moduleCompileOptions
+
+ private[core] override def generateComponent(): Component = {
+ require(!_closed, "Can't generate module more than once")
+ _closed = true
+
+ val names = nameIds(classOf[UserModule])
+
+ // Ports get first naming priority, since they are part of a Module's IO spec
+ for (port <- getModulePorts) {
+ require(names.contains(port), s"Unable to name port $port in $this")
+ port.setRef(ModuleIO(this, _namespace.name(names(port))))
+ }
+
+ // Then everything else gets named
+ for ((node, name) <- names) {
+ node.suggestName(name)
+ }
+
+ // All suggestions are in, force names to every node.
+ for (id <- getIds) {
+ id match {
+ case id: BaseModule => id.forceName(default=id.desiredName, _namespace)
+ case id => id.forceName(default="_T", _namespace)
+ }
+ id._onModuleClose
+ }
+
+ val firrtlPorts = for (port <- getModulePorts) yield {
+ // Port definitions need to know input or output at top-level. 'flipped' means Input.
+ val direction = if(Data.isFirrtlFlipped(port)) Direction.Input else Direction.Output
+ firrtl.Port(port, direction)
+ }
+ _firrtlPorts = Some(firrtlPorts)
+
+ // Generate IO invalidation commands to initialize outputs as unused
+ val invalidateCommands = getModulePorts map {port => DefInvalid(UnlocatableSourceInfo, port.ref)}
+
+ val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands)
+ _component = Some(component)
+ component
+ }
+
+ // There is no initialization to be done by default.
+ private[core] def initializeInParent() {}
+}
+
+/** Abstract base class for Modules, which behave much like Verilog modules.
+ * These may contain both logic and state which are written in the Module
+ * body (constructor).
+ *
+ * @note Module instantiations must be wrapped in a Module() call.
+ */
+abstract class ImplicitModule(implicit moduleCompileOptions: CompileOptions)
+ extends UserModule {
+ // Implicit clock and reset pins
+ val clock = IO(Input(Clock()))
+ val reset = IO(Input(Bool()))
+
+ // Setup ClockAndReset
+ Builder.currentClockAndReset = Some(ClockAndReset(clock, reset))
+
+ private[core] override def initializeInParent() {
+ implicit val sourceInfo = UnlocatableSourceInfo
+
+ for (port <- getModulePorts) {
+ pushCommand(DefInvalid(sourceInfo, port.ref))
+ }
+
+ clock := Builder.forcedClock
+ reset := Builder.forcedReset
+ }
+}
+
+/** Legacy Module class that restricts IOs to just io, clock, and reset, and provides a constructor
+ * for threading through explicit clock and reset.
+ *
+ * While this class isn't planned to be removed anytime soon (there are benefits to restricting
+ * IO), the clock and reset constructors will be phased out. Recommendation is to wrap the module
+ * in a withClock/withReset/withClockAndReset block, or directly hook up clock or reset IO pins.
+ */
+abstract class LegacyModule(
+ override_clock: Option[Clock]=None, override_reset: Option[Bool]=None)
+ (implicit moduleCompileOptions: CompileOptions)
+ extends ImplicitModule {
+ // _clock and _reset can be clock and reset in these 2ary constructors
+ // once chisel2 compatibility issues are resolved
+ def this(_clock: Clock)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), None)(moduleCompileOptions)
+ def this(_reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(None, Option(_reset))(moduleCompileOptions)
+ def this(_clock: Clock, _reset: Bool)(implicit moduleCompileOptions: CompileOptions) = this(Option(_clock), Option(_reset))(moduleCompileOptions)
+
+ // IO for this Module. At the Scala level (pre-FIRRTL transformations),
+ // connections in and out of a Module may only go through `io` elements.
+ def io: Record
+
+ // Allow access to bindings from the compatibility package
+ protected def _ioPortBound() = portsContains(io)
+
+ protected override def nameIds(rootClass: Class[_]): HashMap[HasId, String] = {
+ val names = super.nameIds(rootClass)
+
+ // Allow IO naming without reflection
+ names.put(io, "io")
+ names.put(clock, "clock")
+ names.put(reset, "reset")
+
+ names
+ }
+
+ private[core] override def generateComponent(): Component = {
+ _autoWrapPorts() // pre-IO(...) compatibility hack
+
+ // Restrict IO to just io, clock, and reset
+ require(io != null, "Module must have io")
+ require(portsContains(io), "Module must have io wrapped in IO(...)")
+ require((portsContains(clock)) && (portsContains(reset)), "Internal error, module did not have clock or reset as IO")
+ require(portsSize == 3, "Module must only have io, clock, and reset as IO")
+
+ super.generateComponent()
+ }
+
+ private[core] override def initializeInParent() {
+ // Don't generate source info referencing parents inside a module, since this interferes with
+ // module de-duplication in FIRRTL emission.
+ implicit val sourceInfo = UnlocatableSourceInfo
+
+ pushCommand(DefInvalid(sourceInfo, io.ref))
+
+ override_clock match {
+ case Some(override_clock) => clock := override_clock
+ case _ => clock := Builder.forcedClock
+ }
+
+ override_reset match {
+ case Some(override_reset) => reset := override_reset
+ case _ => reset := Builder.forcedReset
+ }
+ }
+}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
index e0cbf302..73556750 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Builder.scala
@@ -70,7 +70,7 @@ trait InstanceId {
private[chisel3] trait HasId extends InstanceId {
private[chisel3] def _onModuleClose: Unit = {} // scalastyle:ignore method.name
- private[chisel3] val _parent: Option[Module] = Builder.currentModule
+ private[chisel3] val _parent: Option[BaseModule] = Builder.currentModule
_parent.foreach(_.addId(this))
private[chisel3] val _id: Long = Builder.idGen.next
@@ -148,7 +148,7 @@ private[chisel3] class DynamicContext() {
val globalNamespace = Namespace.empty
val components = ArrayBuffer[Component]()
val annotations = ArrayBuffer[ChiselAnnotation]()
- var currentModule: Option[Module] = None
+ var currentModule: Option[BaseModule] = None
// Set by object Module.apply before calling class Module constructor
// Used to distinguish between no Module() wrapping, multiple wrappings, and rewrapping
var readyForModuleConstr: Boolean = false
@@ -170,17 +170,24 @@ private[chisel3] object Builder {
def annotations: ArrayBuffer[ChiselAnnotation] = dynamicContext.annotations
def namingStack: internal.naming.NamingStack = dynamicContext.namingStack
- def currentModule: Option[Module] = dynamicContext.currentModule
- def currentModule_=(target: Option[Module]): Unit = {
+ def currentModule: Option[BaseModule] = dynamicContext.currentModule
+ def currentModule_=(target: Option[BaseModule]): Unit = {
dynamicContext.currentModule = target
}
- def forcedModule: Module = currentModule match {
+ def forcedModule: BaseModule = currentModule match {
case Some(module) => module
case None => throwException(
"Error: Not in a Module. Likely cause: Missed Module() wrap or bare chisel API call."
// A bare api call is, e.g. calling Wire() from the scala console).
)
}
+ def forcedUserModule: UserModule = currentModule match {
+ case Some(module: UserModule) => module
+ case _ => throwException(
+ "Error: Not in a UserModule. Likely cause: Missed Module() wrap, bare chisel API call, or attempting to construct hardware inside a BlackBox."
+ // A bare api call is, e.g. calling Wire() from the scala console).
+ )
+ }
def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr
def readyForModuleConstr_=(target: Boolean): Unit = {
dynamicContext.readyForModuleConstr = target
@@ -203,15 +210,12 @@ private[chisel3] object Builder {
// TODO(twigg): Ideally, binding checks and new bindings would all occur here
// However, rest of frontend can't support this yet.
def pushCommand[T <: Command](c: T): T = {
- forcedModule match {
- case _: BlackBox => throwException("Cannot add hardware to a BlackBox")
- case m => m._commands += c
- }
+ forcedUserModule.addCommand(c)
c
}
def pushOp[T <: Data](cmd: DefPrim[T]): T = {
// Bind each element of the returned Data to being a Op
- Binding.bind(cmd.id, OpBinder(forcedModule), "Error: During op creation, fresh result")
+ Binding.bind(cmd.id, OpBinder(forcedUserModule), "Error: During op creation, fresh result")
pushCommand(cmd).id
}
@@ -230,7 +234,7 @@ private[chisel3] object Builder {
throwException(m)
}
- def build[T <: Module](f: => T): Circuit = {
+ def build[T <: UserModule](f: => T): Circuit = {
dynamicContextVar.withValue(Some(new DynamicContext())) {
errors.info("Elaborating design...")
val mod = f
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
index bba7c806..25a3ec2a 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/Error.scala
@@ -55,7 +55,7 @@ private[chisel3] class ErrorLog {
private def findFirstUserFrame(stack: Array[StackTraceElement]): Option[StackTraceElement] = {
def isUserCode(ste: StackTraceElement): Boolean = {
def isUserModule(c: Class[_]): Boolean =
- c != null && (c == classOf[Module] || isUserModule(c.getSuperclass))
+ c != null && (c == classOf[UserModule] || isUserModule(c.getSuperclass))
isUserModule(Class.forName(ste.getClassName))
}
diff --git a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
index bee72817..18df7f51 100644
--- a/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
+++ b/chiselFrontend/src/main/scala/chisel3/internal/firrtl/IR.scala
@@ -98,7 +98,7 @@ case class FPLit(n: BigInt, w: Width, binaryPoint: BinaryPoint) extends LitArg(n
}
case class Ref(name: String) extends Arg
-case class ModuleIO(mod: Module, name: String) extends Arg {
+case class ModuleIO(mod: BaseModule, name: String) extends Arg {
override def fullName(ctx: Component): String =
if (mod eq ctx.id) name else s"${mod.getRef.name}.$name"
}
@@ -258,7 +258,7 @@ case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg,
case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: Int) extends Definition
case class DefSeqMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: Int) extends Definition
case class DefMemPort[T <: Data](sourceInfo: SourceInfo, id: T, source: Node, dir: MemPortDirection, index: Arg, clock: Arg) extends Definition
-case class DefInstance(sourceInfo: SourceInfo, id: Module, ports: Seq[Port]) extends Definition
+case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition
case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command
case class WhenEnd(sourceInfo: SourceInfo) extends Command
case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command
@@ -269,11 +269,11 @@ case class Stop(sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Command
case class Port(id: Data, dir: Direction)
case class Printf(sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Command
abstract class Component extends Arg {
- def id: Module
+ def id: BaseModule
def name: String
def ports: Seq[Port]
}
-case class DefModule(id: Module, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component
-case class DefBlackBox(id: Module, name: String, ports: Seq[Port], params: Map[String, Param]) extends Component
+case class DefModule(id: UserModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component
+case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], params: Map[String, Param]) extends Component
case class Circuit(name: String, components: Seq[Component], annotations: Seq[Annotation] = Seq.empty)