diff options
Diffstat (limited to 'core/src/main/scala/chisel3')
| -rw-r--r-- | core/src/main/scala/chisel3/BlackBox.scala | 18 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/Module.scala | 5 | ||||
| -rw-r--r-- | core/src/main/scala/chisel3/RawModule.scala | 99 |
3 files changed, 87 insertions, 35 deletions
diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala index 1b093ee1..2aa9a243 100644 --- a/core/src/main/scala/chisel3/BlackBox.scala +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -134,15 +134,11 @@ package experimental { */ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param])(implicit compileOptions: CompileOptions) extends BaseBlackBox { - @deprecated("Removed for causing issues in Scala 2.12+. You remain free to define io Bundles " + - "in your BlackBoxes, but you cannot rely on an io field in every BlackBox. " + - "For more information, see: https://github.com/freechipsproject/chisel3/pull/1550.", - "Chisel 3.4" - ) - def io: Record - - // Private accessor to reduce number of deprecation warnings - private[chisel3] def _io: Record = io + // Find a Record port named "io" for purposes of stripping the prefix + private[chisel3] lazy val _io: Record = + this.findPort("io") + .collect { case r: Record => r } // Must be a Record + .getOrElse(null) // null handling occurs in generateComponent // Allow access to bindings from the compatibility package protected def _compatIoPortBound() = portsContains(_io) @@ -150,8 +146,8 @@ abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param private[chisel3] override def generateComponent(): Component = { _compatAutoWrapPorts() // pre-IO(...) compatibility hack - // Restrict IO to just io, clock, and reset - require(_io != null, "BlackBox must have io") + // Restrict IO to just io, clock, and reset + require(_io != null, "BlackBox must have a port named 'io' of type Record!") require(portsContains(_io), "BlackBox must have io wrapped in IO(...)") require(portsSize == 1, "BlackBox must only have io as IO") diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 9a1a0ce1..d34211f1 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -257,6 +257,11 @@ package experimental { // 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. diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index bb84c444..ac6a2d1f 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -3,6 +3,7 @@ package chisel3 import scala.collection.mutable.{ArrayBuffer, HashMap} +import scala.util.Try import scala.collection.JavaConversions._ import scala.language.experimental.macros @@ -150,40 +151,62 @@ trait RequireSyncReset extends Module { override private[chisel3] def mkReset: Bool = Bool() } -package internal { +package object internal { + + // Private reflective version of "val io" to maintain Chisel.Module semantics without having + // io as a virtual method. See https://github.com/freechipsproject/chisel3/pull/1550 for more + // information about the removal of "val io" + private def reflectivelyFindValIO(self: BaseModule): Record = { + // Java reflection is faster and works for the common case + def tryJavaReflect: Option[Record] = Try { + self.getClass.getMethod("io").invoke(self).asInstanceOf[Record] + }.toOption + // Anonymous subclasses don't work with Java reflection, so try slower, Scala reflection + def tryScalaReflect: Option[Record] = { + val ru = scala.reflect.runtime.universe + import ru.{Try => _, _} + val m = ru.runtimeMirror(self.getClass.getClassLoader) + val im = m.reflect(self) + val tpe = im.symbol.toType + // For some reason, in anonymous subclasses, looking up the Term by name (TermName("io")) + // hits an internal exception. Searching for the term seems to work though so we use that. + val ioTerm: Option[TermSymbol] = tpe.decls.collectFirst { + case d if d.name.toString == "io" && d.isTerm => d.asTerm + } + ioTerm.flatMap { term => + Try { + im.reflectField(term).get.asInstanceOf[Record] + }.toOption + } + } + + tryJavaReflect + .orElse(tryScalaReflect) + .map(_.autoSeed("io")) + .orElse { + // Fallback if reflection fails, user can wrap in IO(...) + self.findPort("io") + .collect { case r: Record => r } + }.getOrElse(throwException( + s"Compatibility mode Module '$this' must have a 'val io' Bundle. " + + "If there is such a field and you still see this error, autowrapping has failed (sorry!). " + + "Please wrap the Bundle declaration in IO(...)." + )) + } /** 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. + * This is only intended as a bridge from chisel3 to Chisel.Module, do not extend this in user + * code, use [[Module]] */ abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) extends Module { - // IO for this Module. At the Scala level (pre-FIRRTL transformations), - // connections in and out of a Module may only go through `io` elements. - @deprecated("Removed for causing issues in Scala 2.12+. You remain free to define io Bundles " + - "in your Modules, but you cannot rely on an io field in every Module. " + - "For more information, see: https://github.com/freechipsproject/chisel3/pull/1550.", - "Chisel 3.4" - ) - def io: Record - - // Private accessor to reduce number of deprecation warnings - private[chisel3] def _io: Record = io + + private lazy val _io: Record = reflectivelyFindValIO(this) // Allow access to bindings from the compatibility package protected def _compatIoPortBound() = portsContains(_io) - private[chisel3] override def namePorts(names: HashMap[HasId, String]): Unit = { - for (port <- getModulePorts) { - // This should already have been caught - if (!names.contains(port)) throwException(s"Unable to name port $port in $this") - val name = names(port) - port.setRef(ModuleIO(this, _namespace.name(name))) - } - } - private[chisel3] override def generateComponent(): Component = { _compatAutoWrapPorts() // pre-IO(...) compatibility hack @@ -195,5 +218,33 @@ package internal { super.generateComponent() } + + override def _compatAutoWrapPorts(): Unit = { + if (!_compatIoPortBound() && _io != null) { + _bindIoInPlace(_io) + } + } + } + + import chisel3.experimental.Param + + /** Legacy BlackBox class will reflectively autowrap val io + * + * '''Do not use this class in user code'''. Use whichever `Module` is imported by your wildcard + * import (preferably `import chisel3._`). + */ + abstract class LegacyBlackBox(params: Map[String, Param] = Map.empty[String, Param]) + (implicit moduleCompileOptions: CompileOptions) + extends chisel3.BlackBox(params) { + + override private[chisel3] lazy val _io: Record = reflectivelyFindValIO(this) + + // This class auto-wraps the BlackBox with IO(...), allowing legacy code (where IO(...) wasn't + // required) to build. + override def _compatAutoWrapPorts(): Unit = { + if (!_compatIoPortBound()) { + _bindIoInPlace(_io) + } + } } } |
