From 5d70874eba009d9f17886a2edd19ce0fa02a77b3 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 1 Aug 2022 21:48:17 +0000 Subject: Remove Tuesday from Dev Meeting in README (#2653) (#2654) We no longer meet on Tuesdays, only Mondays. (cherry picked from commit 28c79fd4b4a0948aa6122f88c56d729b5240ab62) Co-authored-by: Jack Koenig --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d7b2dfd3..2383944f 100644 --- a/README.md +++ b/README.md @@ -237,7 +237,7 @@ If you are migrating from Chisel2, see [the migration guide](https://www.chisel- ### Chisel Dev Meeting -Chisel/FIRRTL development meetings happen every Monday and Tuesday from 1100--1200 PT. +Chisel/FIRRTL development meetings happen every Monday from 1100--1200 PT. Call-in info and meeting notes are available [here](https://docs.google.com/document/d/1BLP2DYt59DqI-FgFCcjw8Ddl4K-WU0nHmQu0sZ_wAGo/). -- cgit v1.2.3 From 6b658d740d976c7706c53c756acd069262d9ef59 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 2 Aug 2022 00:31:11 +0000 Subject: Remove remaining chiselNames (#2635) (#2656) (cherry picked from commit 8538269a14e0d5a1163298a79aa43b77a380aabc) Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com>--- src/main/scala/chisel3/compatibility.scala | 2 -- src/main/scala/chisel3/util/Decoupled.scala | 3 --- 2 files changed, 5 deletions(-) diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index f3754e00..d140725f 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -4,7 +4,6 @@ * while moving to the more standard package naming convention `chisel3` (lowercase c). */ import chisel3._ // required for implicit conversions. -import chisel3.experimental.chiselName import chisel3.util.random.FibonacciLFSR import chisel3.stage.{phases, ChiselCircuitAnnotation, ChiselOutputFileAnnotation, ChiselStage} @@ -617,7 +616,6 @@ package object Chisel { /** Generates a 16-bit linear feedback shift register, returning the register contents. * @param increment optional control to gate when the LFSR updates. */ - @chiselName def apply(increment: Bool = true.B): UInt = VecInit( FibonacciLFSR diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index b21bd04f..f8c8f9e9 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -7,7 +7,6 @@ package chisel3.util import chisel3._ import chisel3.experimental.{requireIsChiselType, DataMirror, Direction} -import chisel3.internal.naming._ // can't use chisel3_ version because of compile order import scala.annotation.nowarn @@ -136,7 +135,6 @@ object Decoupled { * * @note unsafe (and will error) on the producer (input) side of an IrrevocableIO */ - @chiselName def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = { require( DataMirror.directionOf(irr.bits) == Direction.Output, @@ -403,7 +401,6 @@ object Queue { * consumer.io.in <> Queue(producer.io.out, 16) * }}} */ - @chiselName def irrevocable[T <: Data]( enq: ReadyValidIO[T], entries: Int = 2, -- cgit v1.2.3 From 945416c628656498be7a98dcd4899f2b9e830c00 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 2 Aug 2022 16:28:23 +0000 Subject: Fix example in Chisel Type vs Scala Type article (#2655) (#2657) (cherry picked from commit 0b2c211beefeefb72c86ea69a0b2a0101b2f2c20) Co-authored-by: Megan Wachs --- docs/src/explanations/chisel-type-vs-scala-type.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/src/explanations/chisel-type-vs-scala-type.md b/docs/src/explanations/chisel-type-vs-scala-type.md index 6c311a21..ead509d3 100644 --- a/docs/src/explanations/chisel-type-vs-scala-type.md +++ b/docs/src/explanations/chisel-type-vs-scala-type.md @@ -245,13 +245,13 @@ that you have more information than it can infer to convert Scala types: ```scala mdoc:silent class ScalaCastingModule(gen: () => Bundle) extends Module { - val io = gen().asInstanceOf[MyBundle] + val io = IO(Output(gen().asInstanceOf[MyBundle])) io.foo := 0.U } ``` This works if we do indeed have more information than the compiler: -``` scala mdoc:silent +```scala mdoc:silent ChiselStage.elaborate(new ScalaCastingModule( () => new MyBundle(3))) ``` -- cgit v1.2.3 From db18ae16a26dab5231ca83172c88b9735a977582 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 5 Aug 2022 00:59:23 +0000 Subject: Replace some options with nullable vars (backport #2658) (#2659) * Replace some options with nullable vars (#2658) Co-authored-by: Jack Koenig (cherry picked from commit ac460bfeb16c8e7d0dc00975bb03f73c0fea2103) # Conflicts: # core/src/main/scala/chisel3/internal/Builder.scala * Fix backport conflicts (#2661) Co-authored-by: Zachary Yedidia --- core/src/main/scala/chisel3/Data.scala | 10 +++++--- core/src/main/scala/chisel3/internal/Builder.scala | 30 +++++++++++++++------- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 592ebe25..956c7996 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -501,14 +501,15 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { // Binding stores information about this node's position in the hardware graph. // This information is supplemental (more than is necessary to generate FIRRTL) and is used to // perform checks in Chisel, where more informative error messages are possible. - private var _binding: Option[Binding] = None + private var _bindingVar: Binding = null // using nullable var for better memory usage + private def _binding: Option[Binding] = Option(_bindingVar) // Only valid after node is bound (synthesizable), crashes otherwise protected[chisel3] def binding: Option[Binding] = _binding protected def binding_=(target: Binding) { if (_binding.isDefined) { throw RebindingException(s"Attempted reassignment of binding to $this, from: ${target}") } - _binding = Some(target) + _bindingVar = target } // Similar to topBindingOpt except it explicitly excludes SampleElements which are bound but not @@ -540,14 +541,15 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { // Both are only valid after binding is set. // Direction of this node, accounting for parents (force Input / Output) and children. - private var _direction: Option[ActualDirection] = None + private var _directionVar: ActualDirection = null // using nullable var for better memory usage + private def _direction: Option[ActualDirection] = Option(_directionVar) private[chisel3] def direction: ActualDirection = _direction.get private[chisel3] def direction_=(actualDirection: ActualDirection) { if (_direction.isDefined) { throw RebindingException(s"Attempted reassignment of resolved direction to $this") } - _direction = Some(actualDirection) + _directionVar = actualDirection } private[chisel3] def stringAccessor(chiselType: String): String = { diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 61f94f8f..25f4c44c 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -88,10 +88,19 @@ trait InstanceId { private[chisel3] trait HasId extends InstanceId { private[chisel3] def _onModuleClose: Unit = {} - private[chisel3] var _parent: Option[BaseModule] = Builder.currentModule + // using nullable var for better memory usage + private var _parentVar: BaseModule = Builder.currentModule.getOrElse(null) + private[chisel3] def _parent: Option[BaseModule] = Option(_parentVar) + private[chisel3] def _parent_=(target: Option[BaseModule]): Unit = { + _parentVar = target.getOrElse(null) + } // Set if the returned top-level module of a nested call to the Chisel Builder, see Definition.apply - private[chisel3] var _circuit: Option[BaseModule] = None + private var _circuitVar: BaseModule = null // using nullable var for better memory usage + private[chisel3] def _circuit: Option[BaseModule] = Option(_circuitVar) + private[chisel3] def _circuit_=(target: Option[BaseModule]): Unit = { + _circuitVar = target.getOrElse(null) + } private[chisel3] val _id: Long = Builder.idGen.next @@ -100,10 +109,12 @@ private[chisel3] trait HasId extends InstanceId { override def equals(that: Any): Boolean = super.equals(that) // Contains suggested seed (user-decided seed) - private var suggested_seed: Option[String] = None + private var suggested_seedVar: String = null // using nullable var for better memory usage + private def suggested_seed: Option[String] = Option(suggested_seedVar) // Contains the seed computed automatically by the compiler plugin - private var auto_seed: Option[String] = None + private var auto_seedVar: String = null // using nullable var for better memory usage + private def auto_seed: Option[String] = Option(auto_seedVar) // Prefix for use in naming // - Defaults to prefix at time when object is created @@ -131,7 +142,7 @@ private[chisel3] trait HasId extends InstanceId { private[chisel3] def autoSeed(seed: String): this.type = forceAutoSeed(seed) // Bypass the overridden behavior of autoSeed in [[Data]], apply autoSeed even to ports private[chisel3] def forceAutoSeed(seed: String): this.type = { - auto_seed = Some(seed) + auto_seedVar = seed for (hook <- auto_postseed_hooks.reverse) { hook(seed) } naming_prefix = Builder.getPrefix this @@ -159,7 +170,7 @@ private[chisel3] trait HasId extends InstanceId { * @return this object */ def suggestName(seed: => String): this.type = { - if (suggested_seed.isEmpty) suggested_seed = Some(seed) + if (suggested_seed.isEmpty) suggested_seedVar = seed naming_prefix = Builder.getPrefix for (hook <- suggest_postseed_hooks.reverse) { hook(seed) } this @@ -171,7 +182,7 @@ private[chisel3] trait HasId extends InstanceId { private[chisel3] def forceFinalName(seed: String): this.type = { // This could be called with user prefixes, ignore them noPrefix { - suggested_seed = Some(seed) + suggested_seedVar = seed this.suggestName(seed) } } @@ -212,11 +223,12 @@ private[chisel3] trait HasId extends InstanceId { naming_prefix = Nil } - private var _ref: Option[Arg] = None + private var _refVar: Arg = null // using nullable var for better memory usage + private def _ref: Option[Arg] = Option(_refVar) private[chisel3] def setRef(imm: Arg): Unit = setRef(imm, false) private[chisel3] def setRef(imm: Arg, force: Boolean): Unit = { if (_ref.isEmpty || force) { - _ref = Some(imm) + _refVar = imm } } private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name)) -- cgit v1.2.3 From 7bad3d2ec316f24f3da79d1dfef19e128cfe8bf5 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 12 Aug 2022 20:04:33 +0000 Subject: Add ability to suppress enum cast warnings (#2671) (#2674) (cherry picked from commit 1ad820f7f549eddcd7188b737f59a240e48a7f0a) Co-authored-by: Zachary Yedidia --- core/src/main/scala/chisel3/StrongEnum.scala | 32 ++++++++++++++++- core/src/main/scala/chisel3/internal/Builder.scala | 4 +++ docs/src/explanations/chisel-enum.md | 25 +++++++++++++ src/test/scala/chiselTests/StrongEnum.scala | 41 ++++++++++++++++++++++ 4 files changed, 101 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala index cd6f11ee..6d8ceb2f 100644 --- a/core/src/main/scala/chisel3/StrongEnum.scala +++ b/core/src/main/scala/chisel3/StrongEnum.scala @@ -339,7 +339,7 @@ abstract class EnumFactory { } else if (n.getWidth > this.getWidth) { throwException(s"The UInt being cast to $enumTypeName is wider than $enumTypeName's width ($getWidth)") } else { - if (warn && !this.isTotal) { + if (!Builder.suppressEnumCastWarning && warn && !this.isTotal) { Builder.warning( s"Casting non-literal UInt to $enumTypeName. You can use $enumTypeName.safe to cast without this warning." ) @@ -409,3 +409,33 @@ private[chisel3] class UnsafeEnum(override val width: Width) extends EnumType(Un override def cloneType: this.type = new UnsafeEnum(width).asInstanceOf[this.type] } private object UnsafeEnum extends EnumFactory + +/** Suppress enum cast warnings + * + * Users should use [[EnumFactory.safe .safe]] when possible. + * + * This is primarily used for casting from [[UInt]] to a Bundle type that contains an Enum. + * {{{ + * class MyBundle extends Bundle { + * val addr = UInt(8.W) + * val op = OpEnum() + * } + * + * // Since this is a cast to a Bundle, cannot use OpCode.safe + * val bundle = suppressEnumCastWarning { + * someUInt.asTypeOf(new MyBundle) + * } + * }}} + */ +object suppressEnumCastWarning { + def apply[T](block: => T): T = { + val parentWarn = Builder.suppressEnumCastWarning + + Builder.suppressEnumCastWarning = true + + val res = block // execute block + + Builder.suppressEnumCastWarning = parentWarn + res + } +} diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 25f4c44c..b2c98309 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -435,6 +435,7 @@ private[chisel3] class DynamicContext( var currentReset: Option[Reset] = None val errors = new ErrorLog val namingStack = new NamingStack + // Used to indicate if this is the top-level module of full elaboration, or from a Definition var inDefinition: Boolean = false } @@ -451,6 +452,9 @@ private[chisel3] object Builder extends LazyLogging { dynamicContextVar.value.get } + // Used to suppress warnings when casting from a UInt to an Enum + var suppressEnumCastWarning: Boolean = false + // Returns the current dynamic context def captureContext(): DynamicContext = dynamicContext // Sets the current dynamic contents diff --git a/docs/src/explanations/chisel-enum.md b/docs/src/explanations/chisel-enum.md index a390aea4..16b5570d 100644 --- a/docs/src/explanations/chisel-enum.md +++ b/docs/src/explanations/chisel-enum.md @@ -17,6 +17,7 @@ import chisel3._ import chisel3.util._ import chisel3.stage.ChiselStage import chisel3.experimental.ChiselEnum +import chisel3.experimental.suppressEnumCastWarning ``` ```scala mdoc:invisible @@ -167,6 +168,30 @@ val (log2, _) = grabLog(ChiselStage.emitChirrtl(new SafeFromUInt)) println(s"```\n$log2```") ``` +You can also suppress the warning by using `suppressEnumCastWarning`. This is +primarily used for casting from [[UInt]] to a Bundle type that contains an +Enum, where the [[UInt]] is known to be valid for the Bundle type. + +```scala mdoc +class MyBundle extends Bundle { + val addr = UInt(8.W) + val op = Opcode() +} + +class SuppressedFromUInt extends Module { + val in = IO(Input(UInt(15.W))) + val out = IO(Output(new MyBundle())) + suppressEnumCastWarning { + out := in.asTypeOf(new MyBundle) + } +} +``` + +```scala mdoc:invisible +val (log3, _) = grabLog(ChiselStage.emitChirrtl(new SuppressedFromUInt)) +assert(log3.isEmpty) +``` + ## Testing The _Type_ of the enums values is `.Type` which can be useful for passing the values diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index 44ed77f9..5b1b13fd 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -5,6 +5,7 @@ package chiselTests import chisel3._ import chisel3.experimental.ChiselEnum import chisel3.experimental.AffectsChiselPrefix +import chisel3.experimental.suppressEnumCastWarning import chisel3.internal.firrtl.UnknownWidth import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util._ @@ -481,6 +482,46 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { (log should not).include("warn") } + it should "suppress warning using suppressEnumCastWarning" in { + object TestEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out = IO(Output(TestEnum())) + suppressEnumCastWarning { + val res = TestEnum(in) + out := res + } + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + (log should not).include("warn") + } + + it should "suppress exactly one warning using suppressEnumCastWarning" in { + object TestEnum1 extends ChiselEnum { + val e0, e1, e2 = Value + } + object TestEnum2 extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(TestEnum1())) + val out2 = IO(Output(TestEnum2())) + suppressEnumCastWarning { + out1 := TestEnum1(in) + } + out2 := TestEnum2(in) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + log should include("warn") + log should include("TestEnum2") // not suppressed + (log should not).include("TestEnum1") // suppressed + } + "Casting a UInt to an Enum with .safe" should "NOT warn" in { object MyEnum extends ChiselEnum { val e0, e1, e2 = Value -- cgit v1.2.3 From d344e8a91bdbfedc28527c3fc7d6d243dff9e3e6 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 12 Aug 2022 20:56:42 +0000 Subject: Show equivalent warnings/errors only once (#2673) (#2675) (cherry picked from commit ae76ff4cb303a6646e48dc044be47051b67e7cbb) Co-authored-by: Zachary Yedidia --- core/src/main/scala/chisel3/internal/Error.scala | 41 ++++++++++++++---------- src/test/scala/chiselTests/WarningSpec.scala | 35 ++++++++++++++++++++ 2 files changed, 59 insertions(+), 17 deletions(-) create mode 100644 src/test/scala/chiselTests/WarningSpec.scala diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index 3b0846eb..98db7109 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -177,20 +177,31 @@ private[chisel3] object ErrorLog { } private[chisel3] class ErrorLog { + def getLoc(loc: Option[StackTraceElement]): String = { + loc match { + case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}" + case None => "(unknown)" + } + } /** Log an error message */ - def error(m: => String): Unit = - errors += new Error(m, getUserLineNumber) + def error(m: => String): Unit = { + val loc = getUserLineNumber + errors += (((m, getLoc(loc)), new Error(m, loc))) + } /** Log a warning message */ - def warning(m: => String): Unit = - errors += new Warning(m, getUserLineNumber) - - /** Log a warning message without a source locator */ - def warningNoLoc(m: => String): Unit = { - errors += new Warning(m, None) + def warning(m: => String): Unit = { + val loc = getUserLineNumber + errors += (((m, getLoc(loc)), new Warning(m, loc))) } + /** Log a warning message without a source locator. This is used when the + * locator wouldn't be helpful (e.g., due to lazy values). + */ + def warningNoLoc(m: => String): Unit = + errors += (((m, ""), new Warning(m, None))) + /** Emit an informational message */ @deprecated("This method will be removed in 3.5", "3.4") def info(m: String): Unit = @@ -200,11 +211,7 @@ private[chisel3] class ErrorLog { def deprecated(m: => String, location: Option[String]): Unit = { val sourceLoc = location match { case Some(loc) => loc - case None => - getUserLineNumber match { - case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}" - case None => "(unknown)" - } + case None => getLoc(getUserLineNumber) } val thisEntry = (m, sourceLoc) @@ -217,7 +224,7 @@ private[chisel3] class ErrorLog { case ((message, sourceLoc), count) => logger.warn(s"${ErrorLog.depTag} $sourceLoc ($count calls): $message") } - errors.foreach(e => logger.error(e.toString)) + errors.foreach(e => logger.error(e._2.toString)) if (!deprecations.isEmpty) { logger.warn( @@ -233,8 +240,8 @@ private[chisel3] class ErrorLog { logger.warn(s"""${ErrorLog.warnTag} scalacOptions := Seq("-unchecked", "-deprecation")""") } - val allErrors = errors.filter(_.isFatal) - val allWarnings = errors.filter(!_.isFatal) + val allErrors = errors.filter(_._2.isFatal) + val allWarnings = errors.filter(!_._2.isFatal) if (!allWarnings.isEmpty && !allErrors.isEmpty) { logger.warn( @@ -289,7 +296,7 @@ private[chisel3] class ErrorLog { .headOption } - private val errors = ArrayBuffer[LogEntry]() + private val errors = LinkedHashMap[(String, String), LogEntry]() private val deprecations = LinkedHashMap[(String, String), Int]() private val startTime = System.currentTimeMillis diff --git a/src/test/scala/chiselTests/WarningSpec.scala b/src/test/scala/chiselTests/WarningSpec.scala new file mode 100644 index 00000000..bf3830d6 --- /dev/null +++ b/src/test/scala/chiselTests/WarningSpec.scala @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.util._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.experimental.ChiselEnum +import chisel3.experimental.EnumType +import chiselTests.ChiselFlatSpec + +class WarningSpec extends ChiselFlatSpec with Utils { + behavior.of("Warnings") + + "Warnings" should "be de-duplicated" in { + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } + + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(MyEnum())) + val out2 = IO(Output(MyEnum())) + def func(out: EnumType): Unit = { + out := MyEnum(in) + } + func(out1) + func(out2) + } + val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) + def countSubstring(s: String, sub: String) = + s.sliding(sub.length).count(_ == sub) + countSubstring(log, "Casting non-literal UInt") should be(1) + } +} -- cgit v1.2.3 From c4dec947d54a52c3092bd7855180d42afaae3776 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Sat, 13 Aug 2022 00:17:56 +0000 Subject: Add option to treat warnings as errors (backport #2676) (#2677) * Add option to treat warnings as errors (#2676) Add --warnings-as-errors option (cherry picked from commit 498946663726955c380a1e420f5d7b9630000aad) # Conflicts: # core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala # core/src/main/scala/chisel3/internal/Builder.scala # src/main/scala/chisel3/aop/injecting/InjectingAspect.scala # src/main/scala/chisel3/stage/ChiselOptions.scala # src/main/scala/chisel3/stage/package.scala # src/main/scala/chisel3/stage/phases/Elaborate.scala * Resolve backport conflicts Co-authored-by: Zachary Yedidia Co-authored-by: Jack Koenig --- .../experimental/hierarchy/Definition.scala | 2 +- core/src/main/scala/chisel3/internal/Builder.scala | 8 +++-- core/src/main/scala/chisel3/internal/Error.scala | 9 ++++-- .../chisel3/aop/injecting/InjectingAspect.scala | 7 +++- .../scala/chisel3/stage/ChiselAnnotations.scala | 18 +++++++++++ src/main/scala/chisel3/stage/ChiselCli.scala | 1 + src/main/scala/chisel3/stage/ChiselOptions.scala | 3 ++ src/main/scala/chisel3/stage/package.scala | 1 + .../scala/chisel3/stage/phases/Elaborate.scala | 7 +++- src/test/scala/chiselTests/WarningSpec.scala | 37 +++++++++++++--------- 10 files changed, 69 insertions(+), 24 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala index 36bf6f87..99eacc7d 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Definition.scala @@ -103,7 +103,7 @@ object Definition extends SourceInfoDoc { ): Definition[T] = { val dynamicContext = { val context = Builder.captureContext() - new DynamicContext(Nil, context.throwOnFirstError, context.warnReflectiveNaming) + new DynamicContext(Nil, context.throwOnFirstError, context.warnReflectiveNaming, context.warningsAsErrors) } Builder.globalNamespace.copyTo(dynamicContext.globalNamespace) dynamicContext.inDefinition = true diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index b2c98309..75d08f92 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -244,7 +244,8 @@ private[chisel3] trait HasId extends InstanceId { // These accesses occur after Chisel elaboration so we cannot use the normal // Builder.deprecated mechanism, we have to create our own one off ErrorLog and print the // warning right away. - val errors = new ErrorLog + // It's especially bad because --warnings-as-errors does not work with these warnings + val errors = new ErrorLog(false) val logger = new _root_.logger.Logger(this.getClass.getName) val msg = "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated. " + "This will become an error in Chisel 3.6." @@ -376,7 +377,8 @@ private[chisel3] class ChiselContext() { private[chisel3] class DynamicContext( val annotationSeq: AnnotationSeq, val throwOnFirstError: Boolean, - val warnReflectiveNaming: Boolean) { + val warnReflectiveNaming: Boolean, + val warningsAsErrors: Boolean) { val importDefinitionAnnos = annotationSeq.collect { case a: ImportDefinitionAnnotation[_] => a } // Map holding the actual names of extModules @@ -433,7 +435,7 @@ private[chisel3] class DynamicContext( var whenStack: List[WhenContext] = Nil var currentClock: Option[Clock] = None var currentReset: Option[Reset] = None - val errors = new ErrorLog + val errors = new ErrorLog(warningsAsErrors) val namingStack = new NamingStack // Used to indicate if this is the top-level module of full elaboration, or from a Definition diff --git a/core/src/main/scala/chisel3/internal/Error.scala b/core/src/main/scala/chisel3/internal/Error.scala index 98db7109..da968d2a 100644 --- a/core/src/main/scala/chisel3/internal/Error.scala +++ b/core/src/main/scala/chisel3/internal/Error.scala @@ -176,7 +176,7 @@ private[chisel3] object ErrorLog { val errTag = s"[${Console.RED}error${Console.RESET}]" } -private[chisel3] class ErrorLog { +private[chisel3] class ErrorLog(warningsAsErrors: Boolean) { def getLoc(loc: Option[StackTraceElement]): String = { loc match { case Some(elt: StackTraceElement) => s"${elt.getFileName}:${elt.getLineNumber}" @@ -190,17 +190,20 @@ private[chisel3] class ErrorLog { errors += (((m, getLoc(loc)), new Error(m, loc))) } + private def warn(m: => String, loc: Option[StackTraceElement]): LogEntry = + if (warningsAsErrors) new Error(m, loc) else new Warning(m, loc) + /** Log a warning message */ def warning(m: => String): Unit = { val loc = getUserLineNumber - errors += (((m, getLoc(loc)), new Warning(m, loc))) + errors += (((m, getLoc(loc)), warn(m, loc))) } /** Log a warning message without a source locator. This is used when the * locator wouldn't be helpful (e.g., due to lazy values). */ def warningNoLoc(m: => String): Unit = - errors += (((m, ""), new Warning(m, None))) + errors += (((m, ""), warn(m, None))) /** Emit an informational message */ @deprecated("This method will be removed in 3.5", "3.4") diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 087bdae2..ecce19e1 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -59,7 +59,12 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module => val chiselOptions = view[ChiselOptions](annotationsInAspect) val dynamicContext = - new DynamicContext(annotationsInAspect, chiselOptions.throwOnFirstError, chiselOptions.warnReflectiveNaming) + new DynamicContext( + annotationsInAspect, + chiselOptions.throwOnFirstError, + chiselOptions.warnReflectiveNaming, + chiselOptions.warningsAsErrors + ) // Add existing module names into the namespace. If injection logic instantiates new modules // which would share the same name, they will get uniquified accordingly moduleNames.foreach { n => diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index eda05a7d..f65fabdc 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -79,6 +79,24 @@ case object ThrowOnFirstErrorAnnotation } +/** When enabled, warnings will be treated as errors. + */ +case object WarningsAsErrorsAnnotation + extends NoTargetAnnotation + with ChiselOption + with HasShellOptions + with Unserializable { + + val options = Seq( + new ShellOption[Unit]( + longOption = "warnings-as-errors", + toAnnotationSeq = _ => Seq(WarningsAsErrorsAnnotation), + helpText = "Treat warnings as errors" + ) + ) + +} + /** Warn when reflective naming changes names of signals */ case object WarnReflectiveNamingAnnotation extends NoTargetAnnotation diff --git a/src/main/scala/chisel3/stage/ChiselCli.scala b/src/main/scala/chisel3/stage/ChiselCli.scala index 8c5eb79a..60e30152 100644 --- a/src/main/scala/chisel3/stage/ChiselCli.scala +++ b/src/main/scala/chisel3/stage/ChiselCli.scala @@ -10,6 +10,7 @@ trait ChiselCli { this: Shell => NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation, ThrowOnFirstErrorAnnotation, + WarningsAsErrorsAnnotation, WarnReflectiveNamingAnnotation, ChiselOutputFileAnnotation, ChiselGeneratorAnnotation diff --git a/src/main/scala/chisel3/stage/ChiselOptions.scala b/src/main/scala/chisel3/stage/ChiselOptions.scala index a03f3d7b..42a0ce68 100644 --- a/src/main/scala/chisel3/stage/ChiselOptions.scala +++ b/src/main/scala/chisel3/stage/ChiselOptions.scala @@ -9,6 +9,7 @@ class ChiselOptions private[stage] ( val printFullStackTrace: Boolean = false, val throwOnFirstError: Boolean = false, val warnReflectiveNaming: Boolean = false, + val warningsAsErrors: Boolean = false, val outputFile: Option[String] = None, val chiselCircuit: Option[Circuit] = None) { @@ -17,6 +18,7 @@ class ChiselOptions private[stage] ( printFullStackTrace: Boolean = printFullStackTrace, throwOnFirstError: Boolean = throwOnFirstError, warnReflectiveNaming: Boolean = warnReflectiveNaming, + warningsAsErrors: Boolean = warningsAsErrors, outputFile: Option[String] = outputFile, chiselCircuit: Option[Circuit] = chiselCircuit ): ChiselOptions = { @@ -26,6 +28,7 @@ class ChiselOptions private[stage] ( printFullStackTrace = printFullStackTrace, throwOnFirstError = throwOnFirstError, warnReflectiveNaming = warnReflectiveNaming, + warningsAsErrors = warningsAsErrors, outputFile = outputFile, chiselCircuit = chiselCircuit ) diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala index 10c8c524..c76dac17 100644 --- a/src/main/scala/chisel3/stage/package.scala +++ b/src/main/scala/chisel3/stage/package.scala @@ -19,6 +19,7 @@ package object stage { case PrintFullStackTraceAnnotation => c.copy(printFullStackTrace = true) case ThrowOnFirstErrorAnnotation => c.copy(throwOnFirstError = true) case WarnReflectiveNamingAnnotation => c.copy(warnReflectiveNaming = true) + case WarningsAsErrorsAnnotation => c.copy(warningsAsErrors = true) case ChiselOutputFileAnnotation(f) => c.copy(outputFile = Some(f)) case ChiselCircuitAnnotation(a) => c.copy(chiselCircuit = Some(a)) } diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala index ba29e5f2..a3fc4990 100644 --- a/src/main/scala/chisel3/stage/phases/Elaborate.scala +++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala @@ -30,7 +30,12 @@ class Elaborate extends Phase { val chiselOptions = view[ChiselOptions](annotations) try { val context = - new DynamicContext(annotations, chiselOptions.throwOnFirstError, chiselOptions.warnReflectiveNaming) + new DynamicContext( + annotations, + chiselOptions.throwOnFirstError, + chiselOptions.warnReflectiveNaming, + chiselOptions.warningsAsErrors + ) val (circuit, dut) = Builder.build(Module(gen()), context) Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut)) diff --git a/src/test/scala/chiselTests/WarningSpec.scala b/src/test/scala/chiselTests/WarningSpec.scala index bf3830d6..1cef1ffc 100644 --- a/src/test/scala/chiselTests/WarningSpec.scala +++ b/src/test/scala/chiselTests/WarningSpec.scala @@ -4,32 +4,39 @@ package chiselTests import chisel3._ import chisel3.util._ -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} +import chisel3.stage.ChiselStage import chisel3.experimental.ChiselEnum import chisel3.experimental.EnumType -import chiselTests.ChiselFlatSpec class WarningSpec extends ChiselFlatSpec with Utils { behavior.of("Warnings") - "Warnings" should "be de-duplicated" in { - object MyEnum extends ChiselEnum { - val e0, e1, e2 = Value - } + object MyEnum extends ChiselEnum { + val e0, e1, e2 = Value + } - class MyModule extends Module { - val in = IO(Input(UInt(2.W))) - val out1 = IO(Output(MyEnum())) - val out2 = IO(Output(MyEnum())) - def func(out: EnumType): Unit = { - out := MyEnum(in) - } - func(out1) - func(out2) + class MyModule extends Module { + val in = IO(Input(UInt(2.W))) + val out1 = IO(Output(MyEnum())) + val out2 = IO(Output(MyEnum())) + def func(out: EnumType): Unit = { + out := MyEnum(in) } + func(out1) + func(out2) + } + + "Warnings" should "be de-duplicated" in { val (log, _) = grabLog(ChiselStage.elaborate(new MyModule)) def countSubstring(s: String, sub: String) = s.sliding(sub.length).count(_ == sub) countSubstring(log, "Casting non-literal UInt") should be(1) } + + "Warnings" should "be treated as errors with warningsAsErrors" in { + a[ChiselException] should be thrownBy extractCause[ChiselException] { + val args = Array("--warnings-as-errors") + (new ChiselStage).emitChirrtl(new MyModule, args) + } + } } -- cgit v1.2.3 From 29702d7dd10a9eb7595a102bfffcee665bcf7394 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 15 Aug 2022 20:37:55 +0000 Subject: Printables for verification preconditions (backport #2663) (#2680) * Printables for verification preconditions (#2663) Add support for printable within assert and assume verification statements Co-authored-by: Girish Pai Co-authored-by: Megan Wachs Co-authored-by: Jack Koenig (cherry picked from commit 7df5653309b5e48e1732b335610d9a7e8449f903) * Waive MiMa false positive Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com> Co-authored-by: Jack Koenig --- build.sbt | 1 + .../main/scala/chisel3/VerificationStatement.scala | 230 ++++++++++++++++++--- src/test/scala/chiselTests/Assert.scala | 41 ++++ 3 files changed, 240 insertions(+), 32 deletions(-) diff --git a/build.sbt b/build.sbt index fb9ca94b..b64132fc 100644 --- a/build.sbt +++ b/build.sbt @@ -191,6 +191,7 @@ lazy val core = (project in file("core")). ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.VerificationStatement.forceName"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule._computeName"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule.forceName"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.VerificationStatement.failureMessage"), ) ). settings( diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala index 7229c412..1b13b86c 100644 --- a/core/src/main/scala/chisel3/VerificationStatement.scala +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -11,37 +11,71 @@ import chisel3.internal.sourceinfo.SourceInfo import scala.reflect.macros.blackbox -object assert { +/** Scaladoc information for internal verification statement macros + * that are used in objects assert, assume and cover. + * + * @groupdesc VerifPrintMacros + * + *

+ * '''These internal methods are not part of the public-facing API!''' + *

+ *
+ * + * @groupprio VerifPrintMacros 1001 + */ +trait VerifPrintMacrosDoc + +object assert extends VerifPrintMacrosDoc { + + /** Checks for a condition to be valid in the circuit at rising clock edge + * when not in reset. If the condition evaluates to false, the circuit + * simulation stops with an error. + * + * @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 _applyMacroWithStringMessage /** 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. + * Does not fire when in reset (defined as the current implicit reset, e.g. as set by + * the enclosing `withReset` or Module.reset. * * 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 + * @param cond condition, assertion fires (simulation fails) on a rising clock edge when false and reset is not asserted + * @param message optional chisel Printable type message * - * @note See [[printf.apply(fmt:String* printf]] for format string documentation + * @note See [[printf.apply(fmt:Printable)]] for documentation on printf using Printables * @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* + message: Printable )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions - ): Assert = macro _applyMacroWithMessage + ): Assert = macro _applyMacroWithPrintableMessage + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = macro _applyMacroWithNoMessage @@ -56,6 +90,11 @@ object assert { import VerificationStatement._ + /** @group VerifPrintMacros */ + @deprecated( + "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", + "Chisel 3.5" + ) def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, @@ -65,10 +104,38 @@ object assert { 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)" + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)},_root_.scala.Some(_root_.chisel3.Printable.pack($message,..$data)))($sourceInfo, $compileOptions)" } + /** @group VerifPrintMacros */ + def _applyMacroWithStringMessage( + 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("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)},_root_.scala.Some(_root_.chisel3.Printable.pack($message,..$data)))($sourceInfo, $compileOptions)" + } + + /** @group VerifPrintMacros */ + def _applyMacroWithPrintableMessage( + 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("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)" + } + + /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree @@ -76,11 +143,14 @@ object assert { compileOptions: c.Tree ): c.Tree = { import c.universe._ - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" } - /** Used by our macros. Do not call directly! */ + /** This will be removed in Chisel 3.6 in favor of the Printable version + * + * @group VerifPrintMacros + */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, @@ -92,14 +162,31 @@ object assert { ): Assert = { val id = new Assert() when(!Module.reset.asBool()) { - failureMessage("Assertion", line, cond, message, data) + failureMessage("Assertion", line, cond, message.map(Printable.pack(_, data: _*))) + Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) + } + id + } + + /** @group VerifPrintMacros */ + def _applyWithSourceLinePrintable( + cond: Bool, + line: SourceLineInfo, + message: Option[Printable] + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Assert = { + val id = new Assert() + when(!Module.reset.asBool()) { + failureMessage("Assertion", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) } id } } -object assume { +object assume extends VerifPrintMacrosDoc { /** Assumes a condition to be valid in the circuit at all times. * Acts like an assertion in simulation and imposes a declarative @@ -127,7 +214,33 @@ object assume { )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions - ): Assume = macro _applyMacroWithMessage + ): Assume = macro _applyMacroWithStringMessage + + /** 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 Printable type message when the assertion fires + * + * @note See [[printf.apply(fmt:Printable]] for documentation on printf using Printables + */ + def apply( + cond: Bool, + message: Printable + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Assume = macro _applyMacroWithPrintableMessage + def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assume = macro _applyMacroWithNoMessage @@ -142,6 +255,11 @@ object assume { import VerificationStatement._ + /** @group VerifPrintMacros */ + @deprecated( + "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", + "Chisel 3.5" + ) def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, @@ -151,10 +269,38 @@ object assume { 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)" + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)" } + /** @group VerifPrintMacros */ + def _applyMacroWithStringMessage( + 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("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)" + } + + /** @group VerifPrintMacros */ + def _applyMacroWithPrintableMessage( + 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("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)" + } + + /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree @@ -162,11 +308,14 @@ object assume { compileOptions: c.Tree ): c.Tree = { import c.universe._ - val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLine")) + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" } - /** Used by our macros. Do not call directly! */ + /** This will be removed in Chisel 3.6 in favor of the Printable version + * + * @group VerifPrintMacros + */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, @@ -178,14 +327,31 @@ object assume { ): Assume = { val id = new Assume() when(!Module.reset.asBool()) { - failureMessage("Assumption", line, cond, message, data) + failureMessage("Assumption", line, cond, message.map(Printable.pack(_, data: _*))) + Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) + } + id + } + + /** @group VerifPrintMacros */ + def _applyWithSourceLinePrintable( + cond: Bool, + line: SourceLineInfo, + message: Option[Printable] + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Assume = { + val id = new Assume() + when(!Module.reset.asBool()) { + failureMessage("Assumption", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) } id } } -object cover { +object cover extends VerifPrintMacrosDoc { /** Declares a condition to be covered. * At ever clock event, a counter is incremented iff the condition is active @@ -213,6 +379,7 @@ object cover { import VerificationStatement._ + /** @group VerifPrintMacros */ def _applyMacroWithNoMessage( c: blackbox.Context )(cond: c.Tree @@ -224,6 +391,7 @@ object cover { q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.None)($sourceInfo, $compileOptions)" } + /** @group VerifPrintMacros */ def _applyMacroWithMessage( c: blackbox.Context )(cond: c.Tree, @@ -236,7 +404,7 @@ object cover { q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some($message))($sourceInfo, $compileOptions)" } - /** Used by our macros. Do not call directly! */ + /** @group VerifPrintMacros */ def _applyWithSourceLine( cond: Bool, line: SourceLineInfo, @@ -299,13 +467,11 @@ private object VerificationStatement { (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] + message: Option[Printable] )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions @@ -314,11 +480,11 @@ private object VerificationStatement { 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" + p"$kind failed: $msg\n at $lineMsg\n" + case None => p"$kind failed\n at $lineMsg\n" } when(!cond) { - printf.printfWithoutReset(fmt, data: _*) + printf.printfWithoutReset(fmt) } } } diff --git a/src/test/scala/chiselTests/Assert.scala b/src/test/scala/chiselTests/Assert.scala index 1849ddf8..d7885a3b 100644 --- a/src/test/scala/chiselTests/Assert.scala +++ b/src/test/scala/chiselTests/Assert.scala @@ -61,6 +61,29 @@ class BadUnescapedPercentAssertTester extends BasicTester { stop() } +class PrintableFormattedAssertTester extends BasicTester { + val foobar = Wire(UInt(32.W)) + foobar := 123.U + assert(foobar === 123.U, cf"Error! Wire foobar =/= $foobar%x This is 100%% wrong.\n") + stop() +} + +class PrintableBadUnescapedPercentAssertTester extends BasicTester { + assert(1.U === 1.U, p"I'm 110% sure this is an invalid message") + stop() +} + +class PrintableAssumeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + val w = Wire(UInt(8.W)) + w := 255.U + assume(w === 255.U, cf"Assumption failed, Wire w =/= $w%x") + + out := in +} + class AssertSpec extends ChiselFlatSpec with Utils { "A failing assertion" should "fail the testbench" in { assert(!runTester { new FailingAssertTester }) @@ -77,6 +100,12 @@ class AssertSpec extends ChiselFlatSpec with Utils { they should "allow printf-style format strings with arguments" in { assertTesterPasses { new FormattedAssertTester } } + they should "allow printf-style format strings in Assumes" in { + val chirrtl = ChiselStage.emitChirrtl(new PrintableAssumeTester) + chirrtl should include( + """assume(w === 255.U, cf\"Assumption failed, Wire w =/= $w%%%%x\")\n", w)""" + ) + } they should "not allow unescaped % in the message" in { a[java.util.UnknownFormatConversionException] should be thrownBy { extractCause[java.util.UnknownFormatConversionException] { @@ -84,4 +113,16 @@ class AssertSpec extends ChiselFlatSpec with Utils { } } } + + they should "allow printable format strings with arguments" in { + assertTesterPasses { new FormattedAssertTester } + } + they should "not allow unescaped % in the printable message" in { + a[java.util.UnknownFormatConversionException] should be thrownBy { + extractCause[java.util.UnknownFormatConversionException] { + ChiselStage.elaborate { new BadUnescapedPercentAssertTester } + } + } + } + } -- cgit v1.2.3 From 21415b11f1005c3cdfdc07fc02294849ce1c81a4 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 16 Aug 2022 00:35:35 +0000 Subject: Privatize trait VerifPrintMacros (#2683) (#2685) (cherry picked from commit de76e70bc5905fc4ebc8a2e323e16620fa6832ec) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- core/src/main/scala/chisel3/VerificationStatement.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala index 1b13b86c..3b6da3f6 100644 --- a/core/src/main/scala/chisel3/VerificationStatement.scala +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -23,7 +23,7 @@ import scala.reflect.macros.blackbox * * @groupprio VerifPrintMacros 1001 */ -trait VerifPrintMacrosDoc +private[chisel3] trait VerifPrintMacrosDoc object assert extends VerifPrintMacrosDoc { -- cgit v1.2.3 From 96830a4ad502019ff1040889a89375ff1e3a6c6b Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 16 Aug 2022 16:57:34 +0000 Subject: Add a cookbook and publicly visible scaladoc for prefix, noPrefix (#2687) (#2690) * Add a cookbook and publicly visible scaladoc for prefix, noPrefix (cherry picked from commit ae7dc30b3b99f1fbd91c35f54bc19be7c55f74a3) Co-authored-by: Megan Wachs --- .../main/scala/chisel3/experimental/package.scala | 28 ++++++++- docs/src/cookbooks/naming.md | 66 +++++++++++++++++++--- 2 files changed, 83 insertions(+), 11 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/package.scala b/core/src/main/scala/chisel3/experimental/package.scala index b1d9cae4..39131943 100644 --- a/core/src/main/scala/chisel3/experimental/package.scala +++ b/core/src/main/scala/chisel3/experimental/package.scala @@ -235,9 +235,33 @@ package object experimental { } } - // Use to add a prefix to any component generated in input scope + /** Use to add a prefix to any components generated in the provided scope. + * + * @example {{{ + * + * val x1 = prefix("first") { + * // Anything generated here will be prefixed with "first" + * } + * + * val x2 = prefix(mysignal) { + * // Anything generated here will be prefixed with the name of mysignal + * } + * + * }}} + */ val prefix = chisel3.internal.prefix - // Use to remove prefixes not in provided scope + + /** Use to clear existing prefixes so no signals within the scope are prefixed + * by signals/names outside the scope + * + * @example {{{ + * + * val x1 = prefix("first") { + * // Anything generated here will have no prefix. + * // The result returned from this would *still* be called `x1` however. + * } + * }}} + */ val noPrefix = chisel3.internal.noPrefix // ****************************** Hardware equivalents of Scala Tuples ****************************** diff --git a/docs/src/cookbooks/naming.md b/docs/src/cookbooks/naming.md index c7ccdd96..ff3cb892 100644 --- a/docs/src/cookbooks/naming.md +++ b/docs/src/cookbooks/naming.md @@ -6,20 +6,66 @@ section: "chisel3" ```scala mdoc:invisible import chisel3._ -import chisel3.experimental.prefix -import chisel3.experimental.noPrefix import chisel3.stage.ChiselStage ``` # Naming Cookbook + ### I still have _T signals, can this be fixed? -First check - is the compiler plugin properly enabled? Scalac plugins are enabled via the scalac option -`-Xplugin:`. You can check which compiler plugins are enabled by running `show Compile / scalacOptions` in -the sbt prompt. +See the next answer! + +### I have so many wires with the same name, like `x`, `x_1` and `x_2`. How can I make them easier to understand? + +Signals with `_T` names or names that Chisel has to uniquify +often are intermediate values generated within loops, function calls, or `when` predicates. +They can also be consumed by verification statements like `assert` or `prints`. +In these cases, the compiler plugin often can't find a good prefix for the generated +intermediate signals and can't name them at all or has to make up a unique name for them. + +We recommend you manually insert calls to `prefix` to clarify these cases: + +```scala mdoc:silent +import chisel3.experimental.prefix +class ExamplePrefix extends Module { + + Seq.tabulate{2} {i => + Seq.tabulate{2}{ j => + prefix(s"loop_${i}_${j}"){ + val x = WireInit((i*0x10+j).U(8.W)) + dontTouch(x) + } + } + } +} +``` +```scala mdoc:verilog +ChiselStage.emitVerilog(new ExamplePrefix) +``` +### How can I get better names for code generated by `when` clauses? + +The `prefix` API can help with code inside `when` clauses: + +```scala mdoc:silent +class ExampleWhenPrefix extends Module { -If the plugin is enabled, these signals could be intermediate values which are consumed by either assertions or when -predicates. In these cases, the compiler plugin often can't find a good prefix for the generated intermediate signals. -We recommend you manually insert calls to `prefix` to fix these cases. We did this to Rocket Chip and saw huge benefits! + val in = IO(Input(UInt(4.W))) + val out = IO(Output(UInt(4.W))) + + out := DontCare + + Seq.tabulate{2}{ i => + val j = i + 1 + when (in === j.U) { prefix(s"clause_${j}"){ + val foo = Wire(UInt(4.W)) + foo := in +& j.U(4.W) + out := foo + }} + } +} +``` +```scala mdoc:verilog +ChiselStage.emitVerilog(new ExampleWhenPrefix) +``` ### I still see _GEN signals, can this be fixed? @@ -43,11 +89,13 @@ name collisions, which are what triggers all those annoying signal name bumps! Use the `.suggestName` method, which is on all classes which subtype `Data`. -### All this prefixing is annoying, how do I fix it? +### How can I omit the prefix in certain parts of the code? You can use the `noPrefix { ... }` to strip the prefix from all signals generated in that scope. ```scala mdoc +import chisel3.experimental.noPrefix + class ExampleNoPrefix extends Module { val in = IO(Input(UInt(2.W))) val out = IO(Output(UInt())) -- cgit v1.2.3 From 23ef9aa7ffef5bbf8fe124fc9be7683f005c3612 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 16 Aug 2022 19:04:28 +0000 Subject: Add OpaqueType support to Records (backport #2662) (#2679) * Add OpaqueType support to Records (#2662) OpaqueTypes are essentially type aliases that hide the underlying type. They are implemented in Chisel as Records of a single, unnamed element where the wrapping Record does not exist in the emitted FIRRTL. Co-authored-by: Jack Koenig (cherry picked from commit df5afee2d41b5bcd82d4013b977965c0dfe046fd) * Fix test compilation * Fix OpaqueType tests in RecordSpec Need to implement cloneType correctly and to warn instead of error when accessing .toTarget of non-hardware types because that is a warning (not error) in 3.5. * Waive MiMa false positives Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com> Co-authored-by: Jack Koenig --- build.sbt | 4 + core/src/main/scala/chisel3/Aggregate.scala | 22 +++++- core/src/main/scala/chisel3/internal/Builder.scala | 11 ++- .../scala/chisel3/internal/firrtl/Converter.scala | 10 ++- .../main/scala/chisel3/internal/firrtl/IR.scala | 6 ++ src/test/scala/chiselTests/RecordSpec.scala | 88 +++++++++++++++++++++- 6 files changed, 134 insertions(+), 7 deletions(-) diff --git a/build.sbt b/build.sbt index b64132fc..d9acadff 100644 --- a/build.sbt +++ b/build.sbt @@ -192,6 +192,10 @@ lazy val core = (project in file("core")). ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule._computeName"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule.forceName"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.VerificationStatement.failureMessage"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.Data.setRef"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.MemBase.setRef"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.VerificationStatement.setRef"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule.setRef"), ) ). settings( diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 82c02cbc..042e78b1 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -923,6 +923,20 @@ trait VecLike[T <: Data] extends IndexedSeq[T] with HasId with SourceInfoDoc { */ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptions) extends Aggregate { + /** Indicates if this Record represents an "Opaque Type" + * + * Opaque types provide a mechanism for user-defined types + * that do not impose any "boxing" overhead in the emitted FIRRTL and Verilog. + * You can think about an opaque type Record as a box around + * a single element that only exists at Chisel elaboration time. + * Put another way, if opaqueType is overridden to true, + * The Record may only contain a single element with an empty name + * and there will be no `_` in the name for that element in the emitted Verilog. + * + * @see RecordSpec in Chisel's tests for example usage and expected output + */ + def opaqueType: Boolean = false + // Doing this earlier than onModuleClose allows field names to be available for prefixing the names // of hardware created when connecting to one of these elements private def setElementRefs(): Unit = { @@ -930,7 +944,13 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio // identifier; however, Namespace sanitizes identifiers to make them legal for Firrtl/Verilog // which can cause collisions val _namespace = Namespace.empty - for ((name, elt) <- elements) { elt.setRef(this, _namespace.name(name, leadingDigitOk = true)) } + require( + !opaqueType || (elements.size == 1 && elements.head._1 == ""), + s"Opaque types must have exactly one element with an empty name, not ${elements.size}: ${elements.keys.mkString(", ")}" + ) + for ((name, elt) <- elements) { + elt.setRef(this, _namespace.name(name, leadingDigitOk = true), opaque = opaqueType) + } } private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 75d08f92..07d8e7f7 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -231,9 +231,13 @@ private[chisel3] trait HasId extends InstanceId { _refVar = imm } } - private[chisel3] def setRef(parent: HasId, name: String): Unit = setRef(Slot(Node(parent), name)) - private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) - private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref)) + private[chisel3] def setRef(parent: HasId, name: String, opaque: Boolean = false): Unit = { + if (!opaque) setRef(Slot(Node(parent), name)) + else setRef(OpaqueSlot(Node(parent), name)) + } + + private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) + private[chisel3] def setRef(parent: HasId, index: UInt): Unit = setRef(Index(Node(parent), index.ref)) private[chisel3] def getRef: Arg = _ref.get private[chisel3] def getOptionRef: Option[Arg] = _ref @@ -514,6 +518,7 @@ private[chisel3] object Builder extends LazyLogging { def buildAggName(id: HasId): Option[String] = { def getSubName(field: Data): Option[String] = field.getOptionRef.flatMap { case Slot(_, field) => Some(field) // Record + case OpaqueSlot(_, field) => None // Record with single element case Index(_, ILit(n)) => Some(n.toString) // Vec static indexing case Index(_, ULit(n, _)) => Some(n.toString) // Vec lit indexing case Index(_, _: Node) => None // Vec dynamic indexing diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index 2d21a7cb..56422b85 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -68,6 +68,8 @@ private[chisel3] object Converter { fir.Reference(name, fir.UnknownType) case Slot(imm, name) => fir.SubField(convert(imm, ctx, info), name, fir.UnknownType) + case OpaqueSlot(imm, name) => + convert(imm, ctx, info) case Index(imm, ILit(idx)) => fir.SubIndex(convert(imm, ctx, info), castToInt(idx, "Index"), fir.UnknownType) case Index(imm, value) => @@ -301,7 +303,7 @@ private[chisel3] object Converter { case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) case d: Analog => fir.AnalogType(convert(d.width)) case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir, info), d.length) - case d: Record => + case d: Record => { val childClearDir = clearDir || d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output def eltField(elt: Data): fir.Field = (childClearDir, firrtlUserDirOf(elt)) match { @@ -311,7 +313,11 @@ private[chisel3] object Converter { case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info)) } - fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) + if (!d.opaqueType) + fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) + else + extractType(d.elements.head._2, true, info) + } } def convert(name: String, param: Param): fir.Param = param match { diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index dc9ab027..37fb2f8b 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -91,6 +91,7 @@ object Arg { 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(OpaqueSlot(Node(imm), name)) => s"${earlyLocalName(imm)}" case Some(arg) => arg.name case None => id match { @@ -218,6 +219,11 @@ case class Slot(imm: Node, name: String) extends Arg { } } +case class OpaqueSlot(imm: Node, name: String) extends Arg { + override def contextualName(ctx: Component): String = imm.name + override def localName: String = imm.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)}]" diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 30b55812..5aa872b0 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -108,6 +108,58 @@ trait RecordSpecUtils { require(DataMirror.checkTypeEquivalence(wire0, wire1)) require(!DataMirror.checkTypeEquivalence(wire1, wire2)) } + + class SingleElementRecord extends Record { + private val underlying = UInt(8.W) + val elements = SeqMap("" -> underlying) + override def opaqueType = elements.size == 1 + override def cloneType: this.type = (new SingleElementRecord).asInstanceOf[this.type] + + def +(that: SingleElementRecord): SingleElementRecord = { + val _w = Wire(new SingleElementRecord) + _w.underlying := this.underlying + that.underlying + _w + } + } + + class SingleElementRecordModule extends Module { + val in1 = IO(Input(new SingleElementRecord)) + val in2 = IO(Input(new SingleElementRecord)) + val out = IO(Output(new SingleElementRecord)) + + val r = new SingleElementRecord + + out := in1 + in2 + } + + class NamedSingleElementRecord extends Record { + private val underlying = UInt(8.W) + val elements = SeqMap("unused" -> underlying) + + override def opaqueType = elements.size == 1 + override def cloneType: this.type = (new NamedSingleElementRecord).asInstanceOf[this.type] + } + + class NamedSingleElementModule extends Module { + val in = IO(Input(new NamedSingleElementRecord)) + val out = IO(Output(new NamedSingleElementRecord)) + out := in + } + + class ErroneousOverride extends Record { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = true + override def cloneType: this.type = (new ErroneousOverride).asInstanceOf[this.type] + } + + class ErroneousOverrideModule extends Module { + val in = IO(Input(new ErroneousOverride)) + val out = IO(Output(new ErroneousOverride)) + out := in + } } class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { @@ -133,7 +185,7 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { class AliasedFieldRecord extends Record { val foo = UInt(8.W) val elements = SeqMap("foo" -> foo, "bar" -> foo) - override def cloneType: AliasedFieldRecord.this.type = this + override def cloneType: AliasedFieldRecord.this.type = (new AliasedFieldRecord).asInstanceOf[this.type] } val e = intercept[AliasedAggregateFieldException] { @@ -146,6 +198,40 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { e.getMessage should include("contains aliased fields named (bar,foo)") } + they should "be OpaqueType for maps with single unnamed elements" in { + val singleElementChirrtl = ChiselStage.emitChirrtl { new SingleElementRecordModule } + singleElementChirrtl should include("input in1 : UInt<8>") + singleElementChirrtl should include("input in2 : UInt<8>") + singleElementChirrtl should include("add(in1, in2)") + } + + they should "throw an error when map contains a named element and opaqueType is overriden to true" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new NamedSingleElementModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "throw an error when map contains more than one element and opaqueType is overriden to true" in { + (the[Exception] thrownBy extractCause[Exception] { + ChiselStage.elaborate { new ErroneousOverrideModule } + }).getMessage should include("Opaque types must have exactly one element with an empty name") + } + + they should "work with .toTarget" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val q = m.in1.toTarget.toString + assert(q == "~SingleElementRecordModule|SingleElementRecordModule>in1") + } + + they should "work (but warn) with .toTarget on non-data OpaqueType Record" in { + var m: SingleElementRecordModule = null + ChiselStage.elaborate { m = new SingleElementRecordModule; m } + val (log, q) = grabLog(m.r.toTarget) + log should include(".toTarget of non-hardware Data is deprecated.") + assert(q.toString == "~SingleElementRecordModule|SingleElementRecordModule>r") + } + they should "follow UInt serialization/deserialization API" in { assertTesterPasses { new RecordSerializationTest } } -- cgit v1.2.3 From 16dfc84d6667f1f6bbca46935cb445bc288c96d4 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 18 Aug 2022 00:30:39 +0000 Subject: Add generic `Data` equality (===) via extension method (#2669) (#2691) (cherry picked from commit 67cff8253740f19642006dba7eff58b1e5fa1291) Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com>--- core/src/main/scala/chisel3/Data.scala | 80 ++++++- src/test/scala/chiselTests/DataEqualitySpec.scala | 257 ++++++++++++++++++++++ 2 files changed, 336 insertions(+), 1 deletion(-) create mode 100644 src/test/scala/chiselTests/DataEqualitySpec.scala diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 956c7996..d434735a 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -5,7 +5,7 @@ package chisel3 import chisel3.experimental.dataview.reify import scala.language.experimental.macros -import chisel3.experimental.{Analog, BaseModule, DataMirror, FixedPoint, Interval} +import chisel3.experimental.{Analog, BaseModule, DataMirror, EnumType, FixedPoint, Interval} import chisel3.internal.Builder.pushCommand import chisel3.internal._ import chisel3.internal.firrtl._ @@ -885,6 +885,84 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { def toPrintable: Printable } +object Data { + + /** + * Provides generic, recursive equality for [[Bundle]] and [[Vec]] hardware. This avoids the + * need to use workarounds such as `bundle1.asUInt === bundle2.asUInt` by allowing users + * to instead write `bundle1 === bundle2`. + * + * Static type safety of this comparison is guaranteed at compile time as the extension + * method requires the same parameterized type for both the left-hand and right-hand + * sides. It is, however, possible to get around this type safety using `Bundle` subtypes + * that can differ during runtime (e.g. through a generator). These cases are + * subsequently raised as elaboration errors. + * + * @param lhs The [[Data]] hardware on the left-hand side of the equality + */ + implicit class DataEquality[T <: Data](lhs: T)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions) { + + /** Dynamic recursive equality operator for generic [[Data]] + * + * @param rhs a hardware [[Data]] to compare `lhs` to + * @return a hardware [[Bool]] asserted if `lhs` is equal to `rhs` + * @throws ChiselException when `lhs` and `rhs` are different types during elaboration time + */ + def ===(rhs: T): Bool = { + (lhs, rhs) match { + case (thiz: UInt, that: UInt) => thiz === that + case (thiz: SInt, that: SInt) => thiz === that + case (thiz: AsyncReset, that: AsyncReset) => thiz.asBool === that.asBool + case (thiz: Reset, that: Reset) => thiz === that + case (thiz: Interval, that: Interval) => thiz === that + case (thiz: FixedPoint, that: FixedPoint) => thiz === that + case (thiz: EnumType, that: EnumType) => thiz === that + case (thiz: Clock, that: Clock) => thiz.asUInt === that.asUInt + case (thiz: Vec[_], that: Vec[_]) => + if (thiz.length != that.length) { + throwException(s"Cannot compare Vecs $thiz and $that: Vec sizes differ") + } else { + thiz.getElements + .zip(that.getElements) + .map { case (thisData, thatData) => thisData === thatData } + .reduce(_ && _) + } + case (thiz: Record, that: Record) => + if (thiz.elements.size != that.elements.size) { + throwException(s"Cannot compare Bundles $thiz and $that: Bundle types differ") + } else { + thiz.elements.map { + case (thisName, thisData) => + if (!that.elements.contains(thisName)) + throwException( + s"Cannot compare Bundles $thiz and $that: field $thisName (from $thiz) was not found in $that" + ) + + val thatData = that.elements(thisName) + + try { + thisData === thatData + } catch { + case e: ChiselException => + throwException( + s"Cannot compare field $thisName in Bundles $thiz and $that: ${e.getMessage.split(": ").last}" + ) + } + } + .reduce(_ && _) + } + // This should be matching to (DontCare, DontCare) but the compiler wasn't happy with that + case (_: DontCare.type, _: DontCare.type) => true.B + + case (thiz: Analog, that: Analog) => + throwException(s"Cannot compare Analog values $thiz and $that: Equality isn't defined for Analog values") + // Runtime types are different + case (thiz, that) => throwException(s"Cannot compare $thiz and $that: Runtime types differ") + } + } + } +} + trait WireFactory { /** Construct a [[Wire]] from a type template diff --git a/src/test/scala/chiselTests/DataEqualitySpec.scala b/src/test/scala/chiselTests/DataEqualitySpec.scala new file mode 100644 index 00000000..4ac3292d --- /dev/null +++ b/src/test/scala/chiselTests/DataEqualitySpec.scala @@ -0,0 +1,257 @@ +package chiselTests + +import chisel3._ +import chisel3.experimental.VecLiterals._ +import chisel3.experimental.BundleLiterals._ +import chisel3.experimental.{Analog, ChiselEnum, ChiselRange, FixedPoint, Interval} +import chisel3.stage.ChiselStage +import chisel3.testers.BasicTester +import chisel3.util.Valid + +class EqualityModule(lhsGen: => Data, rhsGen: => Data) extends Module { + val out = IO(Output(Bool())) + + val lhs = lhsGen + val rhs = rhsGen + + out := lhs === rhs +} + +class EqualityTester(lhsGen: => Data, rhsGen: => Data) extends BasicTester { + val module = Module(new EqualityModule(lhsGen, rhsGen)) + + assert(module.out) + + stop() +} + +class AnalogBundle extends Bundle { + val analog = Analog(32.W) +} + +class AnalogExceptionModule extends Module { + class AnalogExceptionModuleIO extends Bundle { + val bundle1 = new AnalogBundle + val bundle2 = new AnalogBundle + } + + val io = IO(new AnalogExceptionModuleIO) +} + +class AnalogExceptionTester extends BasicTester { + val module = Module(new AnalogExceptionModule) + + module.io.bundle1 <> DontCare + module.io.bundle2 <> DontCare + + assert(module.io.bundle1 === module.io.bundle2) + + stop() +} + +class DataEqualitySpec extends ChiselFlatSpec with Utils { + object MyEnum extends ChiselEnum { + val sA, sB = Value + } + object MyEnumB extends ChiselEnum { + val sA, sB = Value + } + class MyBundle extends Bundle { + val a = UInt(8.W) + val b = Bool() + val c = MyEnum() + } + class LongBundle extends Bundle { + val a = UInt(48.W) + val b = SInt(32.W) + val c = FixedPoint(16.W, 4.BP) + } + class RuntimeSensitiveBundle(gen: => Bundle) extends Bundle { + val a = UInt(8.W) + val b: Bundle = gen + } + + behavior.of("UInt === UInt") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(0.U, 0.U) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(0.U, 1.U) + } + } + + behavior.of("SInt === SInt") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(0.S, 0.S) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(0.S, 1.S) + } + } + + behavior.of("Reset === Reset") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(true.B, true.B) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(true.B, false.B) + } + } + + behavior.of("AsyncReset === AsyncReset") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(true.B.asAsyncReset, true.B.asAsyncReset) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(true.B.asAsyncReset, false.B.asAsyncReset) + } + } + + behavior.of("Interval === Interval") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(2.I, 2.I) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(2.I, 3.I) + } + } + + behavior.of("FixedPoint === FixedPoint") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(4.5.F(16.W, 4.BP), 4.5.F(16.W, 4.BP)) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(4.5.F(16.W, 4.BP), 4.6.F(16.W, 4.BP)) + } + } + + behavior.of("ChiselEnum === ChiselEnum") + it should "pass with equal values" in { + assertTesterPasses { + new EqualityTester(MyEnum.sA, MyEnum.sA) + } + } + it should "fail with differing values" in { + assertTesterFails { + new EqualityTester(MyEnum.sA, MyEnum.sB) + } + } + + behavior.of("Vec === Vec") + it should "pass with equal sizes, equal values" in { + assertTesterPasses { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U) + ) + } + } + it should "fail with equal sizes, differing values" in { + assertTesterFails { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(3, UInt(8.W)).Lit(0 -> 0.U, 1 -> 1.U, 2 -> 2.U) + ) + } + } + it should "throw a ChiselException with differing sizes" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { + new EqualityTester( + Vec(3, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U), + Vec(4, UInt(8.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U) + ) + } + }).getMessage should include("Vec sizes differ") + } + + behavior.of("Bundle === Bundle") + it should "pass with equal type, equal values" in { + assertTesterPasses { + new EqualityTester( + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB), + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB) + ) + } + } + it should "fail with equal type, differing values" in { + assertTesterFails { + new EqualityTester( + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sB), + (new MyBundle).Lit(_.a -> 42.U, _.b -> false.B, _.c -> MyEnum.sA) + ) + } + } + it should "throw a ChiselException with differing runtime types" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { + new EqualityTester( + (new RuntimeSensitiveBundle(new MyBundle)).Lit( + _.a -> 1.U, + _.b -> (new MyBundle).Lit( + _.a -> 42.U, + _.b -> false.B, + _.c -> MyEnum.sB + ) + ), + (new RuntimeSensitiveBundle(new LongBundle)).Lit( + _.a -> 1.U, + _.b -> (new LongBundle).Lit( + _.a -> 42.U, + _.b -> 0.S, + _.c -> 4.5.F(16.W, 4.BP) + ) + ) + ) + } + }).getMessage should include("Runtime types differ") + } + + behavior.of("DontCare === DontCare") + it should "pass with two invalids" in { + assertTesterPasses { + new EqualityTester(Valid(UInt(8.W)).Lit(_.bits -> 123.U), Valid(UInt(8.W)).Lit(_.bits -> 123.U)) + } + } + it should "exhibit the same behavior as comparing two invalidated wires" in { + // Also check that two invalidated wires are equal + assertTesterPasses { + new EqualityTester(WireInit(UInt(8.W), DontCare), WireInit(UInt(8.W), DontCare)) + } + + // Compare the verilog generated from both test cases and verify that they both are equal to true + val verilog1 = ChiselStage.emitVerilog( + new EqualityModule(Valid(UInt(8.W)).Lit(_.bits -> 123.U), Valid(UInt(8.W)).Lit(_.bits -> 123.U)) + ) + val verilog2 = + ChiselStage.emitVerilog(new EqualityModule(WireInit(UInt(8.W), DontCare), WireInit(UInt(8.W), DontCare))) + + verilog1 should include("assign out = 1'h1;") + verilog2 should include("assign out = 1'h1;") + } + + behavior.of("Analog === Analog") + it should "throw a ChiselException" in { + (the[ChiselException] thrownBy extractCause[ChiselException] { + assertTesterFails { new AnalogExceptionTester } + }).getMessage should include("Equality isn't defined for Analog values") + } +} -- cgit v1.2.3 From 1c5bd39a192272877cf4b8dc3d26a9284eb0c05d Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 23 Aug 2022 21:31:57 +0000 Subject: Add AffectsChiselPrefix tests to PrefixSpec (#2693) (#2695) (cherry picked from commit 1a23b42429bf9de7dfab9f0a8e67334f8c5d4540) Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com>--- src/test/scala/chiselTests/naming/PrefixSpec.scala | 24 ++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/test/scala/chiselTests/naming/PrefixSpec.scala b/src/test/scala/chiselTests/naming/PrefixSpec.scala index 6d52407e..b5eac1d4 100644 --- a/src/test/scala/chiselTests/naming/PrefixSpec.scala +++ b/src/test/scala/chiselTests/naming/PrefixSpec.scala @@ -7,6 +7,7 @@ import chisel3.stage.ChiselStage import chisel3.aop.Select import chisel3.experimental.{dump, noPrefix, prefix, treedump} import chiselTests.{ChiselPropSpec, Utils} +import chisel3.experimental.AffectsChiselPrefix class PrefixSpec extends ChiselPropSpec with Utils { implicit val minimumMajorVersion: Int = 12 @@ -497,4 +498,27 @@ class PrefixSpec extends ChiselPropSpec with Utils { Select.wires(top).map(_.instanceName) should be(List("a_b_c_d")) } } + + property("Prefixing of AffectsChiselPrefix objects should work") { + class NotAData extends AffectsChiselPrefix { + val value = Wire(UInt(3.W)) + } + class NotADataUnprefixed { + val value = Wire(UInt(3.W)) + } + class Test extends Module { + { + val nonData = new NotAData + // Instance name of nonData.value should be nonData_value + nonData.value := RegNext(3.U) + + val nonData2 = new NotADataUnprefixed + // Instance name of nonData2.value should be value + nonData2.value := RegNext(3.U) + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("nonData_value", "value")) + } + } } -- cgit v1.2.3 From c3b06d773f1fabd2519d6c705d68381d13f1c07f Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Tue, 23 Aug 2022 17:20:12 -0700 Subject: Backport .toTarget deprecation warning information (3.5.x) (#2697) --- core/src/main/scala/chisel3/internal/Builder.scala | 14 ++++++++++++-- src/test/scala/chiselTests/RecordSpec.scala | 2 +- src/test/scala/chiselTests/ToTargetSpec.scala | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 07d8e7f7..07fc80eb 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -249,10 +249,20 @@ private[chisel3] trait HasId extends InstanceId { // Builder.deprecated mechanism, we have to create our own one off ErrorLog and print the // warning right away. // It's especially bad because --warnings-as-errors does not work with these warnings + val nameGuess = _computeName(None) match { + case Some(name) => s": '$name'" + case None => "" + } + val parentGuess = _parent match { + case Some(ViewParent) => s", in module '${reifyParent.pathName}'" + case Some(p) => s", in module '${p.pathName}'" + case None => "" + } val errors = new ErrorLog(false) val logger = new _root_.logger.Logger(this.getClass.getName) - val msg = "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated. " + - "This will become an error in Chisel 3.6." + val msg = + "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated" + nameGuess + parentGuess + ". " + + "This will become an error in Chisel 3.6." errors.deprecated(msg, None) errors.checkpoint(logger) _computeName(None).get diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 5aa872b0..718c1acb 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -228,7 +228,7 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { var m: SingleElementRecordModule = null ChiselStage.elaborate { m = new SingleElementRecordModule; m } val (log, q) = grabLog(m.r.toTarget) - log should include(".toTarget of non-hardware Data is deprecated.") + log should include(".toTarget of non-hardware Data is deprecated") assert(q.toString == "~SingleElementRecordModule|SingleElementRecordModule>r") } diff --git a/src/test/scala/chiselTests/ToTargetSpec.scala b/src/test/scala/chiselTests/ToTargetSpec.scala index dc4ec448..de46cdcb 100644 --- a/src/test/scala/chiselTests/ToTargetSpec.scala +++ b/src/test/scala/chiselTests/ToTargetSpec.scala @@ -49,4 +49,21 @@ class ToTargetSpec extends ChiselFlatSpec with Utils { val q = m.q.toTarget.toString assert(q == s"~$mn|Queue") } + + it should "warn on non-hardware types and provide information" in { + class Example extends Module { + val tpe = UInt(8.W) + + val in = IO(Input(tpe)) + val out = IO(Output(tpe)) + out := in + } + + var e: Example = null + chisel3.stage.ChiselStage.elaborate { e = new Example; e } + val (log, foo) = grabLog(e.tpe.toTarget) + log should include( + "Accessing the .instanceName or .toTarget of non-hardware Data is deprecated: 'tpe', in module 'Example'" + ) + } } -- cgit v1.2.3 From ae33fe50a5a9ef99125bb325fc5f10c831bb4186 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Wed, 24 Aug 2022 20:25:46 +0000 Subject: CCC 2022 (#2698) (#2699) (cherry picked from commit 78265e180c7ce6c3036d10aacca2d387224ea696) Co-authored-by: Jiuyang Liu --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 2383944f..1f9fb220 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,31 @@ ![Chisel 3](https://raw.githubusercontent.com/chipsalliance/chisel3/master/docs/src/images/chisel_logo.svg?sanitize=true) +--- +### CCC 2022 +CCC(Chisel Community Conference) is an annual gathering of Chisel community enthusiasts and technical exchange workshop. With the support of the Chisel development community and RISC-V International, this conference will bring together designers and developers with hands-on experience in Chisel from home and abroad to share cutting-edge results and experiences from both the open source community as well as industry. + +CCC2022 will be held online, You can add [ics file](https://calendar.google.com/calendar/embed?src=c_pluc02j9c4ambkiljlud3620fk%40group.calendar.google.com&ctz=Atlantic%2FAzores) to the calendar for conference agenda. + +Conference Time (in various time zones) +* Saturday, August 27 ⋅ 09:00 - 16:00 UTC+8 (Shanghai/China) +* Friday, August 26 ⋅ 21:00 - 04:00 UTC-4 (Eastern Daylight Time) +* Friday, August 26 ⋅ 18:00 - 25:00 UTC-7 (Los Angeles/US) +* Saturday, August 27 ⋅ 02:00 - 9:00 UTC+1 (London/UK) + +Click on [Zoom Link](https://us02web.zoom.us/j/87836037616?pwd=cXVUZjkrWlBDU3JuU0NLTU8zbG0xQT09) to participate. + +The conference agenda is: +1. Constellation, a Open-source Chisel NoC Generator for SoCs - Jerry Zhao@UCB BAR +2. The formal verification capabilities of chiseltest - Kevin Laeufer@UCB BAR +3. Chisel Breakdown 03 - Jack Koenig@SiFive +4. The Next Generation FIRRTL Compiler is Here! - Prithayan Barua&Hideto Ueno@SiFive +5. Implementing RISC-V Scalar Cryptography/Bitmanip extensions in Chisel - Hongren Zheng@Tsinghua University+PLCT +6. SCIRT: Bridging the Type System Gap for Circuit Generators - Ruikang Wang@Tsinghua University+PLCT +7. ChiselDB: Mapping Hardware Data Structures to Database Tables for Efficient Data Analysis Jiawei Lin@ICT +8. From Chisel to Chips in Fully Open-Source - Martin Schoeberl@DTU + +See you this week :) + --- The **Constructing Hardware in a Scala Embedded Language** ([**Chisel**](https://www.chisel-lang.org)) is an open-source hardware description language (HDL) used to describe digital electronics and circuits at the register-transfer level that facilitates **advanced circuit generation and design reuse for both ASIC and FPGA digital logic designs**. -- cgit v1.2.3 From 998913f9379440db26b6aeeaa09e7a11d7615351 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 25 Aug 2022 18:14:54 +0000 Subject: Bugfix - OpaqueSlot replace invalid localName (backport #2701) (#2702) * Bugfix - OpaqueSlot replace invalid localName (#2701) (cherry picked from commit fb8ea2a2fac227f2570da992d7877de2eb1cf801) * Fix cloneTypes (#2703) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- core/src/main/scala/chisel3/internal/Builder.scala | 4 +- .../scala/chisel3/internal/firrtl/Converter.scala | 2 +- .../main/scala/chisel3/internal/firrtl/IR.scala | 6 +-- src/test/scala/chiselTests/RecordSpec.scala | 54 ++++++++++++++++++++++ 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 07fc80eb..9f79fe1e 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -233,7 +233,7 @@ private[chisel3] trait HasId extends InstanceId { } private[chisel3] def setRef(parent: HasId, name: String, opaque: Boolean = false): Unit = { if (!opaque) setRef(Slot(Node(parent), name)) - else setRef(OpaqueSlot(Node(parent), name)) + else setRef(OpaqueSlot(Node(parent))) } private[chisel3] def setRef(parent: HasId, index: Int): Unit = setRef(Index(Node(parent), ILit(index))) @@ -528,7 +528,7 @@ private[chisel3] object Builder extends LazyLogging { def buildAggName(id: HasId): Option[String] = { def getSubName(field: Data): Option[String] = field.getOptionRef.flatMap { case Slot(_, field) => Some(field) // Record - case OpaqueSlot(_, field) => None // Record with single element + case OpaqueSlot(_) => None // OpaqueSlots don't contribute to the name case Index(_, ILit(n)) => Some(n.toString) // Vec static indexing case Index(_, ULit(n, _)) => Some(n.toString) // Vec lit indexing case Index(_, _: Node) => None // Vec dynamic indexing diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index 56422b85..fe95445c 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -68,7 +68,7 @@ private[chisel3] object Converter { fir.Reference(name, fir.UnknownType) case Slot(imm, name) => fir.SubField(convert(imm, ctx, info), name, fir.UnknownType) - case OpaqueSlot(imm, name) => + case OpaqueSlot(imm) => convert(imm, ctx, info) case Index(imm, ILit(idx)) => fir.SubIndex(convert(imm, ctx, info), castToInt(idx, "Index"), fir.UnknownType) diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index 37fb2f8b..d177c859 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -91,7 +91,7 @@ object Arg { 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(OpaqueSlot(Node(imm), name)) => s"${earlyLocalName(imm)}" + case Some(OpaqueSlot(Node(imm))) => s"${earlyLocalName(imm)}" case Some(arg) => arg.name case None => id match { @@ -219,9 +219,9 @@ case class Slot(imm: Node, name: String) extends Arg { } } -case class OpaqueSlot(imm: Node, name: String) extends Arg { +case class OpaqueSlot(imm: Node) extends Arg { override def contextualName(ctx: Component): String = imm.name - override def localName: String = imm.name + override def name: String = imm.name } case class Index(imm: Arg, value: Arg) extends Arg { diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 718c1acb..5080f15f 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -132,6 +132,35 @@ trait RecordSpecUtils { out := in1 + in2 } + class InnerRecord extends Record { + val k = new InnerInnerRecord + val elements = SeqMap("" -> k) + override def opaqueType = elements.size == 1 + override def cloneType: this.type = (new InnerRecord).asInstanceOf[this.type] + } + + class InnerInnerRecord extends Record { + val k = new SingleElementRecord + val elements = SeqMap("" -> k) + override def opaqueType = elements.size == 1 + override def cloneType: this.type = (new InnerInnerRecord).asInstanceOf[this.type] + } + + class NestedRecordModule extends Module { + val in = IO(Input(new InnerRecord)) + val out = IO(Output(new InnerRecord)) + val inst = Module(new InnerModule) + inst.foo := in + out := inst.bar + } + class InnerModule extends Module { + val foo = IO(Input(new InnerRecord)) + val bar = IO(Output(new InnerRecord)) + + // DO NOT do this; just for testing element connections + bar.elements.head._2 := foo.elements.head._2 + } + class NamedSingleElementRecord extends Record { private val underlying = UInt(8.W) val elements = SeqMap("unused" -> underlying) @@ -205,6 +234,31 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { singleElementChirrtl should include("add(in1, in2)") } + they should "work correctly for toTarget in nested opaque type Records" in { + var mod: NestedRecordModule = null + ChiselStage.elaborate { mod = new NestedRecordModule; mod } + val testStrings = Seq( + mod.in.toTarget.toString(), + mod.in.k.toTarget.toString(), + mod.in.k.k.toTarget.toString(), + mod.in.elements.head._2.toTarget.toString(), + mod.in.k.elements.head._2.toTarget.toString(), + mod.in.k.k.elements.head._2.toTarget.toString() + ) + testStrings.foreach(x => assert(x == "~NestedRecordModule|NestedRecordModule>in")) + } + + they should "work correctly when connecting nested opaque type elements" in { + val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } + nestedRecordChirrtl should include("input in : UInt<8>") + nestedRecordChirrtl should include("output out : UInt<8>") + nestedRecordChirrtl should include("inst.foo <= in") + nestedRecordChirrtl should include("out <= inst.bar") + nestedRecordChirrtl should include("input foo : UInt<8>") + nestedRecordChirrtl should include("output bar : UInt<8>") + nestedRecordChirrtl should include("bar <= foo") + } + they should "throw an error when map contains a named element and opaqueType is overriden to true" in { (the[Exception] thrownBy extractCause[Exception] { ChiselStage.elaborate { new NamedSingleElementModule } -- cgit v1.2.3 From df5a95454ff0414d1d3ce16d06dbe27b152e3751 Mon Sep 17 00:00:00 2001 From: Zachary Yedidia Date: Thu, 25 Aug 2022 12:04:37 -0700 Subject: Backport of eager error messages for annotations (3.5.x) (#2700) (#2705) --- .../scala/chisel3/util/experimental/ForceNames.scala | 3 +++ .../chisel3/util/experimental/decode/decoder.scala | 2 ++ .../scala/chiselTests/experimental/ForceNames.scala | 17 ++++++++++++++++- 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/src/main/scala/chisel3/util/experimental/ForceNames.scala b/src/main/scala/chisel3/util/experimental/ForceNames.scala index 53ee2bd2..3070a210 100644 --- a/src/main/scala/chisel3/util/experimental/ForceNames.scala +++ b/src/main/scala/chisel3/util/experimental/ForceNames.scala @@ -3,6 +3,7 @@ package chisel3.util.experimental import chisel3.experimental.{annotate, ChiselAnnotation, RunFirrtlTransform} +import chisel3.internal.Builder import firrtl.Mappers._ import firrtl._ import firrtl.annotations._ @@ -24,6 +25,7 @@ object forceName { * @param name Name to force to */ def apply[T <: chisel3.Element](signal: T, name: String): T = { + if (!signal.isSynthesizable) Builder.deprecated(s"Using forceName '$name' on non-hardware value $signal") annotate(new ChiselAnnotation with RunFirrtlTransform { def toFirrtl = ForceNameAnnotation(signal.toTarget, name) override def transformClass: Class[_ <: Transform] = classOf[ForceNamesTransform] @@ -37,6 +39,7 @@ object forceName { * @param signal Signal to name */ def apply[T <: chisel3.Element](signal: T): T = { + if (!signal.isSynthesizable) Builder.deprecated(s"Using forceName on non-hardware value $signal") annotate(new ChiselAnnotation with RunFirrtlTransform { def toFirrtl = ForceNameAnnotation(signal.toTarget, signal.toTarget.ref) override def transformClass: Class[_ <: Transform] = classOf[ForceNamesTransform] diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala index 4feda672..067dd6f8 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -6,6 +6,7 @@ import chisel3._ import chisel3.experimental.{annotate, ChiselAnnotation} import chisel3.util.{pla, BitPat} import chisel3.util.experimental.{getAnnotations, BitSet} +import chisel3.internal.Builder import firrtl.annotations.Annotation import logger.LazyLogging @@ -30,6 +31,7 @@ object decoder extends LazyLogging { val (plaInput, plaOutput) = pla(minimizedTable.table.toSeq, BitPat(minimizedTable.default.value.U(minimizedTable.default.getWidth.W))) + assert(plaOutput.isSynthesizable, s"Using DecodeTableAnnotation on non-hardware value $plaOutput") annotate(new ChiselAnnotation { override def toFirrtl: Annotation = DecodeTableAnnotation(plaOutput.toTarget, truthTable.toString, minimizedTable.toString) diff --git a/src/test/scala/chiselTests/experimental/ForceNames.scala b/src/test/scala/chiselTests/experimental/ForceNames.scala index 233b4a5f..9ba825c4 100644 --- a/src/test/scala/chiselTests/experimental/ForceNames.scala +++ b/src/test/scala/chiselTests/experimental/ForceNames.scala @@ -59,7 +59,7 @@ object ForceNamesHierarchy { } } -class ForceNamesSpec extends ChiselFlatSpec { +class ForceNamesSpec extends ChiselFlatSpec with Utils { def run[T <: RawModule]( dut: => T, @@ -110,4 +110,19 @@ class ForceNamesSpec extends ChiselFlatSpec { ) } } + + "Force Name of non-hardware value" should "warn" in { + class Example extends Module { + val tpe = UInt(8.W) + forceName(tpe, "foobar") + + val in = IO(Input(tpe)) + val out = IO(Output(tpe)) + out := in + } + + val (log, foo) = grabLog(chisel3.stage.ChiselStage.elaborate(new Example)) + log should include("deprecated") + log should include("Using forceName 'foobar' on non-hardware value UInt<8>") + } } -- cgit v1.2.3 From 9f1484572e2e4185e87a9cfb03b253870636c12c Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 29 Aug 2022 21:05:56 +0000 Subject: Fix OpaqueSlot handling of contextual names (#2708) (#2712) We need to ensure that contextual names stay contextual (ie. sensitive to the module context which is important for naming ports). (cherry picked from commit cee255216c4a1bb658a2d8ddc03d966ce7ffb877) Co-authored-by: Jack Koenig --- .../main/scala/chisel3/internal/firrtl/IR.scala | 2 +- src/test/scala/chiselTests/RecordSpec.scala | 35 +++++++++++----------- 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/core/src/main/scala/chisel3/internal/firrtl/IR.scala b/core/src/main/scala/chisel3/internal/firrtl/IR.scala index d177c859..ddad6b10 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/IR.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/IR.scala @@ -220,7 +220,7 @@ case class Slot(imm: Node, name: String) extends Arg { } case class OpaqueSlot(imm: Node) extends Arg { - override def contextualName(ctx: Component): String = imm.name + override def contextualName(ctx: Component): String = imm.contextualName(ctx) override def name: String = imm.name } diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 5080f15f..cde18da7 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -150,15 +150,17 @@ trait RecordSpecUtils { val in = IO(Input(new InnerRecord)) val out = IO(Output(new InnerRecord)) val inst = Module(new InnerModule) - inst.foo := in - out := inst.bar + inst.io.foo := in + out := inst.io.bar } class InnerModule extends Module { - val foo = IO(Input(new InnerRecord)) - val bar = IO(Output(new InnerRecord)) + val io = IO(new Bundle { + val foo = Input(new InnerRecord) + val bar = Output(new InnerRecord) + }) // DO NOT do this; just for testing element connections - bar.elements.head._2 := foo.elements.head._2 + io.bar.elements.head._2 := io.foo.elements.head._2 } class NamedSingleElementRecord extends Record { @@ -238,25 +240,24 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { var mod: NestedRecordModule = null ChiselStage.elaborate { mod = new NestedRecordModule; mod } val testStrings = Seq( - mod.in.toTarget.toString(), - mod.in.k.toTarget.toString(), - mod.in.k.k.toTarget.toString(), - mod.in.elements.head._2.toTarget.toString(), - mod.in.k.elements.head._2.toTarget.toString(), - mod.in.k.k.elements.head._2.toTarget.toString() + mod.inst.io.foo.toTarget.serialize, + mod.inst.io.foo.k.toTarget.serialize, + mod.inst.io.foo.k.k.toTarget.serialize, + mod.inst.io.foo.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.elements.head._2.toTarget.serialize, + mod.inst.io.foo.k.k.elements.head._2.toTarget.serialize ) - testStrings.foreach(x => assert(x == "~NestedRecordModule|NestedRecordModule>in")) + testStrings.foreach(x => assert(x == "~NestedRecordModule|InnerModule>io.foo")) } they should "work correctly when connecting nested opaque type elements" in { val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } nestedRecordChirrtl should include("input in : UInt<8>") nestedRecordChirrtl should include("output out : UInt<8>") - nestedRecordChirrtl should include("inst.foo <= in") - nestedRecordChirrtl should include("out <= inst.bar") - nestedRecordChirrtl should include("input foo : UInt<8>") - nestedRecordChirrtl should include("output bar : UInt<8>") - nestedRecordChirrtl should include("bar <= foo") + nestedRecordChirrtl should include("inst.io.foo <= in") + nestedRecordChirrtl should include("out <= inst.io.bar") + nestedRecordChirrtl should include("output io : { flip foo : UInt<8>, bar : UInt<8>}") + nestedRecordChirrtl should include("io.bar <= io.foo") } they should "throw an error when map contains a named element and opaqueType is overriden to true" in { -- cgit v1.2.3 From bb3ef96d8911dbba4e22926ad4ce71eb8ab0d869 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Wed, 31 Aug 2022 01:41:09 +0000 Subject: Wires should have source location information in firrtl (#2714) (#2716) - Remove line defeating having wire locators `implicit val noSourceInfo = UnlocatableSourceInfo` from `WireDefault#apply` - Add test to show locators (cherry picked from commit f701a9f8151891e3bf9019cd3229cb3f2cd1833b) Co-authored-by: Chick Markley --- core/src/main/scala/chisel3/Data.scala | 3 +-- src/test/scala/chiselTests/WireSpec.scala | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index d434735a..7c8ec1a9 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -9,7 +9,7 @@ import chisel3.experimental.{Analog, BaseModule, DataMirror, EnumType, FixedPoin import chisel3.internal.Builder.pushCommand import chisel3.internal._ import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.{DeprecatedSourceInfo, SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} +import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform, UnlocatableSourceInfo} import scala.collection.immutable.LazyList // Needed for 2.12 alias import scala.reflect.ClassTag @@ -1075,7 +1075,6 @@ object WireDefault { implicit sourceInfo: SourceInfo, compileOptions: CompileOptions ): T = { - implicit val noSourceInfo = UnlocatableSourceInfo val x = Wire(t) requireIsHardware(init, "wire initializer") x := init diff --git a/src/test/scala/chiselTests/WireSpec.scala b/src/test/scala/chiselTests/WireSpec.scala index 11a1f1a1..058a7f08 100644 --- a/src/test/scala/chiselTests/WireSpec.scala +++ b/src/test/scala/chiselTests/WireSpec.scala @@ -3,6 +3,7 @@ package chiselTests import chisel3._ +import chisel3.stage.ChiselStage class WireSpec extends ChiselFlatSpec { "WireDefault.apply" should "work" in { @@ -17,4 +18,19 @@ class WireSpec extends ChiselFlatSpec { it should "not allow init argument to affect type inference" in { assertDoesNotCompile("val x: UInt = WireDefault(UInt(4.W), 2.S)") } + it should "have source locator information on wires" in { + class Dummy extends chisel3.Module { + val in = IO(Input(Bool())) + val out = IO(Output(Bool())) + + val wire = WireInit(Bool(), true.B) + val wire2 = Wire(Bool()) + wire2 := in + out := in & wire & wire2 + } + + val chirrtl = ChiselStage.emitChirrtl(new Dummy) + chirrtl should include("wire wire : UInt<1> @[WireSpec.scala") + chirrtl should include("wire wire2 : UInt<1> @[WireSpec.scala") + } } -- cgit v1.2.3 From 7ff5fd4aa7dc01099614da9cc3b72d53f61d1cdb Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 1 Sep 2022 01:50:58 +0000 Subject: Revert "Privatize trait VerifPrintMacros (#2683)" (#2718) (#2719) This reverts commit de76e70bc5905fc4ebc8a2e323e16620fa6832ec. (cherry picked from commit e1e0503d969c8f4bb68a3beedebca5d9238192fd) Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/VerificationStatement.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala index 3b6da3f6..1b13b86c 100644 --- a/core/src/main/scala/chisel3/VerificationStatement.scala +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -23,7 +23,7 @@ import scala.reflect.macros.blackbox * * @groupprio VerifPrintMacros 1001 */ -private[chisel3] trait VerifPrintMacrosDoc +trait VerifPrintMacrosDoc object assert extends VerifPrintMacrosDoc { -- cgit v1.2.3 From 341dd51d76b8b068c59b184ceb952624d42abbfa Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 1 Sep 2022 23:55:26 +0000 Subject: Remove incorrect clock warning on Mem.read (backport #2721) (#2722) * Remove incorrect clock warning on Mem.read (#2721) Mem.read is combinational and thus unaffected by the clock, and so it does not make sense to issue warnings about the current clock in this context. (cherry picked from commit 5fdf74f95e64cb69d6097547f20d789a83dbd735) * Keep old version of MemBase.clockWarning for binary compatibility This method is impossible for users to call, but it is easy enough to keep around a version of it to make MiMa happy. Co-authored-by: Andrew Waterman Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Mem.scala | 19 ++++++++++++++----- src/test/scala/chiselTests/MultiClockSpec.scala | 8 -------- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/core/src/main/scala/chisel3/Mem.scala b/core/src/main/scala/chisel3/Mem.scala index d6ab9c4b..91872979 100644 --- a/core/src/main/scala/chisel3/Mem.scala +++ b/core/src/main/scala/chisel3/Mem.scala @@ -60,7 +60,10 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) // ensure memory ports are created with the same clock unless explicitly specified to use a different clock private val clockInst: Option[Clock] = Builder.currentClock - protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = { + // Only kept for binary compatibility reasons, impossible for users to call + protected def clockWarning(sourceInfo: Option[SourceInfo]): Unit = clockWarning(sourceInfo, MemPortDirection.INFER) + + protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = { // Turn into pretty String if possible, if not, Builder.deprecated will find one via stack trace val infoStr = sourceInfo.collect { case SourceLine(file, line, col) => s"$file:$line:$col" } Builder.deprecated( @@ -135,7 +138,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) compileOptions: CompileOptions ): T = { if (warn && clockInst.isDefined && clock != clockInst.get) { - clockWarning(Some(sourceInfo)) + clockWarning(Some(sourceInfo), dir) } makePort(sourceInfo, idx, dir, clock) } @@ -167,7 +170,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) implicit compileOptions: CompileOptions ): Unit = { if (warn && clockInst.isDefined && clock != clockInst.get) { - clockWarning(None) + clockWarning(None, MemPortDirection.WRITE) } implicit val sourceInfo = UnlocatableSourceInfo makePort(UnlocatableSourceInfo, idx, MemPortDirection.WRITE, clock) := data @@ -226,7 +229,7 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) ): Unit = { implicit val sourceInfo = UnlocatableSourceInfo if (warn && clockInst.isDefined && clock != clockInst.get) { - clockWarning(None) + clockWarning(None, MemPortDirection.WRITE) } val accessor = makePort(sourceInfo, idx, MemPortDirection.WRITE, clock).asInstanceOf[Vec[Data]] val dataVec = data.asInstanceOf[Vec[Data]] @@ -274,7 +277,13 @@ sealed abstract class MemBase[T <: Data](val t: T, val length: BigInt) * @note when multiple conflicting writes are performed on a Mem element, the * result is undefined (unlike Vec, where the last assignment wins) */ -sealed class Mem[T <: Data] private[chisel3] (t: T, length: BigInt) extends MemBase(t, length) +sealed class Mem[T <: Data] private[chisel3] (t: T, length: BigInt) extends MemBase(t, length) { + override protected def clockWarning(sourceInfo: Option[SourceInfo], dir: MemPortDirection): Unit = { + // Do not issue clock warnings on reads, since they are not clocked + if (dir != MemPortDirection.READ) + super.clockWarning(sourceInfo, dir) + } +} object SyncReadMem { diff --git a/src/test/scala/chiselTests/MultiClockSpec.scala b/src/test/scala/chiselTests/MultiClockSpec.scala index 381b4009..29ec6509 100644 --- a/src/test/scala/chiselTests/MultiClockSpec.scala +++ b/src/test/scala/chiselTests/MultiClockSpec.scala @@ -166,14 +166,6 @@ class MultiClockSpec extends ChiselFlatSpec with Utils { } "Differing clocks at memory and read accessor instantiation" should "warn" in { - class modMemReadDifferingClock extends Module { - val myClock = IO(Input(Clock())) - val mem = withClock(myClock) { Mem(4, UInt(8.W)) } - val readVal = mem.read(0.U) - } - val (logMemReadDifferingClock, _) = grabLog(ChiselStage.elaborate(new modMemReadDifferingClock)) - logMemReadDifferingClock should include("memory is different") - class modSyncReadMemReadDifferingClock extends Module { val myClock = IO(Input(Clock())) val mem = withClock(myClock) { SyncReadMem(4, UInt(8.W)) } -- cgit v1.2.3 From a7e44dfacda7d8d85c35598a470a0e0aee013483 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 6 Sep 2022 22:18:38 +0000 Subject: Update video links for CCC 2022 (#2725) (#2726) (cherry picked from commit 248daa5ffcfb31a74186f6352c953901d570b393) Co-authored-by: Jiuyang Liu --- README.md | 32 +++++++++++--------------------- 1 file changed, 11 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 1f9fb220..6a903dd6 100644 --- a/README.md +++ b/README.md @@ -4,27 +4,17 @@ ### CCC 2022 CCC(Chisel Community Conference) is an annual gathering of Chisel community enthusiasts and technical exchange workshop. With the support of the Chisel development community and RISC-V International, this conference will bring together designers and developers with hands-on experience in Chisel from home and abroad to share cutting-edge results and experiences from both the open source community as well as industry. -CCC2022 will be held online, You can add [ics file](https://calendar.google.com/calendar/embed?src=c_pluc02j9c4ambkiljlud3620fk%40group.calendar.google.com&ctz=Atlantic%2FAzores) to the calendar for conference agenda. - -Conference Time (in various time zones) -* Saturday, August 27 ⋅ 09:00 - 16:00 UTC+8 (Shanghai/China) -* Friday, August 26 ⋅ 21:00 - 04:00 UTC-4 (Eastern Daylight Time) -* Friday, August 26 ⋅ 18:00 - 25:00 UTC-7 (Los Angeles/US) -* Saturday, August 27 ⋅ 02:00 - 9:00 UTC+1 (London/UK) - -Click on [Zoom Link](https://us02web.zoom.us/j/87836037616?pwd=cXVUZjkrWlBDU3JuU0NLTU8zbG0xQT09) to participate. - -The conference agenda is: -1. Constellation, a Open-source Chisel NoC Generator for SoCs - Jerry Zhao@UCB BAR -2. The formal verification capabilities of chiseltest - Kevin Laeufer@UCB BAR -3. Chisel Breakdown 03 - Jack Koenig@SiFive -4. The Next Generation FIRRTL Compiler is Here! - Prithayan Barua&Hideto Ueno@SiFive -5. Implementing RISC-V Scalar Cryptography/Bitmanip extensions in Chisel - Hongren Zheng@Tsinghua University+PLCT -6. SCIRT: Bridging the Type System Gap for Circuit Generators - Ruikang Wang@Tsinghua University+PLCT -7. ChiselDB: Mapping Hardware Data Structures to Database Tables for Efficient Data Analysis Jiawei Lin@ICT -8. From Chisel to Chips in Fully Open-Source - Martin Schoeberl@DTU - -See you this week :) +The recording of CCC 2022 has been uploaded: +1. [Constellation, a Open-source Chisel NoC Generator for SoCs - Jerry Zhao@UCB BAR](https://www.youtube.com/watch?v=8FGnu0Tq3Qk&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=1) +2. [The formal verification capabilities of chiseltest - Kevin Laeufer@UCB BAR](https://www.youtube.com/watch?v=-BUcTwE_URg&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=2) +3. [Chisel Breakdown 03 - Jack Koenig@SiFive](https://www.youtube.com/watch?v=4vgSrW7Cdkk&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=3) +4. [The Next Generation FIRRTL Compiler is Here! - Prithayan Barua&Hideto Ueno@SiFive](https://www.youtube.com/watch?v=V3h30s31qfk&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=4) +5. [Implementing RISC-V Scalar Cryptography/Bitmanip extensions in Chisel - Hongren Zheng@Tsinghua University+PLCT](https://www.youtube.com/watch?v=hXaZMQPRXo0&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=5) +6. [SCIRT: Bridging the Type System Gap for Circuit Generators - Ruikang Wang@Tsinghua University+PLCT](https://www.youtube.com/watch?v=fPl4eZHh1-M&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=6) +7. [ChiselDB: Mapping Hardware Data Structures to Database Tables for Efficient Data Analysis Jiawei Lin@ICT](https://www.youtube.com/watch?v=Df1l9h351BM&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=7) +8. [From Chisel to Chips in Fully Open-Source - Martin Schoeberl@DTU](https://www.youtube.com/watch?v=FenSOWKBbAw&list=PL02hojk7ZTSek-Zro6Zdj_nDlp6khGssJ&index=8) + +See you next year :) --- -- cgit v1.2.3 From 03f62c8c9bc2f6cc65ab34b8902f5e9a61701595 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 15 Sep 2022 20:46:10 +0000 Subject: Change description for SInt unary negation (#2729) (#2734) Referenced to: chipsalliance/chisel3#2728 (cherry picked from commit a4dae9c340c71c063cf0fdec290a6e011b82746d) Co-authored-by: Marco Origlia <30799310+moriglia@users.noreply.github.com>--- core/src/main/scala/chisel3/Bits.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/Bits.scala b/core/src/main/scala/chisel3/Bits.scala index 72094a65..70704e01 100644 --- a/core/src/main/scala/chisel3/Bits.scala +++ b/core/src/main/scala/chisel3/Bits.scala @@ -896,7 +896,7 @@ sealed class SInt private[chisel3] (width: Width) extends Bits(width) with Num[S private[chisel3] override def cloneTypeWidth(w: Width): this.type = new SInt(w).asInstanceOf[this.type] - /** Unary negation (expanding width) + /** Unary negation (constant width) * * @return a hardware $coll equal to zero minus this $coll * $constantWidth -- cgit v1.2.3 From 90e4ac38d49a9bae2bda1990c5de911c51c8c72a Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 20 Sep 2022 17:56:53 +0000 Subject: Bump to Scala 2.13.9 and 2.12.17 (backport #2739) (#2740) * Bump to Scala 2.13.9 and 2.12.17 (#2739) (cherry picked from commit 647c8a458ebc89ae3df818d540d308f0c417ae9f) # Conflicts: # .github/workflows/test.yml * Resolve backport conflicts * Update mimaPreviousArtifacts Co-authored-by: Jack Koenig --- .github/workflows/test.yml | 2 +- build.sbt | 21 ++++++++++++--------- build.sc | 2 +- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index f1fa715d..bd27edb9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: matrix: system: ["ubuntu-20.04"] jvm: ["adopt@1.8"] - scala: ["2.13.6", "2.12.16"] + scala: ["2.13.9", "2.12.17"] espresso: ["2.4"] runs-on: ${{ matrix.system }} diff --git a/build.sbt b/build.sbt index d9acadff..36102074 100644 --- a/build.sbt +++ b/build.sbt @@ -21,8 +21,8 @@ lazy val commonSettings = Seq ( organization := "edu.berkeley.cs", version := "3.5-SNAPSHOT", autoAPIMappings := true, - scalaVersion := "2.12.16", - crossScalaVersions := Seq("2.13.6", "2.12.16"), + scalaVersion := "2.12.17", + crossScalaVersions := Seq("2.13.9", "2.12.17"), scalacOptions := Seq("-deprecation", "-feature"), libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, // Macros paradise is integrated into 2.13 but requires a scalacOption @@ -113,6 +113,7 @@ lazy val pluginScalaVersions = Seq( "2.12.14", "2.12.15", "2.12.16", + "2.12.17", "2.13.0", "2.13.1", "2.13.2", @@ -121,7 +122,8 @@ lazy val pluginScalaVersions = Seq( "2.13.5", "2.13.6", "2.13.7", - "2.13.8" + "2.13.8", + "2.13.9" ) lazy val plugin = (project in file("plugin")). @@ -141,11 +143,12 @@ lazy val plugin = (project in file("plugin")). ). settings( mimaPreviousArtifacts := { - // There is not yet a 2.12.16 artifact, so suppress until 3.5.4 is released - if (scalaVersion.value == "2.12.16") { + // There is not yet a 2.12.17 nor 2.13.9 artifact, so suppress until 3.5.5 is released + val skipVersions = Seq("2.12.17", "2.13.9") + if (skipVersions.contains(scalaVersion.value)) { Set() } else { - Set("edu.berkeley.cs" % "chisel3-plugin" % "3.5.3" cross CrossVersion.full) + Set("edu.berkeley.cs" % "chisel3-plugin" % "3.5.4" cross CrossVersion.full) } } ) @@ -165,7 +168,7 @@ lazy val macros = (project in file("macros")). settings(name := "chisel3-macros"). settings(commonSettings: _*). settings(publishSettings: _*). - settings(mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3-macros" % "3.5.3")) + settings(mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3-macros" % "3.5.4")) lazy val firrtlRef = ProjectRef(workspaceDirectory / "firrtl", "firrtl") @@ -180,7 +183,7 @@ lazy val core = (project in file("core")). ). settings(publishSettings: _*). settings( - mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3-core" % "3.5.3"), + mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3-core" % "3.5.4"), mimaBinaryIssueFilters ++= Seq( // Modified package private methods (https://github.com/lightbend/mima/issues/53) ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.Data._computeName"), @@ -226,7 +229,7 @@ lazy val chisel = (project in file(".")). dependsOn(core). aggregate(macros, core, plugin). settings( - mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3" % "3.5.3"), + mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3" % "3.5.4"), mimaBinaryIssueFilters ++= Seq( // Modified package private methods (https://github.com/lightbend/mima/issues/53) ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.stage.ChiselOptions.this"), diff --git a/build.sc b/build.sc index d7dbc6d7..9e46098c 100644 --- a/build.sc +++ b/build.sc @@ -6,7 +6,7 @@ import coursier.maven.MavenRepository import $ivy.`com.lihaoyi::mill-contrib-buildinfo:$MILL_VERSION` import mill.contrib.buildinfo.BuildInfo -object chisel3 extends mill.Cross[chisel3CrossModule]("2.13.6", "2.12.14") +object chisel3 extends mill.Cross[chisel3CrossModule]("2.13.9", "2.12.17") // The following stanza is searched for and used when preparing releases. // Please retain it. -- cgit v1.2.3 From fc3fedaa9c16d7861b452388a70ec2f6e2a3dc30 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 20 Sep 2022 18:54:01 +0000 Subject: Cleanup Cookbook and printing docs (#2727) (#2742) * Cleanup Cookbook and printing docs * Format specifiers are actually concise now Co-authored-by: Megan Wachs (cherry picked from commit df2a71833ffc8ee8a053a1e8ea41c482e46be132) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- docs/src/cookbooks/cookbook.md | 8 ++++---- docs/src/explanations/printing.md | 34 ++++++++++++++++------------------ 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md index ab8e76d3..14bf9706 100644 --- a/docs/src/cookbooks/cookbook.md +++ b/docs/src/cookbooks/cookbook.md @@ -55,7 +55,7 @@ class Foo extends RawModule { bundle.foo := 0xc.U bundle.bar := 0x3.U val uint = bundle.asUInt - printf(p"$uint") // 195 + printf(cf"$uint") // 195 // Test assert(uint === 0xc3.U) @@ -78,7 +78,7 @@ class Foo extends RawModule { val uint = 0xb4.U val bundle = uint.asTypeOf(new MyBundle) - printf(p"$bundle") // Bundle(foo -> 11, bar -> 4) + printf(cf"$bundle") // Bundle(foo -> 11, bar -> 4) // Test assert(bundle.foo === 0xb.U) @@ -126,7 +126,7 @@ class Foo extends RawModule { val uint = 0xc.U val vec = VecInit(uint.asBools) - printf(p"$vec") // Vec(0, 0, 1, 1) + printf(cf"$vec") // Vec(0, 0, 1, 1) // Test assert(vec(0) === false.B) @@ -147,7 +147,7 @@ class Foo extends RawModule { val vec = VecInit(true.B, false.B, true.B, true.B) val uint = vec.asUInt - printf(p"$uint") // 13 + printf(cf"$uint") // 13 // Test // (remember leftmost Bool in Vec is low order bit) diff --git a/docs/src/explanations/printing.md b/docs/src/explanations/printing.md index abd1a427..71a6f5cf 100644 --- a/docs/src/explanations/printing.md +++ b/docs/src/explanations/printing.md @@ -13,22 +13,22 @@ Chisel provides the `printf` function for debugging purposes. It comes in two fl ### Scala-style -Chisel also supports printf in a style similar to [Scala's String Interpolation](http://docs.scala-lang.org/overviews/core/string-interpolation.html). Chisel provides a custom string interpolator `p` which can be used as follows: +Chisel also supports printf in a style similar to [Scala's String Interpolation](http://docs.scala-lang.org/overviews/core/string-interpolation.html). Chisel provides a custom string interpolator `cf` which follows C-style format specifiers (see section [C-style](#c-style) below). Here's a few examples of using the `cf` interpolator: ```scala mdoc:invisible import chisel3._ ``` ```scala mdoc:compile-only val myUInt = 33.U -printf(p"myUInt = $myUInt") // myUInt = 33 +printf(cf"myUInt = $myUInt") // myUInt = 33 ``` -Note that when concatenating `p"..."` strings, you need to start with a `p"..."` string: +Note that when concatenating `cf"..."` strings, you need to start with a `cf"..."` string: ```scala mdoc:compile-only // Does not interpolate the second string val myUInt = 33.U -printf("my normal string" + p"myUInt = $myUInt") +printf("my normal string" + cf"myUInt = $myUInt") ``` #### Simple formatting @@ -38,22 +38,20 @@ Other formats are available as follows: ```scala mdoc:compile-only val myUInt = 33.U // Hexadecimal -printf(p"myUInt = 0x${Hexadecimal(myUInt)}") // myUInt = 0x21 +printf(cf"myUInt = 0x$myUInt%x") // myUInt = 0x21 // Binary -printf(p"myUInt = ${Binary(myUInt)}") // myUInt = 100001 +printf(cf"myUInt = $myUInt%b") // myUInt = 100001 // Character -printf(p"myUInt = ${Character(myUInt)}") // myUInt = ! +printf(cf"myUInt = $myUInt%c") // myUInt = ! ``` -We recognize that the format specifiers are verbose, so we are working on a more concise syntax. - #### Aggregate data-types Chisel provides default custom "pretty-printing" for Vecs and Bundles. The default printing of a Vec is similar to printing a Seq or List in Scala while printing a Bundle is similar to printing a Scala Map. ```scala mdoc:compile-only val myVec = VecInit(5.U, 10.U, 13.U) -printf(p"myVec = $myVec") // myVec = Vec(5, 10, 13) +printf(cf"myVec = $myVec") // myVec = Vec(5, 10, 13) val myBundle = Wire(new Bundle { val foo = UInt() @@ -61,7 +59,7 @@ val myBundle = Wire(new Bundle { }) myBundle.foo := 3.U myBundle.bar := 11.U -printf(p"myBundle = $myBundle") // myBundle = Bundle(a -> 3, b -> 11) +printf(cf"myBundle = $myBundle") // myBundle = Bundle(a -> 3, b -> 11) ``` #### Custom Printing @@ -76,11 +74,11 @@ class Message extends Bundle { val data = UInt(64.W) override def toPrintable: Printable = { val char = Mux(valid, 'v'.U, '-'.U) - p"Message:\n" + - p" valid : ${Character(char)}\n" + - p" addr : 0x${Hexadecimal(addr)}\n" + - p" length : $length\n" + - p" data : 0x${Hexadecimal(data)}\n" + cf"Message:\n" + + cf" valid : $char%c\n" + + cf" addr : $addr%x\n" + + cf" length : $length\n" + + cf" data : $data%x\n" } } @@ -90,7 +88,7 @@ myMessage.addr := "h1234".U myMessage.length := 10.U myMessage.data := "hdeadbeef".U -printf(p"$myMessage") +printf(cf"$myMessage") ``` Which prints the following: @@ -103,7 +101,7 @@ Message: data : 0x00000000deadbeef ``` -Notice the use of `+` between `p` interpolated "strings". The results of `p` interpolation can be concatenated by using the `+` operator. For more information, please see the documentation +Notice the use of `+` between `cf` interpolated "strings". The results of `cf` interpolation can be concatenated by using the `+` operator. ### C-Style -- cgit v1.2.3 From 45909f02e86edff010902c0317e813a8753837b9 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 20 Sep 2022 20:06:44 +0000 Subject: Use new lazy serialization in FIRRTL (#2741) (#2744) This enables emission of modules that serialize to >2 GiB of .fir text. (cherry picked from commit 2cbc852d09bb63cd5da291e5d5d942ab971952a4) Co-authored-by: Jack Koenig --- src/main/scala/chisel3/internal/firrtl/Emitter.scala | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index bbf43a45..ca8562e8 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -11,12 +11,14 @@ private[chisel3] object Emitter { Serializer.serialize(fcircuit) } - def emitLazily(circuit: Circuit): Iterable[String] = { - val result = LazyList(s"circuit ${circuit.name} :\n") - val modules = circuit.components.view.map(Converter.convert) - val moduleStrings = modules.flatMap { m => - Array(Serializer.serialize(m, 1), "\n\n") + def emitLazily(circuit: Circuit): Iterable[String] = new Iterable[String] { + def iterator = { + val prelude = Iterator(s"circuit ${circuit.name} :\n") + val modules = circuit.components.iterator.map(Converter.convert) + val moduleStrings = modules.flatMap { m => + Serializer.lazily(m, 1) ++ Seq("\n\n") + } + prelude ++ moduleStrings } - result ++ moduleStrings } } -- cgit v1.2.3 From c11eead29545799baf8c57e8c1b8b7351b98a258 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 20 Sep 2022 23:16:23 +0000 Subject: Improve CI sentinel job for better branch protection (backport #2743) (#2746) * Improve CI sentinel job for better branch protection (#2743) Previously, failed jobs in the CI matrix would cause the sentinel job (all-tests-passed) to be skipped, which for purposes of Github Actions branch protection would count as "success". This allowed PRs with failing CI to be merged. This new approach which uses two sentinel jobs should not suffer from this same issue. (cherry picked from commit cc507a84cb1c319f83f8cbb935347d7265b73387) # Conflicts: # .github/workflows/test.yml * Resolve backport conflicts Co-authored-by: Jack Koenig --- .github/workflows/test.yml | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index bd27edb9..9ace173e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,15 +82,42 @@ jobs: run: sbt integrationTests/test # Sentinel job to simplify how we specify which checks need to pass in branch - # protection and in Mergify + # protection and in Mergify. This job checks that all jobs were successful. # # When adding new jobs, please add them to `needs` below + check-tests: + name: "check tests" + needs: [ci, integration] + runs-on: ubuntu-20.04 + if: success() # only run if all tests have passed + outputs: + success: ${{ steps.setoutput.outputs.success }} + steps: + - id: setoutput + run: echo "::set-output name=success::true" + + # Related to check-tests above, this job _always_ runs (even if tests fail + # and thus check-steps is skipped). This two sentinel job approach avoids an + # issue where failing tests causes a single sentinel job to be skipped which + # counts as passing for purposes of branch protection. + # + # See: https://brunoscheufler.com/blog/2022-04-09-the-required-github-status-check-that-wasnt all_tests_passed: name: "all tests passed" - needs: [ci, integration] runs-on: ubuntu-20.04 + if: always() # Always run so that we never skip this check + needs: check-tests + # Pass only if check-tests set its output value steps: - - run: echo Success! + - run: | + PASSED="${{ needs.check-tests.outputs.success }}" + if [[ $PASSED == "true" ]]; then + echo "All tests passed!" + exit 0 + else + echo "One or more tests FAILED!" + exit 1 + fi # sbt ci-release publishes all cross versions so this job needs to be # separate from a Scala versions build matrix to avoid duplicate publishing -- cgit v1.2.3 From f19556801137091fa79a6d79395985474527b72d Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 27 Sep 2022 19:36:39 +0000 Subject: Use Treadle (on pull requests only) to speed up CI (backport #2341) (#2748) * Support using Treadle for 'sbt test' Treadle will be used as the "defaultBackend" when the environment variable CHISEL3_CI_USE_TREADLE is set. The intent is to set this variable during CI for pre-merge CI (aka on pull requests). (cherry picked from commit 7d39b7bd2b6f38dac90fe25064744ffc0ada0fe4) * Use Treadle for CI on pull requests (cherry picked from commit 82660673e56a816e68fcc068e3e04e127f076faf) Co-authored-by: Jack Koenig --- .github/workflows/test.yml | 3 +++ src/test/scala/chiselTests/ChiselSpec.scala | 5 ++++- src/test/scala/chiselTests/Vec.scala | 4 ++-- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9ace173e..139d5908 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -50,6 +50,9 @@ jobs: - name: Documentation (Scala 2.12 only) if: startsWith(matrix.scala, '2.12') run: sbt ++${{ matrix.scala }} docs/mdoc unidoc + - name: Use Treadle for Pull Requests + if: github.event_name == 'pull_request' + run: echo "CHISEL3_CI_USE_TREADLE=1" >> $GITHUB_ENV - name: Test run: sbt ++${{ matrix.scala }} test - name: Binary compatibility diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala index 6f560b94..e00afcf6 100644 --- a/src/test/scala/chiselTests/ChiselSpec.scala +++ b/src/test/scala/chiselTests/ChiselSpec.scala @@ -38,7 +38,10 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities { annotations: AnnotationSeq = Seq() ): Boolean = { // Change this to enable Treadle as a backend - val defaultBackend = chisel3.testers.TesterDriver.defaultBackend + val defaultBackend = { + val useTreadle = sys.env.get("CHISEL3_CI_USE_TREADLE").isDefined + if (useTreadle) chisel3.testers.TreadleBackend else chisel3.testers.TesterDriver.defaultBackend + } val hasBackend = TestUtils.containsBackend(annotations) val annos: Seq[Annotation] = if (hasBackend) annotations else defaultBackend +: annotations TesterDriver.execute(() => t, additionalVResources, annos) diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 02743187..4a871890 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -6,7 +6,7 @@ import org.scalacheck._ import chisel3._ import chisel3.stage.ChiselStage -import chisel3.testers.BasicTester +import chisel3.testers.{BasicTester, TesterDriver} import chisel3.util._ import org.scalacheck.Shrink import scala.annotation.tailrec @@ -456,7 +456,7 @@ class VecSpec extends ChiselPropSpec with Utils { } property("Infering widths on huge Vecs should not cause a stack overflow") { - assertTesterPasses { new HugeVecTester(10000) } + assertTesterPasses(new HugeVecTester(10000), annotations = TesterDriver.verilatorOnly) } property("A Reg of a Vec of a single 1 bit element should compile and work") { -- cgit v1.2.3 From 9f1eae19445e110bb743176767f59970ce1d36b5 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 27 Sep 2022 22:44:18 +0000 Subject: [ci] Split formatting and docs checks into separate task (backport #2749) (#2750) * [ci] Split formatting and docs checks into separate task (#2749) (cherry picked from commit ed2694293d61aa930341aef9f30c7762b889b5ae) # Conflicts: # .github/workflows/test.yml * Resolve backport conflicts Co-authored-by: Jack Koenig --- .github/workflows/test.yml | 25 ++++++++++++++++++------- 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 139d5908..e48f37ea 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -44,12 +44,6 @@ jobs: java-version: ${{ matrix.jvm }} - name: Cache Scala uses: coursier/cache-action@v5 - - name: Check Formatting (Scala 2.12 only) - if: startsWith(matrix.scala, '2.12') - run: sbt ++${{ matrix.scala }} scalafmtCheckAll - - name: Documentation (Scala 2.12 only) - if: startsWith(matrix.scala, '2.12') - run: sbt ++${{ matrix.scala }} docs/mdoc unidoc - name: Use Treadle for Pull Requests if: github.event_name == 'pull_request' run: echo "CHISEL3_CI_USE_TREADLE=1" >> $GITHUB_ENV @@ -58,6 +52,23 @@ jobs: - name: Binary compatibility run: sbt ++${{ matrix.scala }} mimaReportBinaryIssues + doc: + name: Formatting and Documentation + runs-on: ubuntu-20.04 + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup Scala + uses: olafurpg/setup-scala@v10 + with: + java-version: "adopt@1.11" + - name: Cache Scala + uses: coursier/cache-action@v5 + - name: Check Formatting + run: sbt scalafmtCheckAll + - name: Documentation + run: sbt docs/mdoc unidoc + integration: name: Integration Tests (w/ chiseltest) runs-on: ubuntu-20.04 @@ -90,7 +101,7 @@ jobs: # When adding new jobs, please add them to `needs` below check-tests: name: "check tests" - needs: [ci, integration] + needs: [ci, integration, doc] runs-on: ubuntu-20.04 if: success() # only run if all tests have passed outputs: -- cgit v1.2.3 From 5a79814631bdc8c71c5a7b4722cd43712f7ff445 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 29 Sep 2022 18:53:44 +0000 Subject: Add lexical scope checks to Assert, Assume and Printf (#2706) (#2753) (cherry picked from commit f462c9f9307bebf3012da52432c3729cd752321c) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- core/src/main/scala/chisel3/Data.scala | 2 +- core/src/main/scala/chisel3/Printable.scala | 13 ++++ core/src/main/scala/chisel3/Printf.scala | 3 + .../main/scala/chisel3/VerificationStatement.scala | 2 + src/test/scala/chiselTests/Assert.scala | 77 ++++++++++++++++++++++ src/test/scala/chiselTests/Printf.scala | 39 +++++++++++ 6 files changed, 135 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 7c8ec1a9..f52f99de 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -662,7 +662,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { */ private[chisel3] def typeEquivalent(that: Data): Boolean - private def requireVisible(): Unit = { + private[chisel3] def requireVisible(): Unit = { val mod = topBindingOpt.flatMap(_.location) topBindingOpt match { case Some(tb: TopBinding) if (mod == Builder.currentModule) => diff --git a/core/src/main/scala/chisel3/Printable.scala b/core/src/main/scala/chisel3/Printable.scala index 78655517..82054ee1 100644 --- a/core/src/main/scala/chisel3/Printable.scala +++ b/core/src/main/scala/chisel3/Printable.scala @@ -134,6 +134,19 @@ object Printable { val bufEscapeBackSlash = buf.map(_.replace("\\", "\\\\")) StringContext(bufEscapeBackSlash.toSeq: _*).cf(data: _*) } + + private[chisel3] def checkScope(message: Printable): Unit = { + def getData(x: Printable): Seq[Data] = { + x match { + case y: FirrtlFormat => Seq(y.bits) + case Name(d) => Seq(d) + case FullName(d) => Seq(d) + case Printables(p) => p.flatMap(getData(_)).toSeq + case _ => Seq() // Handles subtypes PString and Percent + } + } + getData(message).foreach(_.requireVisible()) + } } case class Printables(pables: Iterable[Printable]) extends Printable { diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala index bdcca8e1..9410a409 100644 --- a/core/src/main/scala/chisel3/Printf.scala +++ b/core/src/main/scala/chisel3/Printf.scala @@ -108,6 +108,9 @@ object printf { ): Printf = { val clock = Builder.forcedClock val printfId = new Printf(pable) + + Printable.checkScope(pable) + pushCommand(chisel3.internal.firrtl.Printf(printfId, sourceInfo, clock.ref, pable)) printfId } diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala index 1b13b86c..10cece60 100644 --- a/core/src/main/scala/chisel3/VerificationStatement.scala +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -178,6 +178,7 @@ object assert extends VerifPrintMacrosDoc { compileOptions: CompileOptions ): Assert = { val id = new Assert() + message.foreach(Printable.checkScope(_)) when(!Module.reset.asBool()) { failureMessage("Assertion", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assert, sourceInfo, Module.clock.ref, cond.ref, "")) @@ -343,6 +344,7 @@ object assume extends VerifPrintMacrosDoc { compileOptions: CompileOptions ): Assume = { val id = new Assume() + message.foreach(Printable.checkScope(_)) when(!Module.reset.asBool()) { failureMessage("Assumption", line, cond, message) Builder.pushCommand(Verification(id, Formal.Assume, sourceInfo, Module.clock.ref, cond.ref, "")) diff --git a/src/test/scala/chiselTests/Assert.scala b/src/test/scala/chiselTests/Assert.scala index d7885a3b..5e7b6496 100644 --- a/src/test/scala/chiselTests/Assert.scala +++ b/src/test/scala/chiselTests/Assert.scala @@ -84,6 +84,64 @@ class PrintableAssumeTester extends Module { out := in } +class PrintableScopeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + + val w = Wire(UInt(8.W)) + w := 255.U + + val printableWire = cf"$w" + val printablePort = cf"$in" +} + +class AssertPrintableWireScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + assert(1.U === 2.U, mod.printableWire) + stop() +} + +class AssertPrintablePortScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + mod.in := 255.U + assert(1.U === 1.U, mod.printablePort) + stop() +} + +class AssertPrintableFailingWhenScope extends BasicTester { + val mod = Module(new PrintableWhenScopeTester) + assert(1.U === 1.U, mod.printable) + stop() +} + +class AssumePrintableWireScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + assume(1.U === 1.U, mod.printableWire) + stop() +} + +class AssumePrintablePortScope extends BasicTester { + val mod = Module(new PrintableScopeTester) + mod.in := 255.U + assume(1.U === 1.U, mod.printablePort) + stop() +} + +class PrintableWhenScopeTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + + out := in + + val w = Wire(UInt(8.W)) + w := 255.U + var printable = cf"" + when(true.B) { + printable = cf"$w" + } +} + class AssertSpec extends ChiselFlatSpec with Utils { "A failing assertion" should "fail the testbench" in { assert(!runTester { new FailingAssertTester }) @@ -94,6 +152,25 @@ class AssertSpec extends ChiselFlatSpec with Utils { "An assertion" should "not assert until we come out of reset" in { assertTesterPasses { new PipelinedResetTester } } + + "Assert Printables" should "respect port scoping" in { + assertTesterPasses { new AssertPrintablePortScope } + } + "Assert Printables" should "respect wire scoping" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssertPrintableWireScope) } + } + "Assume Printables" should "respect port scoping" in { + assertTesterPasses { new AssumePrintablePortScope } + } + + "Assume Printables" should "respect wire scoping" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssumePrintableWireScope) } + } + + "Assert Printables" should "respect when scope" in { + a[ChiselException] should be thrownBy { ChiselStage.elaborate(new AssertPrintableFailingWhenScope) } + } + "Assertions" should "allow the modulo operator % in the message" in { assertTesterPasses { new ModuloAssertTester } } diff --git a/src/test/scala/chiselTests/Printf.scala b/src/test/scala/chiselTests/Printf.scala index 4171f97f..6c9f05f0 100644 --- a/src/test/scala/chiselTests/Printf.scala +++ b/src/test/scala/chiselTests/Printf.scala @@ -4,6 +4,7 @@ package chiselTests import chisel3._ import chisel3.testers.BasicTester +import chisel3.stage.ChiselStage class SinglePrintfTester() extends BasicTester { val x = 254.U @@ -28,6 +29,38 @@ class ASCIIPrintableTester extends BasicTester { stop() } +class ScopeTesterModule extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + out := in + + val w = Wire(UInt(8.W)) + w := 125.U + + val p = cf"$in" + val wp = cf"$w" +} + +class PrintablePrintfScopeTester extends BasicTester { + ChiselStage.elaborate { + new Module { + val mod = Module(new ScopeTesterModule) + printf(mod.p) + } + } + stop() +} + +class PrintablePrintfWireScopeTester extends BasicTester { + ChiselStage.elaborate { + new Module { + val mod = Module(new ScopeTesterModule) + printf(mod.wp) + } + } + stop() +} + class PrintfSpec extends ChiselFlatSpec { "A printf with a single argument" should "run" in { assertTesterPasses { new SinglePrintfTester } @@ -41,4 +74,10 @@ class PrintfSpec extends ChiselFlatSpec { "A printf with Printable ASCII characters 1-127" should "run" in { assertTesterPasses { new ASCIIPrintableTester } } + "A printf with Printable" should "respect port scopes" in { + assertTesterPasses { new PrintablePrintfScopeTester } + } + "A printf with Printable" should "respect wire scopes" in { + a[ChiselException] should be thrownBy { assertTesterPasses { new PrintablePrintfWireScopeTester } } + } } -- cgit v1.2.3 From cb1bb67194ccf4c17d76f5ad2e8b1e8818c252b8 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 30 Sep 2022 01:53:09 +0000 Subject: Cookbooks: use more mdoc power, fix issues (#2752) (#2754) Co-authored-by: Jack Koenig (cherry picked from commit fc970ca28e562f2ea3ba160963604ea3deaf3467) Co-authored-by: Megan Wachs --- docs/src/cookbooks/cookbook.md | 152 +++++++++++++++++++++++++---------------- 1 file changed, 94 insertions(+), 58 deletions(-) diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md index 14bf9706..ac3a0c5d 100644 --- a/docs/src/cookbooks/cookbook.md +++ b/docs/src/cookbooks/cookbook.md @@ -50,7 +50,7 @@ class MyBundle extends Bundle { val bar = UInt(4.W) } -class Foo extends RawModule { +class Foo extends Module { val bundle = Wire(new MyBundle) bundle.foo := 0xc.U bundle.bar := 0x3.U @@ -62,6 +62,11 @@ class Foo extends RawModule { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` + ### How do I create a Bundle from a UInt? Use the [`asTypeOf`](https://www.chisel-lang.org/api/latest/chisel3/UInt.html#asTypeOf[T%3C:chisel3.Data](that:T):T) method to reinterpret the [`UInt`](https://www.chisel-lang.org/api/latest/chisel3/UInt.html) as the type of the [`Bundle`](https://www.chisel-lang.org/api/latest/chisel3/Bundle.html). @@ -74,7 +79,7 @@ class MyBundle extends Bundle { val bar = UInt(4.W) } -class Foo extends RawModule { +class Foo extends Module { val uint = 0xb4.U val bundle = uint.asTypeOf(new MyBundle) @@ -86,6 +91,11 @@ class Foo extends RawModule { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` + ### How can I tieoff a Bundle/Vec to 0? You can use `asTypeOf` as above. If you don't want to worry about the type of the thing @@ -100,7 +110,7 @@ class MyBundle extends Bundle { val bar = Vec(4, UInt(1.W)) } -class Foo(typ: MyBundle) extends RawModule { +class Foo(typ: MyBundle) extends Module { val bundleA = IO(Output(typ)) val bundleB = IO(Output(typ)) @@ -122,7 +132,7 @@ Use [`VecInit`](https://www.chisel-lang.org/api/latest/chisel3/VecInit$.html) gi ```scala mdoc:silent:reset import chisel3._ -class Foo extends RawModule { +class Foo extends Module { val uint = 0xc.U val vec = VecInit(uint.asBools) @@ -136,6 +146,11 @@ class Foo extends RawModule { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` + ### How do I create a UInt from a Vec of Bool? Use the builtin function [`asUInt`](https://www.chisel-lang.org/api/latest/chisel3/Vec.html#asUInt():chisel3.UInt) @@ -143,7 +158,7 @@ Use the builtin function [`asUInt`](https://www.chisel-lang.org/api/latest/chise ```scala mdoc:silent:reset import chisel3._ -class Foo extends RawModule { +class Foo extends Module { val vec = VecInit(true.B, false.B, true.B, true.B) val uint = vec.asUInt @@ -156,6 +171,11 @@ class Foo extends RawModule { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` + ### How do I connect a subset of Bundle fields? See the [DataView cookbook](dataview#how-do-i-connect-a-subset-of-bundle-fields). @@ -220,7 +240,7 @@ For more information, the API Documentation for [`Vec`](https://www.chisel-lang. ```scala mdoc:silent:reset import chisel3._ -class Foo extends RawModule { +class Foo extends Module { val regOfVec = Reg(Vec(4, UInt(32.W))) // Register of 32-bit UInts regOfVec(0) := 123.U // Assignments to elements of the Vec regOfVec(1) := 456.U @@ -234,6 +254,10 @@ class Foo extends RawModule { val initRegOfVec = RegInit(VecInit(Seq.fill(4)(0.U(32.W)))) } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` ### How do I partially reset an Aggregate Reg? @@ -273,6 +297,12 @@ class MyModule2 extends Module { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new MyModule) +getVerilogString(new MyModule2) +``` + ## Bundles @@ -467,6 +497,11 @@ class DetectTwoOnes extends Module { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new DetectTwoOnes) +``` + Note: the `is` statement can take multiple conditions e.g. `is (sTwo1s, sOne1) { ... }`. ### How do I unpack a value ("reverse concatenation") like in Verilog? @@ -497,7 +532,7 @@ class MyBundle extends Bundle { The easiest way to accomplish this in Chisel would be: ```scala mdoc:silent -class Foo extends RawModule { +class Foo extends Module { val z = Wire(UInt(9.W)) z := DontCare // This is a dummy connection val unpacked = z.asTypeOf(new MyBundle) @@ -507,6 +542,11 @@ class Foo extends RawModule { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` + If you **really** need to do this for a one-off case (Think thrice! It is likely you can better structure the code using bundles), then rocket-chip has a [Split utility](https://github.com/freechipsproject/rocket-chip/blob/723af5e6b69e07b5f94c46269a208a8d65e9d73b/src/main/scala/util/Misc.scala#L140) which can accomplish this. ### How do I do subword assignment (assign to some bits in a UInt)? @@ -516,7 +556,7 @@ Below, the left-hand side connection to `io.out(0)` is not allowed. ```scala mdoc:silent:reset import chisel3._ -import chisel3.stage.{ChiselStage, ChiselGeneratorAnnotation} +import chisel3.stage.ChiselStage class Foo extends Module { val io = IO(new Bundle { @@ -529,7 +569,7 @@ class Foo extends Module { If you try to compile this, you will get an error. ```scala mdoc:crash -(new ChiselStage).execute(Array("-X", "verilog"), Seq(new ChiselGeneratorAnnotation(() => new Foo))) +getVerilogString(new Foo) ``` Chisel3 *does not support subword assignment*. @@ -552,6 +592,10 @@ class Foo extends Module { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new Foo) +``` ### How do I create an optional I/O? @@ -574,6 +618,11 @@ class ModuleWithOptionalIOs(flag: Boolean) extends Module { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new ModuleWithOptionalIOs(true)) +``` + The following is an example where an entire `IO` is optional: ```scala mdoc:silent:reset @@ -587,6 +636,11 @@ class ModuleWithOptionalIO(flag: Boolean) extends Module { } ``` +```scala mdoc:invisible +// Hidden but will make sure this actually compiles +getVerilogString(new ModuleWithOptionalIO(true)) +``` + ### How do I create I/O without a prefix? In most cases, you can simply call `IO` multiple times: @@ -670,7 +724,9 @@ Use the compiler plugin, and check out the [Naming Cookbook](naming) if that sti ### How do I get Chisel to name the results of vector reads properly? Currently, name information is lost when using dynamic indexing. For example: -```scala mdoc:silent +```scala mdoc:silent:reset +import chisel3._ + class Foo extends Module { val io = IO(new Bundle { val in = Input(Vec(4, Bool())) @@ -686,26 +742,9 @@ class Foo extends Module { ``` The above code loses the `x` name, instead using `_GEN_3` (the other `_GEN_*` signals are expected). -```verilog -module Foo( - input clock, - input reset, - input io_in_0, - input io_in_1, - input io_in_2, - input io_in_3, - input [1:0] io_idx, - input io_en, - output io_out -); - wire _GEN_1; // @[main.scala 15:13] - wire _GEN_2; // @[main.scala 15:13] - wire _GEN_3; // @[main.scala 15:13] - assign _GEN_1 = 2'h1 == io_idx ? io_in_1 : io_in_0; // @[main.scala 15:13] - assign _GEN_2 = 2'h2 == io_idx ? io_in_2 : _GEN_1; // @[main.scala 15:13] - assign _GEN_3 = 2'h3 == io_idx ? io_in_3 : _GEN_2; // @[main.scala 15:13] - assign io_out = _GEN_3 & io_en; // @[main.scala 16:10] -endmodule + +```scala mdoc:verilog +getVerilogString(new Foo) ``` This can be worked around by creating a wire and connecting the dynamic index to the wire: @@ -713,28 +752,26 @@ This can be worked around by creating a wire and connecting the dynamic index to val x = WireInit(io.in(io.idx)) ``` +```scala mdoc:invisible +class Foo2 extends Module { + val io = IO(new Bundle { + val in = Input(Vec(4, Bool())) + val idx = Input(UInt(2.W)) + val en = Input(Bool()) + val out = Output(Bool()) + }) + + val x = WireInit(io.in(io.idx)) + val y = x && io.en + io.out := y +} +``` + Which produces: -```verilog -module Foo( - input clock, - input reset, - input io_in_0, - input io_in_1, - input io_in_2, - input io_in_3, - input [1:0] io_idx, - input io_en, - output io_out -); - wire _GEN_1; - wire _GEN_2; - wire x; - assign _GEN_1 = 2'h1 == io_idx ? io_in_1 : io_in_0; - assign _GEN_2 = 2'h2 == io_idx ? io_in_2 : _GEN_1; - assign x = 2'h3 == io_idx ? io_in_3 : _GEN_2; - assign io_out = x & io_en; // @[main.scala 16:10] -endmodule +```scala mdoc:verilog +getVerilogString(new Foo2) ``` + ### How can I dynamically set/parametrize the name of a module? You can override the `desiredName` function. This works with normal Chisel modules and `BlackBox`es. Example: @@ -758,14 +795,9 @@ class Salt extends Module { ``` Elaborating the Chisel module `Salt` yields our "desired names" for `Salt` and `Coffee` in the output Verilog: -```scala mdoc:silent -import chisel3.stage.ChiselStage - -ChiselStage.emitVerilog(new Salt) -``` ```scala mdoc:verilog -ChiselStage.emitVerilog(new Salt) +getVerilogString(new Salt) ``` ## Directionality @@ -775,7 +807,9 @@ ChiselStage.emitVerilog(new Salt) Given a bidirectional port like a `Decoupled`, you will get an error if you try to connect it directly to a register: -```scala mdoc:silent +```scala mdoc:silent:reset +import chisel3._ +import chisel3.stage.ChiselStage import chisel3.util.Decoupled class BadRegConnect extends Module { val io = IO(new Bundle { @@ -796,7 +830,9 @@ While there is no construct to "strip direction" in Chisel3, wrapping a type in set all of the individual elements to output direction. This will have the desired result when used to construct a Register: -```scala mdoc:silent +```scala mdoc:silent:reset +import chisel3._ +import chisel3.stage.ChiselStage import chisel3.util.Decoupled class CoercedRegConnect extends Module { val io = IO(new Bundle { -- cgit v1.2.3 From b72cc42f4f23906db0f201b1d9543a64accbc2ec Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 6 Oct 2022 21:26:30 +0000 Subject: Update toPrintable for Enums (#2707) (#2763) (cherry picked from commit 0ff99ca8d573e3487ef496a21c95d962689c3cba) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- core/src/main/scala/chisel3/StrongEnum.scala | 25 ++++++++++++++++- src/test/scala/chiselTests/StrongEnum.scala | 42 ++++++++++++++++++++++++++++ 2 files changed, 66 insertions(+), 1 deletion(-) diff --git a/core/src/main/scala/chisel3/StrongEnum.scala b/core/src/main/scala/chisel3/StrongEnum.scala index 6d8ceb2f..3c9f4105 100644 --- a/core/src/main/scala/chisel3/StrongEnum.scala +++ b/core/src/main/scala/chisel3/StrongEnum.scala @@ -251,7 +251,28 @@ abstract class EnumType(private[chisel3] val factory: EnumFactory, selfAnnotatin protected def enumTypeName: String = factory.enumTypeName - def toPrintable: Printable = FullName(this) // TODO: Find a better pretty printer + def toPrintable: Printable = { + implicit val sourceInfo = UnlocatableSourceInfo + implicit val compileOptions = ExplicitCompileOptions.Strict + val allNames = factory.allNames.zip(factory.all) + val nameSize = allNames.map(_._1.length).max + def leftPad(str: String): String = { + str.reverse.padTo(nameSize, ' ').reverse + } + val allNamesPadded = allNames.map { case (name, value) => leftPad(name) -> value } + + val result = Wire(Vec(nameSize, UInt(8.W))).suggestName(s"_${enumTypeName}Printable") + result.foreach(_ := '?'.U) + + for ((name, value) <- allNamesPadded) { + when(this === value) { + for ((r, c) <- result.zip(name)) { + r := c.toChar.U + } + } + } + result.map(Character(_)).foldLeft(p"")(_ + _) + } } abstract class EnumFactory { @@ -284,6 +305,8 @@ abstract class EnumFactory { def getWidth: Int = width.get def all: Seq[Type] = enumInstances + /* Accessor for Seq of names in enumRecords */ + def allNames: Seq[String] = enumNames private[chisel3] def nameOfValue(id: BigInt): Option[String] = { enumRecords.find(_.inst.litValue == id).map(_.name) diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index 5b1b13fd..cee1777e 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -164,6 +164,39 @@ class StrongEnumFSM extends Module { } } +object Opcode extends ChiselEnum { + val load = Value(0x03.U) + val imm = Value(0x13.U) + val auipc = Value(0x17.U) + val store = Value(0x23.U) + val litValues = List(0x03.U, 0x13.U, 0x17.U, 0x23.U) +} + +class PrintableExecutionTest extends BasicTester { + val (count, done) = Counter(true.B, 6) + val w = WireDefault(Opcode.load) + when(count === 0.U) { + w := Opcode.load + } + when(count === 1.U) { + w := Opcode.imm + } + when(count === 2.U) { + w := Opcode.auipc + } + when(count === 3.U) { + w := Opcode.store + } + when(count === 4.U) { // invalid state + val invalidWire = WireInit(UInt(6.W), 0.U) + w := Opcode.safe(invalidWire)._1 + } + when(done) { + stop() + } + printf(cf"'$w'\n") +} + class CastToUIntTester extends BasicTester { for ((enum, lit) <- EnumExample.all.zip(EnumExample.litValues)) { val mod = Module(new CastToUInt) @@ -555,6 +588,15 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { it should "correctly check if the enumeration is one of the values in a given sequence" in { assertTesterPasses(new IsOneOfTester) } + + it should "work with Printables" in { + val (log, _, _) = grabStdOutErr(assertTesterPasses { new PrintableExecutionTest }) + log should include("load") + log should include("imm") + log should include("auipc") + log should include("store") + log should include("?????") + } } class StrongEnumAnnotator extends Module { -- cgit v1.2.3 From 5b13d04b28ddd05e4acbc5b9b3755c92ac0d9515 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 7 Oct 2022 19:56:19 +0000 Subject: Make nested IsInstantiables with Data in them work (#2761) (#2766) * Add unit test for Issue 2760 * checkpoint: Fix for nested instance * remove comments about stuff not working * make the test check the output a little more * relax the requirement on returning empty ioMap * Update core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala * Update core/src/main/scala/chisel3/Data.scala * Update core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala Co-authored-by: Jack Koenig * Update src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala Co-authored-by: Jack Koenig * Update core/src/main/scala/chisel3/experimental/hierarchy/core/Lookupable.scala * Add another unit test which unfortunately still passes * Update core/src/main/scala/chisel3/Data.scala * Update src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala Co-authored-by: Jack Koenig Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit 1f9f26dc2bffcb4cc4daf2dc16c5cb455c6769ef) Co-authored-by: Megan Wachs --- .../experimental/hierarchy/Lookupable.scala | 17 ++++-- .../experimental/hierarchy/Examples.scala | 66 ++++++++++++++++++++++ .../experimental/hierarchy/InstanceSpec.scala | 10 ++++ 3 files changed, 89 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala index c83479b0..aa35455d 100644 --- a/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala +++ b/core/src/main/scala/chisel3/experimental/hierarchy/Lookupable.scala @@ -335,11 +335,20 @@ object Lookupable { } def instanceLookup[A](that: A => B, instance: Instance[A]): C = { val ret = that(instance.proto) - 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 + + def getIoMap(hierarchy: Hierarchy[_]): Option[Map[Data, Data]] = { + hierarchy.underlying match { + case Clone(x: ModuleClone[_]) => Some(x.ioMap) + case Proto(x: BaseModule) => Some(x.getChiselPorts.map { case (_, data) => data -> data }.toMap) + case Clone(x: InstantiableClone[_]) => getIoMap(x._innerContext) + case Clone(x: InstanceClone[_]) => None + case other => { + Builder.exception(s"Internal Error! Unexpected case where we can't get IO Map: $other") + } + } } + val ioMap = getIoMap(instance) + if (isView(ret)) { cloneViewToContext(ret, instance.cache, ioMap, instance.getInnerDataContext) } else { diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala index fa26cbde..27725c49 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -271,4 +271,70 @@ object Examples { @public val mem = Mem(8, UInt(32.W)) @public val syncReadMem = SyncReadMem(8, UInt(32.W)) } + + @instantiable + class LeafInstantiable(val bundle: Data) { + @public val bundle = bundle + } + + @instantiable + class NestedInstantiable(val in: LeafInstantiable, val out: LeafInstantiable) { + @public val in = in + @public val out = out + } + + @instantiable + class AddOneNestedInstantiableData(width: Int) extends Module { + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U + + @public val leafOut = new LeafInstantiable(out) + @public val leafIn = new LeafInstantiable(in) + @public val nested = new NestedInstantiable(in = leafIn, out = leafOut) + + } + + class AddTwoNestedInstantiableData(width: Int) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + val addOneDef = Definition(new AddOneNestedInstantiableData(width)) + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + i0.in := in + i1.in := i0.out + out := i1.out + + // both are equivalent to the above + i1.leafIn.bundle := i0.leafOut.bundle + i1.nested.in.bundle := i0.nested.out.bundle + } + + class AddTwoNestedInstantiableDataSubmodule(addOneDef: Definition[AddOneNestedInstantiableData]) extends Module { + val in = IO(Input(UInt(addOneDef.in.getWidth.W))) + val out = IO(Output(UInt(addOneDef.out.getWidth.W))) + val i0 = Instance(addOneDef) + val i1 = Instance(addOneDef) + i0.in := in + i1.in := i0.out + out := i1.out + + // both are equivalent to the above + i1.leafIn.bundle := i0.leafOut.bundle + i1.nested.in.bundle := i0.nested.out.bundle + } + + class AddTwoNestedInstantiableDataWrapper(width: Int) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + + val original = Module(new AddOneNestedInstantiableData(width)) + val copy = Module(new AddTwoNestedInstantiableDataSubmodule(original.toDefinition)) + + original.in := in + copy.in := original.out + out := copy.out + + } + } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala index 8d8f7ea5..6596cd51 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -364,6 +364,16 @@ class InstanceSpec extends ChiselFunSpec with Utils { annos should contain(MarkAnnotation("~Top|Top/i:HasMems>mem".rt, "Mem")) annos should contain(MarkAnnotation("~Top|Top/i:HasMems>syncReadMem".rt, "SyncReadMem")) } + it("(3.p): should make connectable IOs on nested IsInstantiables that have IO Datas in them") { + val (chirrtl, _) = getFirrtlAndAnnos(new AddTwoNestedInstantiableData(4)) + exactly(3, chirrtl.serialize.split('\n')) should include("i1.in <= i0.out") + } + it( + "(3.q): should make connectable IOs on nested IsInstantiables's Data when the Instance and Definition do not have the same parent" + ) { + val (chirrtl, _) = getFirrtlAndAnnos(new AddTwoNestedInstantiableDataWrapper(4)) + exactly(3, chirrtl.serialize.split('\n')) should include("i1.in <= i0.out") + } } describe("4: toInstance") { it("4.0: should work on modules") { -- cgit v1.2.3 From 721adc5c5509af48118afae44afa6b8a0107a926 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 10 Oct 2022 20:21:22 +0000 Subject: Fix traceName module type to RawModule (backport #2765) (#2768) * Fix traceName module type to RawModule (#2765) Change the type of modules that the traceName API can be used for from "Module" to "RawModule". This fixes a bug where this API couldn't be used for RawModules even though it totally works. Signed-off-by: Schuyler Eldridge Signed-off-by: Schuyler Eldridge (cherry picked from commit 74f1c85060cc72ebffe59a49f8d4539a464a4a19) * Fix binary compatibility issue Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/experimental/Trace.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/Trace.scala b/core/src/main/scala/chisel3/experimental/Trace.scala index 4ab615a5..3cc27162 100644 --- a/core/src/main/scala/chisel3/experimental/Trace.scala +++ b/core/src/main/scala/chisel3/experimental/Trace.scala @@ -1,7 +1,7 @@ package chisel3.experimental import chisel3.internal.HasId -import chisel3.{Aggregate, Data, Element, Module} +import chisel3.{Aggregate, Data, Element, Module, RawModule} import firrtl.AnnotationSeq import firrtl.annotations.{Annotation, CompleteTarget, SingleTargetAnnotation} import firrtl.transforms.DontTouchAllTargets @@ -22,7 +22,10 @@ import firrtl.transforms.DontTouchAllTargets object Trace { /** Trace a Instance name. */ - def traceName(x: Module): Unit = { + def traceName(x: Module): Unit = traceName(x: RawModule) + + /** Trace a Instance name. */ + def traceName(x: RawModule): Unit = { annotate(new ChiselAnnotation { def toFirrtl: Annotation = TraceNameAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget) }) -- cgit v1.2.3 From 0167a664c38273d7016a6f828dcafdeeff8d32cd Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 10 Oct 2022 22:32:16 +0000 Subject: Replace execution test with CHIRRTL match test (#2769) (#2770) (cherry picked from commit 693678dbea0cc4b92a8d8de690768fdc7d90fd14) Co-authored-by: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com>--- src/test/scala/chiselTests/StrongEnum.scala | 45 ++++++++++------------------- 1 file changed, 15 insertions(+), 30 deletions(-) diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index cee1777e..e9f412fe 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -169,32 +169,20 @@ object Opcode extends ChiselEnum { val imm = Value(0x13.U) val auipc = Value(0x17.U) val store = Value(0x23.U) - val litValues = List(0x03.U, 0x13.U, 0x17.U, 0x23.U) + val reg = Value(0x33.U) + val lui = Value(0x37.U) + val br = Value(0x63.U) + val jalr = Value(0x67.U) + val jal = Value(0x6f.U) } -class PrintableExecutionTest extends BasicTester { - val (count, done) = Counter(true.B, 6) - val w = WireDefault(Opcode.load) - when(count === 0.U) { - w := Opcode.load - } - when(count === 1.U) { - w := Opcode.imm - } - when(count === 2.U) { - w := Opcode.auipc - } - when(count === 3.U) { - w := Opcode.store - } - when(count === 4.U) { // invalid state - val invalidWire = WireInit(UInt(6.W), 0.U) - w := Opcode.safe(invalidWire)._1 - } - when(done) { - stop() - } - printf(cf"'$w'\n") +class LoadStoreExample extends Module { + val io = IO(new Bundle { + val opcode = Input(Opcode()) + val load_or_store = Output(Bool()) + }) + io.load_or_store := io.opcode.isOneOf(Opcode.load, Opcode.store) + printf(p"${io.opcode}") } class CastToUIntTester extends BasicTester { @@ -590,12 +578,9 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils { } it should "work with Printables" in { - val (log, _, _) = grabStdOutErr(assertTesterPasses { new PrintableExecutionTest }) - log should include("load") - log should include("imm") - log should include("auipc") - log should include("store") - log should include("?????") + ChiselStage.emitChirrtl(new LoadStoreExample) should include( + """printf(clock, UInt<1>("h1"), "%c%c%c%c%c", _chiselTestsOpcodePrintable[0], _chiselTestsOpcodePrintable[1], _chiselTestsOpcodePrintable[2], _chiselTestsOpcodePrintable[3], _chiselTestsOpcodePrintable[4])""" + ) } } -- cgit v1.2.3 From 1e9f4e99167d1bc132a1a1a04e6987c2161c4d0e Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 13 Oct 2022 21:25:07 +0000 Subject: Bump to Scala 2.13.10 (backport #2774) (#2775) * Bump to Scala 2.13.10 (#2774) 2.13.9 has a binary compatibility regression so should be avoided. (cherry picked from commit c53265d6853728280b6bc01b001415166515c79a) # Conflicts: # .github/workflows/test.yml * Fix backport conflicts * Fix scalaVersion in README * Waive binary compatibility check for plugin for 2.13.10 3.5.4 is published for 2.13.10 so we cannot check binary compatibility for it. Co-authored-by: Jack Koenig --- .github/workflows/test.yml | 2 +- README.md | 6 +++--- build.sbt | 9 +++++---- build.sc | 2 +- 4 files changed, 10 insertions(+), 9 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e48f37ea..69426662 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -17,7 +17,7 @@ jobs: matrix: system: ["ubuntu-20.04"] jvm: ["adopt@1.8"] - scala: ["2.13.9", "2.12.17"] + scala: ["2.13.10", "2.12.17"] espresso: ["2.4"] runs-on: ${{ matrix.system }} diff --git a/README.md b/README.md index 6a903dd6..a68ba261 100644 --- a/README.md +++ b/README.md @@ -222,10 +222,10 @@ For example, in SBT this could be expressed as: ```scala // build.sbt scalaVersion := "2.13.8" -addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % "3.5.3" cross CrossVersion.full) -libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.5.3" +addCompilerPlugin("edu.berkeley.cs" % "chisel3-plugin" % "3.5.4" cross CrossVersion.full) +libraryDependencies += "edu.berkeley.cs" %% "chisel3" % "3.5.4" // We also recommend using chiseltest for writing unit tests -libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "0.5.3" % "test" +libraryDependencies += "edu.berkeley.cs" %% "chiseltest" % "0.5.4" % "test" ``` ### Guide For New Contributors diff --git a/build.sbt b/build.sbt index 36102074..49643118 100644 --- a/build.sbt +++ b/build.sbt @@ -22,7 +22,7 @@ lazy val commonSettings = Seq ( version := "3.5-SNAPSHOT", autoAPIMappings := true, scalaVersion := "2.12.17", - crossScalaVersions := Seq("2.13.9", "2.12.17"), + crossScalaVersions := Seq("2.13.10", "2.12.17"), scalacOptions := Seq("-deprecation", "-feature"), libraryDependencies += "org.scala-lang" % "scala-reflect" % scalaVersion.value, // Macros paradise is integrated into 2.13 but requires a scalacOption @@ -123,7 +123,8 @@ lazy val pluginScalaVersions = Seq( "2.13.6", "2.13.7", "2.13.8", - "2.13.9" + "2.13.9", + "2.13.10", ) lazy val plugin = (project in file("plugin")). @@ -143,8 +144,8 @@ lazy val plugin = (project in file("plugin")). ). settings( mimaPreviousArtifacts := { - // There is not yet a 2.12.17 nor 2.13.9 artifact, so suppress until 3.5.5 is released - val skipVersions = Seq("2.12.17", "2.13.9") + // There are not yet artifacts for 2.12.17, 2.13.9, nor 2.13.10; suppress until 3.5.5 is released + val skipVersions = Seq("2.12.17", "2.13.9", "2.13.10") if (skipVersions.contains(scalaVersion.value)) { Set() } else { diff --git a/build.sc b/build.sc index 9e46098c..80ba17fe 100644 --- a/build.sc +++ b/build.sc @@ -6,7 +6,7 @@ import coursier.maven.MavenRepository import $ivy.`com.lihaoyi::mill-contrib-buildinfo:$MILL_VERSION` import mill.contrib.buildinfo.BuildInfo -object chisel3 extends mill.Cross[chisel3CrossModule]("2.13.9", "2.12.17") +object chisel3 extends mill.Cross[chisel3CrossModule]("2.13.10", "2.12.17") // The following stanza is searched for and used when preparing releases. // Please retain it. -- cgit v1.2.3 From 1957f5ef5c43439144cf779a343707872ca92d6a Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 17 Oct 2022 22:50:55 +0000 Subject: Add opt-in AutoCloneType for Records (backport #2781) (#2785) * Add opt-in AutoCloneType for Records (#2781) There is a new trait, chisel3.experimental.AutoCloneType that is mixed in to Bundle and can optionally be mixed in to user-defined Records. The compiler plugin prints a deprecation warning on any user-defined implementation of cloneType, telling the user to mix in AutoCloneType before upgrading to 3.6. (cherry picked from commit a234fd48ac8f5942c38fef5797292014e407b586) # Conflicts: # core/src/main/scala/chisel3/Aggregate.scala # plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala * Resolve backport conflicts * Do not make MixedVec extend AutoCloneType It is a binary incompatible change that can wait for 3.6. * Waive MiMa false positives Co-authored-by: Jack Koenig --- build.sbt | 7 + core/src/main/scala/chisel3/Aggregate.scala | 32 ++- .../chisel3/internal/plugin/BundleComponent.scala | 278 +++++++++++---------- src/test/scala/chiselTests/AutoClonetypeSpec.scala | 41 +++ 4 files changed, 222 insertions(+), 136 deletions(-) diff --git a/build.sbt b/build.sbt index 49643118..b958b19a 100644 --- a/build.sbt +++ b/build.sbt @@ -143,6 +143,13 @@ lazy val plugin = (project in file("plugin")). } ). settings( + // Given that the plugin is 1) a compile-time only dependency and 2) package chisel3.internal, + // I'm not really sure why we both checking binary compatbility + mimaBinaryIssueFilters ++= Seq( + // MyTypingTransformer is private (https://github.com/lightbend/mima/issues/53) + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.internal.plugin.BundleComponent#MyTypingTransformer.isBundle"), + ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.internal.plugin.BundleComponent#MyTypingTransformer.getConstructorAndParams") + ), mimaPreviousArtifacts := { // There are not yet artifacts for 2.12.17, 2.13.9, nor 2.13.10; suppress until 3.5.5 is released val skipVersions = Seq("2.12.17", "2.13.9", "2.13.10") diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 042e78b1..aacf0b1c 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -1158,6 +1158,16 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio * Results in "`\$className(elt0.name -> elt0.value, ...)`" */ def toPrintable: Printable = toPrintableHelper(elements.toList) + + /** Implementation of cloneType that is [optionally for Record] overridden by the compiler plugin + * + * @note This should _never_ be overridden or called in user-code + */ + protected def _cloneTypeImpl: Record = { + throwException( + s"Internal Error! This should have been implemented by the chisel3-plugin. Please file an issue against chisel3" + ) + } } /** @@ -1182,6 +1192,15 @@ package experimental { class BundleLiteralException(message: String) extends ChiselException(message) class VecLiteralException(message: String) extends ChiselException(message) + /** Indicates that the compiler plugin should generate [[cloneType]] for this type + * + * All user-defined [[Record]]s should mix this trait in as it will be required for upgrading to Chisel 3.6. + */ + trait AutoCloneType { self: Record => + + override def cloneType: this.type = _cloneTypeImpl.asInstanceOf[this.type] + + } } /** Base class for data types defined as a bundle of other data types. @@ -1217,7 +1236,7 @@ package experimental { * } * }}} */ -abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { +abstract class Bundle(implicit compileOptions: CompileOptions) extends Record with experimental.AutoCloneType { assert( _usingPlugin, "The Chisel compiler plugin is now required for compiling Chisel code. " + @@ -1385,15 +1404,8 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record { clone } - /** Implementation of cloneType using runtime reflection. This should _never_ be overridden or called in user-code - * - * @note This is overridden by the compiler plugin (this implementation is never called) - */ - protected def _cloneTypeImpl: Bundle = { - throwException( - s"Internal Error! This should have been implemented by the chisel3-plugin. Please file an issue against chisel3" - ) - } + // This is overriden for binary compatibility reasons in 3.5 + override protected def _cloneTypeImpl: Bundle = super._cloneTypeImpl.asInstanceOf[Bundle] /** Default "pretty-print" implementation * Analogous to printing a Map diff --git a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala index eca3b158..f9452f5a 100644 --- a/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala +++ b/plugin/src/main/scala/chisel3/internal/plugin/BundleComponent.scala @@ -42,6 +42,8 @@ private[plugin] class BundleComponent(val global: Global, arguments: ChiselPlugi def inferType(t: Tree): Type = localTyper.typed(t, nsc.Mode.TYPEmode).tpe val bundleTpe: Type = inferType(tq"chisel3.Bundle") + val recordTpe: Type = inferType(tq"chisel3.Record") + val autoCloneTpe: Type = inferType(tq"chisel3.experimental.AutoCloneType") val dataTpe: Type = inferType(tq"chisel3.Data") val ignoreSeqTpe: Type = inferType(tq"chisel3.IgnoreSeqInBundle") val seqOfDataTpe: Type = inferType(tq"scala.collection.Seq[chisel3.Data]") @@ -49,7 +51,11 @@ private[plugin] class BundleComponent(val global: Global, arguments: ChiselPlugi val itStringAnyTpe: Type = inferType(tq"scala.collection.Iterable[(String,Any)]") // Not cached because it should only be run once per class (thus once per Type) - def isBundle(sym: Symbol): Boolean = { sym.tpe <:< bundleTpe } + def isABundle(sym: Symbol): Boolean = { sym.tpe <:< bundleTpe } + + def isARecord(sym: Symbol): Boolean = { sym.tpe <:< recordTpe } + + def isAnAutoCloneType(sym: Symbol): Boolean = { sym.tpe <:< autoCloneTpe } def isIgnoreSeqInBundle(sym: Symbol): Boolean = { sym.tpe <:< ignoreSeqTpe } @@ -86,7 +92,7 @@ private[plugin] class BundleComponent(val global: Global, arguments: ChiselPlugi def isVarArgs(sym: Symbol): Boolean = definitions.isRepeatedParamType(sym.tpe) - def getConstructorAndParams(body: List[Tree]): (Option[DefDef], Seq[Symbol]) = { + def getConstructorAndParams(body: List[Tree], isBundle: Boolean): (Option[DefDef], Seq[Symbol]) = { val paramAccessors = mutable.ListBuffer[Symbol]() var primaryConstructor: Option[DefDef] = None body.foreach { @@ -96,154 +102,174 @@ private[plugin] class BundleComponent(val global: Global, arguments: ChiselPlugi primaryConstructor = Some(con) case d: DefDef if isNullaryMethodNamed("_cloneTypeImpl", d) => val msg = "Users cannot override _cloneTypeImpl. Let the compiler plugin generate it." - global.globalError(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("_elementsImpl", d) => + global.reporter.error(d.pos, msg) + case d: DefDef if isNullaryMethodNamed("_elementsImpl", d) && isBundle => val msg = "Users cannot override _elementsImpl. Let the compiler plugin generate it." - global.globalError(d.pos, msg) - case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) => + global.reporter.error(d.pos, msg) + case d: DefDef if isNullaryMethodNamed("_usingPlugin", d) && isBundle => val msg = "Users cannot override _usingPlugin, it is for the compiler plugin's use only." - global.globalError(d.pos, msg) + global.reporter.error(d.pos, msg) case d: DefDef if isNullaryMethodNamed("cloneType", d) => - val msg = "Users cannot override cloneType. Let the compiler plugin generate it." - global.globalError(d.pos, msg) + val prefix = if (isBundle) "Bundles" else "Records extending AutoCloneType" + val msg = s"$prefix cannot override cloneType. Let the compiler plugin generate it." + global.reporter.error(d.pos, msg) case _ => } (primaryConstructor, paramAccessors.toList) } - override def transform(tree: Tree): Tree = tree match { - - case bundle: ClassDef if isBundle(bundle.symbol) && !bundle.mods.hasFlag(Flag.ABSTRACT) => - // ==================== Generate _cloneTypeImpl ==================== - val (con, params) = getConstructorAndParams(bundle.impl.body) - if (con.isEmpty) { - global.reporter.warning(bundle.pos, "Unable to determine primary constructor!") - return super.transform(tree) - } + def warnOnCloneType(body: List[Tree]): Unit = { + body.foreach { + case d: DefDef if isNullaryMethodNamed("cloneType", d) => + val msg = "It is no longer necessary to implement cloneType. " + + "Mix in chisel3.experimental.AutoCloneType to let the compiler plugin generate it. " + + "This will become an error in Chisel 3.6." + global.reporter.warning(d.pos, msg) + case _ => // Do nothing + } + } - val constructor = con.get - val thiz = gen.mkAttributedThis(bundle.symbol) - - // The params have spaces after them (Scalac implementation detail) - val paramLookup: String => Symbol = params.map(sym => sym.name.toString.trim -> sym).toMap - - val cloneTypeImplOpt = if (!bundle.mods.hasFlag(Flag.ABSTRACT)) { - // Create a this. for each field matching order of constructor arguments - // List of Lists because we can have multiple parameter lists - val conArgs: List[List[Tree]] = - constructor.vparamss.map(_.map { vp => - val p = paramLookup(vp.name.toString) - // Make this. - val select = gen.mkAttributedSelect(thiz.asInstanceOf[Tree], p) - // Clone any Data parameters to avoid field aliasing, need full clone to include direction - val cloned = if (isData(vp.symbol)) cloneTypeFull(select.asInstanceOf[Tree]) else select - // Need to splat varargs - if (isVarArgs(vp.symbol)) q"$cloned: _*" else cloned - }) - - val tparamList = bundle.tparams.map { t => Ident(t.symbol) } - val ttpe = - if (tparamList.nonEmpty) AppliedTypeTree(Ident(bundle.symbol), tparamList) else Ident(bundle.symbol) - val newUntyped = New(ttpe, conArgs) - val neww = localTyper.typed(newUntyped) - - // Create the symbol for the method and have it be associated with the Bundle class - val cloneTypeSym = - bundle.symbol.newMethod(TermName("_cloneTypeImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) - // Handwritten cloneTypes don't have the Method flag set, unclear if it matters - cloneTypeSym.resetFlag(Flags.METHOD) - // Need to set the type to chisel3.Bundle for the override to work - cloneTypeSym.setInfo(NullaryMethodType(bundleTpe)) - - Some(localTyper.typed(DefDef(cloneTypeSym, neww))) - } else { - // Don't create if this Bundle is abstract - None - } + def generateAutoCloneType(record: ClassDef, thiz: global.This, isBundle: Boolean): Option[Tree] = { + val (con, params) = getConstructorAndParams(record.impl.body, isBundle) + if (con.isEmpty) { + global.reporter.warning(record.pos, "Unable to determine primary constructor!") + return None + } - // ==================== Generate val elements ==================== + val constructor = con.get + + // The params have spaces after them (Scalac implementation detail) + val paramLookup: String => Symbol = params.map(sym => sym.name.toString.trim -> sym).toMap + + // Create a this. for each field matching order of constructor arguments + // List of Lists because we can have multiple parameter lists + val conArgs: List[List[Tree]] = + constructor.vparamss.map(_.map { vp => + val p = paramLookup(vp.name.toString) + // Make this. + val select = gen.mkAttributedSelect(thiz.asInstanceOf[Tree], p) + // Clone any Data parameters to avoid field aliasing, need full clone to include direction + val cloned = if (isData(vp.symbol)) cloneTypeFull(select.asInstanceOf[Tree]) else select + // Need to splat varargs + if (isVarArgs(vp.symbol)) q"$cloned: _*" else cloned + }) + + val tparamList = record.tparams.map { t => Ident(t.symbol) } + val ttpe = + if (tparamList.nonEmpty) AppliedTypeTree(Ident(record.symbol), tparamList) else Ident(record.symbol) + val newUntyped = New(ttpe, conArgs) + val neww = localTyper.typed(newUntyped) + + // Create the symbol for the method and have it be associated with the Record class + val cloneTypeSym = + record.symbol.newMethod(TermName("_cloneTypeImpl"), record.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) + // Handwritten cloneTypes don't have the Method flag set, unclear if it matters + cloneTypeSym.resetFlag(Flags.METHOD) + + // Need to set the return type correctly for the override to work + // For binary compatibility reasons in 3.5, Bundles have to return chisel3.Bundle + val returnType = if (isBundle) bundleTpe else recordTpe + cloneTypeSym.setInfo(NullaryMethodType(returnType)) + + Some(localTyper.typed(DefDef(cloneTypeSym, neww))) + } - /* Test to see if the bundle found is amenable to having it's elements - * converted to an immediate form that will not require reflection - */ - def isSupportedBundleType: Boolean = { - arguments.genBundleElements && !bundle.mods.hasFlag(Flag.ABSTRACT) + def generateElements(bundle: ClassDef, thiz: global.This): Tree = { + /* extract the true fields from the super classes a given bundle + * depth argument can be helpful for debugging + */ + def getAllBundleFields(bundleSymbol: Symbol, depth: Int = 0): List[(String, Tree)] = { + + def isBundleField(member: Symbol): Boolean = { + if (!member.isAccessor) { + false + } else if (isData(member.tpe.typeSymbol)) { + true + } else if (isOptionOfData(member)) { + true + } else if (isSeqOfData(member)) { + // This field is passed along, even though it is illegal + // An error for this will be generated in `Bundle.elements` + // It would be possible here to check for Seq[Data] and make a compiler error, but + // that would be a API error difference. See reference in docs/chisel-plugin.md + // If Bundle is subclass of IgnoreSeqInBundle then don't pass this field along + + !isIgnoreSeqInBundle(bundleSymbol) + } else { + // none of the above + false + } } - val elementsImplOpt = if (isSupportedBundleType) { - /* extract the true fields from the super classes a given bundle - * depth argument can be helpful for debugging - */ - def getAllBundleFields(bundleSymbol: Symbol, depth: Int = 0): List[(String, Tree)] = { - - def isBundleField(member: Symbol): Boolean = { - if (!member.isAccessor) { - false - } else if (isData(member.tpe.typeSymbol)) { - true - } else if (isOptionOfData(member)) { - true - } else if (isSeqOfData(member)) { - // This field is passed along, even though it is illegal - // An error for this will be generated in `Bundle.elements` - // It would be possible here to check for Seq[Data] and make a compiler error, but - // that would be a API error difference. See reference in docs/chisel-plugin.md - // If Bundle is subclass of IgnoreSeqInBundle then don't pass this field along - - !isIgnoreSeqInBundle(bundleSymbol) - } else { - // none of the above - false - } - } + val currentFields = bundleSymbol.info.members.flatMap { - val currentFields = bundleSymbol.info.members.flatMap { - - case member if member.isPublic => - if (isBundleField(member)) { - // The params have spaces after them (Scalac implementation detail) - Some(member.name.toString.trim -> gen.mkAttributedSelect(thiz.asInstanceOf[Tree], member)) - } else { - None - } - - case _ => None - }.toList - - val allParentFields = bundleSymbol.parentSymbols.flatMap { parentSymbol => - val fieldsFromParent = if (depth < 1 && !isExactBundle(bundleSymbol)) { - val foundFields = getAllBundleFields(parentSymbol, depth + 1) - foundFields - } else { - List() - } - fieldsFromParent + case member if member.isPublic => + if (isBundleField(member)) { + // The params have spaces after them (Scalac implementation detail) + Some(member.name.toString.trim -> gen.mkAttributedSelect(thiz.asInstanceOf[Tree], member)) + } else { + None } - allParentFields ++ currentFields + + case _ => None + }.toList + + val allParentFields = bundleSymbol.parentSymbols.flatMap { parentSymbol => + val fieldsFromParent = if (depth < 1 && !isExactBundle(bundleSymbol)) { + val foundFields = getAllBundleFields(parentSymbol, depth + 1) + foundFields + } else { + List() } + fieldsFromParent + } + allParentFields ++ currentFields + } - val elementArgs = getAllBundleFields(bundle.symbol) + val elementArgs = getAllBundleFields(bundle.symbol) - val elementsImplSym = - bundle.symbol.newMethod(TermName("_elementsImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) - elementsImplSym.resetFlag(Flags.METHOD) - elementsImplSym.setInfo(NullaryMethodType(itStringAnyTpe)) + val elementsImplSym = + bundle.symbol.newMethod(TermName("_elementsImpl"), bundle.symbol.pos.focus, Flag.OVERRIDE | Flag.PROTECTED) + elementsImplSym.resetFlag(Flags.METHOD) + elementsImplSym.setInfo(NullaryMethodType(itStringAnyTpe)) - val elementsImpl = localTyper.typed( - DefDef(elementsImplSym, q"scala.collection.immutable.Vector.apply[(String, Any)](..$elementArgs)") - ) + val elementsImpl = localTyper.typed( + DefDef(elementsImplSym, q"scala.collection.immutable.Vector.apply[(String, Any)](..$elementArgs)") + ) - Some(elementsImpl) - } else { - // No code generated for elements accessor - None + elementsImpl + } + + override def transform(tree: Tree): Tree = tree match { + + case record: ClassDef if isARecord(record.symbol) && !record.mods.hasFlag(Flag.ABSTRACT) => + val isBundle: Boolean = isABundle(record.symbol) + val isAutoCloneType: Boolean = isAnAutoCloneType(record.symbol) + + if (!isAutoCloneType) { + warnOnCloneType(record.impl.body) + // Other than warning, there is nothing to do on Records that don't mixin AutoCloneType + return super.transform(record) } + val thiz: global.This = gen.mkAttributedThis(record.symbol) + + // ==================== Generate _cloneTypeImpl ==================== + val cloneTypeImplOpt = generateAutoCloneType(record, thiz, isBundle) + + // ==================== Generate val elements (Bundles only) ==================== + val elementsImplOpt = + if (isBundle && arguments.genBundleElements) Some(generateElements(record, thiz)) else None + // ==================== Generate _usingPlugin ==================== - // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. - val usingPluginOpt = Some(localTyper.typed(q"override protected def _usingPlugin: Boolean = true")) + val usingPluginOpt = if (isBundle) { + // Unclear why quasiquotes work here but didn't for cloneTypeSym, maybe they could. + Some(localTyper.typed(q"override protected def _usingPlugin: Boolean = true")) + } else { + None + } - val withMethods = deriveClassDef(bundle) { t => + val withMethods = deriveClassDef(record) { t => deriveTemplate(t)(_ ++ cloneTypeImplOpt ++ usingPluginOpt ++ elementsImplOpt) } diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index 5d2cd496..353ae58c 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -6,6 +6,8 @@ import chisel3._ import chisel3.testers.TestUtils import chisel3.util.QueueIO import chisel3.stage.ChiselStage.elaborate +import chisel3.experimental.AutoCloneType +import scala.collection.immutable.ListMap class BundleWithIntArg(val i: Int) extends Bundle { val out = UInt(i.W) @@ -72,6 +74,25 @@ class InheritingBundle extends QueueIO(UInt(8.W), 8) { val error = Output(Bool()) } +class RecordAutoCloneType[T <: Data](gen: T) extends Record with AutoCloneType { + lazy val elements = ListMap("value" -> gen) + // This is a weird thing to do, but as only Bundles have these methods, it should be legal + protected def _elementsImpl: Iterable[(String, Any)] = elements + protected def _usingPlugin = false +} + +// Records that don't mixin AutoCloneType should still be able to implement the related methods +// NOTE: This is a very weird thing to do, don't do it. +class RecordWithVerbotenMethods(w: Int) extends Record { + lazy val elements = ListMap("value" -> UInt(w.W)) + override def cloneType: this.type = (new RecordWithVerbotenMethods(w)).asInstanceOf[this.type] + // Verboten methods + protected def _usingPlugin = false + protected override def _cloneTypeImpl = this.cloneType + + protected def _elementsImpl: Iterable[(String, Any)] = Nil +} + class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Bundles with Scala args" should "not need clonetype" in { @@ -400,4 +421,24 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } elaborate(new MyModule) } + + it should "support Records that mixin AutoCloneType" in { + class MyModule extends Module { + val gen = new RecordAutoCloneType(UInt(8.W)) + val in = IO(Input(gen)) + val out = IO(Output(gen)) + out := in + } + elaborate(new MyModule) + } + + it should "support Records that don't mixin AutoCloneType and use forbidden methods" in { + class MyModule extends Module { + val gen = new RecordWithVerbotenMethods(8) + val in = IO(Input(gen)) + val out = IO(Output(gen)) + out := in + } + elaborate(new MyModule) + } } -- cgit v1.2.3 From 9b8536b6af9f029a0edfb1c8df4f47a67e861c9d Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 18 Oct 2022 18:48:54 +0000 Subject: Add traceNameV2 for backwards compat. of traceName (#2784) (#2786) Add utilities to enable backwards compatibility of the Trace.traceName API to Chisel 3.5.x. This adds a Trace.traceNameV2 utility which aliases to Trace.traceName. This also removes the TraceNameAnnotation and renames it TraceAnnotation. In 3.5.x, traceName will point at TraceNameAnnotation (which has don't touch behavior) and will be deprecated telling people to use traceNameV2 which will point at TraceAnnotation (which does not have don't touch behavior). This will require fixups to the backport associated with this PR. Signed-off-by: Schuyler Eldridge (cherry picked from commit 47b7227e1ac7ccb0d48cefef03510542cc7e157e) # Conflicts: # core/src/main/scala/chisel3/experimental/Trace.scala Co-authored-by: Schuyler Eldridge --- .../main/scala/chisel3/experimental/Trace.scala | 43 +++++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/Trace.scala b/core/src/main/scala/chisel3/experimental/Trace.scala index 3cc27162..33d18147 100644 --- a/core/src/main/scala/chisel3/experimental/Trace.scala +++ b/core/src/main/scala/chisel3/experimental/Trace.scala @@ -22,16 +22,22 @@ import firrtl.transforms.DontTouchAllTargets object Trace { /** Trace a Instance name. */ + @deprecated("switch to traceNameV2 (until Chisel 3.6)", "3.5.5") def traceName(x: Module): Unit = traceName(x: RawModule) /** Trace a Instance name. */ + @deprecated("switch to traceNameV2 (until Chisel 3.6)", "3.5.5") def traceName(x: RawModule): Unit = { annotate(new ChiselAnnotation { def toFirrtl: Annotation = TraceNameAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget) }) } - /** Trace a Data name. */ + /** Trace a Data name. This adds "don't touch" semantics to anything traced. */ + @deprecated( + "switch to traceNameV2 (until Chisel 3.6) and add dontTouch if you want \"don't touch\" behavior", + "3.5.5" + ) def traceName(x: Data): Unit = { x match { case aggregate: Aggregate => @@ -46,7 +52,29 @@ object Trace { } } - /** An Annotation that records the original target annotate from Chisel. + /** Trace an Instance name. */ + def traceNameV2(x: RawModule): Unit = { + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceAnnotation(x.toAbsoluteTarget, x.toAbsoluteTarget) + }) + } + + /** Trace a Data name. This does NOT add "don't touch" semantics to the traced data. If you want this behavior, use an explicit [[chisel3.dontTouch]]. */ + def traceNameV2(x: Data): Unit = { + x match { + case aggregate: Aggregate => + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceAnnotation(aggregate.toAbsoluteTarget, aggregate.toAbsoluteTarget) + }) + aggregate.getElements.foreach(traceNameV2) + case element: Element => + annotate(new ChiselAnnotation { + def toFirrtl: Annotation = TraceAnnotation(element.toAbsoluteTarget, element.toAbsoluteTarget) + }) + } + } + + /** An Annotation that records the original target annotate from Chisel. This adds don't touch behavior. * * @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. @@ -57,6 +85,16 @@ object Trace { def duplicate(n: T): Annotation = this.copy(target = n) } + /** An Annotation that records the original target annotate from Chisel. This does NOT add don't touch behavior. + * + * @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 TraceAnnotation[T <: CompleteTarget](target: T, chiselTarget: T) + extends SingleTargetAnnotation[T] { + 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` */ @@ -68,5 +106,6 @@ object Trace { */ def finalTargetMap(annos: AnnotationSeq): Map[CompleteTarget, Seq[CompleteTarget]] = annos.collect { case TraceNameAnnotation(t, chiselTarget) => chiselTarget -> t + case TraceAnnotation(t, chiselTarget) => chiselTarget -> t }.groupBy(_._1).map { case (k, v) => k -> v.map(_._2) } } -- cgit v1.2.3 From 80b3b28f451efa85be50994f732599f043f83d86 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Wed, 19 Oct 2022 14:28:34 -0700 Subject: Don't modify the Builder prefix if reinvoking suggestName on a Data (backport #2789) (#2790) * Only set the chisel3 Builder prefix during the first invocation of suggestName (cherry picked from commit b684506abab2f7b99d56181d548cb8119d317323) # Conflicts: # core/src/main/scala/chisel3/internal/Builder.scala * Add simple test to show bug fix (cherry picked from commit 255068b105de77a045a0016e3a157b52a81c86d6) * Fix merge conflict * Fix test to not use Hardware inside a bundle for prefixing Co-authored-by: Jared Barocsi --- core/src/main/scala/chisel3/internal/Builder.scala | 9 +++++--- src/test/scala/chiselTests/naming/PrefixSpec.scala | 26 ++++++++++++++++++++++ 2 files changed, 32 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/chisel3/internal/Builder.scala b/core/src/main/scala/chisel3/internal/Builder.scala index 9f79fe1e..e3dfff09 100644 --- a/core/src/main/scala/chisel3/internal/Builder.scala +++ b/core/src/main/scala/chisel3/internal/Builder.scala @@ -170,9 +170,12 @@ private[chisel3] trait HasId extends InstanceId { * @return this object */ def suggestName(seed: => String): this.type = { - if (suggested_seed.isEmpty) suggested_seedVar = seed - naming_prefix = Builder.getPrefix - for (hook <- suggest_postseed_hooks.reverse) { hook(seed) } + if (suggested_seed.isEmpty) { + suggested_seedVar = seed + // Only set the prefix if a seed hasn't been suggested + naming_prefix = Builder.getPrefix + for (hook <- suggest_postseed_hooks.reverse) { hook(seed) } + } this } diff --git a/src/test/scala/chiselTests/naming/PrefixSpec.scala b/src/test/scala/chiselTests/naming/PrefixSpec.scala index b5eac1d4..d8cb3348 100644 --- a/src/test/scala/chiselTests/naming/PrefixSpec.scala +++ b/src/test/scala/chiselTests/naming/PrefixSpec.scala @@ -521,4 +521,30 @@ class PrefixSpec extends ChiselPropSpec with Utils { Select.wires(top).map(_.instanceName) should be(List("nonData_value", "value")) } } + property("Prefixing should not be affected by repeated calls of suggestName") { + class Test extends Module { + val in = IO(Input(UInt(3.W))) + val prefixed = { + val wire = Wire(UInt(3.W)).suggestName("wire") // "prefixed_wire" + wire := in + + val thisShouldNotBeHere = { + // Second suggestName doesn't modify the instanceName since it was + // already suggested, but also should not modify the prefix either + + // Incorrect behavior would rename the wire to + // "prefixed_thisShouldNotBeHere_wire" + wire.suggestName("wire") + + val out = IO(Output(UInt(3.W))) + out := wire + out + } + thisShouldNotBeHere + } + } + aspectTest(() => new Test) { top: Test => + Select.wires(top).map(_.instanceName) should be(List("prefixed_wire")) + } + } } -- cgit v1.2.3 From d997acb05e5a307afb7c9ad4c136b9b4e1506efc Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Sun, 23 Oct 2022 19:01:43 +0000 Subject: Don't invalidate ExtModule ports in an explicitInvalidate = true context (backport #2795) (#2799) * Don't invalidate ExtModule ports in an explicitInvalidate = true context (#2795) * Don't invalidate ExtModule ports in an explicitInvalidate = true context ExtModule ports were previously invalidated in the emitted FIRRTL, which is correct in a NonStrict / `Chisel._` compatibility context but not in newer chisel3 code where `explicitInvalidate = true`. (cherry picked from commit 8e24a281545d25f6501dcc872eabdfb30bacd69d) # Conflicts: # core/src/main/scala/chisel3/BlackBox.scala * Resolve backport conflicts Co-authored-by: Jared Barocsi <82000041+jared-barocsi@users.noreply.github.com> Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/BlackBox.scala | 12 ++++++++---- docs/src/cookbooks/cookbook.md | 2 ++ src/test/scala/chiselTests/CompatibilitySpec.scala | 22 ++++++++++++++++++++++ src/test/scala/chiselTests/ExtModule.scala | 17 +++++++++++++++++ src/test/scala/chiselTests/aop/InjectionSpec.scala | 1 + 5 files changed, 50 insertions(+), 4 deletions(-) diff --git a/core/src/main/scala/chisel3/BlackBox.scala b/core/src/main/scala/chisel3/BlackBox.scala index f618901f..c3cb3e66 100644 --- a/core/src/main/scala/chisel3/BlackBox.scala +++ b/core/src/main/scala/chisel3/BlackBox.scala @@ -92,8 +92,10 @@ package experimental { private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { implicit val sourceInfo = UnlocatableSourceInfo - for (x <- getModulePorts) { - pushCommand(DefInvalid(sourceInfo, x.ref)) + if (!parentCompileOptions.explicitInvalidate) { + for (x <- getModulePorts) { + pushCommand(DefInvalid(sourceInfo, x.ref)) + } } } } @@ -192,8 +194,10 @@ abstract class BlackBox( } private[chisel3] def initializeInParent(parentCompileOptions: CompileOptions): Unit = { - for ((_, port) <- _io.map(_.elements).getOrElse(Nil)) { - pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) + if (!parentCompileOptions.explicitInvalidate) { + for ((_, port) <- _io.map(_.elements).getOrElse(Nil)) { + pushCommand(DefInvalid(UnlocatableSourceInfo, port.ref)) + } } } } diff --git a/docs/src/cookbooks/cookbook.md b/docs/src/cookbooks/cookbook.md index ac3a0c5d..e7485e66 100644 --- a/docs/src/cookbooks/cookbook.md +++ b/docs/src/cookbooks/cookbook.md @@ -791,6 +791,8 @@ class Salt extends Module { val io = IO(new Bundle {}) val drink = Module(new Coffee) override def desiredName = "SodiumMonochloride" + + drink.io.I := 42.U } ``` diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala index 41cfbec4..5a3b43e6 100644 --- a/src/test/scala/chiselTests/CompatibilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilitySpec.scala @@ -614,4 +614,26 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck ChiselStage.elaborate(new MyModule) } + behavior.of("BlackBox") + + it should "have invalidated ports in a compatibility context" in { + class ExtModuleInvalidatedTester extends Module { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + val inst = Module(new BlackBox { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(UInt(8.W)) + }) + }) + inst.io.in := io.in + io.out := inst.io.out + } + + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleInvalidatedTester) + chirrtl should include("inst.in is invalid") + chirrtl should include("inst.out is invalid") + } } diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala index b5a8ff7c..3ab4cc32 100644 --- a/src/test/scala/chiselTests/ExtModule.scala +++ b/src/test/scala/chiselTests/ExtModule.scala @@ -88,6 +88,17 @@ class ExtModuleWithFlatIOTester extends Module { io <> inst.badIO } +class ExtModuleInvalidatedTester extends Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + val inst = Module(new ExtModule { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(8.W))) + }) + inst.in := in + out := inst.out +} + class ExtModuleSpec extends ChiselFlatSpec { "A ExtModule inverter" should "work" in { assertTesterPasses({ new ExtModuleTester }, Seq("/chisel3/BlackBoxTest.v"), TesterDriver.verilatorOnly) @@ -117,4 +128,10 @@ class ExtModuleSpec extends ChiselFlatSpec { chirrtl should include("inst.in <= io.in") chirrtl shouldNot include("badIO") } + + it should "not have invalidated ports in a chisel3._ context" in { + val chirrtl = ChiselStage.emitChirrtl(new ExtModuleInvalidatedTester) + chirrtl shouldNot include("inst.in is invalid") + chirrtl shouldNot include("inst.out is invalid") + } } diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala index 9b29b0ba..1b69efa3 100644 --- a/src/test/scala/chiselTests/aop/InjectionSpec.scala +++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala @@ -108,6 +108,7 @@ class InjectionSpec extends ChiselFlatSpec with Utils { { _: SubmoduleManipulationTester => // By creating a second SubmoduleA, the module names would conflict unless they were uniquified val moduleSubmoduleC = Module(new SubmoduleC) + moduleSubmoduleC.io <> DontCare //if we're here then we've elaborated correctly stop() } -- cgit v1.2.3 From f86c1ff7b39146f23cd1959bcc63dcb3b0b27125 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Sun, 23 Oct 2022 22:27:06 +0000 Subject: Fix for <> to BlackBox.IO with Compatibility Bundles (#2801) (#2803) MonoConnect.traceFlow now properly handles coerced directions. Also minor improvement to getClassName especially useful in test case printf debugging. (cherry picked from commit 3aba755bdcf996c0fbd846d13268fd6641b29e96) Co-authored-by: Megan Wachs --- core/src/main/scala/chisel3/Aggregate.scala | 20 ++- .../main/scala/chisel3/internal/BiConnect.scala | 9 +- .../main/scala/chisel3/internal/MonoConnect.scala | 30 ++-- src/test/scala/chiselTests/BulkConnectSpec.scala | 15 +- .../CompatibilityInteroperabilitySpec.scala | 155 +++++++++++++++++++++ 5 files changed, 212 insertions(+), 17 deletions(-) diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index aacf0b1c..4fc9b20f 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -1116,7 +1116,12 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio def elements: SeqMap[String, Data] /** Name for Pretty Printing */ - def className: String = this.getClass.getSimpleName + def className: String = try { + this.getClass.getSimpleName + } catch { + // This happens if your class is defined in an object and is anonymous + case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString + } private[chisel3] override def typeEquivalent(that: Data): Boolean = that match { case that: Record => @@ -1243,10 +1248,15 @@ abstract class Bundle(implicit compileOptions: CompileOptions) extends Record wi "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 - case name => name + override def className: String = try { + this.getClass.getSimpleName match { + case name if name.startsWith("$anon$") => "AnonymousBundle" // fallback for anonymous Bundle case + case "" => "AnonymousBundle" // ditto, but on other platforms + case name => name + } + } catch { + // This happens if you have nested objects which your class is defined in + case e: java.lang.InternalError if e.getMessage == "Malformed class name" => this.getClass.toString } /** The collection of [[Data]] diff --git a/core/src/main/scala/chisel3/internal/BiConnect.scala b/core/src/main/scala/chisel3/internal/BiConnect.scala index e8fb2361..74376598 100644 --- a/core/src/main/scala/chisel3/internal/BiConnect.scala +++ b/core/src/main/scala/chisel3/internal/BiConnect.scala @@ -227,9 +227,12 @@ private[chisel3] object BiConnect { context_mod: RawModule ): Unit = { // Verify right has no extra fields that left doesn't have - for ((field, right_sub) <- right_r.elements) { - if (!left_r.elements.isDefinedAt(field)) { - if (connectCompileOptions.connectFieldsMustMatch) { + + // For each field in left, descend with right. + // Don't bother doing this check if we don't expect it to necessarily pass. + if (connectCompileOptions.connectFieldsMustMatch) { + for ((field, right_sub) <- right_r.elements) { + if (!left_r.elements.isDefinedAt(field)) { throw MissingLeftFieldException(field) } } diff --git a/core/src/main/scala/chisel3/internal/MonoConnect.scala b/core/src/main/scala/chisel3/internal/MonoConnect.scala index 31364804..4e762a7c 100644 --- a/core/src/main/scala/chisel3/internal/MonoConnect.scala +++ b/core/src/main/scala/chisel3/internal/MonoConnect.scala @@ -322,21 +322,35 @@ private[chisel3] object MonoConnect { else false } - /** Trace flow from child Data to its parent. */ - @tailrec private[chisel3] def traceFlow(currentlyFlipped: Boolean, data: Data, context_mod: RawModule): Boolean = { - import SpecifiedDirection.{Input => SInput, Flip => SFlip} + /** Trace flow from child Data to its parent. + * + * Returns true if, given the context, + * this signal can be a sink when wantsToBeSink = true, + * or if it can be a source when wantsToBeSink = false. + * Always returns true if the Data does not actually correspond + * to a Port. + */ + @tailrec private[chisel3] def traceFlow( + wantToBeSink: Boolean, + currentlyFlipped: Boolean, + data: Data, + context_mod: RawModule + ): Boolean = { val sdir = data.specifiedDirection - val flipped = sdir == SInput || sdir == SFlip + val coercedFlip = sdir == SpecifiedDirection.Input + val coercedAlign = sdir == SpecifiedDirection.Output + val flipped = sdir == SpecifiedDirection.Flip + val traceFlipped = ((flipped ^ currentlyFlipped) || coercedFlip) && (!coercedAlign) data.binding.get match { - case ChildBinding(parent) => traceFlow(flipped ^ currentlyFlipped, parent, context_mod) + case ChildBinding(parent) => traceFlow(wantToBeSink, traceFlipped, parent, context_mod) case PortBinding(enclosure) => val childPort = enclosure != context_mod - childPort ^ flipped ^ currentlyFlipped + wantToBeSink ^ childPort ^ traceFlipped case _ => true } } - def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, data, context_mod) - def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, data, context_mod) + def canBeSink(data: Data, context_mod: RawModule): Boolean = traceFlow(true, false, data, context_mod) + def canBeSource(data: Data, context_mod: RawModule): Boolean = traceFlow(false, false, data, context_mod) /** Check whether two aggregates can be bulk connected (<=) in FIRRTL. (MonoConnect case) * diff --git a/src/test/scala/chiselTests/BulkConnectSpec.scala b/src/test/scala/chiselTests/BulkConnectSpec.scala index 281890d4..0a1616d3 100644 --- a/src/test/scala/chiselTests/BulkConnectSpec.scala +++ b/src/test/scala/chiselTests/BulkConnectSpec.scala @@ -54,7 +54,15 @@ class BulkConnectSpec extends ChiselPropSpec { }) chirrtl should include("out.buzz.foo <= in.buzz.foo") + chirrtl should include("out.fizz <= in.fizz") + chirrtl should include("deq.bits <- enq.bits") + chirrtl should include("deq.valid <= enq.valid") + chirrtl should include("enq.ready <= deq.ready") + chirrtl shouldNot include("deq <= enq") + chirrtl shouldNot include("deq.bits.foo <= enq.bits.foo") + chirrtl shouldNot include("deq.bits.foo <- enq.bits.foo") + chirrtl shouldNot include("deq.bits.bar") } property("Chisel connects should not emit FIRRTL bulk connects between differing FIRRTL types") { @@ -74,7 +82,9 @@ class BulkConnectSpec extends ChiselPropSpec { out <> in }) // out <- in is illegal FIRRTL - chirrtl should include("out.foo.bar <= in.foo.bar") + exactly(2, chirrtl.split('\n')) should include("out.foo.bar <= in.foo.bar") + chirrtl shouldNot include("out <= in") + chirrtl shouldNot include("out <- in") } property("Chisel connects should not emit a FIRRTL bulk connect for a bidirectional MonoConnect") { @@ -91,6 +101,9 @@ class BulkConnectSpec extends ChiselPropSpec { }) chirrtl shouldNot include("wire <= enq") + chirrtl should include("wire.bits <= enq.bits") + chirrtl should include("wire.valid <= enq.valid") + chirrtl should include("wire.ready <= enq.ready") chirrtl should include("deq <= enq") } diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 1e199297..e2fb2179 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -3,6 +3,7 @@ package chiselTests import scala.collection.immutable.ListMap +import chisel3.stage.ChiselStage.emitChirrtl // Keep Chisel._ separate from chisel3._ below object CompatibilityComponents { @@ -390,4 +391,158 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { compile(new Top(true)) compile(new Top(false)) } + + "A BlackBox with Chisel._ fields in its IO" should "bulk connect in import chisel3._ code correctly" in { + object Compat { + import Chisel._ + class LegacyChiselIO extends Bundle { + val foo = Output(Bool()) + val bar = Output(Bool()) + } + } + object Chisel3 { + import chisel3._ + import chisel3.util.Valid + + class FooModuleIO extends Bundle { + val quz = Input(new QuzIO) + val foo = Output(Bool()) + val bar = Input(Bool()) + } + class QuzIO extends Bundle { + val q = Flipped(Valid(new Compat.LegacyChiselIO)) + } + class FooModule extends Module { + val io = IO(new FooModuleIO) + io <> DontCare + } + class FooMirrorBlackBox extends BlackBox { + val io = IO(Flipped(new FooModuleIO)) + } + class Top extends Module { + val foo = Module(new FooModule) + val mirror = Module(new FooMirrorBlackBox) + foo.io <> mirror.io + } + } + val chirrtl = emitChirrtl(new Chisel3.Top) + chirrtl should include("foo.io.bar <= mirror.bar") + chirrtl should include("mirror.foo <= foo.io.foo") + chirrtl should include("foo.io.quz.q.bits <- mirror.quz.q.bits") + chirrtl should include("foo.io.quz.q.valid <= mirror.quz.q.valid") + } + + "A chisel3.Bundle bulk connected to a Chisel Bundle in either direction" should "work even with mismatched fields" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class BarBundle extends Bundle { + val bar = UInt(8.W) + } + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new BarBundle)) + val out = IO(Output(if (swap) new BarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "work with missing fields in the Chisel._" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle)) + val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "work with missing fields in the chisel3._" in { + object Compat { + import Chisel._ + class FooBundle extends Bundle { + val foo = UInt(width = 8) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = UInt(8.W) + } + + class MyModule(swap: Boolean) extends Module { + val in = IO(Input(if (swap) new Compat.FooBundle else new FooBarBundle)) + val out = IO(Output(if (swap) new FooBarBundle else new Compat.FooBundle)) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 shouldNot include("<=") + chirrtl0 should include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 shouldNot include("<=") + chirrtl1 should include("out <- in") + } + + it should "emit FIRRTL connects if possible" in { + object Compat { + import Chisel._ + class FooBarBundle extends Bundle { + val foo = UInt(8.W) + val bar = Flipped(UInt(8.W)) + } + } + object Chisel3 { + import chisel3._ + class FooBarBundle extends Bundle { + val foo = Output(UInt(8.W)) + val bar = Input(UInt(8.W)) + } + class MyModule(swap: Boolean) extends Module { + val in = IO(Flipped((if (swap) new Compat.FooBarBundle else new FooBarBundle))) + val out = IO(if (swap) new FooBarBundle else new Compat.FooBarBundle) + out <> DontCare + out <> in + } + } + val chirrtl0 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl0 should include("out <= in") + chirrtl0 shouldNot include("out <- in") + val chirrtl1 = emitChirrtl(new Chisel3.MyModule(true)) + chirrtl1 should include("out <= in") + chirrtl1 shouldNot include("out <- in") + } } -- cgit v1.2.3 From 2f890aabc1ecf04c6efcb84cee4f00da67be6a53 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 25 Oct 2022 01:58:03 +0000 Subject: Add Scalafmt for mill (backport #2805) (#2807) * Add Scalafmt for mill (#2805) * add scalafmt for test in mill build system. * reformat codes in integration-tests (cherry picked from commit d881fd29d7af91a7c514565b4b21797f1e44fbdb) # Conflicts: # build.sc # integration-tests/src/test/scala/chiselTests/util/experimental/BitPat.scala * Resolve backport conflicts Co-authored-by: Jiuyang Liu Co-authored-by: Jack Koenig --- build.sc | 2 +- .../src/test/scala/chiselTest/MemFormalSpec.scala | 8 +- .../util/experimental/DecoderSpec.scala | 24 +-- .../util/experimental/algorithm/Bitwise.scala | 6 +- .../experimental/minimizer/MinimizerSpec.scala | 197 +++++++++++---------- 5 files changed, 127 insertions(+), 110 deletions(-) diff --git a/build.sc b/build.sc index 80ba17fe..f7bd1c9b 100644 --- a/build.sc +++ b/build.sc @@ -104,7 +104,7 @@ class chisel3CrossModule(val crossScalaVersion: String) extends CommonModule wit ) } - object test extends Tests { + object test extends Tests with ScalafmtModule { override def scalacPluginClasspath = m.scalacPluginClasspath override def scalacOptions = T { diff --git a/integration-tests/src/test/scala/chiselTest/MemFormalSpec.scala b/integration-tests/src/test/scala/chiselTest/MemFormalSpec.scala index 35d1a299..20f3443a 100644 --- a/integration-tests/src/test/scala/chiselTest/MemFormalSpec.scala +++ b/integration-tests/src/test/scala/chiselTest/MemFormalSpec.scala @@ -10,12 +10,12 @@ import firrtl.annotations.MemoryArrayInitAnnotation import org.scalatest.flatspec.AnyFlatSpec class MemFormalSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { - behavior of "SyncReadMem read enable" + behavior.of("SyncReadMem read enable") private def check(mod: Boolean => ReadEnTestModule, alwaysEnabeld: Boolean = false): Unit = { // we first check that the read is enabled when it should be verify(mod(true), Seq(BoundedCheck(4))) - if(!alwaysEnabeld) { + if (!alwaysEnabeld) { // now we check that it is disabled, when it should be // however, note that this check is not exhaustive/complete! assertThrows[FailedBoundedCheckException] { @@ -65,9 +65,9 @@ abstract class ReadEnTestModule(testShouldRead: Boolean) extends Module { }) // the first cycle after reset, the data will be arbitrary - val firstCycle = RegNext(false.B, init=true.B) + val firstCycle = RegNext(false.B, init = true.B) - if(testShouldRead) { + if (testShouldRead) { when(!firstCycle && RegNext(shouldRead)) { verification.assert(out === RegNext(addr)) } diff --git a/integration-tests/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala b/integration-tests/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala index 2d50555e..c75113f6 100644 --- a/integration-tests/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala +++ b/integration-tests/src/test/scala/chiselTests/util/experimental/DecoderSpec.scala @@ -9,11 +9,10 @@ import org.scalatest.flatspec.AnyFlatSpec import chiseltest._ import chiseltest.formal._ -class DecoderSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { - val xor = TruthTable.fromString( - """10->1 - |01->1 - | 0""".stripMargin) +class DecoderSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { + val xor = TruthTable.fromString("""10->1 + |01->1 + | 0""".stripMargin) def minimizer: Minimizer = QMCMinimizer @@ -23,7 +22,8 @@ class DecoderSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { "decoder" should "fail with a incorrect DecodeTableAnnotation" in { val annos = Seq( - DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), + DecodeTableAnnotation( + ReferenceTarget("", "", Nil, "", Nil), """10->1 |01->1 | 0""".stripMargin, @@ -38,14 +38,16 @@ class DecoderSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { "decoder" should "success with a correct DecodeTableAnnotation" in { val annos = Seq( - DecodeTableAnnotation(ReferenceTarget("", "", Nil, "", Nil), + DecodeTableAnnotation( + ReferenceTarget("", "", Nil, "", Nil), """10->1 |01->1 | 0""".stripMargin, - QMCMinimizer.minimize(TruthTable.fromString( - """10->1 - |01->1 - | 0""".stripMargin)).toString + QMCMinimizer + .minimize(TruthTable.fromString("""10->1 + |01->1 + | 0""".stripMargin)) + .toString ) ) verify(new DecodeTestModule(minimizer, table = xor), BoundedCheck(1) +: annos) diff --git a/integration-tests/src/test/scala/chiselTests/util/experimental/algorithm/Bitwise.scala b/integration-tests/src/test/scala/chiselTests/util/experimental/algorithm/Bitwise.scala index 6c8eb4b4..d0f16701 100644 --- a/integration-tests/src/test/scala/chiselTests/util/experimental/algorithm/Bitwise.scala +++ b/integration-tests/src/test/scala/chiselTests/util/experimental/algorithm/Bitwise.scala @@ -11,7 +11,7 @@ class ScanLeftOrTestModule(width: Int) extends Module { val input = IO(Input(UInt(width.W))) var lsb = false.B - val vec = for(b <- input.asBools) yield { + val vec = for (b <- input.asBools) yield { val cur = b || lsb lsb = cur cur @@ -34,13 +34,13 @@ class ScanRightOrTestModule(width: Int) extends Module { class scanOrTest extends AnyFlatSpec with ChiselScalatestTester with Formal { "scanLeftOr" should "compute correctly" in { - for(i <- 1 to 16) { + for (i <- 1 to 16) { verify(new ScanLeftOrTestModule(i), Seq(BoundedCheck(1))) } } "scanRightOr" should "compute correctly" in { - for(i <- 1 to 16) { + for (i <- 1 to 16) { verify(new ScanRightOrTestModule(i), Seq(BoundedCheck(1))) } } diff --git a/integration-tests/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala b/integration-tests/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala index 2d3e073c..0b6d8e91 100644 --- a/integration-tests/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala +++ b/integration-tests/src/test/scala/chiselTests/util/experimental/minimizer/MinimizerSpec.scala @@ -34,82 +34,92 @@ trait MinimizerSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { // making optimization opportunities to decoder algorithms "case0" should "pass" in { - minimizerTest(TruthTable( - Map( - // BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b?"), - BitPat("b010") -> BitPat("b?"), - // BitPat("b011") -> BitPat("b0"), - BitPat("b100") -> BitPat("b1"), - BitPat("b101") -> BitPat("b1"), - // BitPat("b110") -> BitPat("b0"), - BitPat("b111") -> BitPat("b1") - ), - BitPat("b0") - )) + minimizerTest( + TruthTable( + Map( + // BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b?"), + BitPat("b010") -> BitPat("b?"), + // BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + // BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b0") + ) + ) } "case1" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0"), - BitPat("b001") -> BitPat("b?"), - BitPat("b010") -> BitPat("b?"), - BitPat("b011") -> BitPat("b0"), - // BitPat("b100") -> BitPat("b1"), - // BitPat("b101") -> BitPat("b1"), - BitPat("b110") -> BitPat("b0"), - // BitPat("b111") -> BitPat("b1") - ), - BitPat("b1") - )) + minimizerTest( + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + BitPat("b001") -> BitPat("b?"), + BitPat("b010") -> BitPat("b?"), + BitPat("b011") -> BitPat("b0"), + // BitPat("b100") -> BitPat("b1"), + // BitPat("b101") -> BitPat("b1"), + BitPat("b110") -> BitPat("b0") + // BitPat("b111") -> BitPat("b1") + ), + BitPat("b1") + ) + ) } "caseX" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0"), - // BitPat("b001") -> BitPat("b?"), - // BitPat("b010") -> BitPat("b?"), - BitPat("b011") -> BitPat("b0"), - BitPat("b100") -> BitPat("b1"), - BitPat("b101") -> BitPat("b1"), - BitPat("b110") -> BitPat("b0"), - BitPat("b111") -> BitPat("b1") - ), - BitPat("b?") - )) + minimizerTest( + TruthTable( + Map( + BitPat("b000") -> BitPat("b0"), + // BitPat("b001") -> BitPat("b?"), + // BitPat("b010") -> BitPat("b?"), + BitPat("b011") -> BitPat("b0"), + BitPat("b100") -> BitPat("b1"), + BitPat("b101") -> BitPat("b1"), + BitPat("b110") -> BitPat("b0"), + BitPat("b111") -> BitPat("b1") + ), + BitPat("b?") + ) + ) } "caseMultiDefault" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b000") -> BitPat("b0100"), - BitPat("b001") -> BitPat("b?111"), - BitPat("b010") -> BitPat("b?000"), - BitPat("b011") -> BitPat("b0101"), - BitPat("b111") -> BitPat("b1101") - ), - BitPat("b?100") - )) + minimizerTest( + TruthTable( + Map( + BitPat("b000") -> BitPat("b0100"), + BitPat("b001") -> BitPat("b?111"), + BitPat("b010") -> BitPat("b?000"), + BitPat("b011") -> BitPat("b0101"), + BitPat("b111") -> BitPat("b1101") + ), + BitPat("b?100") + ) + ) } "case7SegDecoder" should "pass" in { - minimizerTest(TruthTable( - Map( - BitPat("b0000") -> BitPat("b111111001"), - BitPat("b0001") -> BitPat("b011000001"), - BitPat("b0010") -> BitPat("b110110101"), - BitPat("b0011") -> BitPat("b111100101"), - BitPat("b0100") -> BitPat("b011001101"), - BitPat("b0101") -> BitPat("b101101101"), - BitPat("b0110") -> BitPat("b101111101"), - BitPat("b0111") -> BitPat("b111000001"), - BitPat("b1000") -> BitPat("b111111101"), - BitPat("b1001") -> BitPat("b111101101"), - ), - BitPat("b???????10") - )) + minimizerTest( + TruthTable( + Map( + BitPat("b0000") -> BitPat("b111111001"), + BitPat("b0001") -> BitPat("b011000001"), + BitPat("b0010") -> BitPat("b110110101"), + BitPat("b0011") -> BitPat("b111100101"), + BitPat("b0100") -> BitPat("b011001101"), + BitPat("b0101") -> BitPat("b101101101"), + BitPat("b0110") -> BitPat("b101111101"), + BitPat("b0111") -> BitPat("b111000001"), + BitPat("b1000") -> BitPat("b111111101"), + BitPat("b1001") -> BitPat("b111101101") + ), + BitPat("b???????10") + ) + ) } // A simple RV32I decode table example @@ -217,6 +227,7 @@ trait MinimizerSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { val FN_SLTU = "1110" val FN_SGEU = "1111" + // format: off minimizerTest(TruthTable( Map( BNE -> Seq(Y, N, N, Y, N, N, Y, Y, N, A2_RS2, A1_RS1, IMM_SB, DW_X, FN_SNE, N, M_X, N, N, N, N, N, N, N, CSR_N, N, N, N, N), @@ -271,40 +282,44 @@ trait MinimizerSpec extends AnyFlatSpec with ChiselScalatestTester with Formal { ).map { case (k, v) => BitPat(s"b$k") -> BitPat(s"b${v.reduce(_ + _)}") }, BitPat(s"b${Seq(N, X, X, X, X, X, X, X, X, A2_X, A1_X, IMM_X, DW_X, FN_X, N, M_X, X, X, X, X, X, X, X, CSR_X, X, X, X, X).reduce(_ + _)}") )) + // format: on } "output is 0" should "pass" in { - minimizerTest(TruthTable.fromString( - """00->0 - |01->? - |10->0 - |11->0 - | ? - |""".stripMargin - - )) + minimizerTest( + TruthTable.fromString( + """00->0 + |01->? + |10->0 + |11->0 + | ? + |""".stripMargin + ) + ) } "output is 1" should "pass" in { - minimizerTest(TruthTable.fromString( - """00->1 - |01->? - |10->1 - |11->1 - | ? - |""".stripMargin - - )) + minimizerTest( + TruthTable.fromString( + """00->1 + |01->? + |10->1 + |11->1 + | ? + |""".stripMargin + ) + ) } // I know this seems to be crazy, but if user is crazy as well... "output is dont care" should "pass" in { - minimizerTest(TruthTable.fromString( - """00->? - |01->? - |10->? - |11->? - | ? - |""".stripMargin - - )) + minimizerTest( + TruthTable.fromString( + """00->? + |01->? + |10->? + |11->? + | ? + |""".stripMargin + ) + ) } } -- cgit v1.2.3 From 0750bc2f46d49c8fdfd0c07a2ac80c74311b3f15 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 25 Oct 2022 13:57:04 +0000 Subject: Publish an Initial Roadmap (#2804) (#2810) * Add initial roadmap Co-authored-by: edwardcwang (cherry picked from commit 7f73d18ecd2b1cf63485f3c268456ba0c775b961) Co-authored-by: Megan Wachs --- README.md | 5 ++++ ROADMAP.md | 92 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 97 insertions(+) create mode 100644 ROADMAP.md diff --git a/README.md b/README.md index a68ba261..d1d42b83 100644 --- a/README.md +++ b/README.md @@ -48,6 +48,7 @@ Chisel is powered by [FIRRTL (Flexible Intermediate Representation for RTL)](htt * [Chisel3 Architecture Overview](#chisel3-architecture-overview) * [Chisel Sub-Projects](#chisel-sub-projects) * [Which version should I use?](#which-version-should-i-use) + * [Chisel Roadmap](#roadmap) --- @@ -388,3 +389,7 @@ If you're developing a Chisel library (or `chisel3` itself), you'll probably wan By default, the master branches of the chisel repositories are configured to build and publish their version of the code as `Z.Y-SNAPSHOT`. Updated SNAPSHOTs are publised on every push to master. You are encouraged to do your development against the latest SNAPSHOT, but note that neither API nor ABI compatibility is guaranteed so your code may break at any time. + +### Roadmap + +See [ROADMAP.md]. diff --git a/ROADMAP.md b/ROADMAP.md new file mode 100644 index 00000000..07d2e94c --- /dev/null +++ b/ROADMAP.md @@ -0,0 +1,92 @@ +# Chisel Roadmap + +This roadmap captures the current plans of the Chisel maintainers and can be +changed through the usual PR process. +Not every small feature add or planned bugfix is logged here, but the high-level +changes and planned direction that are relevant for those using and contributing +to the project. +Two especially notable directions on the roadmap below are: + +* We are migrating Chisel to rely on, exclusively, the + [MLIR-based FIRRTL Compiler](https://github.com/llvm/circt) +* Accelerating improvements and changes to Chisel and the FIRRTL specification + + +## 3.5.x (and Earlier) + +- Continue to maintain binary compatibility on these releases +- Continue to backport bug fixes, new features if possible, documentation + updates, etc. +- Deprecations are backported to ease in users migrating to next major release + (3.6). + +## 3.6 (Target 2022 November) + +This release is intended to provide a smooth path for the Chisel codebase to no +longer depend on the +[Scala FIRRTL Compiler (SFC)](https://github.com/chipsalliance/firrtl) +and use the [MLIR Firrtl Compiler (MFC)](https://github.com/llvm/circt) as the +main backend. While the SFC may continue to work to consume the `.fir` produced +by Chisel, it will not be a requirement for adding new features to the FIRRTL +spec and the Chisel APIs. Thus, the 3.6 release will: + +- Deprecate `Chisel._` +- Turn the deprecations from previous releases into Errors +- Support integration with [MFC](https://github.com/llvm/circt) by bringing + `chisel-circt` in-repo +- Stop using [SFC](https://github.com/chipsalliance/firrtl) except to test + deprecated features +- Deprecate all Chisel features that are not implemented in MFC. A nonexhaustive + list includes: + - Interval Types + - Fixed Point Types + - Injecting Aspects + - EnumAnnotations + - RunFirrtlTransformAnnotation + - LoadMemoryFromFile + - ChiselAnnotation/custom annotations +- Publish Roadmap + + +## 4.0 + +See the Q&A below. + +## 5.0.0 (Target Q1 2023) + +As we look forward to Chisel development, we plan to accelerate coordinated +changes with the +[FIRRTL specification](https://github.com/chipsalliance/firrtl-spec) to add new +functionality. The bump to version 5.0.0 will be a major change in the overall +versioning scheme of the Chisel project. We will switch to +[Semantic Versioning 2.0.0](https://semver.org/), which means we expect the +MAJOR version number to increment much more frequently. To make this change more +apparent, these changes will be made in the Chisel 5.0.0 release: + +- The build artifact will be renamed to `org.chipsalliance.chisel` +- The repository will be renamed to github.com/chipsalliance/chisel (the + `chisel3` URL will still work as an alias). +- All of Chisel’s internal tests will rely on MFC, not SFC, for lowering from + firrtl +- Delete Chisel._ +- The repo will be flattened with its dependencies/dependents as much as + possible + - Bring all things in from SFC into chisel repo that are needed(FIRRTL IR, + Stage, etc) + - chisel-test (or a subset of it that works with MFC) will be brought into the + chisel repo + - The chisel-lang website will be brought in-repo +- Only the chisel repo will be released as an artifact (as all supported + dependents and dependencies will be flattened in) + +## Beyond 5.0.0 + +Faster co-improvements to Chisel, MFC, the FIRRTL IR and Chisel. + +## Q & A + +* What happened to Chisel 4? + +We want to make it clear that there is a change in development versioning and +philosophy (moving faster), so we are skipping 4 and moving straight from +chisel3 to Chisel 5.0.0. -- cgit v1.2.3 From 4149157df6531d124483d992daf96cf4e62a0f0c Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 4 Nov 2022 18:20:07 +0000 Subject: Add PartialDataView.supertype (backport #2826) (#2827) * Add PartialDataView.supertype (#2826) This factory method makes it easy to create PartialDataViews from a Bundle type to its supertype. Because of the typing relationship, there is no need to provide a mapping between fields. The only thing necessary is to provide a function for constructing an instance of the supertype from an instance of the subtype. (cherry picked from commit 251d454a224e5a961438ba0ea41134d7da7a5992) # Conflicts: # core/src/main/scala/chisel3/experimental/dataview/package.scala # src/test/scala/chiselTests/experimental/DataView.scala * Resolve backport conflicts Co-authored-by: Jack Koenig --- .../chisel3/experimental/dataview/DataView.scala | 23 ++++++++++++++ .../chisel3/experimental/dataview/package.scala | 14 ++------- docs/src/cookbooks/dataview.md | 35 ++++++++++++++++++++++ .../scala/chiselTests/experimental/DataView.scala | 22 ++++++++++++++ 4 files changed, 82 insertions(+), 12 deletions(-) diff --git a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala index 7f20964d..cc555b11 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/DataView.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/DataView.scala @@ -592,4 +592,27 @@ object PartialDataView { implicit sourceInfo: SourceInfo ): DataView[T, V] = new DataView[T, V](mkView, mapping, _total = false) + + /** Constructs a non-total [[DataView]] mapping from a [[Bundle]] type to a parent [[Bundle]] type + * + * @param mkView a function constructing an instance `V` from an instance of `T` + * @return the [[DataView]] that enables viewing instances of a [[Bundle]] as instances of a parent type + */ + def supertype[T <: Bundle, V <: Bundle]( + mkView: T => V + )( + implicit ev: SubTypeOf[T, V], + sourceInfo: SourceInfo + ): DataView[T, V] = + mapping[T, V]( + mkView, + { + case (a, b) => + val aElts = a.elements + val bElts = b.elements + val bKeys = bElts.keySet + val keys = aElts.keysIterator.filter(bKeys.contains) + keys.map(k => aElts(k) -> bElts(k)).toSeq + } + ) } diff --git a/core/src/main/scala/chisel3/experimental/dataview/package.scala b/core/src/main/scala/chisel3/experimental/dataview/package.scala index 71ae2d8f..a52e88cf 100644 --- a/core/src/main/scala/chisel3/experimental/dataview/package.scala +++ b/core/src/main/scala/chisel3/experimental/dataview/package.scala @@ -43,24 +43,14 @@ package object dataview { "${A} is not a subtype of ${B}! Did you mean .viewAs[${B}]? " + "Please see https://www.chisel-lang.org/chisel3/docs/cookbooks/dataview" ) - private type SubTypeOf[A, B] = A <:< B + private[dataview] type SubTypeOf[A, B] = A <:< B /** Provides `viewAsSupertype` for subclasses of [[Bundle]] */ implicit class BundleUpcastable[T <: Bundle](target: T) { /** View a [[Bundle]] or [[Record]] as a parent type (upcast) */ def viewAsSupertype[V <: Bundle](proto: V)(implicit ev: SubTypeOf[T, V], sourceInfo: SourceInfo): V = { - implicit val dataView = PartialDataView.mapping[T, V]( - _ => proto, - { - case (a, b) => - val aElts = a.elements - val bElts = b.elements - val bKeys = bElts.keySet - val keys = aElts.keysIterator.filter(bKeys.contains) - keys.map(k => aElts(k) -> bElts(k)).toSeq - } - ) + implicit val dataView = PartialDataView.supertype[T, V](_ => proto) target.viewAs[V] } } diff --git a/docs/src/cookbooks/dataview.md b/docs/src/cookbooks/dataview.md index ed969ca1..f970cfe4 100644 --- a/docs/src/cookbooks/dataview.md +++ b/docs/src/cookbooks/dataview.md @@ -12,6 +12,7 @@ section: "chisel3" * [How do I connect a subset of Bundle fields?](#how-do-i-connect-a-subset-of-bundle-fields) * [How do I view a Bundle as a parent type (superclass)?](#how-do-i-view-a-bundle-as-a-parent-type-superclass) * [How do I view a Bundle as a parent type when the parent type is abstract (like a trait)?](#how-do-i-view-a-bundle-as-a-parent-type-when-the-parent-type-is-abstract-like-a-trait) + * [How can I use `.viewAs` instead of `.viewAsSupertype(type)`?](#how-can-i-use-viewas-instead-of-viewassupertypetype) ## How do I view a Data as a UInt or vice versa? @@ -177,3 +178,37 @@ As indicated in the comment, abstract methods must still be implemented. This is the same that happens when one writes `new Bundle {}`, the curly braces create a new concrete subclass; however, because `Bundle` has no abstract methods, the contents of the body can be empty. + +### How can I use `.viewAs` instead of `.viewAsSupertype(type)`? + +While `viewAsSupertype` is helpful for one-off casts, the need to provide a type template object +each time can be onerous. +Because of the subtyping relationship, you can use `PartialDataView.supertype` to create a +`DataView` from a Bundle type to a parent type by just providing the function to construct an +instance of the parent type from an instance of the child type. +The mapping of corresponding fields is automatically determined by Chisel to be the fields defined +in the supertype. + +```scala mdoc:silent:reset +import chisel3._ +import chisel3.experimental.dataview._ + +class Foo(x: Int) extends Bundle { + val foo = UInt(x.W) +} +class Bar(val x: Int) extends Foo(x) { + val bar = UInt(x.W) +} +// Define a DataView without having to specify the mapping! +implicit val view = PartialDataView.supertype[Bar, Foo](b => new Foo(b.x)) + +class MyModule extends Module { + val foo = IO(Input(new Foo(8))) + val bar = IO(Output(new Bar(8))) + bar.viewAs[Foo] := foo // bar.foo := foo.foo + bar.bar := 123.U // all fields need to be connected +} +``` +```scala mdoc:verilog +chisel3.stage.ChiselStage.emitVerilog(new MyModule) +``` diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index ac8357f0..3673778b 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -177,6 +177,28 @@ class DataViewSpec extends ChiselFlatSpec { chirrtl should include("fooOut.foo <= barIn.foo") } + it should "be easy to make a PartialDataView viewing a Bundle as a Parent Bundle type" in { + class Foo(x: Int) extends Bundle { + val foo = UInt(x.W) + } + class Bar(val x: Int) extends Foo(x) { + val bar = UInt(x.W) + } + implicit val view = PartialDataView.supertype[Bar, Foo](b => new Foo(b.x)) + class MyModule extends Module { + val fooIn = IO(Input(new Foo(8))) + val barOut = IO(Output(new Bar(8))) + barOut.viewAs[Foo] := fooIn + + val barIn = IO(Input(new Bar(8))) + val fooOut = IO(Output(new Foo(8))) + fooOut := barIn.viewAs[Foo] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("barOut.foo <= fooIn.foo") + chirrtl should include("fooOut.foo <= barIn.foo") + } + it should "error if viewing a parent Bundle as a child Bundle type" in { assertTypeError(""" class Foo extends Bundle { -- cgit v1.2.3 From 017bd6b9c96974df2a3c4f35e069d60fec001f2e Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Sat, 5 Nov 2022 22:31:07 +0000 Subject: Support Analog in DataView (#2782) (#2828) Co-authored-by: Megan Wachs (cherry picked from commit 26100a875c69bf56f7442fac82ca9c74ad3596eb) Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Attach.scala | 45 ---------------------- core/src/main/scala/chisel3/Data.scala | 1 + .../main/scala/chisel3/experimental/Analog.scala | 3 +- .../main/scala/chisel3/experimental/Attach.scala | 45 ++++++++++++++++++++++ .../scala/chiselTests/experimental/DataView.scala | 12 +++++- .../chiselTests/experimental/FlatIOSpec.scala | 17 +++++++- 6 files changed, 75 insertions(+), 48 deletions(-) delete mode 100644 core/src/main/scala/chisel3/Attach.scala create mode 100644 core/src/main/scala/chisel3/experimental/Attach.scala diff --git a/core/src/main/scala/chisel3/Attach.scala b/core/src/main/scala/chisel3/Attach.scala deleted file mode 100644 index 5c9cfe53..00000000 --- a/core/src/main/scala/chisel3/Attach.scala +++ /dev/null @@ -1,45 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3.experimental - -import chisel3.RawModule -import chisel3.internal._ -import chisel3.internal.Builder.pushCommand -import chisel3.internal.firrtl._ -import chisel3.internal.sourceinfo.SourceInfo - -object attach { - // Exceptions that can be generated by attach - case class AttachException(message: String) extends ChiselException(message) - def ConditionalAttachException: AttachException = - AttachException(": Conditional attach is not allowed!") - - // Actual implementation - private[chisel3] def impl(elts: Seq[Analog], contextModule: RawModule)(implicit sourceInfo: SourceInfo): Unit = { - if (Builder.whenDepth != 0) throw ConditionalAttachException - - // TODO Check that references are valid and can be attached - - pushCommand(Attach(sourceInfo, elts.map(_.lref))) - } - - /** Create an electrical connection between [[Analog]] components - * - * @param elts The components to attach - * - * @example - * {{{ - * val a1 = Wire(Analog(32.W)) - * val a2 = Wire(Analog(32.W)) - * attach(a1, a2) - * }}} - */ - def apply(elts: Analog*)(implicit sourceInfo: SourceInfo): Unit = { - try { - impl(elts, Builder.forcedUserModule) - } catch { - case AttachException(message) => - throwException(elts.mkString("Attaching (", ", ", s") failed @$message")) - } - } -} diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index f52f99de..52cc041c 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -684,6 +684,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { topBindingOpt match { case Some(binding: ReadOnlyBinding) => throwException(s"internal error: attempted to generate LHS ref to ReadOnlyBinding $binding") + case Some(ViewBinding(target)) => reify(target).lref case Some(binding: TopBinding) => Node(this) case opt => throwException(s"internal error: unknown binding $opt in generating LHS ref") } diff --git a/core/src/main/scala/chisel3/experimental/Analog.scala b/core/src/main/scala/chisel3/experimental/Analog.scala index a366f0c3..7d89025c 100644 --- a/core/src/main/scala/chisel3/experimental/Analog.scala +++ b/core/src/main/scala/chisel3/experimental/Analog.scala @@ -69,7 +69,8 @@ final class Analog private (private[chisel3] val width: Width) extends Element { } targetTopBinding match { - case _: WireBinding | _: PortBinding => direction = ActualDirection.Bidirectional(ActualDirection.Default) + case _: WireBinding | _: PortBinding | _: ViewBinding | _: AggregateViewBinding => + direction = ActualDirection.Bidirectional(ActualDirection.Default) case x => throwException(s"Analog can only be Ports and Wires, not '$x'") } binding = target diff --git a/core/src/main/scala/chisel3/experimental/Attach.scala b/core/src/main/scala/chisel3/experimental/Attach.scala new file mode 100644 index 00000000..5c9cfe53 --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/Attach.scala @@ -0,0 +1,45 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental + +import chisel3.RawModule +import chisel3.internal._ +import chisel3.internal.Builder.pushCommand +import chisel3.internal.firrtl._ +import chisel3.internal.sourceinfo.SourceInfo + +object attach { + // Exceptions that can be generated by attach + case class AttachException(message: String) extends ChiselException(message) + def ConditionalAttachException: AttachException = + AttachException(": Conditional attach is not allowed!") + + // Actual implementation + private[chisel3] def impl(elts: Seq[Analog], contextModule: RawModule)(implicit sourceInfo: SourceInfo): Unit = { + if (Builder.whenDepth != 0) throw ConditionalAttachException + + // TODO Check that references are valid and can be attached + + pushCommand(Attach(sourceInfo, elts.map(_.lref))) + } + + /** Create an electrical connection between [[Analog]] components + * + * @param elts The components to attach + * + * @example + * {{{ + * val a1 = Wire(Analog(32.W)) + * val a2 = Wire(Analog(32.W)) + * attach(a1, a2) + * }}} + */ + def apply(elts: Analog*)(implicit sourceInfo: SourceInfo): Unit = { + try { + impl(elts, Builder.forcedUserModule) + } catch { + case AttachException(message) => + throwException(elts.mkString("Attaching (", ", ", s") failed @$message")) + } + } +} diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index 3673778b..cefc893c 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -7,7 +7,7 @@ import chisel3._ import chisel3.experimental.dataview._ import chisel3.experimental.conversions._ import chisel3.experimental.DataMirror.internal.chiselTypeClone -import chisel3.experimental.HWTuple2 +import chisel3.experimental.{Analog, HWTuple2} import chisel3.stage.ChiselStage import chisel3.util.{Decoupled, DecoupledIO} @@ -91,6 +91,16 @@ class DataViewSpec extends ChiselFlatSpec { chirrtl should include("bar <= in") } + it should "handle viewing Analogs as Analogs" in { + class MyModule extends Module { + val foo = IO(Analog(8.W)) + val bar = IO(Analog(8.W)) + foo <> bar.viewAs[Analog] + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("attach (foo, bar)") + } + it should "handle viewing Bundles as their same concrete type" in { class MyBundle extends Bundle { val foo = UInt(8.W) diff --git a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala index dfce447f..ebb7cbdb 100644 --- a/src/test/scala/chiselTests/experimental/FlatIOSpec.scala +++ b/src/test/scala/chiselTests/experimental/FlatIOSpec.scala @@ -5,7 +5,7 @@ package chiselTests.experimental import chisel3._ import chisel3.util.Valid import chisel3.stage.ChiselStage.emitChirrtl -import chisel3.experimental.FlatIO +import chisel3.experimental.{Analog, FlatIO} import chiselTests.ChiselFlatSpec class FlatIOSpec extends ChiselFlatSpec { @@ -48,4 +48,19 @@ class FlatIOSpec extends ChiselFlatSpec { val chirrtl = emitChirrtl(new MyModule) chirrtl should include("out[addr] <= in[addr]") } + + it should "support Analog members" in { + class MyBundle extends Bundle { + val foo = Output(UInt(8.W)) + val bar = Analog(8.W) + } + class MyModule extends RawModule { + val in = IO(Flipped(new MyBundle)) + val out = IO(new MyBundle) + out <> in + } + val chirrtl = emitChirrtl(new MyModule) + chirrtl should include("out.foo <= in.foo") + chirrtl should include("attach (out.bar, in.bar)") + } } -- cgit v1.2.3 From 086c6806708d14ad5144ca064d4c644d0f62592d Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 7 Nov 2022 18:29:31 +0000 Subject: Add DataMirror.getParent for getting parents of Modules (#2825) (#2833) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit fce8394bb0ddc9ae0d9c6668e034e483bd6b71c5) Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Data.scala | 8 ++++++ .../chiselTests/experimental/DataMirrorSpec.scala | 33 ++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 52cc041c..3af5ade1 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -265,6 +265,14 @@ package experimental { } } + /** Returns the parent module within which a module instance is instantiated + * + * @note Top-level modules in any given elaboration do not have a parent + * @param target a module instance + * @return the parent of the `target`, if one exists + */ + def getParent(target: BaseModule): Option[BaseModule] = target._parent + // Internal reflection-style APIs, subject to change and removal whenever. object internal { def isSynthesizable(target: Data): Boolean = target.isSynthesizable diff --git a/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala index 731596ec..09fdf3c4 100644 --- a/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala +++ b/src/test/scala/chiselTests/experimental/DataMirrorSpec.scala @@ -8,7 +8,26 @@ import chisel3.stage.ChiselStage import chisel3.experimental.DataMirror import chiselTests.ChiselFlatSpec +object DataMirrorSpec { + import org.scalatest.matchers.should.Matchers._ + class GrandChild(parent: RawModule) extends Module { + DataMirror.getParent(this) should be(Some(parent)) + } + class Child(parent: RawModule) extends Module { + val inst = Module(new GrandChild(this)) + DataMirror.getParent(inst) should be(Some(this)) + DataMirror.getParent(this) should be(Some(parent)) + } + class Parent extends Module { + val inst = Module(new Child(this)) + DataMirror.getParent(inst) should be(Some(this)) + DataMirror.getParent(this) should be(None) + } +} + class DataMirrorSpec extends ChiselFlatSpec { + import DataMirrorSpec._ + behavior.of("DataMirror") def assertBinding(x: Data, io: Boolean, wire: Boolean, reg: Boolean) = { @@ -55,4 +74,18 @@ class DataMirrorSpec extends ChiselFlatSpec { } ChiselStage.elaborate(new MyModule) } + + it should "support getParent for normal modules" in { + ChiselStage.elaborate(new Parent) + } + + it should "support getParent for normal modules even when used in a D/I context" in { + import chisel3.experimental.hierarchy._ + class Top extends Module { + val defn = Definition(new Parent) + val inst = Instance(defn) + DataMirror.getParent(this) should be(None) + } + ChiselStage.elaborate(new Top) + } } -- cgit v1.2.3 From 76ada881d077118384907f498576b3b338291ff6 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Mon, 7 Nov 2022 19:13:49 +0000 Subject: Bugfix converter clearing flips (backport #2788) (#2832) * Bugfix converter clearing flips (#2788) * Bugfix: Output on Vec of bundle with mixed field orientations * Bugfix OpaqueTypes clearing flips (cherry picked from commit f05bff1a337589bafebd08783bb0f6a72092a95a) # Conflicts: # src/test/scala/chiselTests/Direction.scala * Resolve backport conflicts Co-authored-by: Adam Izraelevitz Co-authored-by: Jack Koenig Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- .../scala/chisel3/internal/firrtl/Converter.scala | 9 ++-- src/test/scala/chiselTests/Direction.scala | 61 ++++++++++++++++++++++ 2 files changed, 67 insertions(+), 3 deletions(-) diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index fe95445c..f73e85d2 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -301,8 +301,11 @@ private[chisel3] object Converter { case d: SInt => fir.SIntType(convert(d.width)) case d: FixedPoint => fir.FixedType(convert(d.width), convert(d.binaryPoint)) case d: Interval => fir.IntervalType(d.range.lowerBound, d.range.upperBound, d.range.firrtlBinaryPoint) - case d: Analog => fir.AnalogType(convert(d.width)) - case d: Vec[_] => fir.VectorType(extractType(d.sample_element, clearDir, info), d.length) + case d: Analog => fir.AnalogType(convert(d.width)) + case d: Vec[_] => + val childClearDir = clearDir || + d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output + fir.VectorType(extractType(d.sample_element, childClearDir, info), d.length) case d: Record => { val childClearDir = clearDir || d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output @@ -316,7 +319,7 @@ private[chisel3] object Converter { if (!d.opaqueType) fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) else - extractType(d.elements.head._2, true, info) + extractType(d.elements.head._2, childClearDir, info) } } diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala index 0c657273..03755e83 100644 --- a/src/test/scala/chiselTests/Direction.scala +++ b/src/test/scala/chiselTests/Direction.scala @@ -7,6 +7,8 @@ import chisel3._ import chisel3.stage.ChiselStage import org.scalatest.matchers.should.Matchers +import scala.collection.immutable.SeqMap + class DirectionedBundle extends Bundle { val in = Input(UInt(32.W)) val out = Output(UInt(32.W)) @@ -327,4 +329,63 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { } } } + property("Bugfix: marking Vec fields with mixed directionality as Output/Input clears inner directions") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class Coercing extends Bundle { + val source = Output(Vec(1, new Decoupled())) + val sink = Input(Vec(1, new Decoupled())) + } + class MyModule extends RawModule { + val io = IO(new Coercing()) + val source = IO(Output(Vec(1, new Decoupled()))) + val sink = IO(Input(Vec(1, new Decoupled()))) + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "output io : { source : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1], flip sink : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]}" + ) + ) + assert( + emitted.contains( + "output source : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]" + ) + ) + assert( + emitted.contains( + "input sink : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}[1]" + ) + ) + } + property("Bugfix: clearing all flips inside an opaque type") { + + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class MyOpaqueType extends Record { + val k = new Decoupled() + val elements = SeqMap("" -> k) + override def opaqueType = elements.size == 1 + override def cloneType: this.type = (new MyOpaqueType).asInstanceOf[this.type] + } + class MyModule extends RawModule { + val w = Wire(new MyOpaqueType()) + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "wire w : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}" + ) + ) + } } -- cgit v1.2.3 From f2ef3a8ee378a307661bd598cd44d4b895b9352e Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 8 Nov 2022 07:05:53 +0000 Subject: Improve Record.bind and Detect Records with unstable elements (backport #2829) (#2831) * Add Aggregate.elementsIterator and micro-optimize elementsIterator provides a more efficient API for iterating on the elements of Aggregates. It is especially useful for Records where getElements returns a Seq and thus eagerly constructs a new datastructure which may then just be iterated on anyway. This new elementsIterator API is then used throughout the codebase where it makes sense. Also change Vec.getElements to just return the underlying self instead of constructing a new Seq. (cherry picked from commit defa440b349031475daeff4024fad04925cccee6) # Conflicts: # core/src/main/scala/chisel3/Aggregate.scala # core/src/main/scala/chisel3/Module.scala # core/src/main/scala/chisel3/experimental/Trace.scala * Move Aggregate.bind inline into Record.bind Vec overrides bind and does not call the version in Aggregate so the version in Aggregate is misleading in that its only ever used by Records. Now there is no version in Aggregate and the actual functionality and use is more clear. (cherry picked from commit b054c30ba47026cb2a9b28c696a0a0a58b1e2ee7) # Conflicts: # core/src/main/scala/chisel3/Aggregate.scala * Extract and optimize duplicate checking Record.bind This replaces an immutable.Map with a single mutable.HashSet and saves the allocation of # elements Seqs. (cherry picked from commit 832ea52bc23424bb75b9654422b725a9cafaef40) # Conflicts: # core/src/main/scala/chisel3/Aggregate.scala * Add check for Records that define def elements (cherry picked from commit a4f223415de19e2a732e0b6a8fe681f706a19a56) * Resolve backport conflicts * Make elementsIterator final and package private * Waive false MiMa failure Co-authored-by: Jack Koenig --- build.sbt | 2 + core/src/main/scala/chisel3/Aggregate.scala | 123 ++++++++++++--------- core/src/main/scala/chisel3/Data.scala | 16 +-- core/src/main/scala/chisel3/Module.scala | 4 +- .../main/scala/chisel3/experimental/Trace.scala | 2 +- src/main/scala/chisel3/aop/Select.scala | 4 +- src/test/scala/chiselTests/RecordSpec.scala | 13 +++ 7 files changed, 98 insertions(+), 66 deletions(-) diff --git a/build.sbt b/build.sbt index b958b19a..88374949 100644 --- a/build.sbt +++ b/build.sbt @@ -193,6 +193,8 @@ lazy val core = (project in file("core")). settings( mimaPreviousArtifacts := Set("edu.berkeley.cs" %% "chisel3-core" % "3.5.4"), mimaBinaryIssueFilters ++= Seq( + // This is not a problem because the relevant method is implemented (and final) in Vec and Record + ProblemFilters.exclude[ReversedMissingMethodProblem]("chisel3.Aggregate.elementsIterator"), // Modified package private methods (https://github.com/lightbend/mima/issues/53) ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.Data._computeName"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.Data.forceName"), diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 4fc9b20f..16611277 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -23,52 +23,6 @@ class AliasedAggregateFieldException(message: String) extends ChiselException(me * of) other Data objects. */ sealed abstract class Aggregate extends Data { - private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { - _parent.foreach(_.addId(this)) - binding = target - - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - val duplicates = getElements.groupBy(identity).collect { case (x, elts) if elts.size > 1 => x } - if (!duplicates.isEmpty) { - 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) - } - - // Check that children obey the directionality rules. - val childDirections = getElements.map(_.direction).toSet - ActualDirection.Empty - direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { - case Some(dir) => dir - case None => - val childWithDirections = getElements.zip(getElements.map(_.direction)) - throw MixedDirectionAggregateException( - s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections" - ) - } - } /** Return an Aggregate's literal value if it is a literal, None otherwise. * If any element of the aggregate is not a literal with a defined width, the result isn't a literal. @@ -100,7 +54,10 @@ sealed abstract class Aggregate extends Data { */ def getElements: Seq[Data] - private[chisel3] def width: Width = getElements.map(_.width).foldLeft(0.W)(_ + _) + /** Similar to [[getElements]] but allows for more optimized use */ + private[chisel3] def elementsIterator: Iterator[Data] + + private[chisel3] def width: Width = elementsIterator.map(_.width).foldLeft(0.W)(_ + _) private[chisel3] def legacyConnect(that: Data)(implicit sourceInfo: SourceInfo): Unit = { // If the source is a DontCare, generate a DefInvalid for the sink, @@ -221,7 +178,7 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) sample_element.bind(SampleElementBinding(this), resolvedDirection) - for (child <- getElements) { // assume that all children are the same + for (child <- elementsIterator) { // assume that all children are the same child.bind(ChildBinding(this), resolvedDirection) } @@ -342,8 +299,9 @@ sealed class Vec[T <: Data] private[chisel3] (gen: => T, val length: Int) extend new Vec(gen.cloneTypeFull, length).asInstanceOf[this.type] } - override def getElements: Seq[Data] = - (0 until length).map(apply(_)) + override def getElements: Seq[Data] = self + + final override private[chisel3] def elementsIterator: Iterator[Data] = self.iterator /** Default "pretty-print" implementation * Analogous to printing a Seq @@ -953,9 +911,66 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio } } + /** Checks that there are no duplicate elements (aka aliased fields) in the Record */ + private def checkForAndReportDuplicates(): Unit = { + // Using List to avoid allocation in the common case of no duplicates + var duplicates: List[Data] = Nil + // Is there a more optimized datastructure we could use with the Int identities? BitSet? Requires benchmarking. + val seen = mutable.HashSet.empty[Data] + this.elementsIterator.foreach { e => + if (seen(e)) { + duplicates = e :: duplicates + } + seen += e + } + if (!duplicates.isEmpty) { + // 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 => + this.elements.collect { case x if x._2._id == duplicate._id => x }.toSeq + .sortBy(_._2._id) + .map(_._1) + .reverse + .mkString("(", ",", ")") + } + .mkString(",") + throw new AliasedAggregateFieldException( + s"${this.className} contains aliased fields named ${dupNames}" + ) + } + } + private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { try { - super.bind(target, parentDirection) + _parent.foreach(_.addId(this)) + binding = target + + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + + checkForAndReportDuplicates() + + for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) { + if (child != sameChild) { + throwException( + s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?" + ) + } + child.bind(ChildBinding(this), resolvedDirection) + } + + // Check that children obey the directionality rules. + val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty + direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { + case Some(dir) => dir + case None => + val childWithDirections = getElements.zip(getElements.map(_.direction)) + throw MixedDirectionAggregateException( + s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections" + ) + } } catch { // nasty compatibility mode shim, where anything flies case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality => val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) @@ -1142,9 +1157,11 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio } } - private[chisel3] final def allElements: Seq[Element] = elements.toIndexedSeq.flatMap(_._2.allElements) + private[chisel3] final def allElements: Seq[Element] = elementsIterator.flatMap(_.allElements).toIndexedSeq + + override def getElements: Seq[Data] = elementsIterator.toIndexedSeq - override def getElements: Seq[Data] = elements.toIndexedSeq.map(_._2) + final override private[chisel3] def elementsIterator: Iterator[Data] = elements.iterator.map(_._2) // Helper because Bundle elements are reversed before printing private[chisel3] def toPrintableHelper(elts: Seq[(String, Data)]): Printable = { diff --git a/core/src/main/scala/chisel3/Data.scala b/core/src/main/scala/chisel3/Data.scala index 3af5ade1..50093333 100644 --- a/core/src/main/scala/chisel3/Data.scala +++ b/core/src/main/scala/chisel3/Data.scala @@ -361,7 +361,7 @@ private[chisel3] object getRecursiveFields { _ ++ _ } case data: Vec[_] => - data.getElements.zipWithIndex.map { + data.elementsIterator.zipWithIndex.map { case (fieldData, fieldIndex) => getRecursiveFields(fieldData, path = s"$path($fieldIndex)") }.fold(Seq(data -> path)) { @@ -379,7 +379,7 @@ private[chisel3] object getRecursiveFields { } case data: Vec[_] => LazyList(data -> path) ++ - data.getElements.view.zipWithIndex.flatMap { + data.elementsIterator.zipWithIndex.flatMap { case (fieldData, fieldIndex) => getRecursiveFields(fieldData, path = s"$path($fieldIndex)") } @@ -406,8 +406,8 @@ private[chisel3] object getMatchedFields { _ ++ _ } case (x: Vec[_], y: Vec[_]) => - (x.getElements - .zip(y.getElements)) + (x.elementsIterator + .zip(y.elementsIterator)) .map { case (xElt, yElt) => getMatchedFields(xElt, yElt) @@ -464,7 +464,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { @deprecated("pending removal once all instances replaced", "chisel3") private[chisel3] def flatten: IndexedSeq[Element] = { this match { - case elt: Aggregate => elt.getElements.toIndexedSeq.flatMap { _.flatten } + case elt: Aggregate => elt.elementsIterator.toIndexedSeq.flatMap { _.flatten } case elt: Element => IndexedSeq(elt) case elt => throwException(s"Cannot flatten type ${elt.getClass}") } @@ -749,7 +749,7 @@ abstract class Data extends HasId with NamedComponent with SourceInfoDoc { data match { case _: Element => case agg: Aggregate => - agg.getElements.foreach(rec) + agg.elementsIterator.foreach(rec) } } rec(this) @@ -931,8 +931,8 @@ object Data { if (thiz.length != that.length) { throwException(s"Cannot compare Vecs $thiz and $that: Vec sizes differ") } else { - thiz.getElements - .zip(that.getElements) + thiz.elementsIterator + .zip(that.elementsIterator) .map { case (thisData, thatData) => thisData === thatData } .reduce(_ && _) } diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index ba2d2e32..9315a44b 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -709,9 +709,9 @@ package experimental { data match { case record: Record => val compatRecord = !record.compileOptions.dontAssumeDirectionality - record.getElements.foreach(assignCompatDir(_, compatRecord)) + record.elementsIterator.foreach(assignCompatDir(_, compatRecord)) case vec: Vec[_] => - vec.getElements.foreach(assignCompatDir(_, insideCompat)) + vec.elementsIterator.foreach(assignCompatDir(_, insideCompat)) } case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do } diff --git a/core/src/main/scala/chisel3/experimental/Trace.scala b/core/src/main/scala/chisel3/experimental/Trace.scala index 33d18147..eb2ed46a 100644 --- a/core/src/main/scala/chisel3/experimental/Trace.scala +++ b/core/src/main/scala/chisel3/experimental/Trace.scala @@ -66,7 +66,7 @@ object Trace { annotate(new ChiselAnnotation { def toFirrtl: Annotation = TraceAnnotation(aggregate.toAbsoluteTarget, aggregate.toAbsoluteTarget) }) - aggregate.getElements.foreach(traceNameV2) + aggregate.elementsIterator.foreach(traceNameV2) case element: Element => annotate(new ChiselAnnotation { def toFirrtl: Annotation = TraceAnnotation(element.toAbsoluteTarget, element.toAbsoluteTarget) diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 3a2a8931..738d6f31 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -26,7 +26,7 @@ object Select { * @param d Component to find leafs if aggregate typed. Intermediate fields/indicies are not included */ def getLeafs(d: Data): Seq[Data] = d match { - case r: Record => r.getElements.flatMap(getLeafs) + case r: Record => r.elementsIterator.flatMap(getLeafs).toSeq case v: Vec[_] => v.getElements.flatMap(getLeafs) case other => Seq(other) } @@ -36,7 +36,7 @@ object Select { * @param d Component to find leafs if aggregate typed. Intermediate fields/indicies ARE included */ def getIntermediateAndLeafs(d: Data): Seq[Data] = d match { - case r: Record => r +: r.getElements.flatMap(getIntermediateAndLeafs) + case r: Record => r +: r.elementsIterator.flatMap(getIntermediateAndLeafs).toSeq case v: Vec[_] => v +: v.getElements.flatMap(getIntermediateAndLeafs) case other => Seq(other) } diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index cde18da7..509edbbc 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -325,4 +325,17 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { "CustomBundle" should "check the types" in { ChiselStage.elaborate { new RecordTypeTester } } + + "Record with unstable elements" should "error" in { + class MyRecord extends Record { + def elements = SeqMap("a" -> UInt(8.W)) + override def cloneType: this.type = (new MyRecord).asInstanceOf[this.type] + } + val e = the[ChiselException] thrownBy { + ChiselStage.elaborate(new Module { + val io = IO(Input(new MyRecord)) + }) + } + e.getMessage should include("does not return the same objects when calling .elements multiple times") + } } -- cgit v1.2.3 From bfa9f7465e6069b1e624126f9e14245b69e7c0a9 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Tue, 8 Nov 2022 17:27:07 +0000 Subject: Switch to using experimental trait for OpaqueTypes (backport #2783) (#2836) * Switch to using experimental trait for OpaqueTypes (#2783) This makes it more clear that the feature is experimental. Users may still override the opaqueType method for more dynamic control over when instances of a given Record are OpaqueTypes or not, but they are discouraged from doing so. (cherry picked from commit 7525dc71ccc2050d8e4a68b38f3b76920ba693fc) * Fix cloneType in RecordSpec Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Aggregate.scala | 20 ++--- .../scala/chisel3/experimental/OpaqueType.scala | 27 +++++++ .../scala/chisel3/internal/firrtl/Converter.scala | 2 +- src/test/scala/chiselTests/Direction.scala | 4 +- src/test/scala/chiselTests/RecordSpec.scala | 85 ++++++++++++++++++---- 5 files changed, 106 insertions(+), 32 deletions(-) create mode 100644 core/src/main/scala/chisel3/experimental/OpaqueType.scala diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index 16611277..f22f5e63 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -8,7 +8,7 @@ import chisel3.experimental.dataview.{isView, reifySingleData, InvalidViewExcept import scala.collection.immutable.{SeqMap, VectorMap} import scala.collection.mutable.{HashSet, LinkedHashMap} import scala.language.experimental.macros -import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, VecLiteralException} +import chisel3.experimental.{BaseModule, BundleLiteralException, ChiselEnum, EnumType, OpaqueType, VecLiteralException} import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.firrtl._ @@ -881,23 +881,15 @@ trait VecLike[T <: Data] extends IndexedSeq[T] with HasId with SourceInfoDoc { */ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptions) extends Aggregate { - /** Indicates if this Record represents an "Opaque Type" - * - * Opaque types provide a mechanism for user-defined types - * that do not impose any "boxing" overhead in the emitted FIRRTL and Verilog. - * You can think about an opaque type Record as a box around - * a single element that only exists at Chisel elaboration time. - * Put another way, if opaqueType is overridden to true, - * The Record may only contain a single element with an empty name - * and there will be no `_` in the name for that element in the emitted Verilog. - * - * @see RecordSpec in Chisel's tests for example usage and expected output - */ - def opaqueType: Boolean = false + private[chisel3] def _isOpaqueType: Boolean = this match { + case maybe: OpaqueType => maybe.opaqueType + case _ => false + } // Doing this earlier than onModuleClose allows field names to be available for prefixing the names // of hardware created when connecting to one of these elements private def setElementRefs(): Unit = { + val opaqueType = this._isOpaqueType // Since elements is a map, it is impossible for two elements to have the same // identifier; however, Namespace sanitizes identifiers to make them legal for Firrtl/Verilog // which can cause collisions diff --git a/core/src/main/scala/chisel3/experimental/OpaqueType.scala b/core/src/main/scala/chisel3/experimental/OpaqueType.scala new file mode 100644 index 00000000..e7a2a15d --- /dev/null +++ b/core/src/main/scala/chisel3/experimental/OpaqueType.scala @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental + +import chisel3._ + +/** Indicates if this Record represents an "Opaque Type" + * + * Opaque types provide a mechanism for user-defined types + * that do not impose any "boxing" overhead in the emitted FIRRTL and Verilog. + * You can think about an opaque type Record as a box around + * a single element that only exists at Chisel elaboration time. + * Put another way, if this trait is mixed into a Record, + * the Record may only contain a single element with an empty name + * and there will be no `_` in the name for that element in the emitted Verilog. + * + * @see RecordSpec in Chisel's tests for example usage and expected output + */ +trait OpaqueType { self: Record => + + /** If set to true, indicates that this Record is an OpaqueType + * + * Users can override this if they need more dynamic control over the behavior for when + * instances of this type are considered opaque + */ + def opaqueType: Boolean = true +} diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index f73e85d2..3d6e0d79 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -316,7 +316,7 @@ private[chisel3] object Converter { case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => fir.Field(getRef(elt, info).name, fir.Flip, extractType(elt, false, info)) } - if (!d.opaqueType) + if (!d._isOpaqueType) fir.BundleType(d.elements.toIndexedSeq.reverse.map { case (_, e) => eltField(e) }) else extractType(d.elements.head._2, childClearDir, info) diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala index 03755e83..642a507c 100644 --- a/src/test/scala/chiselTests/Direction.scala +++ b/src/test/scala/chiselTests/Direction.scala @@ -4,6 +4,7 @@ package chiselTests import org.scalatest._ import chisel3._ +import chisel3.experimental.OpaqueType import chisel3.stage.ChiselStage import org.scalatest.matchers.should.Matchers @@ -370,10 +371,9 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { val valid = Bool() val ready = Flipped(Bool()) } - class MyOpaqueType extends Record { + class MyOpaqueType extends Record with OpaqueType { val k = new Decoupled() val elements = SeqMap("" -> k) - override def opaqueType = elements.size == 1 override def cloneType: this.type = (new MyOpaqueType).asInstanceOf[this.type] } class MyModule extends RawModule { diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index 509edbbc..3414ec8a 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -6,7 +6,7 @@ import chisel3._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import chisel3.util.{Counter, Queue} -import chisel3.experimental.DataMirror +import chisel3.experimental.{DataMirror, OpaqueType} import scala.collection.immutable.SeqMap @@ -109,10 +109,9 @@ trait RecordSpecUtils { require(!DataMirror.checkTypeEquivalence(wire1, wire2)) } - class SingleElementRecord extends Record { + class SingleElementRecord extends Record with OpaqueType { private val underlying = UInt(8.W) val elements = SeqMap("" -> underlying) - override def opaqueType = elements.size == 1 override def cloneType: this.type = (new SingleElementRecord).asInstanceOf[this.type] def +(that: SingleElementRecord): SingleElementRecord = { @@ -132,17 +131,15 @@ trait RecordSpecUtils { out := in1 + in2 } - class InnerRecord extends Record { + class InnerRecord extends Record with OpaqueType { val k = new InnerInnerRecord val elements = SeqMap("" -> k) - override def opaqueType = elements.size == 1 override def cloneType: this.type = (new InnerRecord).asInstanceOf[this.type] } - class InnerInnerRecord extends Record { + class InnerInnerRecord extends Record with OpaqueType { val k = new SingleElementRecord val elements = SeqMap("" -> k) - override def opaqueType = elements.size == 1 override def cloneType: this.type = (new InnerInnerRecord).asInstanceOf[this.type] } @@ -163,11 +160,10 @@ trait RecordSpecUtils { io.bar.elements.head._2 := io.foo.elements.head._2 } - class NamedSingleElementRecord extends Record { + class NamedSingleElementRecord extends Record with OpaqueType { private val underlying = UInt(8.W) val elements = SeqMap("unused" -> underlying) - override def opaqueType = elements.size == 1 override def cloneType: this.type = (new NamedSingleElementRecord).asInstanceOf[this.type] } @@ -177,7 +173,7 @@ trait RecordSpecUtils { out := in } - class ErroneousOverride extends Record { + class ErroneousOverride extends Record with OpaqueType { private val underlyingA = UInt(8.W) private val underlyingB = UInt(8.W) val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) @@ -191,6 +187,44 @@ trait RecordSpecUtils { val out = IO(Output(new ErroneousOverride)) out := in } + + class NotActuallyOpaqueType extends Record with OpaqueType { + private val underlyingA = UInt(8.W) + private val underlyingB = UInt(8.W) + val elements = SeqMap("x" -> underlyingA, "y" -> underlyingB) + + override def opaqueType = false + override def cloneType: this.type = (new NotActuallyOpaqueType).asInstanceOf[this.type] + } + + class NotActuallyOpaqueTypeModule extends Module { + val in = IO(Input(new NotActuallyOpaqueType)) + val out = IO(Output(new NotActuallyOpaqueType)) + out := in + } + + // Illustrate how to dyanmically decide between OpaqueType or not + sealed trait MaybeBoxed[T <: Data] extends Record { + def underlying: T + def boxed: Boolean + } + object MaybeBoxed { + def apply[T <: Data](gen: T, boxed: Boolean): MaybeBoxed[T] = { + if (boxed) new Boxed(gen) else new Unboxed(gen) + } + } + class Boxed[T <: Data](gen: T) extends MaybeBoxed[T] { + def boxed = true + lazy val elements = SeqMap("underlying" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Boxed(gen)).asInstanceOf[this.type] + } + class Unboxed[T <: Data](gen: T) extends MaybeBoxed[T] with OpaqueType { + def boxed = false + lazy val elements = SeqMap("" -> gen.cloneType) + def underlying = elements.head._2 + override def cloneType: this.type = (new Unboxed(gen)).asInstanceOf[this.type] + } } class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { @@ -229,14 +263,14 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { e.getMessage should include("contains aliased fields named (bar,foo)") } - they should "be OpaqueType for maps with single unnamed elements" in { + they should "support OpaqueType for maps with single unnamed elements" in { val singleElementChirrtl = ChiselStage.emitChirrtl { new SingleElementRecordModule } singleElementChirrtl should include("input in1 : UInt<8>") singleElementChirrtl should include("input in2 : UInt<8>") singleElementChirrtl should include("add(in1, in2)") } - they should "work correctly for toTarget in nested opaque type Records" in { + they should "work correctly for toTarget in nested OpaqueType Records" in { var mod: NestedRecordModule = null ChiselStage.elaborate { mod = new NestedRecordModule; mod } val testStrings = Seq( @@ -250,7 +284,7 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { testStrings.foreach(x => assert(x == "~NestedRecordModule|InnerModule>io.foo")) } - they should "work correctly when connecting nested opaque type elements" in { + they should "work correctly when connecting nested OpaqueType elements" in { val nestedRecordChirrtl = ChiselStage.emitChirrtl { new NestedRecordModule } nestedRecordChirrtl should include("input in : UInt<8>") nestedRecordChirrtl should include("output out : UInt<8>") @@ -260,18 +294,39 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { nestedRecordChirrtl should include("io.bar <= io.foo") } - they should "throw an error when map contains a named element and opaqueType is overriden to true" in { + they should "throw an error when map contains a named element and OpaqueType is mixed in" in { (the[Exception] thrownBy extractCause[Exception] { ChiselStage.elaborate { new NamedSingleElementModule } }).getMessage should include("Opaque types must have exactly one element with an empty name") } - they should "throw an error when map contains more than one element and opaqueType is overriden to true" in { + they should "throw an error when map contains more than one element and OpaqueType is mixed in" in { (the[Exception] thrownBy extractCause[Exception] { ChiselStage.elaborate { new ErroneousOverrideModule } }).getMessage should include("Opaque types must have exactly one element with an empty name") } + they should "work correctly when an OpaqueType overrides the def as false" in { + val chirrtl = ChiselStage.emitChirrtl(new NotActuallyOpaqueTypeModule) + chirrtl should include("input in : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("output out : { y : UInt<8>, x : UInt<8>}") + chirrtl should include("out <= in") + } + + they should "support conditional OpaqueTypes via traits and factory methods" in { + class MyModule extends Module { + val in0 = IO(Input(MaybeBoxed(UInt(8.W), true))) + val out0 = IO(Output(MaybeBoxed(UInt(8.W), true))) + val in1 = IO(Input(MaybeBoxed(UInt(8.W), false))) + val out1 = IO(Output(MaybeBoxed(UInt(8.W), false))) + out0 := in0 + out1 := in1 + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include("input in0 : { underlying : UInt<8>}") + chirrtl should include("input in1 : UInt<8>") + } + they should "work with .toTarget" in { var m: SingleElementRecordModule = null ChiselStage.elaborate { m = new SingleElementRecordModule; m } -- cgit v1.2.3 From be4463a7756351dcab09ba3f576f5e3687fb0ebf Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 10 Nov 2022 20:03:45 +0000 Subject: Unify Chisel2 and chisel3 directionality (backport #2634) (#2837) * Unify Chisel2 and chisel3 directionality (#2634) Co-authored-by: Jack Koenig (cherry picked from commit 1aea4ef96466cbe08150d20c85c88b81e4e4f80f) # Conflicts: # core/src/main/scala/chisel3/Aggregate.scala # core/src/main/scala/chisel3/Module.scala # src/test/scala/chiselTests/Direction.scala * fix up backport * fix up backport * clean up diff * make test order like it was on master Co-authored-by: Adam Izraelevitz Co-authored-by: Megan Wachs --- core/src/main/scala/chisel3/Aggregate.scala | 42 ++++++--------- core/src/main/scala/chisel3/Module.scala | 27 +++++----- src/test/scala/chiselTests/Direction.scala | 84 +++++++++++++++++++++++------ 3 files changed, 99 insertions(+), 54 deletions(-) diff --git a/core/src/main/scala/chisel3/Aggregate.scala b/core/src/main/scala/chisel3/Aggregate.scala index f22f5e63..b6836ea7 100644 --- a/core/src/main/scala/chisel3/Aggregate.scala +++ b/core/src/main/scala/chisel3/Aggregate.scala @@ -936,37 +936,29 @@ abstract class Record(private[chisel3] implicit val compileOptions: CompileOptio } private[chisel3] override def bind(target: Binding, parentDirection: SpecifiedDirection): Unit = { - try { - _parent.foreach(_.addId(this)) - binding = target + _parent.foreach(_.addId(this)) + binding = target - val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) + val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - checkForAndReportDuplicates() + checkForAndReportDuplicates() - for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) { - if (child != sameChild) { - throwException( - s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?" - ) - } - child.bind(ChildBinding(this), resolvedDirection) + for ((child, sameChild) <- this.elementsIterator.zip(this.elementsIterator)) { + if (child != sameChild) { + throwException( + s"${this.className} does not return the same objects when calling .elements multiple times. Did you make it a def by mistake?" + ) } + child.bind(ChildBinding(this), resolvedDirection) + } - // Check that children obey the directionality rules. - val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty - direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { - case Some(dir) => dir - case None => - val childWithDirections = getElements.zip(getElements.map(_.direction)) - throw MixedDirectionAggregateException( - s"Aggregate '$this' can't have elements that are both directioned and undirectioned: $childWithDirections" - ) - } - } catch { // nasty compatibility mode shim, where anything flies - case e: MixedDirectionAggregateException if !compileOptions.dontAssumeDirectionality => + // Check that children obey the directionality rules. + val childDirections = elementsIterator.map(_.direction).toSet - ActualDirection.Empty + direction = ActualDirection.fromChildren(childDirections, resolvedDirection) match { + case Some(dir) => dir + case None => val resolvedDirection = SpecifiedDirection.fromParent(parentDirection, specifiedDirection) - direction = resolvedDirection match { + resolvedDirection match { case SpecifiedDirection.Unspecified => ActualDirection.Bidirectional(ActualDirection.Default) case SpecifiedDirection.Flip => ActualDirection.Bidirectional(ActualDirection.Flipped) case _ => ActualDirection.Bidirectional(ActualDirection.Default) diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 9315a44b..48c33083 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -692,33 +692,34 @@ package experimental { */ protected def _bindIoInPlace(iodef: Data): Unit = { // Compatibility code: Chisel2 did not require explicit direction on nodes - // (unspecified treated as output, and flip on nothing was input). - // This sets assigns the explicit directions required by newer semantics on - // Bundles defined in compatibility mode. + // (unspecified treated as output, and flip on nothing was input). + // However, we are going to go back to Chisel2 semantics, so we need to make it work + // even for chisel3 code. + // This assigns the explicit directions required by both semantics on all Bundles. // This recursively walks the tree, and assigns directions if no explicit - // direction given by upper-levels (override Input / Output) AND element is - // directly inside a compatibility Bundle determined by compile options. - def assignCompatDir(data: Data, insideCompat: Boolean): Unit = { + // direction given by upper-levels (override Input / Output) + def assignCompatDir(data: Data): Unit = { data match { - case data: Element if insideCompat => data._assignCompatibilityExplicitDirection - case data: Element => // Not inside a compatibility Bundle, nothing to be done + case data: Element => data._assignCompatibilityExplicitDirection case data: Aggregate => data.specifiedDirection match { // Recurse into children to ensure explicit direction set somewhere case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => data match { case record: Record => - val compatRecord = !record.compileOptions.dontAssumeDirectionality - record.elementsIterator.foreach(assignCompatDir(_, compatRecord)) + record.elementsIterator.foreach(assignCompatDir(_)) case vec: Vec[_] => - vec.elementsIterator.foreach(assignCompatDir(_, insideCompat)) + vec.elementsIterator.foreach(assignCompatDir(_)) } - case SpecifiedDirection.Input | SpecifiedDirection.Output => // forced assign, nothing to do + case SpecifiedDirection.Input | SpecifiedDirection.Output => + // forced assign, nothing to do + // Note this is because Input and Output recurse down their types to align all fields to that SpecifiedDirection + // Thus, no implicit assigment is necessary. } } } - assignCompatDir(iodef, false) + assignCompatDir(iodef) iodef.bind(PortBinding(this)) _ports += iodef diff --git a/src/test/scala/chiselTests/Direction.scala b/src/test/scala/chiselTests/Direction.scala index 642a507c..ddbd99d2 100644 --- a/src/test/scala/chiselTests/Direction.scala +++ b/src/test/scala/chiselTests/Direction.scala @@ -86,15 +86,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { }) } - property("Empty Vecs with no direction on the sample_element *should* cause direction errors") { - an[Exception] should be thrownBy extractCause[Exception] { - ChiselStage.elaborate(new Module { - val io = IO(new Bundle { - val foo = Input(UInt(8.W)) - val x = Vec(0, UInt(8.W)) - }) + property( + "Empty Vecs with no direction on the sample_element should not cause direction errors, as Chisel and chisel3 directions are merged" + ) { + ChiselStage.elaborate(new Module { + val io = IO(new Bundle { + val foo = Input(UInt(8.W)) + val x = Vec(0, UInt(8.W)) }) - } + }) } property("Empty Bundles should not cause direction errors") { @@ -120,15 +120,15 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { }) } - property("Explicitly directioned but empty Bundles should cause direction errors") { - an[Exception] should be thrownBy extractCause[Exception] { - ChiselStage.elaborate(new Module { - val io = IO(new Bundle { - val foo = UInt(8.W) - val x = Input(new Bundle {}) - }) + property( + "Explicitly directioned but empty Bundles should not cause direction errors because Chisel and chisel3 directionality are merged" + ) { + ChiselStage.elaborate(new Module { + val io = IO(new Bundle { + val foo = UInt(8.W) + val x = Input(new Bundle {}) }) - } + }) } import chisel3.experimental.{DataMirror, Direction} @@ -330,6 +330,58 @@ class DirectionSpec extends ChiselPropSpec with Matchers with Utils { } } } + property("Can now describe a Decoupled bundle using Flipped, not Input/Output in chisel3") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class MyModule extends RawModule { + val incoming = IO(Flipped(new Decoupled)) + val outgoing = IO(new Decoupled) + + outgoing <> incoming + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + // Check that emitted directions are correct. + assert(emitted.contains("input incoming : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}")) + assert(emitted.contains("output outgoing : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}")) + assert(emitted.contains("outgoing <= incoming")) + } + property("Can now mix Input/Output and Flipped within the same bundle") { + class Decoupled extends Bundle { + val bits = UInt(3.W) + val valid = Bool() + val ready = Flipped(Bool()) + } + class DecoupledAndMonitor extends Bundle { + val producer = new Decoupled() + val consumer = Flipped(new Decoupled()) + val monitor = Input(new Decoupled()) // Same as Flipped(stripFlipsIn(..)) + val driver = Output(new Decoupled()) // Same as stripFlipsIn(..) + } + class MyModule extends RawModule { + val io = IO(Flipped(new DecoupledAndMonitor())) + io.consumer <> io.producer + io.monitor.bits := io.driver.bits + io.monitor.valid := io.driver.valid + io.monitor.ready := io.driver.ready + } + + val emitted: String = ChiselStage.emitChirrtl(new MyModule) + + assert( + emitted.contains( + "input io : { producer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip consumer : { bits : UInt<3>, valid : UInt<1>, flip ready : UInt<1>}, flip monitor : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}, driver : { bits : UInt<3>, valid : UInt<1>, ready : UInt<1>}}" + ) + ) + assert(emitted.contains("io.consumer <= io.producer")) + assert(emitted.contains("io.monitor.bits <= io.driver.bits")) + assert(emitted.contains("io.monitor.valid <= io.driver.valid")) + assert(emitted.contains("io.monitor.ready <= io.driver.ready")) + } property("Bugfix: marking Vec fields with mixed directionality as Output/Input clears inner directions") { class Decoupled extends Bundle { val bits = UInt(3.W) -- cgit v1.2.3 From 17c04998d8cd5eeb4eff9506465fd2d6892793d2 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 10 Nov 2022 21:11:55 +0000 Subject: Add unit tests and fix for #2794 , add unit tests for #2773 (backport #2792) (#2834) * Fixup and unit tests for D/I of IOs without explicit Input/Output (#2792) (cherry picked from commit f24a624863f0fc460fd862238688ea8612ffdf5e) # Conflicts: # core/src/main/scala/chisel3/Module.scala * Resolve backport conflicts Co-authored-by: Megan Wachs Co-authored-by: Jack Koenig --- core/src/main/scala/chisel3/Module.scala | 67 ++--- .../CompatibilityInteroperabilitySpec.scala | 284 +++++++++++++++++++++ 2 files changed, 322 insertions(+), 29 deletions(-) diff --git a/core/src/main/scala/chisel3/Module.scala b/core/src/main/scala/chisel3/Module.scala index 48c33083..e5c2a848 100644 --- a/core/src/main/scala/chisel3/Module.scala +++ b/core/src/main/scala/chisel3/Module.scala @@ -106,6 +106,38 @@ object Module extends SourceInfoDoc { module } + + /** Assign directionality on any IOs that are still Unspecified/Flipped + * + * Chisel2 did not require explicit direction on nodes + * (unspecified treated as output, and flip on nothing was input). + * As of 3.6, chisel3 is now also using these semantics, so we need to make it work + * even for chisel3 code. + * This assigns the explicit directions required by both semantics on all Bundles. + * This recursively walks the tree, and assigns directions if no explicit + * direction given by upper-levels (override Input / Output) + */ + private[chisel3] def assignCompatDir(data: Data): Unit = { + data match { + case data: Element => data._assignCompatibilityExplicitDirection + case data: Aggregate => + data.specifiedDirection match { + // Recurse into children to ensure explicit direction set somewhere + case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => + data match { + case record: Record => + record.elementsIterator.foreach(assignCompatDir(_)) + case vec: Vec[_] => + vec.elementsIterator.foreach(assignCompatDir(_)) + assignCompatDir(vec.sample_element) // This is used in fromChildren computation + } + case SpecifiedDirection.Input | SpecifiedDirection.Output => + // forced assign, nothing to do + // The .bind algorithm will automatically assign the direction here. + // Thus, no implicit assignment is necessary. + } + } + } } /** Abstract base class for Modules, which behave much like Verilog modules. @@ -382,6 +414,10 @@ package internal { new ClonePorts(proto.getChiselPorts :+ ("io" -> b._io.get): _*) case _ => new ClonePorts(proto.getChiselPorts: _*) } + // getChiselPorts (nor cloneTypeFull in general) + // does not recursively copy the right specifiedDirection, + // still need to fix it up here. + Module.assignCompatDir(clonePorts) clonePorts.bind(PortBinding(cloneParent)) clonePorts.setAllParents(Some(cloneParent)) cloneParent._portsRecord = clonePorts @@ -691,35 +727,8 @@ package experimental { * io, then do operations on it. This binds a Chisel type in-place (mutably) as an IO. */ protected def _bindIoInPlace(iodef: Data): Unit = { - // Compatibility code: Chisel2 did not require explicit direction on nodes - // (unspecified treated as output, and flip on nothing was input). - // However, we are going to go back to Chisel2 semantics, so we need to make it work - // even for chisel3 code. - // This assigns the explicit directions required by both semantics on all Bundles. - // This recursively walks the tree, and assigns directions if no explicit - // direction given by upper-levels (override Input / Output) - def assignCompatDir(data: Data): Unit = { - data match { - case data: Element => data._assignCompatibilityExplicitDirection - case data: Aggregate => - data.specifiedDirection match { - // Recurse into children to ensure explicit direction set somewhere - case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => - data match { - case record: Record => - record.elementsIterator.foreach(assignCompatDir(_)) - case vec: Vec[_] => - vec.elementsIterator.foreach(assignCompatDir(_)) - } - case SpecifiedDirection.Input | SpecifiedDirection.Output => - // forced assign, nothing to do - // Note this is because Input and Output recurse down their types to align all fields to that SpecifiedDirection - // Thus, no implicit assigment is necessary. - } - } - } - - assignCompatDir(iodef) + // Assign any signals (Chisel or chisel3) with Unspecified/Flipped directions to Output/Input + Module.assignCompatDir(iodef) iodef.bind(PortBinding(this)) _ports += iodef diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index e2fb2179..eb5aaaba 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -392,6 +392,290 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { compile(new Top(false)) } + "A Chisel.Bundle with only unspecified directions" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirUnspecifiedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirUnspecifiedBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A Chisel.Bundle with mixed Specified and Unspecified directions" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirMixedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + val explicit = Output(Bool()) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirMixedBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A Chisel.Bundle with only unspecified vec direction" should "work with D/I" in { + + object Compat { + import Chisel._ + import chisel3.experimental.hierarchy.{instantiable, public} + + class CompatBiDirUnspecifiedVecBundle extends Bundle { + val out = Vec(3, Bool()) + val in = Flipped(Vec(3, Bool())) + } + + @instantiable + class CompatModule extends Module { + @public val io = IO(new CompatBiDirUnspecifiedVecBundle) + } + } + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.Instance + class Example extends Module { + val mod = Module(new Compat.CompatModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified directions" should "work with D/I" in { + + // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirUnspecifiedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirUnspecifiedBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with mixed Specified and Unspecified directions" should "work with D/I" in { + + // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirMixedBundle extends Bundle { + val out = Bool() + val in = Flipped(Bool()) + val explicit = Output(Bool()) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirMixedBundle) + io <> DontCare + } + class Example extends Module { + val mod = Module(new MyModule) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified vec direction" should "work with D/I" in { + + // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class BiDirUnspecifiedVecBundle extends Bundle { + val out = Vec(3, Bool()) + val in = Flipped(Vec(3, Bool())) + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new BiDirUnspecifiedVecBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + mod.io.in <> DontCare + val inst = Instance(mod.toDefinition) + inst.io.in <> mod.io.out + mod.io.in <> inst.io.out + } + } + compile(new Chisel3.Example) + } + + "A chisel3.Bundle with only unspecified vec direction within an unspecified direction parent Bundle" should "work with D/I" in { + + // This test is NOT expected to work in 3.5.x, it should throw an error in the IO construction. + + object Chisel3 { + import chisel3._ + import chisel3.experimental.hierarchy.{instantiable, public, Instance} + + class UnspecifiedVecBundle extends Bundle { + val vec = Vec(3, Bool()) + } + + class UnspecifiedParentBundle extends Bundle { + val out = new UnspecifiedVecBundle + } + + @instantiable + class MyModule extends Module { + @public val io = IO(new UnspecifiedParentBundle) + io <> DontCare + } + + class Example extends Module { + val mod = Module(new MyModule()) + + val wire = Wire(new UnspecifiedParentBundle) + wire.out.vec <> mod.io.out.vec + val inst = Instance(mod.toDefinition) + wire.out.vec <> inst.io.out.vec + + } + } + compile(new Chisel3.Example) + } + + "A undirectioned Chisel.Bundle used in a MixedVec " should "bulk connect in import chisel3._ code correctly" in { + + // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. + + object Compat { + + import Chisel._ + import chisel3.util.MixedVec + + class ChiselModule extends Module { + val io = IO(new Bundle { + val out = MixedVec(Seq.fill(3) { Bool() }) + val in = Flipped(MixedVec(Seq.fill(3) { Bool() })) + }) + io.out := RegNext(io.in) + } + + } + object Chisel3 { + import chisel3._ + + class Chisel3Module extends Compat.ChiselModule + + class Example extends Module { + val oldMod = Module(new Compat.ChiselModule) + val newMod = Module(new Chisel3Module) + + oldMod.io.in <> DontCare + newMod.io.in <> DontCare + + } + } + compile(new Chisel3.Example) + } + + "A undirectioned Chisel.Bundle with Records with undirectioned and directioned fields " should "work" in { + + // This test should fail on 3.5.x + + object Compat { + + import Chisel._ + + class ChiselModule(gen: () => Data) extends Module { + val io = IO(new Bundle { + val mixed = new Chisel3.MyRecord(gen) + }) + } + + } + object Chisel3 { + import chisel3._ + import scala.collection.immutable.SeqMap + + class MyRecord(gen: () => Data) extends Record with chisel3.experimental.AutoCloneType { + val elements = SeqMap("genDirectioned" -> Output(gen()), "genUndirectioned" -> gen()) + } + + class Example extends Module { + val newMod = Module(new Compat.ChiselModule(() => Bool())) + } + } + compile(new Chisel3.Example) + } + "A BlackBox with Chisel._ fields in its IO" should "bulk connect in import chisel3._ code correctly" in { object Compat { import Chisel._ -- cgit v1.2.3 From c51fcfea32b6c73e623657442460fb782ff0733b Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Thu, 10 Nov 2022 13:41:00 -0800 Subject: Warn on S-interpolator usage for assert, assume and printf (backport #2751) (#2757) * Add internal methods to maintain binary compatibility Co-authored-by: Megan Wachs Co-authored-by: Jack Koenig --- build.sbt | 1 - core/src/main/scala/chisel3/Printf.scala | 52 ++++++++++++++++++-- .../main/scala/chisel3/VerificationStatement.scala | 56 ++++++++++++++++++++-- docs/src/explanations/printing.md | 14 +++++- src/test/scala/chiselTests/IntervalSpec.scala | 7 +-- src/test/scala/chiselTests/Vec.scala | 4 +- src/test/scala/chiselTests/VecLiteralSpec.scala | 4 +- 7 files changed, 120 insertions(+), 18 deletions(-) diff --git a/build.sbt b/build.sbt index 88374949..a51d1d2e 100644 --- a/build.sbt +++ b/build.sbt @@ -297,7 +297,6 @@ lazy val docs = project // new documentation project .settings(commonSettings) .settings( scalacOptions ++= Seq( - "-Xfatal-warnings", "-language:reflectiveCalls", "-language:implicitConversions" ), diff --git a/core/src/main/scala/chisel3/Printf.scala b/core/src/main/scala/chisel3/Printf.scala index 9410a409..a7338072 100644 --- a/core/src/main/scala/chisel3/Printf.scala +++ b/core/src/main/scala/chisel3/Printf.scala @@ -2,10 +2,11 @@ package chisel3 -import scala.language.experimental.macros import chisel3.internal._ import chisel3.internal.Builder.pushCommand import chisel3.internal.sourceinfo.SourceInfo +import scala.language.experimental.macros +import scala.reflect.macros.blackbox /** Prints a message in simulation * @@ -76,7 +77,44 @@ object printf { * @param data format string varargs containing data to print */ def apply(fmt: String, data: Bits*)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = - apply(Printable.pack(fmt, data: _*)) + macro _applyMacroWithInterpolatorCheck + + def _applyMacroWithInterpolatorCheck( + c: blackbox.Context + )(fmt: c.Tree, + data: c.Tree* + )(sourceInfo: c.Tree, + compileOptions: c.Tree + ): c.Tree = { + import c.universe._ + fmt match { + case q"scala.StringContext.apply(..$_).s(..$_)" => + c.warning( + c.enclosingPosition, + "The s-interpolator prints the Scala .toString of Data objects rather than the value " + + "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " + + "an elaboration time print, use println." + ) + case _ => + } + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("printfWithReset")) + q"$apply_impl_do(_root_.chisel3.Printable.pack($fmt, ..$data))($sourceInfo, $compileOptions)" + } + + // Private internal methods that serve to maintain binary + // compatibility after interpolator check updates + @deprecated("This Printf.apply method has been deprecated and will be removed in Chisel 3.6") + def apply(fmt: String, sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = + apply(fmt, Nil, sourceInfo, compileOptions) + + @deprecated("This Printf.apply method has been deprecated and will be removed in Chisel 3.6") + def apply( + fmt: String, + data: Seq[Bits], + sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Printf = + apply(Printable.pack(fmt, data: _*))(sourceInfo, compileOptions) /** Prints a message in simulation * @@ -92,7 +130,15 @@ object printf { * @see [[Printable]] documentation * @param pable [[Printable]] to print */ - def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = { + def apply(pable: Printable)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Printf = + printfWithReset(pable)(sourceInfo, compileOptions) + + private[chisel3] def printfWithReset( + pable: Printable + )( + implicit sourceInfo: SourceInfo, + compileOptions: CompileOptions + ): Printf = { var printfId: Printf = null when(!Module.reset.asBool) { printfId = printfWithoutReset(pable) diff --git a/core/src/main/scala/chisel3/VerificationStatement.scala b/core/src/main/scala/chisel3/VerificationStatement.scala index 10cece60..a0040d78 100644 --- a/core/src/main/scala/chisel3/VerificationStatement.scala +++ b/core/src/main/scala/chisel3/VerificationStatement.scala @@ -48,7 +48,7 @@ object assert extends VerifPrintMacrosDoc { )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions - ): Assert = macro _applyMacroWithStringMessage + ): Assert = macro _applyMacroWithInterpolatorCheck /** 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. @@ -79,6 +79,32 @@ object assert extends VerifPrintMacrosDoc { def apply(cond: Bool)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Assert = macro _applyMacroWithNoMessage + import VerificationStatement._ + + /** @group VerifPrintMacros */ + def _applyMacroWithInterpolatorCheck( + c: blackbox.Context + )(cond: c.Tree, + message: c.Tree, + data: c.Tree* + )(sourceInfo: c.Tree, + compileOptions: c.Tree + ): c.Tree = { + import c.universe._ + message match { + case q"scala.StringContext.apply(..$_).s(..$_)" => + c.warning( + c.enclosingPosition, + "The s-interpolator prints the Scala .toString of Data objects rather than the value " + + "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " + + "an elaboration time check, call assert with a Boolean condition." + ) + case _ => + } + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)" + } + /** An elaboration-time assertion. Calls the built-in Scala assert function. */ def apply(cond: Boolean, message: => String): Unit = Predef.assert(cond, message) @@ -88,8 +114,6 @@ object assert extends VerifPrintMacrosDoc { /** Named class for assertions. */ final class Assert private[chisel3] () extends VerificationStatement - import VerificationStatement._ - /** @group VerifPrintMacros */ @deprecated( "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", @@ -215,7 +239,7 @@ object assume extends VerifPrintMacrosDoc { )( implicit sourceInfo: SourceInfo, compileOptions: CompileOptions - ): Assume = macro _applyMacroWithStringMessage + ): Assume = macro _applyMacroWithInterpolatorCheck /** Assumes a condition to be valid in the circuit at all times. * Acts like an assertion in simulation and imposes a declarative @@ -256,6 +280,30 @@ object assume extends VerifPrintMacrosDoc { import VerificationStatement._ + /** @group VerifPrintMacros */ + def _applyMacroWithInterpolatorCheck( + c: blackbox.Context + )(cond: c.Tree, + message: c.Tree, + data: c.Tree* + )(sourceInfo: c.Tree, + compileOptions: c.Tree + ): c.Tree = { + import c.universe._ + message match { + case q"scala.StringContext.apply(..$_).s(..$_)" => + c.warning( + c.enclosingPosition, + "The s-interpolator prints the Scala .toString of Data objects rather than the value " + + "of the hardware wire during simulation. Use the cf-interpolator instead. If you want " + + "an elaboration time check, call assert with a Boolean condition." + ) + case _ => + } + val apply_impl_do = symbolOf[this.type].asClass.module.info.member(TermName("_applyWithSourceLinePrintable")) + q"$apply_impl_do($cond, ${getLine(c)}, _root_.scala.Some(_root_.chisel3.Printable.pack($message, ..$data)))($sourceInfo, $compileOptions)" + } + /** @group VerifPrintMacros */ @deprecated( "This method has been deprecated in favor of _applyMacroWithStringMessage. Please use the same.", diff --git a/docs/src/explanations/printing.md b/docs/src/explanations/printing.md index 71a6f5cf..80e9ec70 100644 --- a/docs/src/explanations/printing.md +++ b/docs/src/explanations/printing.md @@ -13,11 +13,23 @@ Chisel provides the `printf` function for debugging purposes. It comes in two fl ### Scala-style -Chisel also supports printf in a style similar to [Scala's String Interpolation](http://docs.scala-lang.org/overviews/core/string-interpolation.html). Chisel provides a custom string interpolator `cf` which follows C-style format specifiers (see section [C-style](#c-style) below). Here's a few examples of using the `cf` interpolator: +Chisel also supports printf in a style similar to [Scala's String Interpolation](http://docs.scala-lang.org/overviews/core/string-interpolation.html). Chisel provides a custom string interpolator `cf` which follows C-style format specifiers (see section [C-style](#c-style) below). + +Note that the Scala s-interpolator is not supported in Chisel constructs and will issue a compile-time warning: ```scala mdoc:invisible import chisel3._ ``` + +```scala mdoc:warn +class MyModule extends Module { + val in = IO(Input(UInt(8.W))) + printf(s"in = $in\n") +} +``` + +Instead, use Chisel's `cf` interpolator as in the following examples: + ```scala mdoc:compile-only val myUInt = 33.U printf(cf"myUInt = $myUInt") // myUInt = 33 diff --git a/src/test/scala/chiselTests/IntervalSpec.scala b/src/test/scala/chiselTests/IntervalSpec.scala index c0338f6d..a2d36579 100644 --- a/src/test/scala/chiselTests/IntervalSpec.scala +++ b/src/test/scala/chiselTests/IntervalSpec.scala @@ -211,7 +211,7 @@ class ClipSqueezeWrapDemo( val wrapped = counter.wrap(0.U.asInterval(targetRange)) when(counter === startValue) { - printf(s"Target range is $range\n") + printf(cf"Target range is $range\n") printf("value clip squeeze wrap\n") } @@ -245,10 +245,7 @@ class SqueezeFunctionalityTester(range: IntervalRange, startNum: BigDecimal, end squeezeTemplate := toSqueeze.squeeze(squeezeInterval) printf( - s"SqueezeTest %d %d.squeeze($range) => %d\n", - counter, - toSqueeze.asSInt(), - squeezeTemplate.asSInt() + cf"SqueezeTest $counter%d ${toSqueeze.asSInt()}%d.squeeze($range) => ${squeezeTemplate.asSInt()}%d\n" ) } diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 4a871890..e46774dd 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -111,7 +111,7 @@ class FillTester(n: Int, value: Int) extends BasicTester { val x = VecInit(Array.fill(n)(value.U)) val u = VecInit.fill(n)(value.U) - assert(x.asUInt() === u.asUInt(), s"Expected Vec to be filled like $x, instead VecInit.fill created $u") + assert(x.asUInt() === u.asUInt(), cf"Expected Vec to be filled like $x, instead VecInit.fill created $u") stop() } @@ -235,7 +235,7 @@ class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester { val testVec = VecInit.iterate(start.U, len)(f) assert( controlVec.asUInt() === testVec.asUInt(), - s"Expected Vec to be filled like $controlVec, instead creaeted $testVec\n" + cf"Expected Vec to be filled like $controlVec, instead created $testVec\n" ) stop() } diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index fa97a8c8..e2eb791d 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -205,7 +205,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { assertTesterPasses { new BasicTester { - chisel3.assert(outsideVecLit(0) === 0xdd.U, s"v(0)") + chisel3.assert(outsideVecLit(0) === 0xdd.U, "v(0)") stop() } } @@ -216,7 +216,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { assertTesterPasses { new BasicTester { - chisel3.assert(outsideVecLit(0) === 0xdd.U, s"v(0)") + chisel3.assert(outsideVecLit(0) === 0xdd.U, "v(0)") chisel3.assert(outsideVecLit(1) === 0xcc.U) chisel3.assert(outsideVecLit(2) === 0xbb.U) chisel3.assert(outsideVecLit(3) === 0xaa.U) -- cgit v1.2.3 From c70e5bebaeaf5b0bd54ee84dc644ddd6973a1b86 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Thu, 10 Nov 2022 23:55:25 +0000 Subject: Update CompatibilityInteroperabilitySpec.scala (#2840) (#2842) (cherry picked from commit aa596b98d5e030294041f2e90eee1f78ca1e401b) Co-authored-by: Megan Wachs --- .../chiselTests/CompatibilityInteroperabilitySpec.scala | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index eb5aaaba..d388c093 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -488,8 +488,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A chisel3.Bundle with only unspecified directions" should "work with D/I" in { - // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. - object Chisel3 { import chisel3._ import chisel3.experimental.hierarchy.{instantiable, public, Instance} @@ -518,8 +516,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A chisel3.Bundle with mixed Specified and Unspecified directions" should "work with D/I" in { - // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. - object Chisel3 { import chisel3._ import chisel3.experimental.hierarchy.{instantiable, public, Instance} @@ -548,8 +544,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A chisel3.Bundle with only unspecified vec direction" should "work with D/I" in { - // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction - object Chisel3 { import chisel3._ import chisel3.experimental.hierarchy.{instantiable, public, Instance} @@ -578,8 +572,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A chisel3.Bundle with only unspecified vec direction within an unspecified direction parent Bundle" should "work with D/I" in { - // This test is NOT expected to work in 3.5.x, it should throw an error in the IO construction. - object Chisel3 { import chisel3._ import chisel3.experimental.hierarchy.{instantiable, public, Instance} @@ -613,8 +605,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A undirectioned Chisel.Bundle used in a MixedVec " should "bulk connect in import chisel3._ code correctly" in { - // This test is NOT expected to work on 3.5.x, it should throw an error in the IO construction. - object Compat { import Chisel._ @@ -648,8 +638,6 @@ class CompatibilityInteroperabilitySpec extends ChiselFlatSpec { "A undirectioned Chisel.Bundle with Records with undirectioned and directioned fields " should "work" in { - // This test should fail on 3.5.x - object Compat { import Chisel._ -- cgit v1.2.3 From c8046636a25474be4c547c6fe9c6d742ea7b1d13 Mon Sep 17 00:00:00 2001 From: mergify[bot] Date: Fri, 11 Nov 2022 01:40:53 +0000 Subject: Change RawModule._commands to a VectorBuilder (backport #2839) (#2841) * Change RawModule._commands to a VectorBuilder (#2839) * Change RawModule._commands to a VectorBuilder Use the resulting Vector to build the underlying Component's commands and then use those instead of copying the original ArrayBuffer when iterating on commands. Previously, the Component was using a List to hold the commands which is particularly memory inefficient, especially for large modules. * Optimize Converter's handling of Seq[Command] It previously converted the Commands to a List (which, while not captured in the type system, they were already a List) and then used head and tail iteration. This is less efficient with the new underlying Vector implementation. (cherry picked from commit 48a1ef0a3872c6b68d46145764d977926923a270) * Waive false binary compatibility failures Co-authored-by: Jack Koenig Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- build.sbt | 6 ++ core/src/main/scala/chisel3/RawModule.scala | 14 ++- .../scala/chisel3/internal/firrtl/Converter.scala | 116 ++++++++++++--------- 3 files changed, 83 insertions(+), 53 deletions(-) diff --git a/build.sbt b/build.sbt index a51d1d2e..80fa8240 100644 --- a/build.sbt +++ b/build.sbt @@ -209,6 +209,12 @@ lazy val core = (project in file("core")). ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.MemBase.setRef"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.VerificationStatement.setRef"), ProblemFilters.exclude[DirectMissingMethodProblem]("chisel3.experimental.BaseModule.setRef"), + // Methods in inner class defined within package private object + ProblemFilters.exclude[IncompatibleResultTypeProblem]("chisel3.internal.firrtl.Converter#WhenFrame.outer"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("chisel3.internal.firrtl.Converter#WhenFrame.copy"), + ProblemFilters.exclude[IncompatibleResultTypeProblem]("chisel3.internal.firrtl.Converter#WhenFrame.copy$default$2"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("chisel3.internal.firrtl.Converter#WhenFrame.this"), + ProblemFilters.exclude[IncompatibleMethTypeProblem]("chisel3.internal.firrtl.Converter#WhenFrame.apply"), ) ). settings( diff --git a/core/src/main/scala/chisel3/RawModule.scala b/core/src/main/scala/chisel3/RawModule.scala index f2ce4c70..9668313a 100644 --- a/core/src/main/scala/chisel3/RawModule.scala +++ b/core/src/main/scala/chisel3/RawModule.scala @@ -2,7 +2,6 @@ package chisel3 -import scala.collection.mutable.{ArrayBuffer, HashMap} import scala.util.Try import scala.language.experimental.macros import scala.annotation.nowarn @@ -13,6 +12,7 @@ import chisel3.internal.Builder._ import chisel3.internal.firrtl._ import chisel3.internal.sourceinfo.UnlocatableSourceInfo import _root_.firrtl.annotations.{IsModule, ModuleTarget} +import scala.collection.immutable.VectorBuilder /** Abstract base class for Modules that contain Chisel RTL. * This abstract base class is a user-defined module which does not include implicit clock and reset and supports @@ -23,14 +23,18 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) extends // // RTL construction internals // - private val _commands = ArrayBuffer[Command]() + // Perhaps this should be an ArrayBuffer (or ArrayBuilder), but DefModule is public and has Seq[Command] + // so our best option is to share a single Seq datastructure with that + private val _commands = new VectorBuilder[Command]() private[chisel3] def addCommand(c: Command) { require(!_closed, "Can't write to module after module close") _commands += c } - protected def getCommands = { + protected def getCommands: Seq[Command] = { require(_closed, "Can't get commands before module close") - _commands.toSeq + // Unsafe cast but we know that any RawModule uses a DefModule + // _component is defined as a var on BaseModule and we cannot override mutable vars + _component.get.asInstanceOf[DefModule].commands } // @@ -153,7 +157,7 @@ abstract class RawModule(implicit moduleCompileOptions: CompileOptions) extends Seq() } } - val component = DefModule(this, name, firrtlPorts, invalidateCommands ++ getCommands) + val component = DefModule(this, name, firrtlPorts, invalidateCommands ++: _commands.result()) _component = Some(component) _component } diff --git a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala index 3d6e0d79..ca39608f 100644 --- a/core/src/main/scala/chisel3/internal/firrtl/Converter.scala +++ b/core/src/main/scala/chisel3/internal/firrtl/Converter.scala @@ -8,7 +8,7 @@ import firrtl.{ir => fir} import chisel3.internal.{castToInt, throwException, HasId} import scala.annotation.{nowarn, tailrec} -import scala.collection.immutable.Queue +import scala.collection.immutable.{Queue, VectorBuilder} import scala.collection.immutable.LazyList // Needed for 2.12 alias @nowarn("msg=class Port") // delete when Port becomes private @@ -215,7 +215,7 @@ private[chisel3] object Converter { * @param alt Indicates if currently processing commands in the alternate (else) of the when scope */ // TODO we should probably have a different structure in the IR to close elses - private case class WhenFrame(when: fir.Conditionally, outer: Queue[fir.Statement], alt: Boolean) + private case class WhenFrame(when: fir.Conditionally, outer: VectorBuilder[fir.Statement], alt: Boolean) /** Convert Chisel IR Commands into FIRRTL Statements * @@ -226,52 +226,72 @@ private[chisel3] object Converter { * @return FIRRTL Statement that is equivalent to the input cmds */ def convert(cmds: Seq[Command], ctx: Component): fir.Statement = { - @tailrec - def rec(acc: Queue[fir.Statement], scope: List[WhenFrame])(cmds: Seq[Command]): Seq[fir.Statement] = { - if (cmds.isEmpty) { - assert(scope.isEmpty) - acc - } else - convertSimpleCommand(cmds.head, ctx) match { - // Most Commands map 1:1 - case Some(stmt) => - rec(acc :+ stmt, scope)(cmds.tail) - // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames - // Please see WhenFrame for more details - case None => - cmds.head match { - case WhenBegin(info, pred) => - val when = fir.Conditionally(convert(info), convert(pred, ctx, info), fir.EmptyStmt, fir.EmptyStmt) - val frame = WhenFrame(when, acc, false) - rec(Queue.empty, frame +: scope)(cmds.tail) - case WhenEnd(info, depth, _) => - val frame = scope.head - val when = - if (frame.alt) frame.when.copy(alt = fir.Block(acc)) - else frame.when.copy(conseq = fir.Block(acc)) - // Check if this when has an else - cmds.tail.headOption match { - case Some(AltBegin(_)) => - assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when - rec(Queue.empty, frame.copy(when = when, alt = true) +: scope.tail)(cmds.drop(2)) - case _ => // Not followed by otherwise - // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd - // If we're nested we need to add more WhenEnds to ensure each When scope gets - // properly closed - val cmdsx = if (depth > 0) WhenEnd(info, depth - 1, false) +: cmds.tail else cmds.tail - rec(frame.outer :+ when, scope.tail)(cmdsx) - } - case OtherwiseEnd(info, depth) => - val frame = scope.head - val when = frame.when.copy(alt = fir.Block(acc)) - // TODO For some reason depth == 1 indicates the last closing otherwise whereas - // depth == 0 indicates last closing when - val cmdsx = if (depth > 1) OtherwiseEnd(info, depth - 1) +: cmds.tail else cmds.tail - rec(scope.head.outer :+ when, scope.tail)(cmdsx) - } - } + var stmts = new VectorBuilder[fir.Statement]() + var scope: List[WhenFrame] = Nil + var cmdsIt = cmds.iterator.buffered + // Extra var because sometimes we want to push a Command to the head of cmdsIt + // This is more efficient than changing the iterator + var nextCmd: Command = null + while (nextCmd != null || cmdsIt.hasNext) { + val cmd = if (nextCmd != null) { + val _nextCmd = nextCmd + nextCmd = null + _nextCmd + } else { + cmdsIt.next() + } + convertSimpleCommand(cmd, ctx) match { + // Most Commands map 1:1 + case Some(stmt) => + stmts += stmt + // When scoping logic does not map 1:1 and requires pushing/popping WhenFrames + // Please see WhenFrame for more details + case None => + cmd match { + case WhenBegin(info, pred) => + val when = fir.Conditionally(convert(info), convert(pred, ctx, info), fir.EmptyStmt, fir.EmptyStmt) + val frame = WhenFrame(when, stmts, false) + stmts = new VectorBuilder[fir.Statement] + scope = frame :: scope + case WhenEnd(info, depth, _) => + val frame = scope.head + val when = + if (frame.alt) frame.when.copy(alt = fir.Block(stmts.result())) + else frame.when.copy(conseq = fir.Block(stmts.result())) + // Check if this when has an else + cmdsIt.headOption match { + case Some(AltBegin(_)) => + assert(!frame.alt, "Internal Error! Unexpected when structure!") // Only 1 else per when + scope = frame.copy(when = when, alt = true) :: scope.tail + cmdsIt.next() // Consume the AltBegin + stmts = new VectorBuilder[fir.Statement] + case _ => // Not followed by otherwise + // If depth > 0 then we need to close multiple When scopes so we add a new WhenEnd + // If we're nested we need to add more WhenEnds to ensure each When scope gets + // properly closed + if (depth > 0) { + nextCmd = WhenEnd(info, depth - 1, false) + } + stmts = frame.outer + stmts += when + scope = scope.tail + } + case OtherwiseEnd(info, depth) => + val frame = scope.head + val when = frame.when.copy(alt = fir.Block(stmts.result())) + // TODO For some reason depth == 1 indicates the last closing otherwise whereas + // depth == 0 indicates last closing when + if (depth > 1) { + nextCmd = OtherwiseEnd(info, depth - 1) + } + stmts = frame.outer + stmts += when + scope = scope.tail + } + } } - fir.Block(rec(Queue.empty, List.empty)(cmds)) + assert(scope.isEmpty) + fir.Block(stmts.result()) } def convert(width: Width): fir.Width = width match { @@ -347,7 +367,7 @@ private[chisel3] object Converter { def convert(component: Component): fir.DefModule = component match { case ctx @ DefModule(_, name, ports, cmds) => - fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds.toList, ctx)) + fir.Module(fir.NoInfo, name, ports.map(p => convert(p)), convert(cmds, ctx)) case ctx @ DefBlackBox(id, name, ports, topDir, params) => fir.ExtModule( fir.NoInfo, -- cgit v1.2.3