summaryrefslogtreecommitdiff
path: root/chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala
diff options
context:
space:
mode:
authorRichard Lin2017-04-13 22:59:00 -0700
committerGitHub2017-04-13 22:59:00 -0700
commite07248b8f6022fafdb84f5d1c0ebe3fc90a5475a (patch)
treef2bb938fd35651b4fc7b88cbcd20e163cc75dd2e /chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala
parent97902cdc53eec52aa0cd806b8cb49a0e3f2fb769 (diff)
Module Hierarchy Refactor (#469)
Diffstat (limited to 'chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala')
-rw-r--r--chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala137
1 files changed, 114 insertions, 23 deletions
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))
+ }
+ }
}