summaryrefslogtreecommitdiff
path: root/core/src
diff options
context:
space:
mode:
authorJack Koenig2021-01-20 18:55:16 -0800
committerJack Koenig2021-01-21 15:36:55 -0800
commit8a73362bb6fe87817a1867cc2482c1841f95c077 (patch)
treea439d2a5fb52941baeffa22297b38160dc2d1249 /core/src
parentb88ae1fb5cd106f114fa2152ac53c197ae69c164 (diff)
Remove val io
Chisel projects no longer need -Xsource:2.11 when compiling with Scala 2.12. Autowrapping of "val io" for compatibility mode Modules is now implemented using reflection instead of calling the virtual method. Also move Chisel.BlackBox to new chisel3.internal.LegacyBlackBox
Diffstat (limited to 'core/src')
-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)
+ }
+ }
}
}