diff options
Diffstat (limited to 'core/src')
36 files changed, 1703 insertions, 768 deletions
diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 17e46cb3..db354e1f 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -3,7 +3,7 @@ package chisel3 import chisel3.experimental.VecLiterals.AddVecLiteralConstructor -import chisel3.experimental.dataview.{InvalidViewException, isView} +import chisel3.experimental.dataview.{InvalidViewException, isView, reifySingleData} import scala.collection.immutable.{SeqMap, VectorMap} import scala.collection.mutable.{HashSet, LinkedHashMap} @@ -29,7 +29,26 @@ sealed abstract class Aggregate extends Data { val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) val duplicates = getElements.groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } if (!duplicates.isEmpty) { - throw new AliasedAggregateFieldException(s"Aggregate $this contains aliased fields $duplicates") + this match { + case b: Record => + // show groups of names of fields with duplicate id's + // The sorts make the displayed order of fields deterministic and matching the order of occurrence in the Bundle. + // It's a bit convoluted but happens rarely and makes the error message easier to understand + val dupNames = duplicates.toSeq.sortBy(_._id).map { duplicate => + b.elements + .collect { case x if x._2._id == duplicate._id => x } + .toSeq.sortBy(_._2._id) + .map(_._1).reverse + .mkString("(", ",", ")") + }.mkString(",") + throw new AliasedAggregateFieldException( + s"${b.className} contains aliased fields named ${dupNames}" + ) + case _ => + throw new AliasedAggregateFieldException( + s"Aggregate ${this.getClass} contains aliased fields $duplicates ${duplicates.mkString(",")}" + ) + } } for (child <- getElements) { child.bind(ChildBinding(this), resolvedDirection) @@ -54,7 +73,7 @@ sealed abstract class Aggregate extends Data { override def litOption: Option[BigInt] = { // Shift the accumulated value by our width and add in our component, masked by our width. def shiftAdd(accumulator: Option[BigInt], elt: Data): Option[BigInt] = { - (accumulator, elt.litOption()) match { + (accumulator, elt.litOption) match { case (Some(accumulator), Some(eltLit)) => val width = elt.width.get val masked = ((BigInt(1) << width) - 1) & eltLit // also handles the negative case with two's complement @@ -164,16 +183,14 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extends Aggregate with VecLike[T] { override def toString: String = { - val bindingString = topBindingOpt match { + topBindingOpt match { case Some(VecLitBinding(vecLitBinding)) => val contents = vecLitBinding.zipWithIndex.map { case ((data, lit), index) => s"$index=$lit" }.mkString(", ") - s"($contents)" - case _ => bindingToString + s"${sample_element.cloneType}[$length]($contents)" + case _ => stringAccessor(s"${sample_element.cloneType}[$length]") } - val elementType = sample_element.cloneType - s"$elementType[$length]$bindingString" } private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { @@ -260,9 +277,20 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) def do_apply(p: UInt)(implicit compileOptions: CompileOptions): T = { requireIsHardware(this, "vec") requireIsHardware(p, "vec index") + + // Special handling for views if (isView(this)) { - throw InvalidViewException("Dynamic indexing of Views is not yet supported") + reifySingleData(this) match { + // Views complicate things a bit, but views that correspond exactly to an identical Vec can just forward the + // dynamic indexing to the target Vec + // In theory, we could still do this forwarding if the sample element were different by deriving a DataView + case Some(target: Vec[T @unchecked]) if this.length == target.length && + this.sample_element.typeEquivalent(target.sample_element) => + return target.do_apply(p) + case _ => throw InvalidViewException("Dynamic indexing of Views is not yet supported") + } } + val port = gen // Reconstruct the resolvedDirection (in Aggregate.bind), since it's not stored. @@ -329,11 +357,11 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) def do_reduceTree(redOp: (T, T) => T, layerOp: (T) => T = (x: T) => x) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : T = { require(!isEmpty, "Cannot apply reduction on a vec of size 0") - var curLayer = this + var curLayer : Seq[T] = this while (curLayer.length > 1) { - curLayer = VecInit(curLayer.grouped(2).map( x => + curLayer = curLayer.grouped(2).map( x => if (x.length == 1) layerOp(x(0)) else redOp(x(0), x(1)) - ).toSeq) + ).toSeq } curLayer(0) } @@ -922,15 +950,14 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio * }}} */ override def toString: String = { - val bindingString = topBindingOpt match { + topBindingOpt match { case Some(BundleLitBinding(_)) => val contents = elements.toList.reverse.map { case (name, data) => s"$name=$data" }.mkString(", ") - s"($contents)" - case _ => bindingToString + s"$className($contents)" + case _ => stringAccessor(s"$className") } - s"$className$bindingString" } def elements: SeqMap[String, Data] @@ -1033,6 +1060,10 @@ package experimental { * }}} */ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { + assert(_usingPlugin, "The Chisel compiler plugin is now required for compiling Chisel code. " + + "Please see https://github.com/chipsalliance/chisel3#build-your-own-chisel-projects." + ) + override def className: String = this.getClass.getSimpleName match { case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case case "" => "AnonymousBundle" // ditto, but on other platforms @@ -1109,16 +1140,6 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { */ protected def _usingPlugin: Boolean = false - // Memoize the outer instance for autoclonetype, especially where this is context-dependent - // (like the outer module or enclosing Bundles). - private var _outerInst: Option[Object] = None - - // For reflective autoclonetype, record possible candidates for outer instance. - // _outerInst should always take precedence, since it should be propagated from the original - // object which has the most accurate context. - private val _containingModule: Option[BaseModule] = if (_usingPlugin) None else Builder.currentModule - private val _containingBundles: Seq[Bundle] = if (_usingPlugin) Nil else Builder.updateBundleStack(this) - private def checkClone(clone: Bundle): Unit = { for ((name, field) <- elements) { if (clone.elements(name) eq field) { @@ -1142,220 +1163,10 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { /** Implementation of cloneType using runtime reflection. This should _never_ be overridden or called in user-code * - * @note This is overridden by the compiler plugin (it is never called when using the plugin) + * @note This is overridden by the compiler plugin (this implementation is never called) */ protected def _cloneTypeImpl: Bundle = { - assert(Builder.allowReflectiveAutoCloneType, "reflective autoclonetype is disallowed, this should only happen in testing") - // This attempts to infer constructor and arguments to clone this Bundle subtype without - // requiring the user explicitly overriding cloneType. - import scala.language.existentials - import scala.reflect.runtime.universe._ - - val clazz = this.getClass - - def autoClonetypeError(desc: String): Nothing = - throw new AutoClonetypeException( - s"Unable to automatically infer cloneType on $clazz. " + - "cloneType is now implemented by the Chisel compiler plugin so please ensure you are using it in your build. " + - "If you cannot use the compiler plugin or you are using it and you still see this message, please file an issue and let us know. " + - s"For those not using the plugin, here is the 'runtime reflection' cloneType error message: $desc" - ) - - def validateClone(clone: Bundle, equivDiagnostic: String): Unit = { - if (!clone.typeEquivalent(this)) { - autoClonetypeError(s"Automatically cloned $clone not type-equivalent to base $this. " + equivDiagnostic) - } - checkClone(clone) - } - - val mirror = runtimeMirror(clazz.getClassLoader) - - val classSymbolOption = try { - Some(mirror.reflect(this).symbol) - } catch { - case e: scala.reflect.internal.Symbols#CyclicReference => None // Workaround for a scala bug - } - - val enclosingClassOption = (clazz.getEnclosingClass, classSymbolOption) match { - case (null, _) => None - case (_, Some(classSymbol)) if classSymbol.isStatic => None // allows support for members of companion objects - case (outerClass, _) => Some(outerClass) - } - - // For compatibility with pre-3.1, where null is tried as an argument to the constructor. - // This stores potential error messages which may be used later. - var outerClassError: Option[String] = None - - // Check if this is an inner class, and if so, try to get the outer instance - val outerClassInstance = enclosingClassOption.map { outerClass => - def canAssignOuterClass(x: Object) = outerClass.isAssignableFrom(x.getClass) - - val outerInstance = _outerInst match { - case Some(outerInstance) => outerInstance // use _outerInst if defined - case None => // determine outer instance if not already recorded - try { - // Prefer this if it works, but doesn't work in all cases, namely anonymous inner Bundles - val outer = clazz.getDeclaredField("$outer").get(this) - _outerInst = Some(outer) - outer - } catch { - case (_: NoSuchFieldException | _: IllegalAccessException) => - // Fallback using guesses based on common patterns - val allOuterCandidates = Seq( - _containingModule.toSeq, - _containingBundles - ).flatten.distinct - allOuterCandidates.filter(canAssignOuterClass(_)) match { - case outer :: Nil => - _outerInst = Some(outer) // record the guess for future use - outer - case Nil => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped - outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + - s" no candidates assignable to outer class types; examined $allOuterCandidates") - null - case candidates => // TODO: replace with fatal autoClonetypeError once compatibility period is dropped - outerClassError = Some(s"Unable to determine instance of outer class $outerClass," + - s" multiple possible candidates $candidates assignable to outer class type") - null - } - } - } - (outerClass, outerInstance) - } - - // If possible (constructor with no arguments), try Java reflection first - // This handles two cases that Scala reflection doesn't: - // 1. getting the ClassSymbol of a class with an anonymous outer class fails with a - // CyclicReference exception - // 2. invoking the constructor of an anonymous inner class seems broken (it expects the outer - // class as an argument, but fails because the number of arguments passed in is incorrect) - if (clazz.getConstructors.size == 1) { - var ctor = clazz.getConstructors.head - val argTypes = ctor.getParameterTypes.toList - val clone = (argTypes, outerClassInstance) match { - case (Nil, None) => // no arguments, no outer class, invoke constructor directly - Some(ctor.newInstance().asInstanceOf[this.type]) - case (argType :: Nil, Some((_, outerInstance))) => - if (outerInstance == null) { - Builder.deprecated(s"chisel3.1 autoclonetype failed, falling back to 3.0 behavior using null as the outer instance." + - s" Autoclonetype failure reason: ${outerClassError.get}", - Some(s"$clazz")) - Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) - } else if (argType isAssignableFrom outerInstance.getClass) { - Some(ctor.newInstance(outerInstance).asInstanceOf[this.type]) - } else { - None - } - case _ => None - - } - clone match { - case Some(clone) => - clone._outerInst = this._outerInst - validateClone(clone, "Constructor argument values were not inferred, ensure constructor is deterministic.") - return clone.asInstanceOf[this.type] - case None => - } - } - - // Get constructor parameters and accessible fields - val classSymbol = classSymbolOption.getOrElse(autoClonetypeError(s"scala reflection failed." + - " This is known to occur with inner classes on anonymous outer classes." + - " In those cases, autoclonetype only works with no-argument constructors, or you can define a custom cloneType.")) - - val decls = classSymbol.typeSignature.decls - val ctors = decls.collect { case meth: MethodSymbol if meth.isConstructor => meth } - if (ctors.size != 1) { - autoClonetypeError(s"found multiple constructors ($ctors)." + - " Either remove all but the default constructor, or define a custom cloneType method.") - } - val ctor = ctors.head - val ctorParamss = ctor.paramLists - val ctorParams = ctorParamss match { - case Nil => List() - case ctorParams :: Nil => ctorParams - case ctorParams :: ctorImplicits :: Nil => ctorParams ++ ctorImplicits - case _ => autoClonetypeError(s"internal error, unexpected ctorParamss = $ctorParamss") - } - val ctorParamsNames = ctorParams.map(_.name.toString) - - // Special case for anonymous inner classes: their constructor consists of just the outer class reference - // Scala reflection on anonymous inner class constructors seems broken - if (ctorParams.size == 1 && outerClassInstance.isDefined && - ctorParams.head.typeSignature == mirror.classSymbol(outerClassInstance.get._1).toType) { - // Fall back onto Java reflection - val ctors = clazz.getConstructors - require(ctors.size == 1) // should be consistent with Scala constructors - try { - val clone = ctors.head.newInstance(outerClassInstance.get._2).asInstanceOf[this.type] - clone._outerInst = this._outerInst - - validateClone(clone, "Outer class instance was inferred, ensure constructor is deterministic.") - return clone - } catch { - case e @ (_: java.lang.reflect.InvocationTargetException | _: IllegalArgumentException) => - autoClonetypeError(s"unexpected failure at constructor invocation, got $e.") - } - } - - // Get all the class symbols up to (but not including) Bundle and get all the accessors. - // (each ClassSymbol's decls only includes those declared in the class itself) - val bundleClassSymbol = mirror.classSymbol(classOf[Bundle]) - val superClassSymbols = classSymbol.baseClasses.takeWhile(_ != bundleClassSymbol) - val superClassDecls = superClassSymbols.map(_.typeSignature.decls).flatten - val accessors = superClassDecls.collect { case meth: MethodSymbol if meth.isParamAccessor => meth } - - // Get constructor argument values - // Check that all ctor params are immutable and accessible. Immutability is required to avoid - // potential subtle bugs (like values changing after cloning). - // This also generates better error messages (all missing elements shown at once) instead of - // failing at the use site one at a time. - val accessorsName = accessors.filter(_.isStable).map(_.name.toString) - val paramsDiff = ctorParamsNames.toSet -- accessorsName.toSet - if (!paramsDiff.isEmpty) { - autoClonetypeError(s"constructor has parameters (${paramsDiff.toList.sorted.mkString(", ")}) that are not both immutable and accessible." + - " Either make all parameters immutable and accessible (vals) so cloneType can be inferred, or define a custom cloneType method.") - } - - // Get all the argument values - val accessorsMap = accessors.map(accessor => accessor.name.toString -> accessor).toMap - val instanceReflect = mirror.reflect(this) - val ctorParamsNameVals = ctorParamsNames.map { - paramName => paramName -> instanceReflect.reflectMethod(accessorsMap(paramName)).apply() - } - - // Opportunistic sanity check: ensure any arguments of type Data is not bound - // (which could lead to data conflicts, since it's likely the user didn't know to re-bind them). - // This is not guaranteed to catch all cases (for example, Data in Tuples or Iterables). - val boundDataParamNames = ctorParamsNameVals.collect { - case (paramName, paramVal: Data) if paramVal.topBindingOpt.isDefined => paramName - } - if (boundDataParamNames.nonEmpty) { - autoClonetypeError(s"constructor parameters (${boundDataParamNames.sorted.mkString(", ")}) have values that are hardware types, which is likely to cause subtle errors." + - " Use chisel types instead: use the value before it is turned to a hardware type (with Wire(...), Reg(...), etc) or use chiselTypeOf(...) to extract the chisel type.") - } - - // Clone unbound parameters in case they are being used as bundle fields. - val ctorParamsVals = ctorParamsNameVals.map { - case (_, paramVal: Data) => paramVal.cloneTypeFull - case (_, paramVal) => paramVal - } - - // Invoke ctor - val classMirror = outerClassInstance match { - case Some((_, null)) => autoClonetypeError(outerClassError.get) // deals with the null hack for 3.0 compatibility - case Some((_, outerInstance)) => mirror.reflect(outerInstance).reflectClass(classSymbol) - case _ => mirror.reflectClass(classSymbol) - } - val clone = classMirror.reflectConstructor(ctor).apply(ctorParamsVals:_*).asInstanceOf[this.type] - clone._outerInst = this._outerInst - - validateClone(clone, - "Constructor argument values were inferred:" + - " ensure that variable names are consistent and have the same value throughout the constructor chain," + - " and that the constructor is deterministic." - ) - clone + throwException(s"Internal Error! This should have been implemented by the chisel3-plugin. Please file an issue against chisel3") } /** Default "pretty-print" implementation diff --git a/core/src/main/scala/chisel3/Assert.scala b/core/src/main/scala/chisel3/Assert.scala deleted file mode 100644 index 9a497e1f..00000000 --- a/core/src/main/scala/chisel3/Assert.scala +++ /dev/null @@ -1,92 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3 - -import scala.reflect.macros.blackbox.Context -import scala.language.experimental.macros - -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -object assert { - /** Checks for a condition to be valid in the circuit at all times. If the - * condition evaluates to false, the circuit simulation stops with an error. - * - * Does not fire when in reset (defined as the encapsulating Module's - * reset). If your definition of reset is not the encapsulating Module's - * reset, you will need to gate this externally. - * - * May be called outside of a Module (like defined in a function), so - * functions using assert make the standard Module assumptions (single clock - * and single reset). - * - * @param cond condition, assertion fires (simulation fails) when false - * @param message optional format string to print when the assertion fires - * @param data optional bits to print in the message formatting - * - * @note See [[printf.apply(fmt:String* printf]] for format string documentation - * @note currently cannot be used in core Chisel / libraries because macro - * defs need to be compiled first and the SBT project is not set up to do - * that - */ - // Macros currently can't take default arguments, so we need two functions to emulate defaults. - def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl_msg_data - def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = macro apply_impl - - def apply_impl_msg_data(c: Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { - import c.universe._ - val p = c.enclosingPosition - val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) - q"$apply_impl_do($cond, $condStr, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)" - } - - def apply_impl(c: Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { - import c.universe._ - val p = c.enclosingPosition - val condStr = s"${p.source.file.name}:${p.line} ${p.lineContent.trim}" - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("apply_impl_do")) - q"$apply_impl_do($cond, $condStr, _root_.scala.None)($sourceInfo, $compileOptions)" - } - - def apply_impl_do(cond: Bool, line: String, message: Option[String], data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { - val escLine = line.replaceAll("%", "%%") - when (!(cond || Module.reset.asBool)) { - val fmt = message match { - case Some(msg) => - s"Assertion failed: $msg\n at $escLine\n" - case None => s"Assertion failed\n at $escLine\n" - } - printf.printfWithoutReset(fmt, data:_*) - pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, 1)) - } - } - - /** An elaboration-time assertion, otherwise the same as the above run-time - * assertion. */ - def apply(cond: Boolean, message: => String) { - Predef.assert(cond, message) - } - - /** A workaround for default-value overloading problems in Scala, just - * 'assert(cond, "")' */ - def apply(cond: Boolean) { - Predef.assert(cond, "") - } -} - -object stop { - /** Terminate execution with a failure code. */ - def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - when (!Module.reset.asBool) { - pushCommand(Stop(sourceInfo, Builder.forcedClock.ref, code)) - } - } - - /** Terminate execution, indicating success. */ - def apply()(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = { - stop(0) - } -} diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala index 670f6e7a..5ab04d13 100644 --- a/core/src/main/scala/chisel3/Bits.scala +++ b/core/src/main/scala/chisel3/Bits.scala @@ -25,7 +25,10 @@ private[chisel3] sealed trait ToBoolable extends Element { * * @note The width must be known and equal to 1 */ - final def asBool(): Bool = macro SourceInfoWhiteboxTransform.noArg + final def asBool: Bool = macro SourceInfoWhiteboxTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asBool(dummy: Int*): Bool = macro SourceInfoWhiteboxTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool @@ -222,7 +225,10 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi * @return this $coll with each bit inverted * @group Bitwise */ - final def unary_~ (): Bits = macro SourceInfoWhiteboxTransform.noArg + final def unary_~ : Bits = macro SourceInfoWhiteboxTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_~(dummy: Int*): Bits = macro SourceInfoWhiteboxTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_unary_~ (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits @@ -304,10 +310,10 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bits /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ - final def toBools(): Seq[Bool] = macro SourceInfoTransform.noArg + final def asBools: Seq[Bool] = macro SourceInfoTransform.noArg - /** Returns the contents of this wire as a [[scala.collection.Seq]] of [[Bool]]. */ - final def asBools(): Seq[Bool] = macro SourceInfoTransform.noArg + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asBools(dummy: Int*): Seq[Bool] = macro SourceInfoWhiteboxTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asBools(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Seq[Bool] = @@ -318,7 +324,10 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi * @note The arithmetic value is not preserved if the most-significant bit is set. For example, a [[UInt]] of * width 3 and value 7 (0b111) would become an [[SInt]] of width 3 and value -1. */ - final def asSInt(): SInt = macro SourceInfoTransform.noArg + final def asSInt: SInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asSInt(dummy: Int*): SInt = macro SourceInfoWhiteboxTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asSInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt @@ -390,11 +399,10 @@ sealed abstract class Bits(private[chisel3] val width: Width) extends Element wi */ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[UInt] { override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString + litOption match { + case Some(value) => s"UInt$width($value)" + case _ => stringAccessor(s"UInt$width") } - s"UInt$width$bindingString" } private[chisel3] override def typeEquivalent(that: Data): Boolean = @@ -410,7 +418,10 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U * $constantWidth * @group Arithmetic */ - final def unary_- (): UInt = macro SourceInfoTransform.noArg + final def unary_- : UInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-(dummy: Int*): UInt = macro SourceInfoTransform.noArgDummy /** Unary negation (constant width) * @@ -418,7 +429,10 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U * $constantWidth * @group Arithmetic */ - final def unary_-% (): UInt = macro SourceInfoTransform.noArg + final def unary_-% : UInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_%(dummy: Int*): UInt = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : UInt = 0.U - this @@ -522,7 +536,7 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U */ final def ^ (that: UInt): UInt = macro SourceInfoTransform.thatArg - // override def abs: UInt = macro SourceInfoTransform.noArg + // override def abs: UInt = macro SourceInfoTransform.noArgDummy def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = this /** @group SourceInfoTransformMacro */ @@ -545,21 +559,30 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U * @return a hardware [[Bool]] resulting from every bit of this $coll or'd together * @group Bitwise */ - final def orR(): Bool = macro SourceInfoTransform.noArg + final def orR: Bool = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def orR(dummy: Int*): Bool = macro SourceInfoTransform.noArgDummy /** And reduction operator * * @return a hardware [[Bool]] resulting from every bit of this $coll and'd together * @group Bitwise */ - final def andR(): Bool = macro SourceInfoTransform.noArg + final def andR: Bool = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def andR(dummy: Int*): Bool = macro SourceInfoTransform.noArgDummy /** Exclusive or (xor) reduction operator * * @return a hardware [[Bool]] resulting from every bit of this $coll xor'd together * @group Bitwise */ - final def xorR(): Bool = macro SourceInfoTransform.noArg + final def xorR: Bool = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def xorR(dummy: Int*): Bool = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_orR(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = redop(sourceInfo, OrReduceOp) @@ -599,7 +622,10 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U * @return a hardware [[Bool]] asserted if this $coll equals zero * @group Bitwise */ - final def unary_! () : Bool = macro SourceInfoTransform.noArg + final def unary_! : Bool = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_! (dummy: Int*): Bool = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_unary_! (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : Bool = this === 0.U(1.W) @@ -617,6 +643,51 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U override def do_>> (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = binop(sourceInfo, UInt(this.width), DynamicShiftRightOp, that) + /** + * Circular shift to the left + * @param that number of bits to rotate + * @return UInt of same width rotated left n bits + */ + final def rotateLeft(that: Int): UInt = macro SourceInfoWhiteboxTransform.thatArg + + def do_rotateLeft(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = width match { + case _ if (n == 0) => this + case KnownWidth(w) if (w <= 1) => this + case KnownWidth(w) if n >= w => do_rotateLeft(n % w) + case _ if (n < 0) => do_rotateRight(-n) + case _ => tail(n) ## head(n) + } + + /** + * Circular shift to the right + * @param that number of bits to rotate + * @return UInt of same width rotated right n bits + */ + final def rotateRight(that: Int): UInt = macro SourceInfoWhiteboxTransform.thatArg + + def do_rotateRight(n: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = width match { + case _ if (n <= 0) => do_rotateLeft(-n) + case KnownWidth(w) if (w <= 1) => this + case KnownWidth(w) if n >= w => do_rotateRight(n % w) + case _ => this(n - 1, 0) ## (this >> n) + } + + final def rotateRight(that: UInt): UInt = macro SourceInfoWhiteboxTransform.thatArg + + private def dynamicShift(n: UInt, staticShift: (UInt,Int) => UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : UInt = + n.asBools().zipWithIndex.foldLeft(this){ + case (in, (en, sh)) => Mux(en, staticShift(in, 1 << sh), in) + } + + def do_rotateRight(n: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + dynamicShift(n, _ rotateRight _) + + final def rotateLeft(that: UInt): UInt = macro SourceInfoWhiteboxTransform.thatArg + + def do_rotateLeft(n: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt = + dynamicShift(n, _ rotateLeft _) + + /** Conditionally set or clear a bit * * @param off a dynamic offset @@ -639,7 +710,11 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U * @return an [[SInt]] equal to this $coll with an additional zero in its most significant bit * @note The width of the returned [[SInt]] is `width of this` + `1`. */ - final def zext(): SInt = macro SourceInfoTransform.noArg + final def zext: SInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def zext(dummy: Int*): SInt = macro SourceInfoTransform.noArgDummy + /** @group SourceInfoTransformMacro */ def do_zext(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = pushOp(DefPrim(sourceInfo, SInt(width + 1), ConvertOp, ref)) @@ -697,11 +772,10 @@ sealed class UInt private[chisel3] (width: Width) extends Bits(width) with Num[U */ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[SInt] { override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString + litOption match { + case Some(value) => s"SInt$width($value)" + case _ => stringAccessor(s"SInt$width") } - s"SInt$width$bindingString" } private[chisel3] override def typeEquivalent(that: Data): Boolean = @@ -716,7 +790,10 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S * $constantWidth * @group Arithmetic */ - final def unary_- (): SInt = macro SourceInfoTransform.noArg + final def unary_- : SInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-(dummy: Int*): SInt = macro SourceInfoTransform.noArgDummy /** Unary negation (constant width) * @@ -724,12 +801,15 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S * $constantWidth * @group Arithmetic */ - final def unary_-% (): SInt = macro SourceInfoTransform.noArg + final def unary_-% : SInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-%(dummy: Int*): SInt = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ - def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S - this + def do_unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S - this /** @group SourceInfoTransformMacro */ - def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S -% this + def do_unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = 0.S -% this /** add (default - no growth) operator */ override def do_+ (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = @@ -755,7 +835,7 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S final def * (that: UInt): SInt = macro SourceInfoTransform.thatArg /** @group SourceInfoTransformMacro */ def do_* (that: UInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { - val thatToSInt = that.zext() + val thatToSInt = that.zext val result = binop(sourceInfo, SInt(this.width + thatToSInt.width), TimesOp, thatToSInt) result.tail(1).asSInt } @@ -876,10 +956,10 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S /** @group SourceInfoTransformMacro */ def do_=== (that: SInt)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) -// final def abs(): UInt = macro SourceInfoTransform.noArg +// final def abs(): UInt = macro SourceInfoTransform.noArgDummy def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = { - Mux(this < 0.S, (-this), this) + Mux(this < 0.S, -this, this) } override def do_<< (that: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): SInt = @@ -938,7 +1018,10 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S sealed trait Reset extends Element with ToBoolable { /** Casts this $coll to an [[AsyncReset]] */ - final def asAsyncReset(): AsyncReset = macro SourceInfoWhiteboxTransform.noArg + final def asAsyncReset: AsyncReset = macro SourceInfoWhiteboxTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asAsyncReset(dummy: Int*): AsyncReset = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asAsyncReset(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): AsyncReset @@ -954,7 +1037,7 @@ object Reset { * super type due to Bool inheriting from abstract class UInt */ final class ResetType(private[chisel3] val width: Width = Width(1)) extends Element with Reset { - override def toString: String = s"Reset$bindingToString" + override def toString: String = stringAccessor("Reset") def cloneType: this.type = Reset().asInstanceOf[this.type] @@ -996,7 +1079,7 @@ object AsyncReset { * asychronously reset registers. */ sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends Element with Reset { - override def toString: String = s"AsyncReset$bindingToString" + override def toString: String = stringAccessor("AsyncReset") def cloneType: this.type = AsyncReset().asInstanceOf[this.type] @@ -1036,11 +1119,10 @@ sealed class AsyncReset(private[chisel3] val width: Width = Width(1)) extends El */ sealed class Bool() extends UInt(1.W) with Reset { override def toString: String = { - val bindingString = litToBooleanOption match { - case Some(value) => s"($value)" - case _ => bindingToString + litToBooleanOption match { + case Some(value) => s"Bool($value)" + case _ => stringAccessor("Bool") } - s"Bool$bindingString" } private[chisel3] override def cloneTypeWidth(w: Width): this.type = { @@ -1124,7 +1206,10 @@ sealed class Bool() extends UInt(1.W) with Reset { def do_&& (that: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this & that /** Reinterprets this $coll as a clock */ - def asClock(): Clock = macro SourceInfoTransform.noArg + def asClock: Clock = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def asClock(dummy: Int*): Clock = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asClock(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Clock = pushOp(DefPrim(sourceInfo, Clock(), AsClockOp, ref)) @@ -1194,11 +1279,10 @@ package experimental { extends Bits(width) with Num[FixedPoint] with HasBinaryPoint { override def toString: String = { - val bindingString = litToDoubleOption match { - case Some(value) => s"($value)" - case _ => bindingToString + litToDoubleOption match { + case Some(value) => s"FixedPoint$width$binaryPoint($value)" + case _ => stringAccessor(s"FixedPoint$width$binaryPoint") } - s"FixedPoint$width$binaryPoint$bindingString" } private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { @@ -1220,7 +1304,10 @@ package experimental { * $expandingWidth * @group Arithmetic */ - final def unary_- (): FixedPoint = macro SourceInfoTransform.noArg + final def unary_- : FixedPoint = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-(dummy: Int*): FixedPoint = macro SourceInfoTransform.noArgDummy /** Unary negation (constant width) * @@ -1228,12 +1315,14 @@ package experimental { * $constantWidth * @group Arithmetic */ - final def unary_-% (): FixedPoint = macro SourceInfoTransform.noArg + final def unary_-% : FixedPoint = macro SourceInfoTransform.noArg + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-%(dummy: Int*): FixedPoint = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ - def unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) - this + def do_unary_- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) - this /** @group SourceInfoTransformMacro */ - def unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) -% this + def do_unary_-% (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = FixedPoint.fromBigInt(0) -% this /** add (default - no growth) operator */ override def do_+ (that: FixedPoint)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): FixedPoint = @@ -1609,11 +1698,10 @@ package experimental { extends Bits(range.getWidth) with Num[Interval] with HasBinaryPoint { override def toString: String = { - val bindingString = litOption match { - case Some(value) => s"($value)" - case _ => bindingToString + litOption match { + case Some(value) => s"Interval$width($value)" + case _ => stringAccessor(s"Interval$width") } - s"Interval$width$bindingString" } private[chisel3] override def cloneTypeWidth(w: Width): this.type = @@ -1663,13 +1751,21 @@ package experimental { } } - final def unary_-(): Interval = macro SourceInfoTransform.noArg - final def unary_-%(): Interval = macro SourceInfoTransform.noArg + final def unary_- : Interval = macro SourceInfoTransform.noArg - def unary_-(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-(dummy: Int*): Interval = macro SourceInfoTransform.noArgDummy + + final def unary_-% : Interval = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def unary_-%(dummy: Int*): Interval = macro SourceInfoTransform.noArgDummy + + /** @group SourceInfoTransformMacro */ + def do_unary_-(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { Interval.Zero - this } - def unary_-%(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { + def do_unary_-%(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { Interval.Zero -% this } @@ -1779,7 +1875,7 @@ package experimental { def do_=/= (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, NotEqualOp, that) def do_=== (that: Interval)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = compop(sourceInfo, EqualOp, that) - // final def abs(): UInt = macro SourceInfoTransform.noArg + // final def abs(): UInt = macro SourceInfoTransform.noArgDummy def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Interval = { Mux(this < Interval.Zero, (Interval.Zero - this), this) diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala index 38b08193..ec5de0cd 100644 --- a/core/src/main/scala/chisel3/BlackBox.scala +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -8,6 +8,7 @@ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ import chisel3.internal.throwException import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo} +import scala.annotation.nowarn package internal { @@ -61,6 +62,7 @@ package experimental { * }}} * @note The parameters API is experimental and may change */ + @nowarn("msg=class Port") // delete when Port becomes private abstract class ExtModule(val params: Map[String, Param] = Map.empty[String, Param]) extends BaseBlackBox { private[chisel3] override def generateComponent(): Option[Component] = { require(!_closed, "Can't generate module more than once") @@ -134,6 +136,7 @@ package experimental { * }}} * @note The parameters API is experimental and may change */ +@nowarn("msg=class Port") // delete when Port becomes private abstract class BlackBox(val params: Map[String, Param] = Map.empty[String, Param])(implicit compileOptions: CompileOptions) extends BaseBlackBox { // Find a Record port named "io" for purposes of stripping the prefix diff --git a/core/src/main/scala/chisel3/Clock.scala b/core/src/main/scala/chisel3/Clock.scala index edb07908..e4be6558 100644 --- a/core/src/main/scala/chisel3/Clock.scala +++ b/core/src/main/scala/chisel3/Clock.scala @@ -14,7 +14,7 @@ object Clock { // TODO: Document this. sealed class Clock(private[chisel3] val width: Width = Width(1)) extends Element { - override def toString: String = s"Clock$bindingToString" + override def toString: String = stringAccessor("Clock") def cloneType: this.type = Clock().asInstanceOf[this.type] @@ -32,8 +32,12 @@ sealed class Clock(private[chisel3] val width: Width = Width(1)) extends Element def toPrintable: Printable = PString("CLOCK") /** Returns the contents of the clock wire as a [[Bool]]. */ - final def asBool(): Bool = macro SourceInfoTransform.noArg - def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this.asUInt().asBool() + final def asBool: Bool = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asBool(dummy: Int*): Bool = macro SourceInfoTransform.noArgDummy + + def do_asBool(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = this.asUInt.asBool override def do_asUInt(implicit sourceInfo: SourceInfo, connectCompileOptions: CompileOptions): UInt = pushOp(DefPrim(sourceInfo, UInt(this.width), AsUIntOp, ref)) private[chisel3] override def connectFromBits(that: Bits)(implicit sourceInfo: SourceInfo, diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 32d83008..2bca5f98 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -154,13 +154,58 @@ package experimental { **/ def checkTypeEquivalence(x: Data, y: Data): Boolean = x.typeEquivalent(y) - // Returns the top-level module ports - // TODO: maybe move to something like Driver or DriverUtils, since this is mainly for interacting - // with compiled artifacts (vs. elaboration-time reflection)? + /** Returns the ports of a module + * {{{ + * class MyModule extends Module { + * val io = IO(new Bundle { + * val in = Input(UInt(8.W)) + * val out = Output(Vec(2, UInt(8.W))) + * }) + * val extra = IO(Input(UInt(8.W))) + * val delay = RegNext(io.in) + * io.out(0) := delay + * io.out(1) := delay + extra + * } + * val mod = Module(new MyModule) + * DataMirror.modulePorts(mod) + * // returns: Seq( + * // "clock" -> mod.clock, + * // "reset" -> mod.reset, + * // "io" -> mod.io, + * // "extra" -> mod.extra + * // ) + * }}} + */ def modulePorts(target: BaseModule): Seq[(String, Data)] = target.getChiselPorts - /** Returns all module ports with underscore-qualified names - * return includes [[Module.clock]] and [[Module.reset]] + /** Returns a recursive representation of a module's ports with underscore-qualified names + * {{{ + * class MyModule extends Module { + * val io = IO(new Bundle { + * val in = Input(UInt(8.W)) + * val out = Output(Vec(2, UInt(8.W))) + * }) + * val extra = IO(Input(UInt(8.W))) + * val delay = RegNext(io.in) + * io.out(0) := delay + * io.out(1) := delay + extra + * } + * val mod = Module(new MyModule) + * DataMirror.fullModulePorts(mod) + * // returns: Seq( + * // "clock" -> mod.clock, + * // "reset" -> mod.reset, + * // "io" -> mod.io, + * // "io_out" -> mod.io.out, + * // "io_out_0" -> mod.io.out(0), + * // "io_out_1" -> mod.io.out(1), + * // "io_in" -> mod.io.in, + * // "extra" -> mod.extra + * // ) + * }}} + * @note The returned ports are redundant. An [[Aggregate]] port will be present along with all + * of its children. + * @see [[DataMirror.modulePorts]] for a non-recursive representation of the ports. */ def fullModulePorts(target: BaseModule): Seq[(String, Data)] = { def getPortNames(name: String, data: Data): Seq[(String, Data)] = Seq(name -> data) ++ (data match { @@ -435,27 +480,44 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { _direction = Some(actualDirection) } + private[chisel3] def stringAccessor(chiselType: String): String = { + topBindingOpt match { + case None => chiselType + // Handle DontCares specially as they are "literal-like" but not actually literals + case Some(DontCareBinding()) => s"$chiselType(DontCare)" + case Some(topBinding) => + val binding: String = _bindingToString(topBinding) + val name = earlyName + val mod = parentNameOpt.map(_ + ".").getOrElse("") + + s"$mod$name: $binding[$chiselType]" + } + } + // User-friendly representation of the binding as a helper function for toString. // Provides a unhelpful fallback for literals, which should have custom rendering per // Data-subtype. // TODO Is this okay for sample_element? It *shouldn't* be visible to users - protected def bindingToString: String = Try(topBindingOpt match { - case None => "" - case Some(OpBinding(enclosure, _)) => s"(OpResult in ${enclosure.desiredName})" - case Some(MemoryPortBinding(enclosure, _)) => s"(MemPort in ${enclosure.desiredName})" - case Some(PortBinding(enclosure)) if !enclosure.isClosed => s"(IO in unelaborated ${enclosure.desiredName})" - case Some(PortBinding(enclosure)) if enclosure.isClosed => - DataMirror.fullModulePorts(enclosure).find(_._2 eq this) match { - case Some((name, _)) => s"(IO $name in ${enclosure.desiredName})" - case None => s"(IO (unknown) in ${enclosure.desiredName})" - } - case Some(RegBinding(enclosure, _)) => s"(Reg in ${enclosure.desiredName})" - case Some(WireBinding(enclosure, _)) => s"(Wire in ${enclosure.desiredName})" - case Some(DontCareBinding()) => s"(DontCare)" - case Some(ElementLitBinding(litArg)) => s"(unhandled literal)" - case Some(BundleLitBinding(litMap)) => s"(unhandled bundle literal)" - case Some(VecLitBinding(litMap)) => s"(unhandled vec literal)" - }).getOrElse("") + @deprecated("This was never intended to be visible to user-defined types", "Chisel 3.5.0") + protected def bindingToString: String = _bindingToString(topBinding) + + private[chisel3] def _bindingToString(topBindingOpt: TopBinding): String = + topBindingOpt match { + case OpBinding(_, _) => "OpResult" + case MemoryPortBinding(_, _) => "MemPort" + case PortBinding(_) => "IO" + case RegBinding(_, _) => "Reg" + case WireBinding(_, _) => "Wire" + case DontCareBinding() => "(DontCare)" + case ElementLitBinding(litArg) => "(unhandled literal)" + case BundleLitBinding(litMap) => "(unhandled bundle literal)" + case VecLitBinding(litMap) => "(unhandled vec literal)" + case _ => "" + } + + private[chisel3] def earlyName: String = Arg.earlyLocalName(this) + + private[chisel3] def parentNameOpt: Option[String] = this._parent.map(_.name) // Return ALL elements at root of this type. // Contasts with flatten, which returns just Bits @@ -477,7 +539,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { } catch { case MonoConnectException(message) => throwException( - s"Connection between sink ($this) and source ($that) failed @$message" + s"Connection between sink ($this) and source ($that) failed @: $message" ) } } else { @@ -645,18 +707,28 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { } } - def isLit(): Boolean = litOption.isDefined + def isLit: Boolean = litOption.isDefined + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def isLit(dummy: Int*): Boolean = isLit + /** * If this is a literal that is representable as bits, returns the value as a BigInt. * If not a literal, or not representable as bits (for example, is or contains Analog), returns None. */ - def litOption(): Option[BigInt] + def litOption: Option[BigInt] + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def litOption(dummy: Int*): Option[BigInt] = litOption /** * Returns the literal value if this is a literal that is representable as bits, otherwise crashes. */ - def litValue(): BigInt = litOption.get + def litValue: BigInt = litOption.get + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def litValue(dummy: Int*): BigInt = litValue /** Returns the width, in bits, if currently known. */ final def getWidth: Int = @@ -679,7 +751,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { /** @group SourceInfoTransformMacro */ def do_asTypeOf[T <: Data](that: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T = { val thatCloned = Wire(that.cloneTypeFull) - thatCloned.connectFromBits(this.asUInt()) + thatCloned.connectFromBits(this.asUInt) thatCloned } @@ -695,7 +767,10 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { * @note Aggregates are recursively packed with the first element appearing * in the least-significant bits of the result. */ - final def asUInt(): UInt = macro SourceInfoTransform.noArg + final def asUInt: UInt = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def asUInt(dummy: Int*): UInt = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_asUInt(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): UInt @@ -715,7 +790,7 @@ trait WireFactory { val x = t.cloneTypeFull // Bind each element of x to being a Wire - x.bind(WireBinding(Builder.forcedUserModule, Builder.currentWhen())) + x.bind(WireBinding(Builder.forcedUserModule, Builder.currentWhen)) pushCommand(DefWire(sourceInfo, x)) if (!compileOptions.explicitInvalidate) { diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala index 183620b6..aeacf052 100644 --- a/core/src/main/scala/chisel3/Mem.scala +++ b/core/src/main/scala/chisel3/Mem.scala @@ -130,7 +130,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) extends H t.cloneTypeFull, Node(this), dir, i.ref, Builder.forcedClock.ref) ).id // Bind each element of port to being a MemoryPort - port.bind(MemoryPortBinding(Builder.forcedUserModule, Builder.currentWhen())) + port.bind(MemoryPortBinding(Builder.forcedUserModule, Builder.currentWhen)) port } } diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 3ae48821..7ba24585 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -39,7 +39,7 @@ object Module extends SourceInfoDoc { // Save then clear clock and reset to prevent leaking scope, must be set again in the Module val (saveClock, saveReset) = (Builder.currentClock, Builder.currentReset) - val savePrefix = Builder.getPrefix() + val savePrefix = Builder.getPrefix Builder.clearPrefix() Builder.currentClock = None Builder.currentReset = None @@ -88,6 +88,16 @@ object Module extends SourceInfoDoc { def reset: Reset = Builder.forcedReset /** Returns the current Module */ def currentModule: Option[BaseModule] = Builder.currentModule + + private[chisel3] def do_pseudo_apply[T <: BaseModule](bc: => T) + (implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions): T = { + val parent = Builder.currentModule + + val module: T = bc // bc is actually evaluated here + + module + } } /** Abstract base class for Modules, which behave much like Verilog modules. @@ -182,23 +192,33 @@ package experimental { package internal { import chisel3.experimental.BaseModule - import chisel3.experimental.hierarchy.IsInstantiable + import chisel3.experimental.hierarchy.{IsInstantiable, Proto, Clone} object BaseModule { /** Represents a clone of an underlying object. This is used to support CloneModuleAsRecord and Instance/Definition. * * @note We don't actually "clone" anything in the traditional sense but is a placeholder so we lazily clone internal state */ - private [chisel3] trait IsClone[+T] { + trait IsClone[+T] { // Underlying object of which this is a clone of - val _proto: T - def getProto: T = _proto - def isACloneOf(a: Any): Boolean = this == a || _proto == a + private[chisel3] def getProto: T + + /** Determines whether another object is a clone of the same underlying proto + * + * @param a + */ + def hasSameProto(a: Any): Boolean = { + val aProto = a match { + case x: IsClone[BaseModule] => x.getProto + case o => o + } + this == aProto || getProto == aProto + } } // Private internal class to serve as a _parent for Data in cloned ports - private[chisel3] class ModuleClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] { - override def toString = s"ModuleClone(${_proto})" + private[chisel3] class ModuleClone[T <: BaseModule] (val getProto: T) extends PseudoModule with IsClone[T] { + override def toString = s"ModuleClone(${getProto})" def getPorts = _portsRecord // ClonePorts that hold the bound ports for this module // Used for setting the refs of both this module and the Record @@ -211,19 +231,19 @@ package internal { private[chisel3] def generateComponent(): Option[Component] = { require(!_closed, "Can't generate module more than once") _closed = true - _component = _proto._component + _component = getProto._component None } // Maps proto ports to module clone's ports private[chisel3] lazy val ioMap: Map[Data, Data] = { val name2Port = getPorts.elements - _proto.getChiselPorts.map { case (name, data) => data -> name2Port(name) }.toMap + getProto.getChiselPorts.map { case (name, data) => data -> name2Port(name) }.toMap } // This module doesn't actually exist in the FIRRTL so no initialization to do private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = () // Name of this instance's module is the same as the proto's name - override def desiredName: String = _proto.name + override def desiredName: String = getProto.name private[chisel3] def setRefAndPortsRef(namespace: Namespace): Unit = { val record = _portsRecord @@ -235,7 +255,7 @@ package internal { 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 - record.setRef(ModuleCloneIO(_proto, instName), force=true) // force because we did .forceName first + record.setRef(ModuleCloneIO(getProto, instName), force=true) // force because we did .forceName first this.setRef(Ref(instName)) } } @@ -249,8 +269,8 @@ package internal { * @note In addition, the instance name of an InstanceClone is going to be the SAME as the proto, but this is not true * for ModuleClone. */ - private[chisel3] final class InstanceClone[T <: BaseModule] (val _proto: T, val instName: () => String) extends PseudoModule with IsClone[T] { - override def toString = s"InstanceClone(${_proto})" + private[chisel3] final class InstanceClone[T <: BaseModule] (val getProto: T, val instName: () => String) extends PseudoModule with IsClone[T] { + override def toString = s"InstanceClone(${getProto})" // No addition components are generated private[chisel3] def generateComponent(): Option[Component] = None // Necessary for toTarget to work @@ -260,7 +280,7 @@ package internal { // Instance name is the same as proto's instance name override def instanceName = instName() // Module name is the same as proto's module name - override def desiredName: String = _proto.name + override def desiredName: String = getProto.name } /** Represents a Definition root module, when accessing something from a definition @@ -271,20 +291,21 @@ package internal { * target whose root is the Definition. This DefinitionClone is used to represent the root parent of the * InstanceClone (which represents the returned module). */ - private[chisel3] class DefinitionClone[T <: BaseModule] (val _proto: T) extends PseudoModule with IsClone[T] { - override def toString = s"DefinitionClone(${_proto})" + private[chisel3] class DefinitionClone[T <: BaseModule] (val getProto: T) extends PseudoModule with IsClone[T] { + override def toString = s"DefinitionClone(${getProto})" // No addition components are generated private[chisel3] def generateComponent(): Option[Component] = None // Necessary for toTarget to work private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = () // Module name is the same as proto's module name - override def desiredName: String = _proto.name + override def desiredName: String = getProto.name } /** @note If we are cloning a non-module, we need another object which has the proper _parent set! */ - private[chisel3] final class InstantiableClone[T <: IsInstantiable] (val _proto: T) extends IsClone[T] { - private[chisel3] var _parent: Option[BaseModule] = internal.Builder.currentModule + trait InstantiableClone[T <: IsInstantiable] extends IsClone[T] { + private[chisel3] def _innerContext: experimental.hierarchy.Hierarchy[_] + private[chisel3] def getInnerContext: Option[BaseModule] = _innerContext.getInnerDataContext } /** Record type returned by CloneModuleAsRecord @@ -328,13 +349,13 @@ package internal { package experimental { - import chisel3.experimental.hierarchy.IsInstantiable + import chisel3.experimental.hierarchy.{IsInstantiable, Proto} object BaseModule { implicit class BaseModuleExtensions[T <: BaseModule](b: T) { import chisel3.experimental.hierarchy.{Instance, Definition} - def toInstance: Instance[T] = new Instance(Left(b)) - def toDefinition: Definition[T] = new Definition(Left(b)) + def toInstance: Instance[T] = new Instance(Proto(b)) + def toDefinition: Definition[T] = new Definition(Proto(b)) } } /** Abstract base class for Modules, an instantiable organizational unit for RTL. @@ -346,13 +367,19 @@ package experimental { // // Builder Internals - this tracks which Module RTL construction belongs to. // - if (!Builder.readyForModuleConstr) { - throwException("Error: attempted to instantiate a Module without wrapping it in Module().") + this match { + case _: PseudoModule => + case other => + if (!Builder.readyForModuleConstr) { + throwException("Error: attempted to instantiate a Module without wrapping it in Module().") + } } - readyForModuleConstr = false + if (Builder.hasDynamicContext) { + readyForModuleConstr = false - Builder.currentModule = Some(this) - Builder.whenStack = Nil + Builder.currentModule = Some(this) + Builder.whenStack = Nil + } // // Module Construction Internals diff --git a/core/src/main/scala/chisel3/Num.scala b/core/src/main/scala/chisel3/Num.scala index 6dd299f4..219e18f4 100644 --- a/core/src/main/scala/chisel3/Num.scala +++ b/core/src/main/scala/chisel3/Num.scala @@ -148,7 +148,10 @@ trait Num[T <: Data] { * $unchangedWidth * @group Arithmetic */ - final def abs(): T = macro SourceInfoTransform.noArg + final def abs: T = macro SourceInfoTransform.noArg + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + final def abs(dummy: Int*): T = macro SourceInfoTransform.noArgDummy /** @group SourceInfoTransformMacro */ def do_abs(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): T diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala index cf7821b8..be0146bb 100644 --- a/core/src/main/scala/chisel3/Printf.scala +++ b/core/src/main/scala/chisel3/Printf.scala @@ -6,7 +6,6 @@ import scala.language.experimental.macros import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.sourceinfo.SourceInfo -import chisel3.experimental.BaseSim /** Prints a message in simulation * @@ -34,7 +33,7 @@ object printf { } /** Named class for [[printf]]s. */ - final class Printf(val pable: Printable) extends BaseSim + final class Printf private[chisel3](val pable: Printable) extends VerificationStatement /** Prints a message in simulation * diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index 27f16ad4..e977d918 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -5,7 +5,8 @@ package chisel3 import scala.collection.mutable.{ArrayBuffer, HashMap} import scala.util.Try import scala.language.experimental.macros -import chisel3.experimental.{BaseModule, BaseSim} +import scala.annotation.nowarn +import chisel3.experimental.BaseModule import chisel3.internal._ import chisel3.internal.BaseModule.{ModuleClone, InstanceClone} import chisel3.internal.Builder._ @@ -17,6 +18,7 @@ import _root_.firrtl.annotations.{IsModule, ModuleTarget} * This abstract base class is a user-defined module which does not include implicit clock and reset and supports * multiple IO() declarations. */ +@nowarn("msg=class Port") // delete when Port becomes private abstract class RawModule(implicit moduleCompileOptions: CompileOptions) extends BaseModule { // @@ -35,16 +37,16 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) // // Other Internal Functions // - // For debuggers/testers, TODO: refactor out into proper public API private var _firrtlPorts: Option[Seq[firrtl.Port]] = None - @deprecated("Use DataMirror.fullModulePorts instead. this API will be removed in Chisel 3.6", "Chisel 3.5") - lazy val getPorts = _firrtlPorts.get + + @deprecated("Use DataMirror.modulePorts instead. this API will be removed in Chisel 3.6", "Chisel 3.5") + lazy val getPorts: Seq[Port] = _firrtlPorts.get val compileOptions = moduleCompileOptions private[chisel3] def namePorts(names: HashMap[HasId, String]): Unit = { for (port <- getModulePorts) { - port.computeName(None, None).orElse(names.get(port)) match { + port._computeName(None, 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,""" + @@ -81,7 +83,11 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) case id: InstanceClone[_] => id.setAsInstanceRef() case id: BaseModule => id.forceName(None, default=id.desiredName, _namespace) case id: MemBase[_] => id.forceName(None, default="MEM", _namespace) - case id: BaseSim => id.forceName(None, default="SIM", _namespace) + case id: stop.Stop => id.forceName(None, default="stop", _namespace) + case id: assert.Assert => id.forceName(None, default="assert", _namespace) + case id: assume.Assume => id.forceName(None, default="assume", _namespace) + case id: cover.Cover => id.forceName(None, default="cover", _namespace) + case id: printf.Printf => id.forceName(None, default="printf", _namespace) case id: Data => if (id.isSynthesizable) { id.topBinding match { @@ -195,7 +201,7 @@ package object internal { tryJavaReflect .orElse(tryScalaReflect) - .map(_.autoSeed("io")) + .map(_.forceFinalName("io")) .orElse { // Fallback if reflection fails, user can wrap in IO(...) self.findPort("io") diff --git a/core/src/main/scala/chisel3/Reg.scala b/core/src/main/scala/chisel3/Reg.scala index bd9e5311..122c5ebd 100644 --- a/core/src/main/scala/chisel3/Reg.scala +++ b/core/src/main/scala/chisel3/Reg.scala @@ -41,7 +41,7 @@ object Reg { val reg = t.cloneTypeFull val clock = Node(Builder.forcedClock) - reg.bind(RegBinding(Builder.forcedUserModule, Builder.currentWhen())) + reg.bind(RegBinding(Builder.forcedUserModule, Builder.currentWhen)) pushCommand(DefReg(sourceInfo, reg, clock)) reg } @@ -174,7 +174,7 @@ object RegInit { val clock = Builder.forcedClock val reset = Builder.forcedReset - reg.bind(RegBinding(Builder.forcedUserModule, Builder.currentWhen())) + reg.bind(RegBinding(Builder.forcedUserModule, Builder.currentWhen)) requireIsHardware(init, "reg initializer") pushCommand(DefRegInit(sourceInfo, reg, clock.ref, reset.ref, init.ref)) reg diff --git a/core/src/main/scala/chisel3/SeqUtils.scala b/core/src/main/scala/chisel3/SeqUtils.scala index da6fc802..5c86efd3 100644 --- a/core/src/main/scala/chisel3/SeqUtils.scala +++ b/core/src/main/scala/chisel3/SeqUtils.scala @@ -81,7 +81,7 @@ private[chisel3] object SeqUtils { val output = cloneSupertype(in.toSeq map { _._2}, "oneHotMux") def buildAndOrMultiplexor[TT <: Data](inputs: Iterable[(Bool, TT)]): T = { - val masked = for ((s, i) <- inputs) yield Mux(s, i.asUInt(), 0.U) + val masked = for ((s, i) <- inputs) yield Mux(s, i.asUInt, 0.U) masked.reduceLeft(_ | _).asTypeOf(output) } diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala index b3d7cf7d..fa420e80 100644 --- a/core/src/main/scala/chisel3/StrongEnum.scala +++ b/core/src/main/scala/chisel3/StrongEnum.scala @@ -72,17 +72,18 @@ import EnumAnnotations._ abstract class EnumType(private val factory: EnumFactory, selfAnnotating: Boolean = true) extends Element { + + // Use getSimpleName instead of enumTypeName because for debugging purposes + // the fully qualified name isn't necessary (compared to for the + // Enum annotation), and it's more consistent with Bundle printing. override def toString: String = { - val bindingString = litOption match { + litOption match { case Some(value) => factory.nameOfValue(value) match { - case Some(name) => s"($value=$name)" - case None => s"($value=(invalid))" + case Some(name) => s"${factory.getClass.getSimpleName.init}($value=$name)" + case None => stringAccessor(s"${factory.getClass.getSimpleName.init}($value=(invalid))") } - case _ => bindingToString + case _ => stringAccessor(s"${factory.getClass.getSimpleName.init}") } - // Use getSimpleName instead of enumTypeName because for debugging purposes the fully qualified name isn't - // necessary (compared to for the Enum annotation), and it's more consistent with Bundle printing. - s"${factory.getClass.getSimpleName.init}$bindingString" } override def cloneType: this.type = factory().asInstanceOf[this.type] @@ -246,7 +247,7 @@ abstract class EnumFactory { private val enumRecords = mutable.ArrayBuffer.empty[EnumRecord] private def enumNames = enumRecords.map(_.name).toSeq - private def enumValues = enumRecords.map(_.inst.litValue()).toSeq + private def enumValues = enumRecords.map(_.inst.litValue).toSeq private def enumInstances = enumRecords.map(_.inst).toSeq private[chisel3] val enumTypeName = getClass.getName.init @@ -265,7 +266,7 @@ abstract class EnumFactory { def all: Seq[Type] = enumInstances private[chisel3] def nameOfValue(id: BigInt): Option[String] = { - enumRecords.find(_.inst.litValue() == id).map(_.name) + enumRecords.find(_.inst.litValue == id).map(_.name) } protected def Value: Type = macro EnumMacros.ValImpl @@ -291,11 +292,11 @@ abstract class EnumFactory { if (id.litOption.isEmpty) { throwException(s"$enumTypeName defined with a non-literal type") } - if (id.litValue() < this.id) { + if (id.litValue < this.id) { throwException(s"Enums must be strictly increasing: $enumTypeName") } - this.id = id.litValue() + this.id = id.litValue do_Value(name) } diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala new file mode 100644 index 00000000..23adc192 --- /dev/null +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -0,0 +1,236 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3 + +import scala.reflect.macros.blackbox.Context +import scala.language.experimental.macros +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +import scala.reflect.macros.blackbox + +object assert { + /** Checks for a condition to be valid in the circuit at all times. If the + * condition evaluates to false, the circuit simulation stops with an error. + * + * Does not fire when in reset (defined as the encapsulating Module's + * reset). If your definition of reset is not the encapsulating Module's + * reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), so + * functions using assert make the standard Module assumptions (single clock + * and single reset). + * + * @param cond condition, assertion fires (simulation fails) when false + * @param message optional format string to print when the assertion fires + * @param data optional bits to print in the message formatting + * + * @note See [[printf.apply(fmt:String* printf]] for format string documentation + * @note currently cannot be used in core Chisel / libraries because macro + * defs need to be compiled first and the SBT project is not set up to do + * that + */ + // Macros currently can't take default arguments, so we need two functions to emulate defaults. + def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = macro _applyMacroWithMessage + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = macro _applyMacroWithNoMessage + + /** An elaboration-time assertion. Calls the built-in Scala assert function. */ + def apply(cond: Boolean, message: => String): Unit = Predef.assert(cond, message) + /** An elaboration-time assertion. Calls the built-in Scala assert function. */ + def apply(cond: Boolean): Unit = Predef.assert(cond, "") + + /** Named class for assertions. */ + final class Assert private[chisel3]() extends VerificationStatement + + import VerificationStatement._ + + def _applyMacroWithMessage(c: blackbox.Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)" + } + + def _applyMacroWithNoMessage(c: blackbox.Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" + } + + /** Used by our macros. Do not call directly! */ + def _applyWithSourceLine(cond: Bool, line: SourceLineInfo, message: Option[String], data: Bits*) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = { + val id = new Assert() + when(!Module.reset.asBool()) { + Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) + failureMessage("Assertion", line, cond, message, data) + } + id + } +} + + +object assume { + /** Assumes a condition to be valid in the circuit at all times. + * Acts like an assertion in simulation and imposes a declarative + * assumption on the state explored by formal tools. + * + * Does not fire when in reset (defined as the encapsulating Module's + * reset). If your definition of reset is not the encapsulating Module's + * reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), so + * functions using assert make the standard Module assumptions (single clock + * and single reset). + * + * @param cond condition, assertion fires (simulation fails) when false + * @param message optional format string to print when the assertion fires + * @param data optional bits to print in the message formatting + * + * @note See [[printf.apply(fmt:String* printf]] for format string documentation + */ + // Macros currently can't take default arguments, so we need two functions to emulate defaults. + def apply(cond: Bool, message: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume = macro _applyMacroWithMessage + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume = macro _applyMacroWithNoMessage + + /** An elaboration-time assumption. Calls the built-in Scala assume function. */ + def apply(cond: Boolean, message: => String): Unit = Predef.assume(cond, message) + /** An elaboration-time assumption. Calls the built-in Scala assume function. */ + def apply(cond: Boolean): Unit = Predef.assume(cond, "") + + /** Named class for assumptions. */ + final class Assume private[chisel3]() extends VerificationStatement + + import VerificationStatement._ + + def _applyMacroWithMessage(c: blackbox.Context)(cond: c.Tree, message: c.Tree, data: c.Tree*)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message), ..$data)($sourceInfo, $compileOptions)" + } + + def _applyMacroWithNoMessage(c: blackbox.Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" + } + + /** Used by our macros. Do not call directly! */ + def _applyWithSourceLine(cond: Bool, line: SourceLineInfo, message: Option[String], data: Bits*) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume = { + val id = new Assume() + when(!Module.reset.asBool()) { + Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) + failureMessage("Assumption", line, cond, message, data) + } + id + } +} + + +object cover { + /** Declares a condition to be covered. + * At ever clock event, a counter is incremented iff the condition is active + * and reset is inactive. + * + * Does not fire when in reset (defined as the encapsulating Module's + * reset). If your definition of reset is not the encapsulating Module's + * reset, you will need to gate this externally. + * + * May be called outside of a Module (like defined in a function), so + * functions using assert make the standard Module assumptions (single clock + * and single reset). + * + * @param cond condition that will be sampled on every clock tick + * @param message a string describing the cover event + */ + // Macros currently can't take default arguments, so we need two functions to emulate defaults. + def apply(cond: Bool, message: String)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Cover = macro _applyMacroWithMessage + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Cover = macro _applyMacroWithNoMessage + + /** Named class for cover statements. */ + final class Cover private[chisel3]() extends VerificationStatement + + import VerificationStatement._ + + def _applyMacroWithNoMessage(c: blackbox.Context)(cond: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" + } + + def _applyMacroWithMessage(c: blackbox.Context)(cond: c.Tree, message: c.Tree)(sourceInfo: c.Tree, compileOptions: c.Tree): c.Tree = { + import c.universe._ + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)" + } + + /** Used by our macros. Do not call directly! */ + def _applyWithSourceLine(cond: Bool, line: SourceLineInfo, message: Option[String]) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Cover = { + val id = new Cover() + when(!Module.reset.asBool()) { + Builder.pushCommand(Verification(id, Formal.Cover, sourceInfo, Module.clock.ref, cond.ref, "")) + } + id + } +} + +object stop { + /** Terminate execution, indicating success. + * + * @param message a string describing why the simulation was stopped + */ + def apply(message: String = "")(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Stop = { + val stp = new Stop() + when (!Module.reset.asBool) { + pushCommand(Stop(stp, sourceInfo, Builder.forcedClock.ref, 0)) + } + stp + } + + /** Terminate execution with a failure code. */ + @deprecated("Non-zero return codes are not well supported. Please use assert(false.B) if you want to indicate a failure.", "Chisel 3.5") + def apply(code: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Stop = { + val stp = new Stop() + when (!Module.reset.asBool) { + pushCommand(Stop(stp, sourceInfo, Builder.forcedClock.ref, code)) + } + stp + } + + /** Named class for [[stop]]s. */ + final class Stop private[chisel3]()extends VerificationStatement +} + +/** Base class for all verification statements: Assert, Assume, Cover, Stop and Printf. */ +abstract class VerificationStatement extends NamedComponent { + _parent.foreach(_.addId(this)) +} + +/** Helper functions for common functionality required by stop, assert, assume or cover */ +private object VerificationStatement { + + type SourceLineInfo = (String, Int, String) + + def getLine(c: blackbox.Context): SourceLineInfo = { + val p = c.enclosingPosition + (p.source.file.name, p.line, p.lineContent.trim) + } + + // creates a printf to inform the user of a failed assertion or assumption + def failureMessage(kind: String, lineInfo: SourceLineInfo, cond: Bool, message: Option[String], data: Seq[Bits]) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) : Unit = { + val (filename, line, content) = lineInfo + val lineMsg = s"$filename:$line $content".replaceAll("%", "%%") + val fmt = message match { + case Some(msg) => + s"$kind failed: $msg\n at $lineMsg\n" + case None => s"$kind failed\n at $lineMsg\n" + } + when(!cond) { + printf.printfWithoutReset(fmt, data:_*) + } + } +} diff --git a/core/src/main/scala/chisel3/When.scala b/core/src/main/scala/chisel3/When.scala index a2c20d9a..ca383c0f 100644 --- a/core/src/main/scala/chisel3/When.scala +++ b/core/src/main/scala/chisel3/When.scala @@ -50,7 +50,7 @@ object when { implicit val sourceInfo = UnlocatableSourceInfo val whens = Builder.whenStack whens.foldRight(true.B) { - case (ctx, acc) => acc && ctx.localCond() + case (ctx, acc) => acc && ctx.localCond } } } @@ -81,7 +81,7 @@ final class WhenContext private[chisel3] ( private var scopeOpen = false /** Returns the local condition, inverted for an otherwise */ - private[chisel3] def localCond(): Bool = { + private[chisel3] def localCond: Bool = { implicit val compileOptions = ExplicitCompileOptions.Strict implicit val sourceInfo = UnlocatableSourceInfo val alt = altConds.foldRight(true.B) { @@ -111,7 +111,10 @@ final class WhenContext private[chisel3] ( def otherwise(block: => Any)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Unit = new WhenContext(sourceInfo, None, block, firrtlDepth + 1, cond ++: altConds) - def active(): Boolean = scopeOpen + def active: Boolean = scopeOpen + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def active(dummy: Int*): Boolean = active /* * diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala index 6cca81f5..e94bae2d 100644 --- a/core/src/main/scala/chisel3/experimental/Analog.scala +++ b/core/src/main/scala/chisel3/experimental/Analog.scala @@ -27,9 +27,7 @@ import scala.collection.mutable final class Analog private (private[chisel3] val width: Width) extends Element { require(width.known, "Since Analog is only for use in BlackBoxes, width must be known") - override def toString: String = { - s"Analog$width$bindingToString" - } + override def toString: String = stringAccessor(s"Analog$width") private[chisel3] override def typeEquivalent(that: Data): Boolean = that.isInstanceOf[Analog] && this.width == that.width diff --git a/core/src/main/scala/chisel3/experimental/Trace.scala b/core/src/main/scala/chisel3/experimental/Trace.scala new file mode 100644 index 00000000..2d965c7b --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/Trace.scala @@ -0,0 +1,69 @@ +package chisel3.experimental + +import chisel3.internal.HasId +import chisel3.{Aggregate, Data, Element, Module} +import firrtl.AnnotationSeq +import firrtl.annotations.{Annotation, CompleteTarget, SingleTargetAnnotation} +import firrtl.transforms.DontTouchAllTargets + +/** The util that records the reference map from original [[Data]]/[[Module]] annotated in Chisel and final FIRRTL. + * @example + * {{{ + * class Dut extends Module { + * val a = WireDefault(Bool()) + * Trace.traceName(a) + * } + * val annos = (new ChiselStage).execute(Seq(ChiselGeneratorAnnotation(() => new Dut))) + * val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[CollideModule] + * // get final reference of `a` Seq(ReferenceTarget("Dut", "Dut", Seq.empty, "a", Seq.empty)) + * val firrtlReferenceOfDutA = finalTarget(annos)(dut.a) + * }}} + * */ +object Trace { + + /** Trace a Instance name. */ + def traceName(x: Module): Unit = { + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceNameAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget) + }) + } + + /** Trace a Data name. */ + def traceName(x: Data): Unit = { + x match { + case aggregate: Aggregate => + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceNameAnnotation(aggregate.toAbsoluteTarget, aggregate.toAbsoluteTarget) + }) + aggregate.getElements.foreach(traceName) + case element: Element => + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceNameAnnotation(element.toAbsoluteTarget, element.toAbsoluteTarget) + }) + } + } + + /** An Annotation that records the original target annotate from Chisel. + * + * @param target target that should be renamed by [[firrtl.RenameMap]] in the firrtl transforms. + * @param chiselTarget original annotated target in Chisel, which should not be changed or renamed in FIRRTL. + */ + private case class TraceNameAnnotation[T <: CompleteTarget](target: T, chiselTarget: T) + extends SingleTargetAnnotation[T] + with DontTouchAllTargets { + def duplicate(n: T): Annotation = this.copy(target = n) + } + + /** Get [[CompleteTarget]] of the target `x` for `annos`. + * This API can be used to find the final reference to a signal or module which is marked by `traceName` + */ + def finalTarget(annos: AnnotationSeq)(x: HasId): Seq[CompleteTarget] = finalTargetMap(annos) + .getOrElse(x.toAbsoluteTarget, Seq.empty) + + /** Get all traced signal/module for `annos` + * This API can be used to gather all final reference to the signal or module which is marked by `traceName` + */ + def finalTargetMap(annos: AnnotationSeq): Map[CompleteTarget, Seq[CompleteTarget]] = annos.collect { + case TraceNameAnnotation(t, chiselTarget) => chiselTarget -> t + }.groupBy(_._1).map{case (k, v) => k -> v.map(_._2)} +} diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala index 55dd8505..438f97b8 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/DataProduct.scala @@ -3,7 +3,7 @@ package chisel3.experimental.dataview import chisel3.experimental.BaseModule -import chisel3.{Data, getRecursiveFields} +import chisel3.{Data, Vec, getRecursiveFields} import scala.annotation.implicitNotFound @@ -41,17 +41,24 @@ trait DataProduct[-A] { def dataSet(a: A): Data => Boolean = dataIterator(a, "").map(_._1).toSet } -/** Encapsulating object for automatically provided implementations of [[DataProduct]] +/** Low priority built-in implementations of [[DataProduct]] * - * @note DataProduct implementations provided in this object are available in the implicit scope + * @note This trait exists so that `dataDataProduct` can be lower priority than `seqDataProduct` to resolve ambiguity */ -object DataProduct { +sealed trait LowPriorityDataProduct { + /** [[DataProduct]] implementation for [[Data]] */ implicit val dataDataProduct: DataProduct[Data] = new DataProduct[Data] { def dataIterator(a: Data, path: String): Iterator[(Data, String)] = getRecursiveFields.lazily(a, path).iterator } +} +/** Encapsulating object for built-in implementations of [[DataProduct]] + * + * @note DataProduct implementations provided in this object are available in the implicit scope + */ +object DataProduct extends LowPriorityDataProduct { /** [[DataProduct]] implementation for [[BaseModule]] */ implicit val userModuleDataProduct: DataProduct[BaseModule] = new DataProduct[BaseModule] { def dataIterator(a: BaseModule, path: String): Iterator[(Data, String)] = { @@ -69,4 +76,237 @@ object DataProduct { e => e._id > a._id && e._id <= lastId } } + + /** [[DataProduct]] implementation for any `Seq[A]` where `A` has an implementation of `DataProduct`. */ + implicit def seqDataProduct[A : DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] { + def dataIterator(a: Seq[A], path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + a.iterator + .zipWithIndex + .flatMap { case (elt, idx) => + dpa.dataIterator(elt, s"$path[$idx]") + } + } + } + + /** [[DataProduct]] implementation for any [[Tuple2]] where each field has an implementation of `DataProduct`. */ + implicit def tuple2DataProduct[A : DataProduct, B : DataProduct]: DataProduct[(A, B)] = new DataProduct[(A, B)] { + def dataIterator(tup: (A, B), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val (a, b) = tup + dpa.dataIterator(a, s"$path._1") ++ dpb.dataIterator(b, s"$path._2") + } + } + + /** [[DataProduct]] implementation for any [[Tuple3]] where each field has an implementation of `DataProduct`. */ + implicit def tuple3DataProduct[A : DataProduct, B : DataProduct, C : DataProduct]: DataProduct[(A, B, C)] = + new DataProduct[(A, B, C)] { + def dataIterator(tup: (A, B, C), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val (a, b, c) = tup + dpa.dataIterator(a, s"$path._1") ++ dpb.dataIterator(b, s"$path._2") ++ dpc.dataIterator(c, s"$path._3") + } + } + + /** [[DataProduct]] implementation for any [[Tuple4]] where each field has an implementation of `DataProduct`. */ + implicit def tuple4DataProduct[A : DataProduct, B : DataProduct, C : DataProduct, D : DataProduct]: DataProduct[(A, B, C, D)] = + new DataProduct[(A, B, C, D)] { + def dataIterator(tup: (A, B, C, D), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val (a, b, c, d) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") + } + } + + /** [[DataProduct]] implementation for any [[Tuple5]] where each field has an implementation of `DataProduct`. */ + implicit def tuple5DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct]: DataProduct[(A, B, C, D, E)] = + new DataProduct[(A, B, C, D, E)] { + def dataIterator(tup: (A, B, C, D, E), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val (a, b, c, d, e) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") + } + } + + /** [[DataProduct]] implementation for any [[Tuple6]] where each field has an implementation of `DataProduct`. */ + implicit def tuple6DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct, + F : DataProduct]: DataProduct[(A, B, C, D, E, F)] = + new DataProduct[(A, B, C, D, E, F)] { + def dataIterator(tup: (A, B, C, D, E, F), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val dpf = implicitly[DataProduct[F]] + val (a, b, c, d, e, f) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") ++ + dpf.dataIterator(f, s"$path._6") + } + } + + /** [[DataProduct]] implementation for any [[Tuple7]] where each field has an implementation of `DataProduct`. */ + implicit def tuple7DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct, + F : DataProduct, + G : DataProduct]: DataProduct[(A, B, C, D, E, F, G)] = + new DataProduct[(A, B, C, D, E, F, G)] { + def dataIterator(tup: (A, B, C, D, E, F, G), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val dpf = implicitly[DataProduct[F]] + val dpg = implicitly[DataProduct[G]] + val (a, b, c, d, e, f, g) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") ++ + dpf.dataIterator(f, s"$path._6") ++ + dpg.dataIterator(g, s"$path._7") + } + } + + /** [[DataProduct]] implementation for any [[Tuple8]] where each field has an implementation of `DataProduct`. */ + implicit def tuple8DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct, + F : DataProduct, + G : DataProduct, + H : DataProduct]: DataProduct[(A, B, C, D, E, F, G, H)] = + new DataProduct[(A, B, C, D, E, F, G, H)] { + def dataIterator(tup: (A, B, C, D, E, F, G, H), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val dpf = implicitly[DataProduct[F]] + val dpg = implicitly[DataProduct[G]] + val dph = implicitly[DataProduct[H]] + val (a, b, c, d, e, f, g, h) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") ++ + dpf.dataIterator(f, s"$path._6") ++ + dpg.dataIterator(g, s"$path._7") ++ + dph.dataIterator(h, s"$path._8") + } + } + + /** [[DataProduct]] implementation for any [[Tuple9]] where each field has an implementation of `DataProduct`. */ + implicit def tuple9DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct, + F : DataProduct, + G : DataProduct, + H : DataProduct, + I : DataProduct]: DataProduct[(A, B, C, D, E, F, G, H, I)] = + new DataProduct[(A, B, C, D, E, F, G, H, I)] { + def dataIterator(tup: (A, B, C, D, E, F, G, H, I), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val dpf = implicitly[DataProduct[F]] + val dpg = implicitly[DataProduct[G]] + val dph = implicitly[DataProduct[H]] + val dpi = implicitly[DataProduct[I]] + val (a, b, c, d, e, f, g, h, i) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") ++ + dpf.dataIterator(f, s"$path._6") ++ + dpg.dataIterator(g, s"$path._7") ++ + dph.dataIterator(h, s"$path._8") ++ + dpi.dataIterator(i, s"$path._9") + } + } + + /** [[DataProduct]] implementation for any [[Tuple9]] where each field has an implementation of `DataProduct`. */ + implicit def tuple10DataProduct[ + A : DataProduct, + B : DataProduct, + C : DataProduct, + D : DataProduct, + E : DataProduct, + F : DataProduct, + G : DataProduct, + H : DataProduct, + I : DataProduct, + J : DataProduct]: DataProduct[(A, B, C, D, E, F, G, H, I, J)] = + new DataProduct[(A, B, C, D, E, F, G, H, I, J)] { + def dataIterator(tup: (A, B, C, D, E, F, G, H, I, J), path: String): Iterator[(Data, String)] = { + val dpa = implicitly[DataProduct[A]] + val dpb = implicitly[DataProduct[B]] + val dpc = implicitly[DataProduct[C]] + val dpd = implicitly[DataProduct[D]] + val dpe = implicitly[DataProduct[E]] + val dpf = implicitly[DataProduct[F]] + val dpg = implicitly[DataProduct[G]] + val dph = implicitly[DataProduct[H]] + val dpi = implicitly[DataProduct[I]] + val dpj = implicitly[DataProduct[J]] + val (a, b, c, d, e, f, g, h, i, j) = tup + dpa.dataIterator(a, s"$path._1") ++ + dpb.dataIterator(b, s"$path._2") ++ + dpc.dataIterator(c, s"$path._3") ++ + dpd.dataIterator(d, s"$path._4") ++ + dpe.dataIterator(e, s"$path._5") ++ + dpf.dataIterator(f, s"$path._6") ++ + dpg.dataIterator(g, s"$path._7") ++ + dph.dataIterator(h, s"$path._8") ++ + dpi.dataIterator(i, s"$path._9") ++ + dpj.dataIterator(j, s"$path._10") + } + } } diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala index caf004c2..c17a5574 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala @@ -3,9 +3,12 @@ package chisel3.experimental.dataview import chisel3._ -import chisel3.internal.sourceinfo.SourceInfo -import scala.reflect.runtime.universe.WeakTypeTag +import chisel3.experimental.DataMirror.internal.chiselTypeClone +import chisel3.experimental.{HWTuple10, HWTuple2, HWTuple3, HWTuple4, HWTuple5, HWTuple6, HWTuple7, HWTuple8, HWTuple9} +import chisel3.internal.sourceinfo.{SourceInfo, UnlocatableSourceInfo} +import chisel3.ExplicitCompileOptions.Strict +import scala.reflect.runtime.universe.WeakTypeTag import annotation.implicitNotFound @@ -132,9 +135,241 @@ object DataView { case (b, a) => f(a, b).map(_.swap) } + // ****************************** Built-in Implementations of DataView ****************************** + // Sort of the "Standard library" implementations + /** All Chisel Data are viewable as their own type */ implicit def identityView[A <: Data](implicit sourceInfo: SourceInfo): DataView[A, A] = DataView[A, A](chiselTypeOf.apply, { case (x, y) => (x, y) }) + + /** Provides `DataView[Seq[A], Vec[B]]` for all `A` such that there exists `DataView[A, B]` */ + implicit def seqDataView[A : DataProduct, B <: Data](implicit dv: DataView[A, B], sourceInfo: SourceInfo): DataView[Seq[A], Vec[B]] = { + // TODO this would need a better way to determine the prototype for the Vec + DataView.mapping[Seq[A], Vec[B]]( + xs => Vec(xs.size, chiselTypeClone(xs.head.viewAs[B]))(sourceInfo, Strict), // xs.head is not correct in general + { case (s, v) => s.zip(v).map { case (a, b) => a.viewAs[B] -> b } } + ) + } + + /** Provides implementations of [[DataView]] for [[Tuple2]] to [[HWTuple2]] */ + implicit def tuple2DataView[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], sourceInfo: SourceInfo + ): DataView[(T1, T2), HWTuple2[V1, V2]] = + DataView.mapping( + { case (a, b) => new HWTuple2(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType)}, + { case ((a, b), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple3]] to [[HWTuple3]] */ + implicit def tuple3DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], sourceInfo: SourceInfo + ): DataView[(T1, T2, T3), HWTuple3[V1, V2, V3]] = + DataView.mapping( + { case (a, b, c) => new HWTuple3(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType)}, + { case ((a, b, c), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple4]] to [[HWTuple4]] */ + implicit def tuple4DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4), HWTuple4[V1, V2, V3, V4]] = + DataView.mapping( + { case (a, b, c, d) => + new HWTuple4(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType + )}, + { case ((a, b, c, d), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple5]] to [[HWTuple5]] */ + implicit def tuple5DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5), HWTuple5[V1, V2, V3, V4, V5]] = { + DataView.mapping( + { case tup: Tuple5[T1, T2, T3, T4, T5] => + val (a, b, c, d, e) = tup + new HWTuple5(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType + ) + }, + { case ((a, b, c, d, e), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5) + } + ) + } + + /** Provides implementations of [[DataView]] for [[Tuple6]] to [[HWTuple6]] */ + implicit def tuple6DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, + V6 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], + v5: DataView[T5, V5], v6: DataView[T6, V6], + sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5, T6), HWTuple6[V1, V2, V3, V4, V5, V6]] = + DataView.mapping( + { case (a, b, c, d, e, f) => + new HWTuple6(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType, f.viewAs[V6].cloneType + ) + }, + { case ((a, b, c, d, e, f), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5, + f.viewAs[V6] -> hwt._6) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple7]] to [[HWTuple7]] */ + implicit def tuple7DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, + V6 <: Data, V7 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], + v5: DataView[T5, V5], v6: DataView[T6, V6], v7: DataView[T7, V7], + sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5, T6, T7), HWTuple7[V1, V2, V3, V4, V5, V6, V7]] = + DataView.mapping( + { case (a, b, c, d, e, f, g) => + new HWTuple7(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType, f.viewAs[V6].cloneType, g.viewAs[V7].cloneType + ) + }, + { case ((a, b, c, d, e, f, g), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5, + f.viewAs[V6] -> hwt._6, + g.viewAs[V7] -> hwt._7) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple8]] to [[HWTuple8]] */ + implicit def tuple8DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, + V6 <: Data, V7 <: Data, V8 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], + v5: DataView[T5, V5], v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8], + sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5, T6, T7, T8), HWTuple8[V1, V2, V3, V4, V5, V6, V7, V8]] = + DataView.mapping( + { case (a, b, c, d, e, f, g, h) => + new HWTuple8(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType, f.viewAs[V6].cloneType, g.viewAs[V7].cloneType, h.viewAs[V8].cloneType + ) + }, + { case ((a, b, c, d, e, f, g, h), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5, + f.viewAs[V6] -> hwt._6, + g.viewAs[V7] -> hwt._7, + h.viewAs[V8] -> hwt._8) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple9]] to [[HWTuple9]] */ + implicit def tuple9DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, T9 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, + V6 <: Data, V7 <: Data, V8 <: Data, V9 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], + v5: DataView[T5, V5], v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8], + v9: DataView[T9, V9], + sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5, T6, T7, T8, T9), HWTuple9[V1, V2, V3, V4, V5, V6, V7, V8, V9]] = + DataView.mapping( + { case (a, b, c, d, e, f, g, h, i) => + new HWTuple9(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType, f.viewAs[V6].cloneType, g.viewAs[V7].cloneType, h.viewAs[V8].cloneType, + i.viewAs[V9].cloneType + ) + }, + { case ((a, b, c, d, e, f, g, h, i), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5, + f.viewAs[V6] -> hwt._6, + g.viewAs[V7] -> hwt._7, + h.viewAs[V8] -> hwt._8, + i.viewAs[V9] -> hwt._9) + } + ) + + /** Provides implementations of [[DataView]] for [[Tuple10]] to [[HWTuple10]] */ + implicit def tuple10DataView[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, T9 : DataProduct, T10 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, + V6 <: Data, V7 <: Data, V8 <: Data, V9 <: Data, V10 <: Data + ]( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], + v5: DataView[T5, V5], v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8], + v9: DataView[T9, V9], v10: DataView[T10, V10], + sourceInfo: SourceInfo + ): DataView[(T1, T2, T3, T4, T5, T6, T7, T8, T9, T10), HWTuple10[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10]] = + DataView.mapping( + { case (a, b, c, d, e, f, g, h, i, j) => + new HWTuple10(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType, c.viewAs[V3].cloneType, d.viewAs[V4].cloneType, + e.viewAs[V5].cloneType, f.viewAs[V6].cloneType, g.viewAs[V7].cloneType, h.viewAs[V8].cloneType, + i.viewAs[V9].cloneType, j.viewAs[V10].cloneType + ) + }, + { case ((a, b, c, d, e, f, g, h, i, j), hwt) => + Seq(a.viewAs[V1] -> hwt._1, + b.viewAs[V2] -> hwt._2, + c.viewAs[V3] -> hwt._3, + d.viewAs[V4] -> hwt._4, + e.viewAs[V5] -> hwt._5, + f.viewAs[V6] -> hwt._6, + g.viewAs[V7] -> hwt._7, + h.viewAs[V8] -> hwt._8, + i.viewAs[V9] -> hwt._9, + j.viewAs[V10] -> hwt._10) + } + ) } /** Factory methods for constructing non-total [[DataView]]s */ diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala index 0cc3d131..c7b51072 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala @@ -18,13 +18,9 @@ import firrtl.annotations.{IsModule, ModuleTarget} * * These definitions are then used to create multiple [[Instance]]s. * - * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object + * @param underlying The internal representation of the definition, which may be either be directly the object, or a clone of an object */ -case class Definition[+A] private[chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) extends IsLookupable { - private[chisel3] def proto: A = cloned match { - case Left(value: A) => value - case Right(i: IsClone[A]) => i._proto - } +final case class Definition[+A] private[chisel3] (private[chisel3] underlying: Underlying[A]) extends IsLookupable with SealedHierarchy[A] { /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!! * Instead, mark the field you are accessing with [[@public]] * @@ -43,20 +39,20 @@ case class Definition[+A] private[chisel3] (private[chisel3] cloned: Either[A, I lookup.definitionLookup(that, this) } - /** Updated by calls to [[apply]], to avoid recloning returned Data's */ - private [chisel3] val cache = HashMap[Data, Data]() - - /** @return the context of any Data's return from inside the instance */ private[chisel3] def getInnerDataContext: Option[BaseModule] = proto match { case value: BaseModule => - val newChild = Module.do_apply(new internal.BaseModule.DefinitionClone(value))(chisel3.internal.sourceinfo.UnlocatableSourceInfo, chisel3.ExplicitCompileOptions.Strict) + val newChild = Module.do_pseudo_apply(new internal.BaseModule.DefinitionClone(value))(chisel3.internal.sourceinfo.UnlocatableSourceInfo, chisel3.ExplicitCompileOptions.Strict) newChild._circuit = value._circuit.orElse(Some(value)) newChild._parent = None Some(newChild) case value: IsInstantiable => None } + override def toDefinition: Definition[A] = this + override def toInstance: Instance[A] = new Instance(underlying) + + } /** Factory methods for constructing [[Definition]]s */ @@ -90,11 +86,12 @@ object Definition extends SourceInfoDoc { val dynamicContext = new DynamicContext(Nil) Builder.globalNamespace.copyTo(dynamicContext.globalNamespace) dynamicContext.inDefinition = true - val (ir, module) = Builder.build(Module(proto), dynamicContext) + val (ir, module) = Builder.build(Module(proto), dynamicContext, false) Builder.components ++= ir.components Builder.annotations ++= ir.annotations module._circuit = Builder.currentModule dynamicContext.globalNamespace.copyTo(Builder.globalNamespace) - new Definition(Left(module)) + new Definition(Proto(module)) } + } diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala new file mode 100644 index 00000000..503e437b --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Hierarchy.scala @@ -0,0 +1,112 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental.hierarchy + +import chisel3._ +import scala.collection.mutable.{HashMap, HashSet} +import scala.reflect.runtime.universe.TypeTag +import chisel3.internal.BaseModule.IsClone +import chisel3.experimental.BaseModule +import _root_.firrtl.annotations.IsModule +import scala.annotation.implicitNotFound + +/** Super-trait for Instance and Definition + * + * Enables writing functions which are Instance/Definition agnostic + */ +sealed trait Hierarchy[+A] { + private[chisel3] def underlying: Underlying[A] + private[chisel3] def proto: A = underlying match { + case Proto(value: A) => value + case Clone(i: IsClone[A]) => i.getProto + } + + /** Updated by calls to [[_lookup]], to avoid recloning returned Data's */ + private[chisel3] val cache = HashMap[Data, Data]() + private[chisel3] def getInnerDataContext: Option[BaseModule] + + /** Determine whether underlying proto is of type provided. + * + * @note IMPORTANT: this function requires summoning a TypeTag[B], which will fail if B is an inner class. + * @note IMPORTANT: this function IGNORES type parameters, akin to normal type erasure. + * @note IMPORTANT: this function relies on Java reflection for underlying proto, but Scala reflection for provided type + * + * E.g. isA[List[Int]] will return true, even if underlying proto is of type List[String] + * @return Whether underlying proto is of provided type (with caveats outlined above) + */ + def isA[B : TypeTag]: Boolean = { + val tptag = implicitly[TypeTag[B]] + val name = tptag.tpe.toString + inBaseClasses(name) + } + + + // This code handles a special-case where, within an mdoc context, the type returned from + // scala reflection (typetag) looks different than when returned from java reflection. + // This function detects this case and reshapes the string to match. + private def modifyReplString(clz: String): String = { + if(clz != null) { + clz.split('.').toList match { + case "repl" :: "MdocSession" :: app :: rest => s"$app.this." + rest.mkString(".") + case other => clz + } + } else clz + } + private lazy val superClasses = calculateSuperClasses(proto.getClass()) + private def calculateSuperClasses(clz: Class[_]): Set[String] = { + if(clz != null) { + Set(modifyReplString(clz.getCanonicalName())) ++ + clz.getInterfaces().flatMap(i => calculateSuperClasses(i)) ++ + calculateSuperClasses(clz.getSuperclass()) + } else { + Set.empty[String] + } + } + private def inBaseClasses(clz: String): Boolean = superClasses.contains(clz) + + + /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!! + * Instead, mark the field you are accessing with [[@public]] + * + * Given a selector function (that) which selects a member from the original, return the + * corresponding member from the hierarchy. + * + * Our @instantiable and @public macros generate the calls to this apply method + * + * By calling this function, we summon the proper Lookupable typeclass from our implicit scope. + * + * @param that a user-specified lookup function + * @param lookup typeclass which contains the correct lookup function, based on the types of A and B + * @param macroGenerated a value created in the macro, to make it harder for users to use this API + */ + def _lookup[B, C](that: A => B)(implicit lookup: Lookupable[B], macroGenerated: chisel3.internal.MacroGenerated): lookup.C + + /** @return Return the underlying Definition[A] of this Hierarchy[A] */ + def toDefinition: Definition[A] + + /** @return Convert this Hierarchy[A] as a top-level Instance[A] */ + def toInstance: Instance[A] +} + +// Used to effectively seal Hierarchy, without requiring Definition and Instance to be in this file. +private[chisel3] trait SealedHierarchy[+A] extends Hierarchy[A] + +object Hierarchy { + implicit class HierarchyBaseModuleExtensions[T <: BaseModule](i: Hierarchy[T]) { + /** Returns the toTarget of this hierarchy + * @return target of this hierarchy + */ + def toTarget: IsModule = i match { + case d: Definition[T] => new Definition.DefinitionBaseModuleExtensions(d).toTarget + case i: Instance[T] => new Instance.InstanceBaseModuleExtensions(i).toTarget + } + + /** Returns the toAbsoluteTarget of this hierarchy + * @return absoluteTarget of this Hierarchy + */ + def toAbsoluteTarget: IsModule = i match { + case d: Definition[T] => new Definition.DefinitionBaseModuleExtensions(d).toAbsoluteTarget + case i: Instance[T] => new Instance.InstanceBaseModuleExtensions(i).toAbsoluteTarget + } + } +} diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala index 9b17bfce..97b62c23 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Instance.scala @@ -14,38 +14,29 @@ import firrtl.annotations.IsModule * Represents a unique instance of type [[A]] which are marked as @instantiable * Can be created using Instance.apply method. * - * @param cloned The internal representation of the instance, which may be either be directly the object, or a clone of an object + * @param underlying The internal representation of the instance, which may be either be directly the object, or a clone of an object */ -case class Instance[+A] private [chisel3] (private[chisel3] cloned: Either[A, IsClone[A]]) { - - /** Returns the original object which is instantiated here. - * If this is an instance of a clone, return that clone's original proto - * - * @return the original object which was instantiated - */ - private[chisel3] def proto: A = cloned match { - case Left(value: A) => value - case Right(i: IsClone[A]) => i._proto +final case class Instance[+A] private [chisel3] (private[chisel3] underlying: Underlying[A]) extends SealedHierarchy[A] { + underlying match { + case Proto(p: IsClone[_]) => chisel3.internal.throwException("Cannot have a Proto with a clone!") + case other => //Ok } /** @return the context of any Data's return from inside the instance */ - private[chisel3] def getInnerDataContext: Option[BaseModule] = cloned match { - case Left(value: BaseModule) => Some(value) - case Left(value: IsInstantiable) => None - case Right(i: BaseModule) => Some(i) - case Right(i: InstantiableClone[_]) => i._parent + private[chisel3] def getInnerDataContext: Option[BaseModule] = underlying match { + case Proto(value: BaseModule) => Some(value) + case Proto(value: IsInstantiable) => None + case Clone(i: BaseModule) => Some(i) + case Clone(i: InstantiableClone[_]) => i.getInnerContext } /** @return the context this instance. Note that for non-module clones, getInnerDataContext will be the same as getClonedParent */ - private[chisel3] def getClonedParent: Option[BaseModule] = cloned match { - case Left(value: BaseModule) => value._parent - case Right(i: BaseModule) => i._parent - case Right(i: InstantiableClone[_]) => i._parent + private[chisel3] def getClonedParent: Option[BaseModule] = underlying match { + case Proto(value: BaseModule) => value._parent + case Clone(i: BaseModule) => i._parent + case Clone(i: InstantiableClone[_]) => i.getInnerContext } - /** Updated by calls to [[apply]], to avoid recloning returned Data's */ - private [chisel3] val cache = HashMap[Data, Data]() - /** Used by Chisel's internal macros. DO NOT USE in your normal Chisel code!!! * Instead, mark the field you are accessing with [[@public]] * @@ -65,7 +56,8 @@ case class Instance[+A] private [chisel3] (private[chisel3] cloned: Either[A, Is } /** Returns the definition of this Instance */ - def toDefinition: Definition[A] = new Definition(Left(proto)) + override def toDefinition: Definition[A] = new Definition(Proto(proto)) + override def toInstance: Instance[A] = this } @@ -75,17 +67,17 @@ object Instance extends SourceInfoDoc { /** If this is an instance of a Module, returns the toTarget of this instance * @return target of this instance */ - def toTarget: IsModule = i.cloned match { - case Left(x: BaseModule) => x.getTarget - case Right(x: IsClone[_] with BaseModule) => x.getTarget + def toTarget: IsModule = i.underlying match { + case Proto(x: BaseModule) => x.getTarget + case Clone(x: IsClone[_] with BaseModule) => x.getTarget } /** If this is an instance of a Module, returns the toAbsoluteTarget of this instance * @return absoluteTarget of this instance */ - def toAbsoluteTarget: IsModule = i.cloned match { - case Left(x) => x.toAbsoluteTarget - case Right(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget + def toAbsoluteTarget: IsModule = i.underlying match { + case Proto(x) => x.toAbsoluteTarget + case Clone(x: IsClone[_] with BaseModule) => x.toAbsoluteTarget } } @@ -105,7 +97,7 @@ object Instance extends SourceInfoDoc { val ports = experimental.CloneModuleAsRecord(definition.proto) val clone = ports._parent.get.asInstanceOf[ModuleClone[T]] clone._madeFromDefinition = true - new Instance(Right(clone)) + new Instance(Clone(clone)) } } diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala index 26ba0286..4f3c2d42 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/IsInstantiable.scala @@ -12,6 +12,6 @@ trait IsInstantiable object IsInstantiable { implicit class IsInstantiableExtensions[T <: IsInstantiable](i: T) { - def toInstance: Instance[T] = new Instance(Left(i)) + def toInstance: Instance[T] = new Instance(Proto(i)) } } diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala b/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala new file mode 100644 index 00000000..c16cc633 --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/hierarchy/LibraryHooks.scala @@ -0,0 +1,31 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental.hierarchy + +import scala.annotation.implicitNotFound + +@implicitNotFound("These functions are only for building hierarchy-compatible Chisel libraries! Users beware!") +// DO NOT extend unless you know what you are doing!!!!!! Not for the casual user! +trait InsideHierarchyLibraryExtension + +// Collection of public functions to give non-core-Chisel libraries the ability to build integrations with +// the experimental hierarchy package +object LibraryHooks { + + /** Builds a new instance given a definition and function to create a new instance-specific Underlying, from the + * definition's Underlying + * @note Implicitly requires being inside a Hierarchy Library Extension + */ + def buildInstance[A](definition: Definition[A], + createUnderlying: Underlying[A] => Underlying[A] + )(implicit inside: InsideHierarchyLibraryExtension): Instance[A] = { + new Instance(createUnderlying(definition.underlying)) + } + + /** Builds a new definition given an Underlying implementation + * @note Implicitly requires being inside a Hierarchy Library Extension + */ + def buildDefinition[A](underlying: Underlying[A])(implicit inside: InsideHierarchyLibraryExtension): Definition[A] = { + new Definition(underlying) + } +} diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala index b9617723..ff4d676c 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala @@ -19,7 +19,7 @@ import chisel3.internal.{AggregateViewBinding, Builder, ChildBinding, ViewBindin */ @implicitNotFound("@public is only legal within a class marked @instantiable and only on vals of type" + " Data, BaseModule, IsInstantiable, IsLookupable, or Instance[_], or in an Iterable or Option") -sealed trait Lookupable[-B] { +trait Lookupable[-B] { type C // Return type of the lookup /** Function called to modify the returned value of type B from A, into C * @@ -36,9 +36,11 @@ sealed trait Lookupable[-B] { * @return */ def definitionLookup[A](that: A => B, definition: Definition[A]): C + protected def getProto[A](h: Hierarchy[A]): A = h.proto + protected def getUnderlying[A](h: Hierarchy[A]): Underlying[A] = h.underlying } -private[chisel3] object Lookupable { +object Lookupable { /** Clones a data and sets its internal references to its parent module to be in a new context. * @@ -52,10 +54,10 @@ private[chisel3] object Lookupable { data._parent match { case None => data case Some(parent) => - val newParent = cloneModuleToContext(Left(parent), context) + val newParent = cloneModuleToContext(Proto(parent), context) newParent match { - case Left(p) if p == parent => data - case Right(m: BaseModule) => + case Proto(p) if p == parent => data + case Clone(m: BaseModule) => val newChild = data.cloneTypeFull newChild.setRef(data.getRef, true) newChild.bind(internal.CrossModuleBinding) @@ -145,7 +147,7 @@ private[chisel3] object Lookupable { val result = data.cloneTypeFull - // We have to lookup the target(s) of the view since they may need to be cloned into the current context + // We have to lookup the target(s) of the view since they may need to be underlying into the current context val newBinding = data.topBinding match { case ViewBinding(target) => ViewBinding(lookupData(reify(target))) case avb @ AggregateViewBinding(map, targetOpt) => data match { @@ -199,51 +201,51 @@ private[chisel3] object Lookupable { * This function effectively recurses up the parents of module to find whether: * (1) A parent is already in the context; then we do nothing and return module * (2) A parent is in a different clone of the context; then we clone all the parents up - * to that parent and set their parents to be in this cloned context + * to that parent and set their parents to be in this underlying context * (3) A parent has no root; in that case, we do nothing and return the module. * - * @param module original or clone to be cloned into a new context + * @param module original or clone to be underlying into a new context * @param context new context * @return original or clone in the new context */ - private[chisel3] def cloneModuleToContext[T <: BaseModule](module: Either[T, IsClone[T]], context: BaseModule) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Either[T, IsClone[T]] = { + private[chisel3] def cloneModuleToContext[T <: BaseModule](module: Underlying[T], context: BaseModule) + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Underlying[T] = { // Recursive call - def rec[A <: BaseModule](m: A): Either[A, IsClone[A]] = { - def clone(x: A, p: Option[BaseModule], name: () => String): Either[A, IsClone[A]] = { - val newChild = Module.do_apply(new internal.BaseModule.InstanceClone(x, name)) + def rec[A <: BaseModule](m: A): Underlying[A] = { + def clone(x: A, p: Option[BaseModule], name: () => String): Underlying[A] = { + val newChild = Module.do_pseudo_apply(new internal.BaseModule.InstanceClone(x, name)) newChild._parent = p - Right(newChild) + Clone(newChild) } (m, context) match { - case (c, ctx) if ctx == c => Left(c) - case (c, ctx: IsClone[_]) if ctx.isACloneOf(c) => Right(ctx.asInstanceOf[IsClone[A]]) - case (c, ctx) if c._parent.isEmpty => Left(c) + case (c, ctx) if ctx == c => Proto(c) + case (c, ctx: IsClone[_]) if ctx.hasSameProto(c) => Clone(ctx.asInstanceOf[IsClone[A]]) + case (c, ctx) if c._parent.isEmpty => Proto(c) case (_, _) => - cloneModuleToContext(Left(m._parent.get), context) match { - case Left(p) => Left(m) - case Right(p: BaseModule) => + cloneModuleToContext(Proto(m._parent.get), context) match { + case Proto(p) => Proto(m) + case Clone(p: BaseModule) => clone(m, Some(p), () => m.instanceName) } } } module match { - case Left(m) => rec(m) - case Right(m: ModuleClone[_]) => + case Proto(m) => rec(m) + case Clone(m: ModuleClone[_]) => rec(m) match { - case Left(mx) => Right(mx) - case Right(i: InstanceClone[_]) => - val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName)) + case Proto(mx) => Clone(mx) + case Clone(i: InstanceClone[_]) => + val newChild = Module.do_pseudo_apply(new InstanceClone(m.getProto, () => m.instanceName)) newChild._parent = i._parent - Right(newChild) + Clone(newChild) } - case Right(m: InstanceClone[_]) => + case Clone(m: InstanceClone[_]) => rec(m) match { - case Left(mx) => Right(mx) - case Right(i: InstanceClone[_]) => - val newChild = Module.do_apply(new InstanceClone(m._proto, () => m.instanceName)) + case Proto(mx) => Clone(mx) + case Clone(i: InstanceClone[_]) => + val newChild = Module.do_pseudo_apply(new InstanceClone(m.getProto, () => m.instanceName)) newChild._parent = i._parent - Right(newChild) + Clone(newChild) } } } @@ -259,14 +261,14 @@ private[chisel3] object Lookupable { type C = Instance[B] def definitionLookup[A](that: A => Instance[B], definition: Definition[A]): C = { val ret = that(definition.proto) - new Instance(cloneModuleToContext(ret.cloned, definition.getInnerDataContext.get)) + new Instance(cloneModuleToContext(ret.underlying, definition.getInnerDataContext.get)) } def instanceLookup[A](that: A => Instance[B], instance: Instance[A]): C = { val ret = that(instance.proto) - instance.cloned match { + instance.underlying match { // If instance is just a normal module, no changing of context is necessary - case Left(_) => new Instance(ret.cloned) - case Right(_) => new Instance(cloneModuleToContext(ret.cloned, instance.getInnerDataContext.get)) + case Proto(_) => new Instance(ret.underlying) + case Clone(_) => new Instance(cloneModuleToContext(ret.underlying, instance.getInnerDataContext.get)) } } } @@ -275,14 +277,14 @@ private[chisel3] object Lookupable { type C = Instance[B] def definitionLookup[A](that: A => B, definition: Definition[A]): C = { val ret = that(definition.proto) - new Instance(cloneModuleToContext(Left(ret), definition.getInnerDataContext.get)) + new Instance(cloneModuleToContext(Proto(ret), definition.getInnerDataContext.get)) } def instanceLookup[A](that: A => B, instance: Instance[A]): C = { val ret = that(instance.proto) - instance.cloned match { + instance.underlying match { // If instance is just a normal module, no changing of context is necessary - case Left(_) => new Instance(Left(ret)) - case Right(_) => new Instance(cloneModuleToContext(Left(ret), instance.getInnerDataContext.get)) + case Proto(_) => new Instance(Proto(ret)) + case Clone(_) => new Instance(cloneModuleToContext(Proto(ret), instance.getInnerDataContext.get)) } } } @@ -299,9 +301,9 @@ private[chisel3] object Lookupable { } def instanceLookup[A](that: A => B, instance: Instance[A]): C = { val ret = that(instance.proto) - val ioMap: Option[Map[Data, Data]] = instance.cloned match { - case Right(x: ModuleClone[_]) => Some(x.ioMap) - case Left(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap) + val ioMap: Option[Map[Data, Data]] = instance.underlying match { + case Clone(x: ModuleClone[_]) => Some(x.ioMap) + case Proto(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap) case _ => None } if (isView(ret)) { @@ -342,15 +344,19 @@ private[chisel3] object Lookupable { type C = Instance[B] def definitionLookup[A](that: A => B, definition: Definition[A]): C = { val ret = that(definition.proto) - val cloned = new InstantiableClone(ret) - cloned._parent = definition.getInnerDataContext - new Instance(Right(cloned)) + val underlying = new InstantiableClone[B] { + val getProto = ret + lazy val _innerContext = definition + } + new Instance(Clone(underlying)) } def instanceLookup[A](that: A => B, instance: Instance[A]): C = { val ret = that(instance.proto) - val cloned = new InstantiableClone(ret) - cloned._parent = instance.getInnerDataContext - new Instance(Right(cloned)) + val underlying = new InstantiableClone[B] { + val getProto = ret + lazy val _innerContext = instance + } + new Instance(Clone(underlying)) } } diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala new file mode 100644 index 00000000..864cc8af --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Underlying.scala @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental.hierarchy + +import chisel3.internal.BaseModule.IsClone + +/** Represents the underlying implementation of a Definition or Instance */ +sealed trait Underlying[+T] + +/** A clone of a real implementation */ +final case class Clone[+T](isClone: IsClone[T]) extends Underlying[T] + +/** An actual implementation */ +final case class Proto[+T](proto: T) extends Underlying[T] diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index 8018159f..5397a1c3 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -2,7 +2,8 @@ package chisel3 -import chisel3.internal.NamedComponent +import chisel3.ExplicitCompileOptions.Strict +import chisel3.experimental.DataMirror.internal.chiselTypeClone import chisel3.internal.sourceinfo.SourceInfo /** Package for experimental features, which may have their API changed, be removed, etc. @@ -167,8 +168,149 @@ package object experimental { // Use to remove prefixes not in provided scope val noPrefix = chisel3.internal.noPrefix - /** Base simulation-only component. */ - abstract class BaseSim extends NamedComponent { - _parent.foreach(_.addId(this)) + // ****************************** Hardware equivalents of Scala Tuples ****************************** + // These are intended to be used via DataView + + /** [[Data]] equivalent of Scala's [[Tuple2]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple2` in + * `chisel3.experimental.conversions` + */ + final class HWTuple2[+A <: Data, +B <: Data] private[chisel3] (val _1: A, val _2: B) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple2(chiselTypeClone(_1), chiselTypeClone(_2)) + } + + /** [[Data]] equivalent of Scala's [[Tuple3]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple3` in + * `chisel3.experimental.conversions` + */ + final class HWTuple3[+A <: Data, +B <: Data, +C <: Data] private[chisel3] ( + val _1: A, val _2: B, val _3: C + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple3( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple4]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple4` in + * `chisel3.experimental.conversions` + */ + final class HWTuple4[+A <: Data, +B <: Data, +C <: Data, +D <: Data] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple4( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple5]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple5` in + * `chisel3.experimental.conversions` + */ + final class HWTuple5[+A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple5( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple6]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple6` in + * `chisel3.experimental.conversions` + */ + final class HWTuple6[+A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data, +F <: Data] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E, val _6: F + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple6( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5), + chiselTypeClone(_6) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple7]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple7` in + * `chisel3.experimental.conversions` + */ + final class HWTuple7[+A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data, +F <: Data, +G <: Data] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E, val _6: F, val _7: G + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple7( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5), + chiselTypeClone(_6), chiselTypeClone(_7) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple8]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple8` in + * `chisel3.experimental.conversions` + */ + final class HWTuple8[ + +A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data, +F <: Data, +G <: Data, +H <: Data + ] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E, val _6: F, val _7: G, val _8: H + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple8( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5), + chiselTypeClone(_6), chiselTypeClone(_7), chiselTypeClone(_8) + ) + } + + /** [[Data]] equivalent of Scala's [[Tuple9]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple9` in + * `chisel3.experimental.conversions` + */ + final class HWTuple9[ + +A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data, +F <: Data, +G <: Data, +H <: Data, +I <: Data + ] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E, val _6: F, val _7: G, val _8: H, val _9: I + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple9( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5), + chiselTypeClone(_6), chiselTypeClone(_7), chiselTypeClone(_8), chiselTypeClone(_9) + ) + } + + + /** [[Data]] equivalent of Scala's [[Tuple9]] + * + * Users may not instantiate this class directly. Instead they should use the implicit conversion from `Tuple9` in + * `chisel3.experimental.conversions` + */ + final class HWTuple10[ + +A <: Data, +B <: Data, +C <: Data, +D <: Data, +E <: Data, +F <: Data, +G <: Data, +H <: Data, +I <: Data, +J <: Data + ] private[chisel3] ( + val _1: A, val _2: B, val _3: C, val _4: D, val _5: E, val _6: F, val _7: G, val _8: H, val _9: I, val _10: J + ) extends Bundle()(Strict) { + // Because this implementation exists in chisel3.core, it cannot compile with the plugin, so we implement the behavior manually + override protected def _usingPlugin: Boolean = true + override protected def _cloneTypeImpl: Bundle = new HWTuple10( + chiselTypeClone(_1), chiselTypeClone(_2), chiselTypeClone(_3), chiselTypeClone(_4), chiselTypeClone(_5), + chiselTypeClone(_6), chiselTypeClone(_7), chiselTypeClone(_8), chiselTypeClone(_9), chiselTypeClone(_10) + ) } } diff --git a/core/src/main/scala/chisel3/experimental/verification/package.scala b/core/src/main/scala/chisel3/experimental/verification/package.scala deleted file mode 100644 index 190083fd..00000000 --- a/core/src/main/scala/chisel3/experimental/verification/package.scala +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.experimental - -import chisel3._ -import chisel3.internal.Builder -import chisel3.internal.firrtl.{Formal, Verification} -import chisel3.internal.sourceinfo.SourceInfo - -package object verification { - - object assert { - /** Named class for assertions. */ - final class Assert(private[chisel3] val predicate: Bool) extends BaseSim - - - def apply(predicate: Bool, msg: String = "")( - implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Assert = { - val a = new Assert(predicate) - when (!Module.reset.asBool) { - val clock = Module.clock - Builder.pushCommand(Verification(a, Formal.Assert, sourceInfo, clock.ref, predicate.ref, msg)) - } - a - } - } - - object assume { - /** Named class for assumes. */ - final class Assume(private[chisel3] val predicate: Bool) extends BaseSim - - def apply(predicate: Bool, msg: String = "")( - implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Assume = { - val a = new Assume(predicate) - when (!Module.reset.asBool) { - val clock = Module.clock - Builder.pushCommand(Verification(a, Formal.Assume, sourceInfo, clock.ref, predicate.ref, msg)) - } - a - } - } - - object cover { - /** Named class for covers. */ - final class Cover(private[chisel3] val predicate: Bool) extends BaseSim - - def apply(predicate: Bool, msg: String = "")( - implicit sourceInfo: SourceInfo, - compileOptions: CompileOptions): Cover = { - val clock = Module.clock - val c = new Cover(predicate) - when (!Module.reset.asBool) { - Builder.pushCommand(Verification(c, Formal.Cover, sourceInfo, clock.ref, predicate.ref, msg)) - } - c - } - } -} diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 441abc92..71894887 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -6,7 +6,7 @@ import scala.util.DynamicVariable import scala.collection.mutable.ArrayBuffer import chisel3._ import chisel3.experimental._ -import chisel3.experimental.hierarchy.Instance +import chisel3.experimental.hierarchy.{Instance, Clone} import chisel3.internal.firrtl._ import chisel3.internal.naming._ import _root_.firrtl.annotations.{CircuitName, ComponentName, IsMember, ModuleName, Named, ReferenceTarget} @@ -105,7 +105,7 @@ private[chisel3] trait HasId extends InstanceId { private var auto_seed: Option[String] = None // Prefix at time when this class is constructed - private val construction_prefix: Prefix = Builder.getPrefix() + private val construction_prefix: Prefix = Builder.getPrefix // Prefix when the latest [[suggestSeed]] or [[autoSeed]] is called private var prefix_seed: Prefix = Nil @@ -133,7 +133,7 @@ private[chisel3] trait HasId extends InstanceId { private[chisel3] def forceAutoSeed(seed: String): this.type = { auto_seed = Some(seed) for(hook <- auto_postseed_hooks) { hook(seed) } - prefix_seed = Builder.getPrefix() + prefix_seed = Builder.getPrefix this } @@ -149,17 +149,28 @@ private[chisel3] trait HasId extends InstanceId { */ def suggestName(seed: =>String): this.type = { if(suggested_seed.isEmpty) suggested_seed = Some(seed) - prefix_seed = Builder.getPrefix() + prefix_seed = Builder.getPrefix for(hook <- suggest_postseed_hooks) { hook(seed) } this } + // Internal version of .suggestName that can override a user-suggested name + // This only exists for maintaining "val io" naming in compatibility-mode Modules without IO + // wrapping + private[chisel3] def forceFinalName(seed: String): this.type = { + // This could be called with user prefixes, ignore them + noPrefix { + suggested_seed = Some(seed) + this.suggestName(seed) + } + } + /** Computes the name of this HasId, if one exists * @param defaultPrefix Optionally provide a default prefix for computing the name * @param defaultSeed Optionally provide default seed for computing the name * @return the name, if it can be computed */ - def computeName(defaultPrefix: Option[String], defaultSeed: Option[String]): Option[String] = { + private[chisel3] def _computeName(defaultPrefix: Option[String], defaultSeed: Option[String]): Option[String] = { /** Computes a name of this signal, given the seed and prefix * @param seed * @param prefix @@ -203,7 +214,7 @@ private[chisel3] trait HasId extends InstanceId { // (e.g. tried to suggest a name to part of a Record) private[chisel3] def forceName(prefix: Option[String], default: =>String, namespace: Namespace): Unit = if(_ref.isEmpty) { - val candidate_name = computeName(prefix, Some(default)).get + val candidate_name = _computeName(prefix, Some(default)).get val available_name = namespace.name(candidate_name) setRef(Ref(available_name)) } @@ -223,7 +234,7 @@ private[chisel3] trait HasId extends InstanceId { private def refName(c: Component): String = _ref match { case Some(arg) => arg fullName c - case None => computeName(None, None).get + case None => _computeName(None, None).get } // Helper for reifying views if they map to a single Target @@ -332,9 +343,6 @@ private[chisel3] trait NamedComponent extends HasId { private[chisel3] class ChiselContext() { val idGen = new IdGen - // Record the Bundle instance, class name, method name, and reverse stack trace position of open Bundles - val bundleStack: ArrayBuffer[(Bundle, String, String, Int)] = ArrayBuffer() - // Records the different prefixes which have been scoped at this point in time var prefixStack: Prefix = Nil @@ -349,8 +357,6 @@ private[chisel3] class DynamicContext(val annotationSeq: AnnotationSeq) { val components = ArrayBuffer[Component]() val annotations = ArrayBuffer[ChiselAnnotation]() var currentModule: Option[BaseModule] = None - // This is only used for testing, it can be removed if the plugin becomes mandatory - var allowReflectiveAutoCloneType = true /** Contains a mapping from a elaborated module to their aspect * Set by [[ModuleAspect]] @@ -485,7 +491,7 @@ private[chisel3] object Builder extends LazyLogging { } // Returns the prefix stack at this moment - def getPrefix(): Prefix = chiselContext.get().prefixStack + def getPrefix: Prefix = chiselContext.get().prefixStack def currentModule: Option[BaseModule] = dynamicContextVar.value match { case Some(dyanmicContext) => dynamicContext.currentModule @@ -550,6 +556,8 @@ private[chisel3] object Builder extends LazyLogging { // A bare api call is, e.g. calling Wire() from the scala console). ) } + def hasDynamicContext: Boolean = dynamicContextVar.value.isDefined + def readyForModuleConstr: Boolean = dynamicContext.readyForModuleConstr def readyForModuleConstr_=(target: Boolean): Unit = { dynamicContext.readyForModuleConstr = target @@ -572,7 +580,7 @@ private[chisel3] object Builder extends LazyLogging { dynamicContext.whenStack = s } - def currentWhen(): Option[WhenContext] = dynamicContext.whenStack.headOption + def currentWhen: Option[WhenContext] = dynamicContext.whenStack.headOption def currentClock: Option[Clock] = dynamicContext.currentClock def currentClock_=(newClock: Option[Clock]): Unit = { @@ -590,16 +598,6 @@ private[chisel3] object Builder extends LazyLogging { .getOrElse(false) } - // This should only be used for testing, must be true outside of Builder context - def allowReflectiveAutoCloneType: Boolean = { - dynamicContextVar.value - .map(_.allowReflectiveAutoCloneType) - .getOrElse(true) - } - def allowReflectiveAutoCloneType_=(value: Boolean): Unit = { - dynamicContext.allowReflectiveAutoCloneType = value - } - def forcedClock: Clock = currentClock.getOrElse( throwException("Error: No implicit clock.") ) @@ -615,52 +613,18 @@ private[chisel3] object Builder extends LazyLogging { } def pushOp[T <: Data](cmd: DefPrim[T]): T = { // Bind each element of the returned Data to being a Op - cmd.id.bind(OpBinding(forcedUserModule, currentWhen())) + cmd.id.bind(OpBinding(forcedUserModule, currentWhen)) pushCommand(cmd).id } - // Called when Bundle construction begins, used to record a stack of open Bundle constructors to - // record candidates for Bundle autoclonetype. This is a best-effort guess. - // Returns the current stack of open Bundles - // Note: elt will NOT have finished construction, its elements cannot be accessed - def updateBundleStack(elt: Bundle): Seq[Bundle] = { - val stackElts = Thread.currentThread().getStackTrace() - .reverse // so stack frame numbers are deterministic across calls - .dropRight(2) // discard Thread.getStackTrace and updateBundleStack - - // Determine where we are in the Bundle stack - val eltClassName = elt.getClass.getName - val eltStackPos = stackElts.map(_.getClassName).lastIndexOf(eltClassName) - - // Prune the existing Bundle stack of closed Bundles - // If we know where we are in the stack, discard frames above that - val stackEltsTop = if (eltStackPos >= 0) eltStackPos else stackElts.size - val pruneLength = chiselContext.get.bundleStack.reverse.prefixLength { case (_, cname, mname, pos) => - pos >= stackEltsTop || stackElts(pos).getClassName != cname || stackElts(pos).getMethodName != mname - } - chiselContext.get.bundleStack.trimEnd(pruneLength) - - // Return the stack state before adding the most recent bundle - val lastStack = chiselContext.get.bundleStack.map(_._1).toSeq - - // Append the current Bundle to the stack, if it's on the stack trace - if (eltStackPos >= 0) { - val stackElt = stackElts(eltStackPos) - chiselContext.get.bundleStack.append((elt, eltClassName, stackElt.getMethodName, eltStackPos)) - } - // Otherwise discard the stack frame, this shouldn't fail noisily - - lastStack - } - /** Recursively suggests names to supported "container" classes * Arbitrary nestings of supported classes are allowed so long as the * innermost element is of type HasId * (Note: Map is Iterable[Tuple2[_,_]] and thus excluded) */ def nameRecursively(prefix: String, nameMe: Any, namer: (HasId, String) => Unit): Unit = nameMe match { - case (id: Instance[_]) => id.cloned match { - case Right(m: internal.BaseModule.ModuleClone[_]) => namer(m.getPorts, prefix) + case (id: Instance[_]) => id.underlying match { + case Clone(m: internal.BaseModule.ModuleClone[_]) => namer(m.getPorts, prefix) case _ => } case (id: HasId) => namer(id, prefix) @@ -727,14 +691,16 @@ private[chisel3] object Builder extends LazyLogging { renames } - private [chisel3] def build[T <: BaseModule](f: => T, dynamicContext: DynamicContext): (Circuit, T) = { + private[chisel3] def build[T <: BaseModule](f: => T, dynamicContext: DynamicContext, forceModName: Boolean = true): (Circuit, T) = { dynamicContextVar.withValue(Some(dynamicContext)) { ViewParent // Must initialize the singleton in a Builder context or weird things can happen // in tiny designs/testcases that never access anything in chisel3.internal checkScalaVersion() logger.info("Elaborating design...") val mod = f - mod.forceName(None, mod.name, globalNamespace) + if (forceModName) { // This avoids definition name index skipping with D/I + mod.forceName(None, mod.name, globalNamespace) + } errors.checkpoint(logger) logger.info("Done elaborating.") diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala index 5cbab329..6173fc91 100644 --- a/core/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -36,33 +36,35 @@ import chisel3.internal.sourceinfo.SourceInfo */ private[chisel3] object MonoConnect { + def formatName(data: Data) = s"""${data.earlyName} in ${data.parentNameOpt.getOrElse("(unknown)")}""" + // These are all the possible exceptions that can be thrown. // These are from element-level connection - def UnreadableSourceException = - MonoConnectException(": Source is unreadable from current module.") - def UnwritableSinkException = - MonoConnectException(": Sink is unwriteable by current module.") - def SourceEscapedWhenScopeException = - MonoConnectException(": Source has escaped the scope of the when in which it was constructed.") - def SinkEscapedWhenScopeException = - MonoConnectException(": Sink has escaped the scope of the when in which it was constructed.") + def UnreadableSourceException(sink: Data, source: Data) = + MonoConnectException(s"""${formatName(source)} cannot be read from module ${sink.parentNameOpt.getOrElse("(unknown)")}.""") + def UnwritableSinkException(sink: Data, source: Data) = + MonoConnectException(s"""${formatName(sink)} cannot be written from module ${source.parentNameOpt.getOrElse("(unknown)")}.""") + def SourceEscapedWhenScopeException(source: Data) = + MonoConnectException(s"Source ${formatName(source)} has escaped the scope of the when in which it was constructed.") + def SinkEscapedWhenScopeException(sink: Data) = + MonoConnectException(s"Sink ${formatName(sink)} has escaped the scope of the when in which it was constructed.") def UnknownRelationException = - MonoConnectException(": Sink or source unavailable to current module.") + MonoConnectException("Sink or source unavailable to current module.") // These are when recursing down aggregate types def MismatchedVecException = - MonoConnectException(": Sink and Source are different length Vecs.") + MonoConnectException("Sink and Source are different length Vecs.") def MissingFieldException(field: String) = - MonoConnectException(s": Source Record missing field ($field).") - def MismatchedException(sink: String, source: String) = - MonoConnectException(s": Sink ($sink) and Source ($source) have different types.") + MonoConnectException(s"Source Record missing field ($field).") + def MismatchedException(sink: Data, source: Data) = + MonoConnectException(s"Sink (${sink.cloneType.toString}) and Source (${source.cloneType.toString}) have different types.") def DontCareCantBeSink = - MonoConnectException(": DontCare cannot be a connection sink (LHS)") - def AnalogCantBeMonoSink = - MonoConnectException(": Analog cannot participate in a mono connection (sink - LHS)") - def AnalogCantBeMonoSource = - MonoConnectException(": Analog cannot participate in a mono connection (source - RHS)") - def AnalogMonoConnectionException = - MonoConnectException(": Analog cannot participate in a mono connection (source and sink)") + MonoConnectException("DontCare cannot be a connection sink") + def AnalogCantBeMonoSink(sink: Data) = + MonoConnectException(s"Sink ${formatName(sink)} of type Analog cannot participate in a mono connection (:=)") + def AnalogCantBeMonoSource(source: Data) = + MonoConnectException(s"Source ${formatName(source)} of type Analog cannot participate in a mono connection (:=)") + def AnalogMonoConnectionException(source: Data, sink: Data) = + MonoConnectException(s"Source ${formatName(source)} and sink ${formatName(sink)} of type Analog cannot participate in a mono connection (:=)") def checkWhenVisibility(x: Data): Boolean = { x.topBinding match { @@ -169,13 +171,13 @@ private[chisel3] object MonoConnect { // DontCare as a sink is illegal. case (DontCare, _) => throw DontCareCantBeSink // Analog is illegal in mono connections. - case (_: Analog, _:Analog) => throw AnalogMonoConnectionException + case (_: Analog, _:Analog) => throw AnalogMonoConnectionException(source, sink) // Analog is illegal in mono connections. - case (_: Analog, _) => throw AnalogCantBeMonoSink + case (_: Analog, _) => throw AnalogCantBeMonoSink(sink) // Analog is illegal in mono connections. - case (_, _: Analog) => throw AnalogCantBeMonoSource + case (_, _: Analog) => throw AnalogCantBeMonoSource(source) // Sink and source are different subtypes of data so fail - case (sink, source) => throw MismatchedException(sink.toString, source.toString) + case (sink, source) => throw MismatchedException(sink, source) } // This function (finally) issues the connection operation @@ -196,7 +198,7 @@ private[chisel3] object MonoConnect { val source = reify(_source) // If source has no location, assume in context module // This can occur if is a literal, unbound will error previously - val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException) + val sink_mod: BaseModule = sink.topBinding.location.getOrElse(throw UnwritableSinkException(sink, source)) val source_mod: BaseModule = source.topBinding.location.getOrElse(context_mod) val sink_parent = Builder.retrieveParent(sink_mod, context_mod).getOrElse(None) @@ -206,11 +208,11 @@ private[chisel3] object MonoConnect { val source_direction = BindingDirection.from(source.topBinding, source.direction) if (!checkWhenVisibility(sink)) { - throw SinkEscapedWhenScopeException + throw SinkEscapedWhenScopeException(sink) } if (!checkWhenVisibility(source)) { - throw SourceEscapedWhenScopeException + throw SourceEscapedWhenScopeException(source) } // CASE: Context is same module that both left node and right node are in @@ -220,7 +222,7 @@ private[chisel3] object MonoConnect { // CURRENT MOD CURRENT MOD case (Output, _) => issueConnect(sink, source) case (Internal, _) => issueConnect(sink, source) - case (Input, _) => throw UnwritableSinkException + case (Input, _) => throw UnwritableSinkException(sink, source) } } @@ -238,11 +240,11 @@ private[chisel3] object MonoConnect { if (!(connectCompileOptions.dontAssumeDirectionality)) { issueConnect(sink, source) } else { - throw UnreadableSourceException + throw UnreadableSourceException(sink, source) } } case (Input, Output) if (!(connectCompileOptions.dontTryConnectionsSwapped)) => issueConnect(source, sink) - case (Input, _) => throw UnwritableSinkException + case (Input, _) => throw UnwritableSinkException(sink, source) } } @@ -253,8 +255,8 @@ private[chisel3] object MonoConnect { // SINK SOURCE // CHILD MOD CURRENT MOD case (Input, _) => issueConnect(sink, source) - case (Output, _) => throw UnwritableSinkException - case (Internal, _) => throw UnwritableSinkException + case (Output, _) => throw UnwritableSinkException(sink, source) + case (Internal, _) => throw UnwritableSinkException(sink, source) } } @@ -268,15 +270,15 @@ private[chisel3] object MonoConnect { // CHILD MOD CHILD MOD case (Input, Input) => issueConnect(sink, source) case (Input, Output) => issueConnect(sink, source) - case (Output, _) => throw UnwritableSinkException + case (Output, _) => throw UnwritableSinkException(sink, source) case (_, Internal) => { if (!(connectCompileOptions.dontAssumeDirectionality)) { issueConnect(sink, source) } else { - throw UnreadableSourceException + throw UnreadableSourceException(sink, source) } } - case (Internal, _) => throw UnwritableSinkException + case (Internal, _) => throw UnwritableSinkException(sink, source) } } diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index f56c3b15..ac784882 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -7,10 +7,11 @@ import chisel3.internal.sourceinfo.{NoSourceInfo, SourceInfo, SourceLine, Unloca import firrtl.{ir => fir} import chisel3.internal.{HasId, castToInt, throwException} -import scala.annotation.tailrec +import scala.annotation.{nowarn, tailrec} import scala.collection.immutable.Queue import scala.collection.immutable.LazyList // Needed for 2.12 alias +@nowarn("msg=class Port") // delete when Port becomes private private[chisel3] object Converter { // TODO modeled on unpack method on Printable, refactor? def unpack(pable: Printable, ctx: Component): (String, Seq[Arg]) = pable match { @@ -142,8 +143,8 @@ private[chisel3] object Converter { Some(fir.IsInvalid(convert(info), convert(arg, ctx, info))) case e @ DefInstance(info, id, _) => Some(fir.DefInstance(convert(info), e.name, id.name)) - case Stop(info, clock, ret) => - Some(fir.Stop(convert(info), ret, convert(clock, ctx, info), firrtl.Utils.one)) + case e @ Stop(_, info, clock, ret) => + Some(fir.Stop(convert(info), ret, convert(clock, ctx, info), firrtl.Utils.one, e.name)) case e @ Printf(_, info, clock, pable) => val (fmt, args) = unpack(pable, ctx) Some(fir.Print(convert(info), fir.StringLit(fmt), diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 0b568548..68f5f18e 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -13,6 +13,7 @@ import _root_.firrtl.annotations.Annotation import scala.collection.immutable.NumericRange import scala.math.BigDecimal.RoundingMode +import scala.annotation.nowarn case class PrimOp(name: String) { @@ -64,7 +65,7 @@ object PrimOp { val AsAsyncResetOp = PrimOp("asAsyncReset") } -abstract class Arg { +sealed abstract class Arg { def localName: String = name def contextualName(ctx: Component): String = name def fullName(ctx: Component): String = contextualName(ctx) @@ -86,6 +87,19 @@ case class Node(id: HasId) extends Arg { } } +object Arg { + def earlyLocalName(id: HasId): String = id.getOptionRef match { + case Some(Index(Node(imm), Node(value))) => s"${earlyLocalName(imm)}[${earlyLocalName(imm)}]" + case Some(Index(Node(imm), arg)) => s"${earlyLocalName(imm)}[${arg.localName}]" + case Some(Slot(Node(imm), name)) => s"${earlyLocalName(imm)}.$name" + case Some(arg) => arg.name + case None => id match { + case data: Data => data._computeName(None, Some("?")).get + case _ => "?" + } + } +} + abstract class LitArg(val num: BigInt, widthArg: Width) extends Arg { private[chisel3] def forcedWidth = widthArg.known private[chisel3] def width: Width = if (forcedWidth) widthArg else Width(minWidth) @@ -196,6 +210,7 @@ case class Slot(imm: Node, name: String) extends Arg { if (immName.isEmpty) name else s"$immName.$name" } } + case class Index(imm: Arg, value: Arg) extends Arg { def name: String = s"[$value]" override def contextualName(ctx: Component): String = s"${imm.contextualName(ctx)}[${value.contextualName(ctx)}]" @@ -775,6 +790,7 @@ case class DefRegInit(sourceInfo: SourceInfo, id: Data, clock: Arg, reset: Arg, case class DefMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt) extends Definition case class DefSeqMemory(sourceInfo: SourceInfo, id: HasId, t: Data, size: BigInt, readUnderWrite: fir.ReadUnderWrite.Value) extends Definition case class DefMemPort[T <: Data](sourceInfo: SourceInfo, id: T, source: Node, dir: MemPortDirection, index: Arg, clock: Arg) extends Definition +@nowarn("msg=class Port") // delete when Port becomes private case class DefInstance(sourceInfo: SourceInfo, id: BaseModule, ports: Seq[Port]) extends Definition case class WhenBegin(sourceInfo: SourceInfo, pred: Arg) extends Command case class WhenEnd(sourceInfo: SourceInfo, firrtlDepth: Int, hasAlt: Boolean = false) extends Command @@ -784,7 +800,9 @@ case class Connect(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command case class BulkConnect(sourceInfo: SourceInfo, loc1: Node, loc2: Node) extends Command case class Attach(sourceInfo: SourceInfo, locs: Seq[Node]) extends Command case class ConnectInit(sourceInfo: SourceInfo, loc: Node, exp: Arg) extends Command -case class Stop(sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Command +case class Stop(id: stop.Stop, sourceInfo: SourceInfo, clock: Arg, ret: Int) extends Definition +// Note this is just deprecated which will cause deprecation warnings, use @nowarn +@deprecated("This API should never have been public, for Module port reflection, use DataMirror.modulePorts", "Chisel 3.5") case class Port(id: Data, dir: SpecifiedDirection) case class Printf(id: printf.Printf, sourceInfo: SourceInfo, clock: Arg, pable: Printable) extends Definition object Formal extends Enumeration { @@ -792,14 +810,17 @@ object Formal extends Enumeration { val Assume = Value("assume") val Cover = Value("cover") } -case class Verification[T <: BaseSim](id: T, op: Formal.Value, sourceInfo: SourceInfo, clock: Arg, +case class Verification[T <: VerificationStatement](id: T, op: Formal.Value, sourceInfo: SourceInfo, clock: Arg, predicate: Arg, message: String) extends Definition +@nowarn("msg=class Port") // delete when Port becomes private abstract class Component extends Arg { def id: BaseModule def name: String def ports: Seq[Port] } +@nowarn("msg=class Port") // delete when Port becomes private case class DefModule(id: RawModule, name: String, ports: Seq[Port], commands: Seq[Command]) extends Component +@nowarn("msg=class Port") // delete when Port becomes private case class DefBlackBox(id: BaseBlackBox, name: String, ports: Seq[Port], topDir: SpecifiedDirection, params: Map[String, Param]) extends Component case class Circuit(name: String, components: Seq[Component], annotations: Seq[ChiselAnnotation], renames: RenameMap) { diff --git a/core/src/main/scala/chisel3/internal/plugin/package.scala b/core/src/main/scala/chisel3/internal/plugin/package.scala index c17baf22..9b9b41cd 100644 --- a/core/src/main/scala/chisel3/internal/plugin/package.scala +++ b/core/src/main/scala/chisel3/internal/plugin/package.scala @@ -3,27 +3,8 @@ package chisel3.internal package object plugin { - /** Used by Chisel's compiler plugin to automatically name signals - * DO NOT USE in your normal Chisel code!!! - * - * @param name The name to use - * @param nameMe The thing to be named - * @tparam T The type of the thing to be named - * @return The thing, but now named - * @note This is the version called by chisel3-plugin prior to v3.4.1 - */ - def autoNameRecursively[T <: Any](name: String, nameMe: T): T = { - chisel3.internal.Builder.nameRecursively( - name.replace(" ", ""), - nameMe, - (id: chisel3.internal.HasId, n: String) => id.autoSeed(n) - ) - nameMe - } // The actual implementation - // Cannot be unified with (String, T) => T (v3.4.0 version) because of special behavior of ports - // in .autoSeed private def _autoNameRecursively[T <: Any](prevId: Long, name: String, nameMe: T): T = { chisel3.internal.Builder.nameRecursively( name, diff --git a/core/src/main/scala/chisel3/internal/prefix.scala b/core/src/main/scala/chisel3/internal/prefix.scala index 9dc14901..620d0864 100644 --- a/core/src/main/scala/chisel3/internal/prefix.scala +++ b/core/src/main/scala/chisel3/internal/prefix.scala @@ -51,7 +51,7 @@ private[chisel3] object prefix { // scalastyle:ignore // This causes extra prefixes to be added, and subsequently cleared in the // Module constructor. Thus, we need to just make sure if the previous push // was an incorrect one, to not pop off an empty stack - if(Builder.getPrefix().nonEmpty) Builder.popPrefix() + if(Builder.getPrefix.nonEmpty) Builder.popPrefix() ret } } @@ -77,7 +77,7 @@ private[chisel3] object noPrefix { * @return The return value of the provided function */ def apply[T](f: => T): T = { - val prefix = Builder.getPrefix() + val prefix = Builder.getPrefix Builder.clearPrefix() val ret = f Builder.setPrefix(prefix) diff --git a/core/src/main/scala/chisel3/package.scala b/core/src/main/scala/chisel3/package.scala index 64cfa8b9..faca3ae4 100644 --- a/core/src/main/scala/chisel3/package.scala +++ b/core/src/main/scala/chisel3/package.scala @@ -50,10 +50,18 @@ package object chisel3 { /** Int to UInt conversion, recommended style for variables. */ - def asUInt(): UInt = UInt.Lit(bigint, Width()) + def asUInt: UInt = UInt.Lit(bigint, Width()) + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def asUInt(dummy: Int*): UInt = asUInt + /** Int to SInt conversion, recommended style for variables. */ - def asSInt(): SInt = SInt.Lit(bigint, Width()) + def asSInt: SInt = SInt.Lit(bigint, Width()) + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def asSInt(dummy: Int*): SInt = asSInt + /** Int to UInt conversion with specified width, recommended style for variables. */ def asUInt(width: Width): UInt = UInt.Lit(bigint, width) @@ -68,17 +76,21 @@ package object chisel3 { implicit class fromStringToLiteral(str: String) { /** String to UInt parse, recommended style for constants. */ - def U: UInt = str.asUInt() + def U: UInt = str.asUInt /** String to UInt parse with specified width, recommended style for constants. */ def U(width: Width): UInt = str.asUInt(width) /** String to UInt parse, recommended style for variables. */ - def asUInt(): UInt = { + def asUInt: UInt = { val bigInt = parse(str) UInt.Lit(bigInt, Width(bigInt.bitLength max 1)) } + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def asUInt(dummy: Int*): UInt = asUInt + /** String to UInt parse with specified width, recommended style for variables. */ def asUInt(width: Width): UInt = UInt.Lit(parse(str), width) @@ -107,7 +119,10 @@ package object chisel3 { /** Boolean to Bool conversion, recommended style for variables. */ - def asBool(): Bool = Bool.Lit(boolean) + def asBool: Bool = Bool.Lit(boolean) + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def asBool(dummy: Int*): Bool = asBool } // Fixed Point is experimental for now, but we alias the implicit conversion classes here @@ -206,6 +221,7 @@ package object chisel3 { def getDataElements(a: Aggregate): Seq[Element] = { a.allElements } + @deprecated("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] = m.getPorts class BindingException(message: String) extends ChiselException(message) |
