diff options
| author | Richard Lin | 2017-04-13 22:59:00 -0700 |
|---|---|---|
| committer | GitHub | 2017-04-13 22:59:00 -0700 |
| commit | e07248b8f6022fafdb84f5d1c0ebe3fc90a5475a (patch) | |
| tree | f2bb938fd35651b4fc7b88cbcd20e163cc75dd2e /chiselFrontend/src/main/scala/chisel3/core/BlackBox.scala | |
| parent | 97902cdc53eec52aa0cd806b8cb49a0e3f2fb769 (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.scala | 137 |
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)) + } + } } |
