diff options
| author | Jack Koenig | 2021-01-21 22:50:12 -0800 |
|---|---|---|
| committer | GitHub | 2021-01-21 22:50:12 -0800 |
| commit | dd6871b8b3f2619178c2a333d9d6083805d99e16 (patch) | |
| tree | 825776855e7d2fc28ef32ebb05df7339c24e00b3 /core/src/main/scala/chisel3/RawModule.scala | |
| parent | 616256c35cb7de8fcd97df56af1986b747abe54d (diff) | |
| parent | 53c24cb0a369d4c4f57c28c098b30e4d3640eac2 (diff) | |
Merge pull request #1745 from chipsalliance/remove-val-io
Remove "val io" and rename MultiIOModule to Module
Diffstat (limited to 'core/src/main/scala/chisel3/RawModule.scala')
| -rw-r--r-- | core/src/main/scala/chisel3/RawModule.scala | 157 |
1 files changed, 85 insertions, 72 deletions
diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index 9f0a24d6..0adacedb 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 @@ -142,86 +143,83 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) } } -trait RequireAsyncReset extends MultiIOModule { +trait RequireAsyncReset extends Module { override private[chisel3] def mkReset: AsyncReset = AsyncReset() } -trait RequireSyncReset extends MultiIOModule { +trait RequireSyncReset extends Module { override private[chisel3] def mkReset: Bool = Bool() } -/** Abstract base class for Modules, which behave much like Verilog modules. - * These may contain both logic and state which are written in the Module - * body (constructor). - * This abstract base class includes an implicit clock and reset. - * - * @note Module instantiations must be wrapped in a Module() call. - */ -abstract class MultiIOModule(implicit moduleCompileOptions: CompileOptions) - extends RawModule { - // Implicit clock and reset pins - final val clock: Clock = IO(Input(Clock())).autoSeed("clock") - final val reset: Reset = IO(Input(mkReset)).autoSeed("reset") - - private[chisel3] def mkReset: Reset = { - // Top module and compatibility mode use Bool for reset - val inferReset = _parent.isDefined && moduleCompileOptions.inferModuleReset - if (inferReset) Reset() else Bool() - } - - // Setup ClockAndReset - Builder.currentClock = Some(clock) - Builder.currentReset = Some(reset) - Builder.clearPrefix() - - private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - implicit val sourceInfo = UnlocatableSourceInfo +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 + } + } - super.initializeInParent(parentCompileOptions) - clock := Builder.forcedClock - reset := Builder.forcedReset + 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(...)." + )) } -} - -package internal { /** 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. + * '''Do not use this class in user code'''. Use whichever `Module` is imported by your wildcard + * import (preferably `import chisel3._`). */ - abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) - extends MultiIOModule { - // These are to be phased out - protected var override_clock: Option[Clock] = None - protected var override_reset: Option[Bool] = None - - // 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 + abstract class LegacyModule(implicit moduleCompileOptions: CompileOptions) extends Module { + // Provide a non-deprecated constructor + def this(override_clock: Option[Clock] = None, override_reset: Option[Bool]=None) + (implicit moduleCompileOptions: CompileOptions) = { + this() + this.override_clock = override_clock + this.override_reset = override_reset + } + def this(_clock: Clock)(implicit moduleCompileOptions: CompileOptions) = + this(Option(_clock), None)(moduleCompileOptions) + def this(_reset: Bool)(implicit moduleCompileOptions: CompileOptions) = + this(None, Option(_reset))(moduleCompileOptions) + def this(_clock: Clock, _reset: Bool)(implicit moduleCompileOptions: CompileOptions) = + this(Option(_clock), Option(_reset))(moduleCompileOptions) + + 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 @@ -234,17 +232,32 @@ package internal { super.generateComponent() } - private[chisel3] override def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - // Don't generate source info referencing parents inside a module, since this interferes with - // module de-duplication in FIRRTL emission. - implicit val sourceInfo = UnlocatableSourceInfo - - if (!parentCompileOptions.explicitInvalidate) { - pushCommand(DefInvalid(sourceInfo, _io.ref)) + override def _compatAutoWrapPorts(): Unit = { + if (!_compatIoPortBound() && _io != null) { + _bindIoInPlace(_io) } + } + } - clock := override_clock.getOrElse(Builder.forcedClock) - reset := override_reset.getOrElse(Builder.forcedReset) + import chisel3.experimental.Param + + /** Legacy BlackBox class will reflectively autowrap val io + * + * '''Do not use this class in user code'''. Use whichever `BlackBox` 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) + } } } } |
