summaryrefslogtreecommitdiff
path: root/core/src/main/scala/chisel3
diff options
context:
space:
mode:
Diffstat (limited to 'core/src/main/scala/chisel3')
-rw-r--r--core/src/main/scala/chisel3/BlackBox.scala18
-rw-r--r--core/src/main/scala/chisel3/Module.scala5
-rw-r--r--core/src/main/scala/chisel3/RawModule.scala99
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)
+ }
+ }
}
}