From e9d996e2a4f27e194ce3503d3ea8d9651b3ac3c2 Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Wed, 5 Jun 2024 13:09:47 -0700 Subject: Readd ports that were deleted for testing --- core/src/main/scala/chisel3/Data.scala | 2 +- core/src/main/scala/chisel3/Module.scala | 104 ++++++++++++++++++++++++++-- core/src/main/scala/chisel3/RawModule.scala | 36 +++++++++- core/src/main/scala/chisel3/package.scala | 2 +- 4 files changed, 137 insertions(+), 7 deletions(-) (limited to 'core/src') diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 73b8e8c8..1a7a0244 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -211,7 +211,7 @@ package experimental { * // ) * }}} */ - def modulePorts(target: BaseModule): Seq[(String, Data)] = Seq.empty + def modulePorts(target: BaseModule): Seq[(String, Data)] = target.getChiselPorts /** Returns a recursive representation of a module's ports with underscore-qualified names * {{{ diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 22dbcc6f..3419cda0 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -122,9 +122,9 @@ object Module { assignCompatDir(vec.sample_element) // This is used in fromChildren computation } case SpecifiedDirection.Input | SpecifiedDirection.Output => - // forced assign, nothing to do - // The .bind algorithm will automatically assign the direction here. - // Thus, no implicit assignment is necessary. + // forced assign, nothing to do + // The .bind algorithm will automatically assign the direction here. + // Thus, no implicit assignment is necessary. } } } @@ -191,7 +191,47 @@ package experimental { package internal { import chisel3.experimental.BaseModule - object BaseModule {} + object BaseModule { + private[chisel3] class ModuleClone[T <: BaseModule](val getProto: T) extends PseudoModule { + override def toString = s"ModuleClone(${getProto})" + // Do not call default addId function, which may modify a module that is already "closed" + def getPorts = _portsRecord + // ClonePorts that hold the bound ports for this module + // Used for setting the refs of both this module and the Record + private[BaseModule] var _portsRecord: Record = scala.compiletime.uninitialized + // Don't generate a component, but point to the one for the cloned Module + private[chisel3] def generateComponent(): Option[Component] = { + require(!_closed, "Can't generate module more than once") + _closed = true + _component = getProto._component + None + } + // Maps proto ports to module clone's ports + private[chisel3] lazy val ioMap: Map[Data, Data] = { + val name2Port = getPorts.elements + getProto.getChiselPorts.map { + case (name, data) => data -> name2Port(name) + }.toMap + } + + private[chisel3] def setRefAndPortsRef(namespace: Namespace): Unit = { + val record = _portsRecord + // Use .forceName to re-use default name resolving behavior + record.forceName(default = this.desiredName, namespace) + // Now take the Ref that forceName set and convert it to the correct Arg + val instName = record.getRef match { + case Ref(name) => name + case bad => throwException(s"Internal Error! Cloned-module Record $record has unexpected ref $bad") + } + // Set both the record and the module to have the same instance name + val ref = ModuleCloneIO(getProto, instName) + record.setRef(ref, force = true) // force because we did .forceName first + this.setRef(Ref(instName)) + } + + private[chisel3] override def initializeInParent(): Unit = () + } + } } package experimental { @@ -248,6 +288,25 @@ package experimental { _ids.toSeq } + private val _ports = new ArrayBuffer[Data]() + + // getPorts unfortunately already used for tester compatibility + protected[chisel3] 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 + + // This is dangerous because it can be called before the module is closed and thus there could + // be more ports and names have not yet been finalized. + // This should only to be used during the process of closing when it is safe to do so. + private[chisel3] def findPort(name: String): Option[Data] = _ports.find(_.seedOpt.contains(name)) + + 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. */ @@ -257,6 +316,26 @@ package experimental { */ private[chisel3] def initializeInParent(): Unit + private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = { + for (port <- getModulePorts) { + port._computeName(None).orElse(names.get(port)) match { + case Some(name) => + if (_namespace.contains(name)) { + Builder.error( + s"""Unable to name port $port to "$name" in $this,""" + + " name is already taken by another port!" + ) + } + port.setRef(ModuleIO(this, _namespace.name(name))) + case None => + Builder.error( + s"Unable to name port $port in $this, " + + "try making it a public field of the Module" + ) + port.setRef(ModuleIO(this, "")) + } + } + } // // Chisel Internals // @@ -330,6 +409,22 @@ package experimental { } } + /** + * Internal API. Returns a list of this module's generated top-level ports as a map of a String + * (FIRRTL name) to the IO object. Only valid after the module is closed. + * + * Note: for BlackBoxes (but not ExtModules), this returns the contents of the top-level io + * object, consistent with what is emitted in FIRRTL. + * + * TODO: Use SeqMap/VectorMap when those data structures become available. + */ + private[chisel3] def getChiselPorts: Seq[(String, Data)] = { + require(_closed, "Can't get ports before module close") + _component.get.ports.map { port => + (port.id.getRef.asInstanceOf[ModuleIO].name, port.id) + } + } + /** Called at the Module.apply(...) level after this Module has finished elaborating. * Returns a map of nodes -> names, for named nodes. * @@ -387,6 +482,7 @@ package experimental { Module.assignCompatDir(iodef) iodef.bind(PortBinding(this)) + _ports += iodef } /** Private accessor for _bindIoInPlace */ diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index 4e724b1b..8eeda03c 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -6,6 +6,7 @@ import scala.util.Try import scala.annotation.nowarn import chisel3.experimental.BaseModule import chisel3.internal._ +import chisel3.internal.BaseModule.ModuleClone import chisel3.internal.Builder._ import chisel3.internal.firrtl._ import _root_.firrtl.annotations.{IsModule, ModuleTarget} @@ -34,6 +35,14 @@ abstract class RawModule extends BaseModule { _component.get.asInstanceOf[DefModule].commands } + // + // Other Internal Functions + // + private var _firrtlPorts: Option[Seq[firrtl.Port]] = None + + @deprecated("Use DataMirror.modulePorts instead. this API will be removed in Chisel 3.6", "Chisel 3.5") + lazy val getPorts: Seq[Port] = _firrtlPorts.get + // This could be factored into a common utility private def canBeNamed(id: HasId): Boolean = id match { case d: Data => @@ -56,6 +65,9 @@ abstract class RawModule extends BaseModule { val names = nameIds(classOf[RawModule]) + // Ports get first naming priority, since they are part of a Module's IO spec + namePorts(names) + // Then everything else gets named val warnReflectiveNaming = Builder.warnReflectiveNaming for ((node, name) <- names) { @@ -80,6 +92,7 @@ abstract class RawModule extends BaseModule { // All suggestions are in, force names to every node. for (id <- getIds) { id match { + case id: ModuleClone[_] => id.setRefAndPortsRef(_namespace) // special handling case id: BaseModule => id.forceName(default = id.desiredName, _namespace) case id: MemBase[_] => id.forceName(default = "MEM", _namespace) // removed till macros are fixed @@ -109,7 +122,28 @@ abstract class RawModule extends BaseModule { closeUnboundIds(names) - val component = DefModule(this, name, null, Seq.empty) + val firrtlPorts = getModulePorts.map { port => + // Special case Vec to make FIRRTL emit the direction of its + // element. + // Just taking the Vec's specifiedDirection is a bug in cases like + // Vec(Flipped()), since the Vec's specifiedDirection is + // Unspecified. + val direction = port match { + case v: Vec[_] => + v.specifiedDirection match { + case SpecifiedDirection.Input => SpecifiedDirection.Input + case SpecifiedDirection.Output => SpecifiedDirection.Output + case SpecifiedDirection.Flip => SpecifiedDirection.flip(v.sample_element.specifiedDirection) + case SpecifiedDirection.Unspecified => v.sample_element.specifiedDirection + } + case _ => port.specifiedDirection + } + + Port(port, direction) + } + _firrtlPorts = Some(firrtlPorts) + + val component = DefModule(this, name, firrtlPorts, _commands.result()) _component = Some(component) _component } diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala index 856cbbd2..72a3515c 100644 --- a/core/src/main/scala/chisel3/package.scala +++ b/core/src/main/scala/chisel3/package.scala @@ -342,7 +342,7 @@ package object chisel3 { "duplicated with DataMirror.fullModulePorts, this returns an internal API, will be removed in Chisel 3.6", "Chisel 3.5" ) - def getModulePorts(m: Module): Seq[Port] = Seq.empty + def getModulePorts(m: Module): Seq[Port] = m.getPorts class BindingException(message: String) extends ChiselException(message) -- cgit v1.2.3