summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJack Koenig2021-09-17 21:01:26 -0700
committerJack Koenig2021-09-17 21:01:26 -0700
commit5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch)
treed9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /src
parente63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff)
parent958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff)
Merge branch 'master' into 3.5-release
Diffstat (limited to 'src')
-rw-r--r--src/main/resources/chisel3/top.cpp12
-rw-r--r--src/main/scala/chisel3/Driver.scala200
-rw-r--r--src/main/scala/chisel3/aop/Select.scala29
-rw-r--r--src/main/scala/chisel3/aop/injecting/InjectingAspect.scala20
-rw-r--r--src/main/scala/chisel3/compatibility.scala5
-rw-r--r--src/main/scala/chisel3/internal/firrtl/Emitter.scala197
-rw-r--r--src/main/scala/chisel3/stage/ChiselAnnotations.scala24
-rw-r--r--src/main/scala/chisel3/stage/ChiselStage.scala50
-rw-r--r--src/main/scala/chisel3/stage/phases/AspectPhase.scala2
-rw-r--r--src/main/scala/chisel3/stage/phases/Convert.scala3
-rw-r--r--src/main/scala/chisel3/stage/phases/DriverCompatibility.scala152
-rw-r--r--src/main/scala/chisel3/stage/phases/Elaborate.scala26
-rw-r--r--src/main/scala/chisel3/util/BitPat.scala65
-rw-r--r--src/main/scala/chisel3/util/BlackBoxUtils.scala36
-rw-r--r--src/main/scala/chisel3/util/Conditional.scala9
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala53
-rw-r--r--src/main/scala/chisel3/util/ExtModuleUtils.scala7
-rw-r--r--src/main/scala/chisel3/util/MixedVec.scala4
-rw-r--r--src/main/scala/chisel3/util/Reg.scala43
-rw-r--r--src/main/scala/chisel3/util/experimental/BoringUtils.scala20
-rw-r--r--src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala82
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala22
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala73
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/Minimizer.scala29
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala298
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/TruthTable.scala117
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/decoder.scala83
-rw-r--r--src/main/scala/chisel3/util/experimental/getAnnotations.scala9
-rw-r--r--src/main/scala/chisel3/util/pla.scala132
-rw-r--r--src/main/scala/chisel3/verilog.scala15
-rw-r--r--src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala71
-rw-r--r--src/test/scala/chisel3/testers/TestUtils.scala12
-rw-r--r--src/test/scala/chiselTests/AsyncResetSpec.scala3
-rw-r--r--src/test/scala/chiselTests/AutoClonetypeSpec.scala212
-rw-r--r--src/test/scala/chiselTests/AutoNestedCloneSpec.scala73
-rw-r--r--src/test/scala/chiselTests/BlackBoxImpl.scala15
-rw-r--r--src/test/scala/chiselTests/BundleLiteralSpec.scala23
-rw-r--r--src/test/scala/chiselTests/BundleSpec.scala34
-rw-r--r--src/test/scala/chiselTests/ChiselSpec.scala127
-rw-r--r--src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala57
-rw-r--r--src/test/scala/chiselTests/CloneModuleSpec.scala66
-rw-r--r--src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala70
-rw-r--r--src/test/scala/chiselTests/CompatibilitySpec.scala68
-rw-r--r--src/test/scala/chiselTests/CustomBundle.scala24
-rw-r--r--src/test/scala/chiselTests/DriverSpec.scala101
-rw-r--r--src/test/scala/chiselTests/ExtModule.scala16
-rw-r--r--src/test/scala/chiselTests/ExtModuleImpl.scala23
-rw-r--r--src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala53
-rw-r--r--src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala55
-rw-r--r--src/test/scala/chiselTests/Module.scala28
-rw-r--r--src/test/scala/chiselTests/OneHotMuxSpec.scala12
-rw-r--r--src/test/scala/chiselTests/PrintableSpec.scala67
-rw-r--r--src/test/scala/chiselTests/QueueFlushSpec.scala259
-rw-r--r--src/test/scala/chiselTests/QueueSpec.scala58
-rw-r--r--src/test/scala/chiselTests/RecordSpec.scala19
-rw-r--r--src/test/scala/chiselTests/Reg.scala25
-rw-r--r--src/test/scala/chiselTests/ResetSpec.scala20
-rw-r--r--src/test/scala/chiselTests/SIntOps.scala32
-rw-r--r--src/test/scala/chiselTests/StrongEnum.scala136
-rw-r--r--src/test/scala/chiselTests/UIntOps.scala33
-rw-r--r--src/test/scala/chiselTests/Vec.scala178
-rw-r--r--src/test/scala/chiselTests/VecLiteralSpec.scala526
-rw-r--r--src/test/scala/chiselTests/WidthSpec.scala59
-rw-r--r--src/test/scala/chiselTests/aop/InjectionSpec.scala25
-rw-r--r--src/test/scala/chiselTests/aop/SelectSpec.scala66
-rw-r--r--src/test/scala/chiselTests/experimental/DataView.scala546
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala57
-rw-r--r--src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala171
-rw-r--r--src/test/scala/chiselTests/experimental/ForceNames.scala2
-rw-r--r--src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala91
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala28
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala493
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Examples.scala186
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala709
-rw-r--r--src/test/scala/chiselTests/experimental/hierarchy/Utils.scala21
-rw-r--r--src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala135
-rw-r--r--src/test/scala/chiselTests/stage/ChiselMainSpec.scala191
-rw-r--r--src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala3
-rw-r--r--src/test/scala/chiselTests/stage/ChiselStageSpec.scala71
-rw-r--r--src/test/scala/chiselTests/util/BitPatSpec.scala44
-rw-r--r--src/test/scala/chiselTests/util/CatSpec.scala30
-rw-r--r--src/test/scala/chiselTests/util/experimental/PlaSpec.scala95
-rw-r--r--src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala63
-rw-r--r--src/test/scala/examples/VendingMachineUtils.scala2
84 files changed, 6151 insertions, 1250 deletions
diff --git a/src/main/resources/chisel3/top.cpp b/src/main/resources/chisel3/top.cpp
index 4e9c1433..a3475e2f 100644
--- a/src/main/resources/chisel3/top.cpp
+++ b/src/main/resources/chisel3/top.cpp
@@ -47,8 +47,15 @@ int main(int argc, char** argv) {
cout << "Starting simulation!\n";
while (!Verilated::gotFinish() && main_time < timeout) {
- if (main_time > 10) {
- top->reset = 0; // Deassert reset
+ // Deassert reset on timestep 10. This needs to occur before the clock
+ // asserts on timestep 11 because there is a single call to top->eval() in
+ // this loop. Verilator evaluates sequential logic (always blocks) before
+ // combinational logic during top->eval(). Staggering the reset update is
+ // necessary to produce the same simulation behavior independent of whether
+ // or not the generated Verilog puts synchronous reset logic inside or
+ // outside its associated always block.
+ if (main_time == 10) {
+ top->reset = 0;
}
if ((main_time % 10) == 1) {
top->clock = 1; // Toggle clock
@@ -92,4 +99,3 @@ int main(int argc, char** argv) {
if (tfp) tfp->close();
#endif
}
-
diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala
index 998f5ca0..fb564446 100644
--- a/src/main/scala/chisel3/Driver.scala
+++ b/src/main/scala/chisel3/Driver.scala
@@ -84,203 +84,3 @@ case class ChiselExecutionSuccess(
*/
@deprecated("This will be removed in Chisel 3.5", "Chisel 3.4")
case class ChiselExecutionFailure(message: String) extends ChiselExecutionResult
-
-@deprecated("Please switch to chisel3.stage.ChiselStage", "3.2.4")
-object Driver extends BackendCompilationUtilities {
-
- /**
- * Elaborate the Module specified in the gen function into a Chisel IR Circuit.
- *
- * @param gen A function that creates a Module hierarchy.
- * @return The resulting Chisel IR in the form of a Circuit. (TODO: Should be FIRRTL IR)
- */
- @deprecated("Use ChiselStage.elaborate or use a ChiselStage class. This will be removed in 3.4.", "3.2.4")
- def elaborate[T <: RawModule](gen: () => T): Circuit = internal.Builder.build(Module(gen()))._1
-
- /**
- * Convert the given Chisel IR Circuit to a FIRRTL Circuit.
- *
- * @param ir Chisel IR Circuit, generated e.g. by elaborate().
- */
- @deprecated("Use ChiselStage.convert or use a ChiselStage class. This will be removed in 3.4.", "3.2.4")
- def toFirrtl(ir: Circuit): firrtl.ir.Circuit = Converter.convert(ir)
-
- /**
- * Emit the Module specified in the gen function directly as a FIRRTL string without
- * invoking FIRRTL.
- *
- * @param gen A function that creates a Module hierarchy.
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl. This will be removed in 3.4.", "3.2.2")
- def emit[T <: RawModule](gen: () => T): String = Driver.emit(elaborate(gen))
-
- /**
- * Emit the given Chisel IR Circuit as a FIRRTL string, without invoking FIRRTL.
- *
- * @param ir Chisel IR Circuit, generated e.g. by elaborate().
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl", "3.2.2")
- def emit[T <: RawModule](ir: Circuit): String = Emitter.emit(ir)
-
- /**
- * Elaborate the Module specified in the gen function into Verilog.
- *
- * @param gen A function that creates a Module hierarchy.
- * @return A String containing the design in Verilog.
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitVerilog. This will be removed in 3.4.", "3.2.2")
- def emitVerilog[T <: RawModule](gen: => T): String = {
- execute(Array[String](), { () => gen }) match {
- case ChiselExecutionSuccess(_, _, Some(firrtl.FirrtlExecutionSuccess(_, verilog))) => verilog
- case _ => sys.error("Cannot get Verilog!")
- }
- }
-
- /**
- * Dump the elaborated Chisel IR Circuit as a FIRRTL String, without invoking FIRRTL.
- *
- * If no File is given as input, it will dump to a default filename based on the name of the
- * top Module.
- *
- * @param c Elaborated Chisel Circuit.
- * @param optName File to dump to. If unspecified, defaults to "<topmodule>.fir".
- * @return The File the circuit was dumped to.
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpFirrtl(ir: Circuit, optName: Option[File]): File = {
- val f = optName.getOrElse(new File(ir.name + ".fir"))
- val w = new FileWriter(f)
- w.write(Driver.emit(ir))
- w.close()
- f
- }
-
- /**
- * Emit the annotations of a circuit
- *
- * @param ir The circuit containing annotations to be emitted
- * @param optName An optional filename (will use s"\${ir.name}.json" otherwise)
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpAnnotations(ir: Circuit, optName: Option[File]): File = {
- val f = optName.getOrElse(new File(ir.name + ".anno.json"))
- val w = new FileWriter(f)
- w.write(JsonProtocol.serialize(ir.annotations.map(_.toFirrtl)))
- w.close()
- f
- }
-
- /**
- * Dump the elaborated Circuit to ProtoBuf.
- *
- * If no File is given as input, it will dump to a default filename based on the name of the
- * top Module.
- *
- * @param c Elaborated Chisel Circuit.
- * @param optFile Optional File to dump to. If unspecified, defaults to "<topmodule>.pb".
- * @return The File the circuit was dumped to.
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpProto(c: Circuit, optFile: Option[File]): File = {
- val f = optFile.getOrElse(new File(c.name + ".pb"))
- val ostream = new java.io.FileOutputStream(f)
- // Lazily convert modules to make intermediate objects garbage collectable
- val modules = c.components.map(m => () => Converter.convert(m))
- firrtl.proto.ToProto.writeToStreamFast(ostream, ir.NoInfo, modules, c.name)
- f
- }
-
- private var target_dir: Option[String] = None
- @deprecated("Use chisel3.stage.ChiselStage with '--target-directory'. This will be removed in 3.4.", "3.2.2")
- def parseArgs(args: Array[String]): Unit = {
- for (i <- 0 until args.size) {
- if (args(i) == "--targetDir") {
- target_dir = Some(args(i + 1))
- }
- }
- }
-
- @deprecated("This has no effect on Chisel3 Driver! This will be removed in 3.4.", "3.2.2")
- def targetDir(): String = { target_dir getOrElse new File(".").getCanonicalPath }
-
- /**
- * Run the chisel3 compiler and possibly the firrtl compiler with options specified
- *
- * @param optionsManager The options specified
- * @param dut The device under test
- * @return An execution result with useful stuff, or failure with message
- */
- @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2")
- def execute(
- optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions,
- dut: () => RawModule): ChiselExecutionResult = {
-
- val annos: AnnotationSeq =
- Seq(DriverCompatibility.OptionsManagerAnnotation(optionsManager), ChiselGeneratorAnnotation(dut)) ++
- optionsManager.chiselOptions.toAnnotations ++
- optionsManager.firrtlOptions.toAnnotations ++
- optionsManager.commonOptions.toAnnotations
-
- val targets =
- Seq( Dependency[DriverCompatibility.AddImplicitOutputFile],
- Dependency[DriverCompatibility.AddImplicitOutputAnnotationFile],
- Dependency[DriverCompatibility.DisableFirrtlStage],
- Dependency[ChiselStage],
- Dependency[DriverCompatibility.MutateOptionsManager],
- Dependency[DriverCompatibility.ReEnableFirrtlStage],
- Dependency[DriverCompatibility.FirrtlPreprocessing],
- Dependency[chisel3.stage.phases.MaybeFirrtlStage] )
- val currentState =
- Seq( Dependency[firrtl.stage.phases.DriverCompatibility.AddImplicitFirrtlFile],
- Dependency[chisel3.stage.phases.Convert] )
-
- val phases: Seq[Phase] = new PhaseManager(targets, currentState) {
- override val wrappers = Seq( DeletedWrapper(_: Phase) )
- }.transformOrder
-
- val annosx = try {
- phases.foldLeft(annos)( (a, p) => p.transform(a) )
- } catch {
- /* ChiselStage and FirrtlStage can throw StageError. Since Driver is not a StageMain, it cannot catch these. While
- * Driver is deprecated and removed in 3.2.1+, the Driver catches all errors.
- */
- case e: StageError => annos
- }
-
- view[ChiselExecutionResult](annosx)
- }
-
- /**
- * Run the chisel3 compiler and possibly the firrtl compiler with options specified via an array of Strings
- *
- * @param args The options specified, command line style
- * @param dut The device under test
- * @return An execution result with useful stuff, or failure with message
- */
- @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2")
- def execute(args: Array[String], dut: () => RawModule): ChiselExecutionResult = {
- val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions
-
- optionsManager.parse(args) match {
- case true =>
- execute(optionsManager, dut)
- case _ =>
- ChiselExecutionFailure("could not parse results")
- }
- }
-
- /**
- * This is just here as command line way to see what the options are
- * It will not successfully run
- * TODO: Look into dynamic class loading as way to make this main useful
- *
- * @param args unused args
- */
- @deprecated("Use chisel3.stage.ChiselMain. This will be removed in 3.4.", "3.2.2")
- def main(args: Array[String]) {
- execute(Array("--help"), null)
- }
-
- val version = BuildInfo.version
- val chiselVersionString = BuildInfo.toString
-}
diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala
index e2689f39..2384c4d3 100644
--- a/src/main/scala/chisel3/aop/Select.scala
+++ b/src/main/scala/chisel3/aop/Select.scala
@@ -3,12 +3,16 @@
package chisel3.aop
import chisel3._
-import chisel3.experimental.{BaseModule, FixedPoint}
-import chisel3.internal.HasId
+import chisel3.internal.{HasId}
+import chisel3.experimental.BaseModule
+import chisel3.experimental.FixedPoint
import chisel3.internal.firrtl._
+import chisel3.internal.PseudoModule
+import chisel3.internal.BaseModule.ModuleClone
import firrtl.annotations.ReferenceTarget
import scala.collection.mutable
+import chisel3.internal.naming.chiselName
/** Use to select Chisel components in a module, after that module has been constructed
* Useful for adding additional Chisel annotations or for use within an [[Aspect]]
@@ -81,8 +85,13 @@ object Select {
def instances(module: BaseModule): Seq[BaseModule] = {
check(module)
module._component.get match {
- case d: DefModule => d.commands.collect {
- case i: DefInstance => i.id
+ case d: DefModule => d.commands.flatMap {
+ case i: DefInstance => i.id match {
+ case m: ModuleClone[_] if !m._madeFromDefinition => None
+ case _: PseudoModule => throw new Exception("Aspect APIs are currently incompatible with Definition/Instance")
+ case other => Some(other)
+ }
+ case _ => None
}
case other => Nil
}
@@ -248,7 +257,7 @@ object Select {
case other =>
}
})
- predicatedConnects
+ predicatedConnects.toSeq
}
/** Selects all stop statements, and includes the predicates surrounding the stop statement
@@ -264,7 +273,7 @@ object Select {
case other =>
}
})
- stops
+ stops.toSeq
}
/** Selects all printf statements, and includes the predicates surrounding the printf statement
@@ -276,11 +285,11 @@ object Select {
val printfs = mutable.ArrayBuffer[Printf]()
searchWhens(module, (cmd: Command, preds: Seq[Predicate]) => {
cmd match {
- case chisel3.internal.firrtl.Printf(_, clock, pable) => printfs += Printf(preds, pable, getId(clock).asInstanceOf[Clock])
+ case chisel3.internal.firrtl.Printf(id, _, clock, pable) => printfs += Printf(id, preds, pable, getId(clock).asInstanceOf[Clock])
case other =>
}
})
- printfs
+ printfs.toSeq
}
// Checks that a module has finished its construction
@@ -321,7 +330,7 @@ object Select {
}
} catch {
case e: ChiselException => i.getOptionRef.get match {
- case l: LitArg => l.num.intValue().toString
+ case l: LitArg => l.num.intValue.toString
}
}
@@ -413,7 +422,7 @@ object Select {
* @param pable
* @param clock
*/
- case class Printf(preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable {
+ case class Printf(id: printf.Printf, preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable {
def serialize: String = {
s"printf when(${preds.map(_.serialize).mkString(" & ")}) on ${getName(clock)}: $pable"
}
diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
index 39590b93..1a476f61 100644
--- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
+++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
@@ -4,6 +4,7 @@ package chisel3.aop.injecting
import chisel3.{Module, ModuleAspect, RawModule, withClockAndReset}
import chisel3.aop._
+import chisel3.experimental.hierarchy.IsInstantiable
import chisel3.internal.{Builder, DynamicContext}
import chisel3.internal.firrtl.DefModule
import chisel3.stage.DesignAnnotation
@@ -54,13 +55,14 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
* @return
*/
final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = {
- val dynamicContext = new DynamicContext()
- // 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 =>
- dynamicContext.globalNamespace.name(n)
- }
RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module =>
+ val dynamicContext = new DynamicContext(annotationsInAspect)
+ // 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 =>
+ dynamicContext.globalNamespace.name(n)
+ }
+
val (chiselIR, _) = Builder.build(Module(new ModuleAspect(module) {
module match {
case x: Module => withClockAndReset(x.clock, x.reset) { injection(module) }
@@ -75,16 +77,20 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
val annotations = chiselIR.annotations.map(_.toFirrtl).filterNot{ a => a.isInstanceOf[DesignAnnotation[_]] }
+ /** Statements to be injected via aspect. */
val stmts = mutable.ArrayBuffer[ir.Statement]()
+ /** Modules to be injected via aspect. */
val modules = Aspect.getFirrtl(chiselIR.copy(components = comps)).modules.flatMap {
+ // for "container" modules, inject their statements
case m: firrtl.ir.Module if m.name == module.name =>
stmts += m.body
Nil
+ // for modules to be injected
case other: firrtl.ir.DefModule =>
Seq(other)
}
- InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts), modules, annotations)
+ InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts.toSeq), modules, annotations)
}.toSeq
}
}
diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala
index e62cba7d..dde2321d 100644
--- a/src/main/scala/chisel3/compatibility.scala
+++ b/src/main/scala/chisel3/compatibility.scala
@@ -363,8 +363,6 @@ package object Chisel {
implicit class fromIntToWidth(x: Int) extends chisel3.fromIntToWidth(x)
type BackendCompilationUtilities = firrtl.util.BackendCompilationUtilities
- @deprecated("Please switch to chisel3.stage.ChiselStage", "3.4")
- val Driver = chisel3.Driver
val ImplicitConversions = chisel3.util.ImplicitConversions
// Deprecated as of Chisel3
@@ -400,8 +398,8 @@ package object Chisel {
}
// Deprecated as of Chsiel3
- @throws(classOf[Exception])
object throwException {
+ @throws(classOf[Exception])
def apply(s: String, t: Throwable = null): Nothing = {
val xcpt = new Exception(s, t)
throw xcpt
@@ -455,7 +453,6 @@ package object Chisel {
val Log2 = chisel3.util.Log2
- val unless = chisel3.util.unless
type SwitchContext[T <: Bits] = chisel3.util.SwitchContext[T]
val is = chisel3.util.is
val switch = chisel3.util.switch
diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
index 354be0c0..a94558ce 100644
--- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala
+++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
@@ -1,194 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
package chisel3.internal.firrtl
-import chisel3._
-import chisel3.experimental.{Interval, _}
-import chisel3.internal.BaseBlackBox
-private[chisel3] object Emitter {
- def emit(circuit: Circuit): String = new Emitter(circuit).toString
-}
-
-private class Emitter(circuit: Circuit) {
- override def toString: String = res.toString
-
- private def emitPort(e: Port, topDir: SpecifiedDirection=SpecifiedDirection.Unspecified): String = {
- val resolvedDir = SpecifiedDirection.fromParent(topDir, e.dir)
- val dirString = resolvedDir match {
- case SpecifiedDirection.Unspecified | SpecifiedDirection.Output => "output"
- case SpecifiedDirection.Flip | SpecifiedDirection.Input => "input"
- }
- val clearDir = resolvedDir match {
- case SpecifiedDirection.Input | SpecifiedDirection.Output => true
- case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false
- }
- s"$dirString ${e.id.getRef.name} : ${emitType(e.id, clearDir)}"
- }
-
- private def emitType(d: Data, clearDir: Boolean = false): String = d match {
- case d: Clock => "Clock"
- case _: AsyncReset => "AsyncReset"
- case _: ResetType => "Reset"
- case d: chisel3.core.EnumType => s"UInt${d.width}"
- case d: UInt => s"UInt${d.width}"
- case d: SInt => s"SInt${d.width}"
- case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}"
- case d: Interval =>
- val binaryPointString = d.binaryPoint match {
- case UnknownBinaryPoint => ""
- case KnownBinaryPoint(value) => s".$value"
- }
- d.toType
- case d: Analog => s"Analog${d.width}"
- case d: Vec[_] => s"${emitType(d.sample_element, clearDir)}[${d.length}]"
- case d: Record => {
- val childClearDir = clearDir ||
- d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output
- def eltPort(elt: Data): String = (childClearDir, firrtlUserDirOf(elt)) match {
- case (true, _) =>
- s"${elt.getRef.name} : ${emitType(elt, true)}"
- case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) =>
- s"${elt.getRef.name} : ${emitType(elt, false)}"
- case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) =>
- s"flip ${elt.getRef.name} : ${emitType(elt, false)}"
- }
- d.elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}")
- }
- }
-
- private def firrtlUserDirOf(d: Data): SpecifiedDirection = d match {
- case d: Vec[_] =>
- SpecifiedDirection.fromParent(d.specifiedDirection, firrtlUserDirOf(d.sample_element))
- case d => d.specifiedDirection
- }
-
- private def emit(e: Command, ctx: Component): String = {
- val firrtlLine = e match {
- case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})"
- case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}"
- case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}"
- case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))"
- case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]"
- case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}], ${e.readUnderWrite}"
- case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}"
- case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}"
- case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}"
- case e: Attach => e.locs.map(_.fullName(ctx)).mkString("attach (", ", ", ")")
- case e: Stop => s"stop(${e.clock.fullName(ctx)}, UInt<1>(1), ${e.ret})"
- case e: Printf =>
- val (fmt, args) = e.pable.unpack(ctx)
- val printfArgs = Seq(e.clock.fullName(ctx), "UInt<1>(1)",
- "\"" + printf.format(fmt) + "\"") ++ args
- printfArgs mkString ("printf(", ", ", ")")
- case e: Verification => s"${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, " +
- s"UInt<1>(1), " + "\"" + s"${printf.format(e.message)}" + "\")"
- case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid"
- case e: DefInstance => s"inst ${e.name} of ${e.id.name}"
- case w: WhenBegin =>
- // When consequences are always indented
- indent()
- s"when ${w.pred.fullName(ctx)} :"
- case w: WhenEnd =>
- // If a when has no else, the indent level must be reset to the enclosing block
- unindent()
- if (!w.hasAlt) { for (i <- 0 until w.firrtlDepth) { unindent() } }
- s"skip"
- case a: AltBegin =>
- // Else blocks are always indented
- indent()
- s"else :"
- case o: OtherwiseEnd =>
- // Chisel otherwise: ends all FIRRTL associated a Chisel when, resetting indent level
- for (i <- 0 until o.firrtlDepth) { unindent() }
- s"skip"
- }
- firrtlLine + e.sourceInfo.makeMessage(" " + _)
- }
-
- private def emitParam(name: String, p: Param): String = {
- val str = p match {
- case IntParam(value) => value.toString
- case DoubleParam(value) => value.toString
- case StringParam(str) => "\"" + str + "\""
- case RawParam(str) => "'" + str + "'"
- }
- s"parameter $name = $str"
- }
-
- /** Generates the FIRRTL module declaration.
- */
- private def moduleDecl(m: Component): String = m.id match {
- case _: BaseBlackBox => newline + s"extmodule ${m.name} : "
- case _: RawModule => newline + s"module ${m.name} : "
- }
-
- /** Generates the FIRRTL module definition.
- */
- private def moduleDefn(m: Component): String = {
- val body = new StringBuilder
- withIndent {
- for (p <- m.ports) {
- val portDef = m match {
- case bb: DefBlackBox => emitPort(p, bb.topDir)
- case mod: DefModule => emitPort(p)
- }
- body ++= newline + portDef
- }
- body ++= newline
+import scala.collection.immutable.LazyList // Needed for 2.12 alias
+import firrtl.ir.Serializer
- m match {
- case bb: DefBlackBox =>
- // Firrtl extmodule can overrule name
- body ++= newline + s"defname = ${bb.id.desiredName}"
- body ++= newline + (bb.params map { case (n, p) => emitParam(n, p) } mkString newline)
- case mod: DefModule => {
- // Preprocess whens & elsewhens, marking those that have no alternative
- val procMod = mod.copy(commands = processWhens(mod.commands))
- for (cmd <- procMod.commands) { body ++= newline + emit(cmd, procMod)}
- }
- }
- body ++= newline
- }
- body.toString()
- }
-
- /** Returns the FIRRTL declaration and body of a module, or nothing if it's a
- * duplicate of something already emitted (on the basis of simple string
- * matching).
- */
- private def emit(m: Component): String = {
- // Generate the body.
- val sb = new StringBuilder
- sb.append(moduleDecl(m))
- sb.append(moduleDefn(m))
- sb.result
+private[chisel3] object Emitter {
+ def emit(circuit: Circuit): String = {
+ val fcircuit = Converter.convertLazily(circuit)
+ Serializer.serialize(fcircuit)
}
- /** Preprocess the command queue, marking when/elsewhen statements
- * that have no alternatives (elsewhens or otherwise). These
- * alternative-free statements reset the indent level to the
- * enclosing block upon emission.
- */
- private def processWhens(cmds: Seq[Command]): Seq[Command] = {
- if (cmds.isEmpty) {
- Seq.empty
- } else {
- cmds.zip(cmds.tail).map{
- case (a: WhenEnd, b: AltBegin) => a.copy(hasAlt = true)
- case (a, b) => a
- } ++ cmds.lastOption
+ 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")
}
+ result ++ moduleStrings
}
-
- private var indentLevel = 0
- private def newline = "\n" + (" " * indentLevel)
- private def indent(): Unit = indentLevel += 1
- private def unindent() { require(indentLevel > 0); indentLevel -= 1 }
- private def withIndent(f: => Unit) { indent(); f; unindent() }
-
- private val res = new StringBuilder()
- res ++= s";${BuildInfo.toString}\n"
- res ++= s"circuit ${circuit.name} : "
- withIndent { circuit.components.foreach(c => res ++= emit(c)) }
- res ++= newline
}
+
diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
index bbe86ab4..de47ef36 100644
--- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala
+++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
@@ -3,7 +3,7 @@
package chisel3.stage
import firrtl.annotations.{Annotation, NoTargetAnnotation}
-import firrtl.options.{CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable}
+import firrtl.options.{BufferedCustomFileEmission, CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable}
import firrtl.options.Viewer.view
import chisel3.{ChiselException, Module}
import chisel3.RawModule
@@ -56,15 +56,7 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot
/** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]]
*/
- def elaborate: AnnotationSeq = try {
- val (circuit, dut) = Builder.build(Module(gen()))
- Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
- } catch {
- case e @ (_: OptionsException | _: ChiselException) => throw e
- case e: Throwable =>
- throw new ChiselException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
- }
-
+ def elaborate: AnnotationSeq = (new chisel3.stage.phases.Elaborate).transform(Seq(this))
}
object ChiselGeneratorAnnotation extends HasShellOptions {
@@ -131,7 +123,7 @@ import CircuitSerializationAnnotation._
*/
case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, format: Format)
extends NoTargetAnnotation
- with CustomFileEmission {
+ with BufferedCustomFileEmission {
/* Caching the hashCode for a large circuit is necessary due to repeated queries.
* Not caching the hashCode will cause severe performance degredations for large [[Circuit]]s.
*/
@@ -141,14 +133,16 @@ case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, fo
protected def suffix: Option[String] = Some(format.extension)
- // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures
- override def getBytes: Iterable[Byte] = format match {
- case FirrtlFileFormat => OldEmitter.emit(circuit).getBytes
+ override def getBytesBuffered: Iterable[Array[Byte]] = format match {
+ case FirrtlFileFormat =>
+ OldEmitter.emitLazily(circuit)
+ .map(_.getBytes)
+ // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures
case ProtoBufFileFormat =>
val ostream = new java.io.ByteArrayOutputStream
val modules = circuit.components.map(m => () => chisel3.internal.firrtl.Converter.convert(m))
firrtl.proto.ToProto.writeToStreamFast(ostream, firrtl.ir.NoInfo, modules, circuit.name)
- ostream.toByteArray
+ List(ostream.toByteArray)
}
}
diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala
index aae7ad8d..0c76f411 100644
--- a/src/main/scala/chisel3/stage/ChiselStage.scala
+++ b/src/main/scala/chisel3/stage/ChiselStage.scala
@@ -13,7 +13,7 @@ import firrtl.{
VerilogEmitter,
SystemVerilogEmitter
}
-import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageError, StageMain}
+import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageMain}
import firrtl.options.phases.DeletedWrapper
import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlCli, RunFirrtlTransformAnnotation}
import firrtl.options.Viewer.view
@@ -28,7 +28,7 @@ class ChiselStage extends Stage {
override def prerequisites = Seq.empty
override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq.empty
+ override def optionalPrerequisiteOf = Seq(Dependency[firrtl.stage.FirrtlStage])
override def invalidates(a: Phase) = false
val shell: Shell = new Shell("chisel") with ChiselCli with FirrtlCli
@@ -42,28 +42,12 @@ class ChiselStage extends Stage {
}
}
- def run(annotations: AnnotationSeq): AnnotationSeq = try {
- phaseManager.transform(annotations)
- } catch {
- case ce: ChiselException =>
- val stackTrace = if (!view[ChiselOptions](annotations).printFullStackTrace) {
- ce.chiselStackTrace
- } else {
- val sw = new StringWriter
- ce.printStackTrace(new PrintWriter(sw))
- sw.toString
- }
- Predef
- .augmentString(stackTrace)
- .lines
- .foreach(line => println(s"${ErrorLog.errTag} $line"))
- throw new StageError(cause=ce)
- }
+ def run(annotations: AnnotationSeq): AnnotationSeq = phaseManager.transform(annotations)
/** Convert a Chisel module to a CHIRRTL string
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the Verilog output
*/
final def emitChirrtl(
@@ -86,7 +70,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to a FIRRTL string
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the FIRRTL output
*/
final def emitFirrtl(
@@ -106,7 +90,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to Verilog
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the Verilog output
*/
final def emitVerilog(
@@ -126,7 +110,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to SystemVerilog
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the SystemVerilog output
*/
final def emitSystemVerilog(
@@ -188,6 +172,26 @@ object ChiselStage {
.get
}
+ /** Return a [[firrtl.ir.Circuit]] for a [[chisel3.internal.firrtl.Circuit]](aka chirrtl)
+ * @param chirrtl [[chisel3.internal.firrtl.Circuit]] which need to be converted to [[firrtl.ir.Circuit]]
+ */
+ def convert(chirrtl: cir.Circuit): fir.Circuit = {
+ val phase = new ChiselPhase {
+ override val targets = Seq(
+ Dependency[chisel3.stage.phases.AddImplicitOutputFile],
+ Dependency[chisel3.stage.phases.AddImplicitOutputAnnotationFile],
+ Dependency[chisel3.stage.phases.MaybeAspectPhase],
+ Dependency[chisel3.stage.phases.Convert] )
+ }
+
+ phase
+ .transform(Seq(ChiselCircuitAnnotation(chirrtl)))
+ .collectFirst {
+ case FirrtlCircuitAnnotation(a) => a
+ }
+ .get
+ }
+
/** Return a CHIRRTL string for a Chisel module
* @param gen a call-by-name Chisel module
*/
diff --git a/src/main/scala/chisel3/stage/phases/AspectPhase.scala b/src/main/scala/chisel3/stage/phases/AspectPhase.scala
index 1a27b8fa..72965861 100644
--- a/src/main/scala/chisel3/stage/phases/AspectPhase.scala
+++ b/src/main/scala/chisel3/stage/phases/AspectPhase.scala
@@ -29,7 +29,7 @@ class AspectPhase extends Phase {
case other => Seq(other)
}
if(dut.isDefined) {
- val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get) }
+ val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get, remainingAnnotations) }
remainingAnnotations ++ newAnnotations
} else annotations
}
diff --git a/src/main/scala/chisel3/stage/phases/Convert.scala b/src/main/scala/chisel3/stage/phases/Convert.scala
index bf42b58a..b5b01b8d 100644
--- a/src/main/scala/chisel3/stage/phases/Convert.scala
+++ b/src/main/scala/chisel3/stage/phases/Convert.scala
@@ -29,8 +29,7 @@ class Convert extends Phase {
/* Convert all Chisel Annotations to FIRRTL Annotations */
a
.circuit
- .annotations
- .map(_.toFirrtl) ++
+ .firrtlAnnotations ++
a
.circuit
.annotations
diff --git a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
index 659914ae..9305c5c9 100644
--- a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
+++ b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
@@ -16,153 +16,5 @@ import chisel3.stage.{ChiselStage, NoRunFirrtlCompilerAnnotation, ChiselOutputFi
* Primarily, this object includes [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s
* derived from the deprecated [[firrtl.stage.phases.DriverCompatibility.TopNameAnnotation]].
*/
-object DriverCompatibility {
-
- /** Adds a [[ChiselOutputFileAnnotation]] derived from a [[TopNameAnnotation]] if no [[ChiselOutputFileAnnotation]]
- * already exists. If no [[TopNameAnnotation]] exists, then no [[firrtl.stage.OutputFileAnnotation]] is added. ''This is not a
- * replacement for [[chisel3.stage.phases.AddImplicitOutputFile AddImplicitOutputFile]] as this only adds an output
- * file based on a discovered top name and not on a discovered elaborated circuit.'' Consequently, this will provide
- * the correct behavior before a circuit has been elaborated.
- * @note the output suffix is unspecified and will be set by the underlying [[firrtl.EmittedComponent]]
- */
- private [chisel3] class AddImplicitOutputFile extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = {
- val hasOutputFile = annotations
- .collectFirst{ case a: ChiselOutputFileAnnotation => a }
- .isDefined
- lazy val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
-
- if (!hasOutputFile && top.isDefined) {
- ChiselOutputFileAnnotation(top.get) +: annotations
- } else {
- annotations
- }
- }
- }
-
- /** If a [[firrtl.options.OutputAnnotationFileAnnotation]] does not exist, this adds one derived from a
- * [[TopNameAnnotation]]. ''This is not a replacement for [[chisel3.stage.phases.AddImplicitOutputAnnotationFile]] as
- * this only adds an output annotation file based on a discovered top name.'' Consequently, this will provide the
- * correct behavior before a circuit has been elaborated.
- * @note the output suffix is unspecified and will be set by [[firrtl.options.phases.WriteOutputAnnotations]]
- */
- private[chisel3] class AddImplicitOutputAnnotationFile extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq =
- annotations
- .collectFirst{ case _: OutputAnnotationFileAnnotation => annotations }
- .getOrElse{
- val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
- if (top.isDefined) {
- OutputAnnotationFileAnnotation(top.get) +: annotations
- } else {
- annotations
- }
- }
- }
-
- private[chisel3] case object RunFirrtlCompilerAnnotation extends NoTargetAnnotation
-
- /** Disables the execution of [[firrtl.stage.FirrtlStage]]. This can be used to call [[chisel3.stage.ChiselStage]] and
- * guarantee that the FIRRTL compiler will not run. This is necessary for certain [[chisel3.Driver]] compatibility
- * situations where you need to do something between Chisel compilation and FIRRTL compilations, e.g., update a
- * mutable data structure.
- */
- private[chisel3] class DisableFirrtlStage extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
- .collectFirst { case NoRunFirrtlCompilerAnnotation => annotations }
- .getOrElse { Seq(RunFirrtlCompilerAnnotation, NoRunFirrtlCompilerAnnotation) ++ annotations }
- }
-
- private[chisel3] class ReEnableFirrtlStage extends Phase {
-
- override def prerequisites = Seq(Dependency[DisableFirrtlStage], Dependency[ChiselStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq.empty
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
- .collectFirst { case RunFirrtlCompilerAnnotation =>
- val a: AnnotationSeq = annotations.filter {
- case NoRunFirrtlCompilerAnnotation | RunFirrtlCompilerAnnotation => false
- case _ => true
- }
- a
- }
- .getOrElse{ annotations }
-
- }
-
- private[chisel3] case class OptionsManagerAnnotation(
- manager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions)
- extends NoTargetAnnotation with Unserializable
-
- /** Mutate an input [[firrtl.ExecutionOptionsManager]] based on information encoded in an [[firrtl.AnnotationSeq]].
- * This is intended to be run between [[chisel3.stage.ChiselStage ChiselStage]] and [[firrtl.stage.FirrtlStage]] if
- * you want to have backwards compatibility with an [[firrtl.ExecutionOptionsManager]].
- */
- private[chisel3] class MutateOptionsManager extends Phase {
-
- override def prerequisites = Seq(Dependency[chisel3.stage.ChiselStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[ReEnableFirrtlStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = {
-
- val optionsManager = annotations
- .collectFirst{ case OptionsManagerAnnotation(a) => a }
- .getOrElse{ throw new OptionsException(
- "An OptionsManagerException must exist for Chisel Driver compatibility mode") }
-
- val firrtlCircuit = annotations.collectFirst{ case FirrtlCircuitAnnotation(a) => a }
- optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
- firrtlCircuit = firrtlCircuit,
- annotations = optionsManager.firrtlOptions.annotations ++ annotations,
- customTransforms = optionsManager.firrtlOptions.customTransforms ++
- annotations.collect{ case RunFirrtlTransformAnnotation(a) => a } )
-
- annotations
-
- }
-
- }
-
- /** A [[Phase]] that lets us run
- * @todo a better solution than the current state hack below may be needed
- */
- private [chisel3] class FirrtlPreprocessing extends Phase {
-
- override def prerequisites = Seq(Dependency[ChiselStage], Dependency[MutateOptionsManager], Dependency[ReEnableFirrtlStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[MaybeFirrtlStage])
- override def invalidates(a: Phase) = false
-
- private val phases =
- Seq( new firrtl.stage.phases.DriverCompatibility.AddImplicitOutputFile,
- new firrtl.stage.phases.DriverCompatibility.AddImplicitEmitter )
-
- override def transform(annotations: AnnotationSeq): AnnotationSeq =
- phases
- .foldLeft(annotations)( (a, p) => p.transform(a) )
-
- }
-
-}
+@deprecated("This object contains no public members. This will be removed in Chisel 3.6.", "Chisel 3.5")
+object DriverCompatibility
diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala
index 04cfc33e..e8f2623e 100644
--- a/src/main/scala/chisel3/stage/phases/Elaborate.scala
+++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala
@@ -2,14 +2,13 @@
package chisel3.stage.phases
-import java.io.{PrintWriter, StringWriter}
-
-import chisel3.ChiselException
-import chisel3.internal.ErrorLog
-import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions}
+import chisel3.Module
+import chisel3.internal.ExceptionHelpers.ThrowableHelpers
+import chisel3.internal.{Builder, DynamicContext}
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselOptions, DesignAnnotation}
import firrtl.AnnotationSeq
+import firrtl.options.Phase
import firrtl.options.Viewer.view
-import firrtl.options.{OptionsException, Phase}
/** Elaborate all [[chisel3.stage.ChiselGeneratorAnnotation]]s into [[chisel3.stage.ChiselCircuitAnnotation]]s.
*/
@@ -21,8 +20,19 @@ class Elaborate extends Phase {
override def invalidates(a: Phase) = false
def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
- case a: ChiselGeneratorAnnotation => a.elaborate
- case a => Some(a)
+ case ChiselGeneratorAnnotation(gen) => try {
+ val (circuit, dut) = Builder.build(Module(gen()), new DynamicContext(annotations))
+ Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
+ } catch {
+ /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace
+ */
+ case scala.util.control.NonFatal(a) =>
+ if (!view[ChiselOptions](annotations).printFullStackTrace) {
+ a.trimStackTraceToUserCode()
+ }
+ throw(a)
+ }
+ case a => Some(a)
}
}
diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala
index 40563e23..0dcb2466 100644
--- a/src/main/scala/chisel3/util/BitPat.scala
+++ b/src/main/scala/chisel3/util/BitPat.scala
@@ -23,6 +23,7 @@ object BitPat {
// If ? parsing is to be exposed, the return API needs further scrutiny
// (especially with things like mask polarity).
require(x.head == 'b', "BitPats must be in binary and be prefixed with 'b'")
+ require(x.length > 1, "BitPat width cannot be 0.")
var bits = BigInt(0)
var mask = BigInt(0)
var count = 0
@@ -57,6 +58,22 @@ object BitPat {
*/
def dontCare(width: Int): BitPat = BitPat("b" + ("?" * width))
+ /** Creates a [[BitPat]] of all 1 of the specified bitwidth.
+ *
+ * @example {{{
+ * val myY = BitPat.Y(4) // equivalent to BitPat("b1111")
+ * }}}
+ */
+ def Y(width: Int = 1): BitPat = BitPat("b" + ("1" * width))
+
+ /** Creates a [[BitPat]] of all 0 of the specified bitwidth.
+ *
+ * @example {{{
+ * val myN = BitPat.N(4) // equivalent to BitPat("b0000")
+ * }}}
+ */
+ def N(width: Int = 1): BitPat = BitPat("b" + ("0" * width))
+
/** Allows BitPats to be used where a UInt is expected.
*
* @note the BitPat must not have don't care bits (will error out otherwise)
@@ -91,12 +108,6 @@ object BitPat {
/** @group SourceInfoTransformMacro */
def do_=/= (that: BitPat)
(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that =/= x
-
- final def != (that: BitPat): Bool = macro SourceInfoTransform.thatArg
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- def do_!= (that: BitPat)
- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that != x
}
}
@@ -111,8 +122,29 @@ object BitPat {
*/
sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends SourceInfoDoc {
def getWidth: Int = width
+ def apply(x: Int): BitPat = macro SourceInfoTransform.xArg
+ def apply(x: Int, y: Int): BitPat = macro SourceInfoTransform.xyArg
def === (that: UInt): Bool = macro SourceInfoTransform.thatArg
def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg
+ def ## (that: BitPat): BitPat = macro SourceInfoTransform.thatArg
+ override def equals(obj: Any): Boolean = {
+ obj match {
+ case y: BitPat => value == y.value && mask == y.mask && getWidth == y.getWidth
+ case _ => false
+ }
+ }
+
+ /** @group SourceInfoTransformMacro */
+ def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ do_apply(x, x)
+ }
+
+ /** @group SourceInfoTransformMacro */
+ def do_apply(x: Int, y: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ require(width > x && y >= 0, s"Invalid bit range ($x, $y), index should be bounded by (${width - 1}, 0)")
+ require(x >= y, s"Invalid bit range ($x, $y), x should be greater or equal to y.")
+ BitPat(s"b${rawString.slice(width - x - 1, width - y)}")
+ }
/** @group SourceInfoTransformMacro */
def do_=== (that: UInt)
@@ -124,12 +156,19 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou
(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
!(this === that)
}
-
- def != (that: UInt): Bool = macro SourceInfoTransform.thatArg
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- def do_!= (that: UInt)
- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
- this =/= that
+ /** @group SourceInfoTransformMacro */
+ def do_##(that: BitPat)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ new BitPat((value << that.getWidth) + that.value, (mask << that.getWidth) + that.mask, this.width + that.getWidth)
}
+
+ /** Generate raw string of a BitPat. */
+ def rawString: String = Seq.tabulate(width) { i =>
+ (value.testBit(width - i - 1), mask.testBit(width - i - 1)) match {
+ case (true, true) => "1"
+ case (false, true) => "0"
+ case (_, false) => "?"
+ }
+ }.mkString
+
+ override def toString = s"BitPat($rawString)"
}
diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala
index 21bd4dfa..443d7f3e 100644
--- a/src/main/scala/chisel3/util/BlackBoxUtils.scala
+++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala
@@ -4,15 +4,39 @@ package chisel3.util
import chisel3._
import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform}
-import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper}
+import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper,
+ BlackBoxNotFoundException}
+import firrtl.annotations.ModuleName
+import logger.LazyLogging
+
+private[util] object BlackBoxHelpers {
+
+ implicit class BlackBoxInlineAnnoHelpers(anno: BlackBoxInlineAnno.type) extends LazyLogging {
+ /** Generate a BlackBoxInlineAnno from a Java Resource and a module name. */
+ def fromResource(resourceName: String, moduleName: ModuleName) = try {
+ val blackBoxFile = os.resource / os.RelPath(resourceName.dropWhile(_ == '/'))
+ val contents = os.read(blackBoxFile)
+ if (contents.size > BigInt(2).pow(20)) {
+ val message =
+ s"Black box resource $resourceName, which will be converted to an inline annotation, is greater than 1 MiB." +
+ "This may affect compiler performance. Consider including this resource via a black box path."
+ logger.warn(message)
+ }
+ BlackBoxInlineAnno(moduleName, blackBoxFile.last, contents)
+ } catch {
+ case e: os.ResourceNotFoundException =>
+ throw new BlackBoxNotFoundException(resourceName, e.getMessage)
+ }
+ }
+}
+
+import BlackBoxHelpers._
trait HasBlackBoxResource extends BlackBox {
self: BlackBox =>
- @deprecated("Use addResource instead", "3.2")
- def setResource(blackBoxResource: String): Unit = addResource(blackBoxResource)
-
- /** Copies a resource file to the target directory
+ /** Copies a Java resource containing some text into the output directory. This is typically used to copy a Verilog file
+ * to the final output directory, but may be used to copy any Java resource (e.g., a C++ testbench).
*
* Resource files are located in project_root/src/main/resources/.
* Example of adding the resource file project_root/src/main/resources/blackbox.v:
@@ -22,7 +46,7 @@ trait HasBlackBoxResource extends BlackBox {
*/
def addResource(blackBoxResource: String): Unit = {
val anno = new ChiselAnnotation with RunFirrtlTransform {
- def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource)
+ def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed)
def transformClass = classOf[BlackBoxSourceHelper]
}
chisel3.experimental.annotate(anno)
diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala
index b934f27f..1ac94bfe 100644
--- a/src/main/scala/chisel3/util/Conditional.scala
+++ b/src/main/scala/chisel3/util/Conditional.scala
@@ -12,15 +12,6 @@ import scala.reflect.macros.blackbox._
import chisel3._
import chisel3.internal.sourceinfo.SourceInfo
-@deprecated("The unless conditional is deprecated, use when(!condition){...} instead", "3.2")
-object unless {
- /** Does the same thing as [[when$ when]], but with the condition inverted.
- */
- def apply(c: Bool)(block: => Any) {
- when (!c) { block }
- }
-}
-
/** Implementation details for [[switch]]. See [[switch]] and [[chisel3.util.is is]] for the
* user-facing API.
* @note DO NOT USE. This API is subject to change without warning.
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 032d731d..8909ffe3 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -108,7 +108,7 @@ object Decoupled
@chiselName
def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.")
- val d = Wire(new DecoupledIO(irr.bits))
+ val d = Wire(new DecoupledIO(chiselTypeOf(irr.bits)))
d.bits := irr.bits
d.valid := irr.valid
irr.ready := d.ready
@@ -139,7 +139,7 @@ object Irrevocable
*/
def apply[T <: Data](dec: DecoupledIO[T]): IrrevocableIO[T] = {
require(DataMirror.directionOf(dec.bits) == Direction.Input, "Only safe to cast consumed Decoupled bits to Irrevocable.")
- val i = Wire(new IrrevocableIO(dec.bits))
+ val i = Wire(new IrrevocableIO(chiselTypeOf(dec.bits)))
dec.bits := i.bits
dec.valid := i.valid
i.ready := dec.ready
@@ -163,8 +163,9 @@ object DeqIO {
/** An I/O Bundle for Queues
* @param gen The type of data to queue
* @param entries The max number of entries in the queue.
+ * @param hasFlush A boolean for whether the generated Queue is flushable
*/
-class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
+class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boolean = false) extends Bundle
{ // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
/* These may look inverted, because the names (enq/deq) are from the perspective of the client,
@@ -177,6 +178,9 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
val deq = Flipped(DeqIO(gen))
/** The current amount of data in the queue */
val count = Output(UInt(log2Ceil(entries + 1).W))
+ /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)*/
+ val flush = if (hasFlush) Some(Input(Bool())) else None
+
}
/** A hardware module implementing a Queue
@@ -186,7 +190,8 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
* combinationally coupled.
* @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately).
* The ''valid'' signals are coupled.
- *
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element.
+ * @param hasFlush True if generated queue requires a flush feature
* @example {{{
* val q = Module(new Queue(UInt(), 16))
* q.io.enq <> producer.io.out
@@ -197,7 +202,9 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
class Queue[T <: Data](val gen: T,
val entries: Int,
val pipe: Boolean = false,
- val flow: Boolean = false)
+ val flow: Boolean = false,
+ val useSyncReadMem: Boolean = false,
+ val hasFlush: Boolean = false)
(implicit compileOptions: chisel3.CompileOptions)
extends Module() {
require(entries > -1, "Queue must have non-negative number of entries")
@@ -213,19 +220,20 @@ class Queue[T <: Data](val gen: T,
}
}
- val io = IO(new QueueIO(genType, entries))
-
- val ram = Mem(entries, genType)
+ val io = IO(new QueueIO(genType, entries, hasFlush))
+ val ram = if (useSyncReadMem) SyncReadMem(entries, genType, SyncReadMem.WriteFirst) else Mem(entries, genType)
val enq_ptr = Counter(entries)
val deq_ptr = Counter(entries)
val maybe_full = RegInit(false.B)
-
val ptr_match = enq_ptr.value === deq_ptr.value
val empty = ptr_match && !maybe_full
val full = ptr_match && maybe_full
val do_enq = WireDefault(io.enq.fire())
val do_deq = WireDefault(io.deq.fire())
+ val flush = io.flush.getOrElse(false.B)
+ // when flush is high, empty the queue
+ // Semantically, any enqueues happen before the flush.
when (do_enq) {
ram(enq_ptr.value) := io.enq.bits
enq_ptr.inc()
@@ -236,10 +244,23 @@ class Queue[T <: Data](val gen: T,
when (do_enq =/= do_deq) {
maybe_full := do_enq
}
+ when(flush) {
+ enq_ptr.reset()
+ deq_ptr.reset()
+ maybe_full := false.B
+ }
io.deq.valid := !empty
io.enq.ready := !full
- io.deq.bits := ram(deq_ptr.value)
+
+ if (useSyncReadMem) {
+ val deq_ptr_next = Mux(deq_ptr.value === (entries.U - 1.U), 0.U, deq_ptr.value + 1.U)
+ val r_addr = WireDefault(Mux(do_deq, deq_ptr_next, deq_ptr.value))
+ io.deq.bits := ram.read(r_addr)
+ }
+ else {
+ io.deq.bits := ram(deq_ptr.value)
+ }
if (flow) {
when (io.enq.valid) { io.deq.valid := true.B }
@@ -255,6 +276,7 @@ class Queue[T <: Data](val gen: T,
}
val ptr_diff = enq_ptr.value - deq_ptr.value
+
if (isPow2(entries)) {
io.count := Mux(maybe_full && ptr_match, entries.U, 0.U) | ptr_diff
} else {
@@ -285,7 +307,9 @@ object Queue
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
- flow: Boolean = false): DecoupledIO[T] = {
+ flow: Boolean = false,
+ useSyncReadMem: Boolean = false,
+ hasFlush: Boolean = false): DecoupledIO[T] = {
if (entries == 0) {
val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits)))
deq.valid := enq.valid
@@ -293,7 +317,7 @@ object Queue
enq.ready := deq.ready
deq
} else {
- val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow))
+ val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, hasFlush))
q.io.enq.valid := enq.valid // not using <> so that override is allowed
q.io.enq.bits := enq.bits
enq.ready := q.io.enq.ready
@@ -311,8 +335,9 @@ object Queue
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
- flow: Boolean = false): IrrevocableIO[T] = {
- val deq = apply(enq, entries, pipe, flow)
+ flow: Boolean = false,
+ useSyncReadMem: Boolean = false): IrrevocableIO[T] = {
+ val deq = apply(enq, entries, pipe, flow, useSyncReadMem)
require(entries > 0, "Zero-entry queues don't guarantee Irrevocability")
val irr = Wire(new IrrevocableIO(chiselTypeOf(deq.bits)))
irr.bits := deq.bits
diff --git a/src/main/scala/chisel3/util/ExtModuleUtils.scala b/src/main/scala/chisel3/util/ExtModuleUtils.scala
index 831639be..62f384bc 100644
--- a/src/main/scala/chisel3/util/ExtModuleUtils.scala
+++ b/src/main/scala/chisel3/util/ExtModuleUtils.scala
@@ -3,7 +3,10 @@
package chisel3.util
import chisel3.experimental.{ChiselAnnotation, ExtModule, RunFirrtlTransform}
-import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper}
+import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper,
+ BlackBoxNotFoundException}
+
+import BlackBoxHelpers._
trait HasExtModuleResource extends ExtModule {
self: ExtModule =>
@@ -18,7 +21,7 @@ trait HasExtModuleResource extends ExtModule {
*/
def addResource(blackBoxResource: String): Unit = {
val anno = new ChiselAnnotation with RunFirrtlTransform {
- def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource)
+ def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed)
def transformClass = classOf[BlackBoxSourceHelper]
}
chisel3.experimental.annotate(anno)
diff --git a/src/main/scala/chisel3/util/MixedVec.scala b/src/main/scala/chisel3/util/MixedVec.scala
index a632ec3a..14d6be38 100644
--- a/src/main/scala/chisel3/util/MixedVec.scala
+++ b/src/main/scala/chisel3/util/MixedVec.scala
@@ -91,6 +91,10 @@ final class MixedVec[T <: Data](private val eltsIn: Seq[T]) extends Record with
eltsIn.foreach(e => requireIsChiselType(e))
}
+ // In Scala 2.13, this is protected in IndexedSeq, must override as public because it's public in
+ // Record
+ override def className: String = "MixedVec"
+
// Clone the inputs so that we have our own references.
private val elts: IndexedSeq[T] = eltsIn.map(_.cloneTypeFull).toIndexedSeq
diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala
index 982d80d0..e2b5d172 100644
--- a/src/main/scala/chisel3/util/Reg.scala
+++ b/src/main/scala/chisel3/util/Reg.scala
@@ -42,14 +42,7 @@ object ShiftRegister
* val regDelayTwo = ShiftRegister(nextVal, 2, ena)
* }}}
*/
- def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = {
- // The order of tests reflects the expected use cases.
- if (n != 0) {
- RegEnable(apply(in, n-1, en), en)
- } else {
- in
- }
- }
+ def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).lastOption.getOrElse(in)
/** Returns the n-cycle delayed version of the input signal with reset initialization.
*
@@ -62,12 +55,30 @@ object ShiftRegister
* val regDelayTwoReset = ShiftRegister(nextVal, 2, 0.U, ena)
* }}}
*/
- def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = {
- // The order of tests reflects the expected use cases.
- if (n != 0) {
- RegEnable(apply(in, n-1, resetData, en), resetData, en)
- } else {
- in
- }
- }
+ def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = ShiftRegisters(in, n, resetData, en).lastOption.getOrElse(in)
+}
+
+
+object ShiftRegisters
+{
+ /** Returns a sequence of delayed input signal registers from 1 to n.
+ *
+ * @param in input to delay
+ * @param n number of cycles to delay
+ * @param en enable the shift
+ *
+ */
+ def apply[T <: Data](in: T, n: Int, en: Bool = true.B): Seq[T] =
+ Seq.iterate(in, n + 1)(util.RegEnable(_, en)).drop(1)
+
+ /** Returns delayed input signal registers with reset initialization from 1 to n.
+ *
+ * @param in input to delay
+ * @param n number of cycles to delay
+ * @param resetData reset value for each register in the shift
+ * @param en enable the shift
+ *
+ */
+ def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): Seq[T] =
+ Seq.iterate(in, n + 1)(util.RegEnable(_, resetData, en)).drop(1)
}
diff --git a/src/main/scala/chisel3/util/experimental/BoringUtils.scala b/src/main/scala/chisel3/util/experimental/BoringUtils.scala
index 18551da8..f2a3e757 100644
--- a/src/main/scala/chisel3/util/experimental/BoringUtils.scala
+++ b/src/main/scala/chisel3/util/experimental/BoringUtils.scala
@@ -19,7 +19,7 @@ class BoringUtilsException(message: String) extends Exception(message)
/** Utilities for generating synthesizable cross module references that "bore" through the hierarchy. The underlying
* cross module connects are handled by FIRRTL's Wiring Transform.
*
- * Consider the following exmple where you want to connect a component in one module to a component in another. Module
+ * Consider the following example where you want to connect a component in one module to a component in another. Module
* `Constant` has a wire tied to `42` and `Expect` will assert unless connected to `42`:
* {{{
* class Constant extends Module {
@@ -45,8 +45,8 @@ class BoringUtilsException(message: String) extends Exception(message)
*
* ===Hierarchical Boring===
*
- * Hierarchcical boring involves connecting one sink instance to another source instance in a parent module. Below,
- * module `Top` contains an instance of `Cosntant` and `Expect`. Using [[BoringUtils.bore]], we can connect
+ * Hierarchical boring involves connecting one sink instance to another source instance in a parent module. Below,
+ * module `Top` contains an instance of `Constant` and `Expect`. Using [[BoringUtils.bore]], we can connect
* `constant.x` to `expect.y`.
*
* {{{
@@ -89,7 +89,7 @@ class BoringUtilsException(message: String) extends Exception(message)
* ==Comments==
*
* Both hierarchical and non-hierarchical boring emit FIRRTL annotations that describe sources and sinks. These are
- * matched by a `name` key that indicates they should be wired together. Hierarhical boring safely generates this name
+ * matched by a `name` key that indicates they should be wired together. Hierarchical boring safely generates this name
* automatically. Non-hierarchical boring unsafely relies on user input to generate this name. Use of non-hierarchical
* naming may result in naming conflicts that the user must handle.
*
@@ -115,7 +115,7 @@ object BoringUtils {
/** Add a named source cross module reference
* @param component source circuit component
* @param name unique identifier for this source
- * @param disableDedup disable dedupblication of this source component (this should be true if you are trying to wire
+ * @param disableDedup disable deduplication of this source component (this should be true if you are trying to wire
* from specific identical sources differently)
* @param uniqueName if true, this will use a non-conflicting name from the global namespace
* @return the name used
@@ -137,7 +137,7 @@ object BoringUtils {
def transformClass = classOf[WiringTransform] },
new ChiselAnnotation { def toFirrtl = DontTouchAnnotation(component.toNamed) } ) ++ maybeDedup
- annotations.map(annotate(_))
+ annotations.foreach(annotate(_))
id
}
@@ -146,8 +146,8 @@ object BoringUtils {
* @param name unique identifier for this sink that must resolve to
* @param disableDedup disable deduplication of this sink component (this should be true if you are trying to wire
* specific, identical sinks differently)
- * @param forceExists if true, require that the provided `name` paramater already exists in the global namespace
- * @throws BoringUtilsException if name is expected to exist and itdoesn't
+ * @param forceExists if true, require that the provided `name` parameter already exists in the global namespace
+ * @throws BoringUtilsException if name is expected to exist and it doesn't
*/
def addSink(
component: InstanceId,
@@ -169,7 +169,7 @@ object BoringUtils {
Seq(new ChiselAnnotation with RunFirrtlTransform {
def toFirrtl = SinkAnnotation(component.toNamed, name)
def transformClass = classOf[WiringTransform] }) ++ maybeDedup
- annotations.map(annotate(_))
+ annotations.foreach(annotate(_))
}
/** Connect a source to one or more sinks
@@ -187,7 +187,7 @@ object BoringUtils {
case _: Exception => "bore"
}
val genName = addSource(source, boringName, true, true)
- sinks.map(addSink(_, genName, true, true))
+ sinks.foreach(addSink(_, genName, true, true))
genName
}
diff --git a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
index d91d97b7..93981485 100644
--- a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
+++ b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
@@ -40,7 +40,7 @@ case class ChiselLoadMemoryAnnotation[T <: Data](
}
-/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file. This relies on
+/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file as a bind module. This relies on
* Verilator and Verilog's `\$readmemh` or `\$readmemb`. The [[https://github.com/freechipsproject/treadle Treadle
* backend]] can also recognize this annotation and load memory at run-time.
*
@@ -116,6 +116,86 @@ object loadMemoryFromFile {
}
}
+
+/** [[loadMemoryFromFileInline]] is an annotation generator that helps with loading a memory from a text file inlined in
+ * the Verilog module. This relies on Verilator and Verilog's `\$readmemh` or `\$readmemb`.
+ * The [[https://github.com/freechipsproject/treadle Treadlebackend]] can also recognize this annotation and load memory at run-time.
+ *
+ * This annotation, when the FIRRTL compiler runs, triggers the [[MemoryFileInlineAnnotation]] that will add Verilog
+ * directives inlined to the module enabling the specified memories to be initialized from files.
+ * The module supports both `hex` and `bin` files by passing the appropriate [[MemoryLoadFileType.FileType]] argument with
+ * [[MemoryLoadFileType.Hex]] or [[MemoryLoadFileType.Binary]]. Hex is the default.
+ *
+ * ==Example module==
+ *
+ * Consider a simple Module containing a memory:
+ * {{{
+ * import chisel3._
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * }
+ * }}}
+ *
+ * ==Above module with annotation==
+ *
+ * To load this memory from the file `/workspace/workdir/mem1.hex.txt` just add an import and annotate the memory:
+ * {{{
+ * import chisel3._
+ * import chisel3.util.experimental.loadMemoryFromFileInline // <<-- new import here
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * loadMemoryFromFileInline(memory, "/workspace/workdir/mem1.hex.txt") // <<-- Note the annotation here
+ * }
+ * }}}
+ *
+ * ==Example file format==
+ *
+ * A memory file should consist of ASCII text in either hex or binary format. The following example shows such a
+ * file formatted to use hex:
+ * {{{
+ * 0
+ * 7
+ * d
+ * 15
+ * }}}
+ *
+ * A binary file can be similarly constructed.
+ * Chisel does not validate the file format or existence. It is supposed to be in a path accessible by the synthesis
+ * tool together with the generated Verilog.
+ *
+ * @see Chisel3 Wiki entry on
+ * [[https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories#loading-memories-in-simulation "Loading Memories
+ * in Simulation"]]
+ */
+object loadMemoryFromFileInline {
+
+
+ /** Annotate a memory such that it can be initialized inline using a file
+ * @param memory the memory
+ * @param fileName the file used for initialization
+ * @param hexOrBinary whether the file uses a hex or binary number representation
+ */
+ def apply[T <: Data](
+ memory: MemBase[T],
+ fileName: String,
+ hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex
+ ): Unit = {
+ annotate(new ChiselAnnotation {
+ override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, fileName, hexOrBinary)
+ })
+ }
+}
+
/** This transform only is activated if Verilog is being generated (determined by presence of the proper emit
* annotation) when activated it creates additional Verilog files that contain modules bound to the modules that
* contain an initializable memory
diff --git a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala
new file mode 100644
index 00000000..2b50ef90
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation}
+
+/** DecodeTableAnnotation used to store a decode result for a specific [[TruthTable]].
+ * This is useful for saving large [[TruthTable]] during a elaboration time.
+ *
+ * @note user should manage the correctness of [[minimizedTable]].
+ *
+ * @param target output wire of a decoder.
+ * @param truthTable input [[TruthTable]] encoded in a serialized [[TruthTable]].
+ * @param minimizedTable minimized [[truthTable]] encoded in a serialized [[TruthTable]].
+ */
+case class DecodeTableAnnotation(
+ target: ReferenceTarget,
+ truthTable: String,
+ minimizedTable: String)
+ extends SingleTargetAnnotation[ReferenceTarget] {
+ override def duplicate(n: ReferenceTarget): Annotation = this.copy(target = n)
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
new file mode 100644
index 00000000..1d725875
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+import logger.LazyLogging
+
+case object EspressoNotFoundException extends Exception
+
+object EspressoMinimizer extends Minimizer with LazyLogging {
+ def minimize(table: TruthTable): TruthTable =
+ TruthTable.merge(TruthTable.split(table).map{case (table, indexes) => (espresso(table), indexes)})
+
+ def espresso(table: TruthTable): TruthTable = {
+ def writeTable(table: TruthTable): String = {
+ def invert(string: String) = string
+ .replace('0', 't')
+ .replace('1', '0')
+ .replace('t', '1')
+ val defaultType: Char = {
+ val t = table.default.rawString.toCharArray.distinct
+ require(t.length == 1, "Internal Error: espresso only accept unified default type.")
+ t.head
+ }
+ val tableType: String = defaultType match {
+ case '?' => "fr"
+ case _ => "fd"
+ }
+ val rawTable = table
+ .toString
+ .split("\n")
+ .filter(_.contains("->"))
+ .mkString("\n")
+ .replace("->", " ")
+ .replace('?', '-')
+ // invert all output, since espresso cannot handle default is on.
+ val invertRawTable = rawTable
+ .split("\n")
+ .map(_.split(" "))
+ .map(row => s"${row(0)} ${invert(row(1))}")
+ .mkString("\n")
+ s""".i ${table.inputWidth}
+ |.o ${table.outputWidth}
+ |.type ${tableType}
+ |""".stripMargin ++ (if (defaultType == '1') invertRawTable else rawTable)
+ }
+
+ def readTable(espressoTable: String): Map[BitPat, BitPat] = {
+ def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?'))
+
+ espressoTable
+ .split("\n")
+ .filterNot(_.startsWith("."))
+ .map(_.split(' '))
+ .map(row => bitPat(row(0)) -> bitPat(row(1)))
+ .toMap
+ }
+
+ val input = writeTable(table)
+ logger.trace(s"""espresso input table:
+ |$input
+ |""".stripMargin)
+ val output = try {
+ os.proc("espresso").call(stdin = input).out.chunks.mkString
+ } catch {
+ case e: java.io.IOException if e.getMessage.contains("error=2, No such file or directory") => throw EspressoNotFoundException
+ }
+ logger.trace(s"""espresso output table:
+ |$output
+ |""".stripMargin)
+ TruthTable(readTable(output), table.default)
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
new file mode 100644
index 00000000..86847710
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+abstract class Minimizer {
+ /** Minimize a multi-input multi-output logic function given by the truth table `table`, with function output values
+ * on unspecified inputs treated as `default`, and return a minimized PLA-like representation of the function.
+ *
+ * Each bit of `table[]._1` encodes one 1-bit input variable of the logic function, and each bit of `default` and
+ * `table[]._2` represents one 1-bit output value of the function.
+ *
+ * @param table Truth table, can have don't cares in both inputs and outputs, specified as [(inputs, outputs), ...]
+ * @return Minimized truth table, [(inputs, outputs), ...]
+ *
+ * @example {{{
+ * minimize(BitPat("b?"), Seq(
+ * (BitPat("b000"), BitPat("b0")),
+ * // (BitPat("b001"), BitPat("b?")), // same as default, can be omitted
+ * // (BitPat("b010"), BitPat("b?")), // same as default, can be omitted
+ * (BitPat("b011"), BitPat("b0")),
+ * (BitPat("b100"), BitPat("b1")),
+ * (BitPat("b101"), BitPat("b1")),
+ * (BitPat("b110"), BitPat("b0")),
+ * (BitPat("b111"), BitPat("b1")),
+ * ))
+ * }}}
+ */
+ def minimize(table: TruthTable): TruthTable
+} \ No newline at end of file
diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
new file mode 100644
index 00000000..c1533f44
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+
+import scala.annotation.tailrec
+import scala.math.Ordered.orderingToOrdered
+import scala.language.implicitConversions
+
+object QMCMinimizer extends Minimizer {
+ private implicit def toImplicant(x: BitPat): Implicant = new Implicant(x)
+
+ private class Implicant(val bp: BitPat) {
+ var isPrime: Boolean = true
+
+ def width = bp.getWidth
+
+ override def equals(that: Any): Boolean = that match {
+ case x: Implicant => bp.value == x.bp.value && bp.mask == x.bp.mask
+ case _ => false
+ }
+
+ override def hashCode = bp.value.toInt
+
+ /** Check whether two implicants have the same value on all of the cared bits (intersection).
+ *
+ * {{{
+ * value ^^ x.value // bits that are different
+ * (bits that are different) & x.mask // bits that are different and `this` care
+ * (bits that are different and `this` care) & y.mask // bits that are different and `both` care
+ * (bits that are different and both care) == 0 // no (bits that are different and we both care) exists
+ * no (bits that are different and we both care) exists // all cared bits are the same, two terms intersect
+ * }}}
+ *
+ * @param y Implicant to be checked with
+ * @return Whether two implicants intersect
+ */
+ def intersects(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask & y.bp.mask) == 0
+
+ /** Check whether two implicants are similar.
+ * Two implicants are "similar" when they satisfy all the following rules:
+ * 1. have the same mask ('?'s are at the same positions)
+ * 1. values only differ by one bit
+ * 1. the bit at the differed position of this term is '1' (that of the other term is '0')
+ *
+ * @example this = 11?0, x = 10?0 -> similar
+ * @example this = 11??, x = 10?0 -> not similar, violated rule 1
+ * @example this = 11?1, x = 10?0 -> not similar, violated rule 2
+ * @example this = 10?0, x = 11?0 -> not similar, violated rule 3
+ * @param y Implicant to be checked with
+ * @return Whether this term is similar to the other
+ */
+ def similar(y: Implicant): Boolean = {
+ val diff = bp.value - y.bp.value
+ bp.mask == y.bp.mask && bp.value > y.bp.value && (diff & diff - 1) == 0
+ }
+
+ /** Merge two similar implicants
+ * Rule of merging: '0' and '1' merge to '?'
+ *
+ * @param y Term to be merged with
+ * @return A new term representing the merge result
+ */
+ def merge(y: Implicant): Implicant = {
+ require(similar(y), s"merge is only reasonable when $this is similar to $y")
+
+ // if two term can be merged, then they both are not prime implicants.
+ isPrime = false
+ y.isPrime = false
+ val bit = bp.value - y.bp.value
+ new BitPat(bp.value &~ bit, bp.mask &~ bit, width)
+ }
+
+ /** Check all bits in `x` cover the correspond position in `y`.
+ *
+ * Rule to define coverage relationship among `0`, `1` and `?`:
+ * 1. '?' covers '0' and '1', '0' covers '0', '1' covers '1'
+ * 1. '1' doesn't cover '?', '1' doesn't cover '0'
+ * 1. '0' doesn't cover '?', '0' doesn't cover '1'
+ *
+ * For all bits that `x` don't care, `y` can be `0`, `1`, `?`
+ * For all bits that `x` care, `y` must be the same value and not masked.
+ * {{{
+ * (~x.mask & -1) | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1
+ * -> ~x.mask | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1
+ * -> ~x.mask | ((x.value xnor y.value) & y.mask) = -1
+ * -> x.mask & ~((x.value xnor y.value) & y.mask) = 0
+ * -> x.mask & (~(x.value xnor y.value) | ~y.mask) = 0
+ * -> x.mask & ((x.value ^ y.value) | ~y.mask) = 0
+ * -> ((x.value ^ y.value) & x.mask | ~y.mask & x.mask) = 0
+ * }}}
+ *
+ * @param y to check is covered by `x` or not.
+ * @return Whether `x` covers `y`
+ */
+ def covers(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask | ~y.bp.mask & bp.mask) == 0
+
+ override def toString = (if (!isPrime) "Non" else "") + "Prime" + bp.toString.replace("BitPat", "Implicant")
+ }
+
+ /**
+ * If two terms have different value, then their order is determined by the value, or by the mask.
+ */
+ private implicit def ordering: Ordering[Implicant] = new Ordering[Implicant] {
+ override def compare(x: Implicant, y: Implicant): Int =
+ if (x.bp.value < y.bp.value || x.bp.value == y.bp.value && x.bp.mask > y.bp.mask) -1 else 1
+ }
+
+ /** Calculate essential prime implicants based on previously calculated prime implicants and all implicants.
+ *
+ * @param primes Prime implicants
+ * @param minterms All implicants
+ * @return (a, b, c)
+ * a: essential prime implicants
+ * b: nonessential prime implicants
+ * c: implicants that are not cover by any of the essential prime implicants
+ */
+ private def getEssentialPrimeImplicants(primes: Seq[Implicant], minterms: Seq[Implicant]): (Seq[Implicant], Seq[Implicant], Seq[Implicant]) = {
+ // primeCovers(i): implicants that `prime(i)` covers
+ val primeCovers = primes.map(p => minterms.filter(p.covers))
+ // eliminate prime implicants that can be covered by other prime implicants
+ for (((icover, pi), i) <- (primeCovers zip primes).zipWithIndex) {
+ for (((jcover, pj), _) <- (primeCovers zip primes).zipWithIndex.drop(i + 1)) {
+ // we prefer prime implicants with wider implicants coverage
+ if (icover.size > jcover.size && jcover.forall(pi.covers)) {
+ // calculate essential prime implicants with `pj` eliminated from prime implicants table
+ return getEssentialPrimeImplicants(primes.filter(_ != pj), minterms)
+ }
+ }
+ }
+
+ // implicants that only one prime implicant covers
+ val essentiallyCovered = minterms.filter(t => primes.count(_.covers(t)) == 1)
+ // essential prime implicants, prime implicants that covers only one implicant
+ val essential = primes.filter(p => essentiallyCovered.exists(p.covers))
+ // {nonessential} = {prime implicants} - {essential prime implicants}
+ val nonessential = primes.filterNot(essential contains _)
+ // implicants that no essential prime implicants covers
+ val uncovered = minterms.filterNot(t => essential.exists(_.covers(t)))
+ if (essential.isEmpty || uncovered.isEmpty)
+ (essential, nonessential, uncovered)
+ else {
+ // now there are implicants (`uncovered`) that are covered by multiple nonessential prime implicants (`nonessential`)
+ // need to reduce prime implicants
+ val (a, b, c) = getEssentialPrimeImplicants(nonessential, uncovered)
+ (essential ++ a, b, c)
+ }
+ }
+
+ /** Use [[https://en.wikipedia.org/wiki/Petrick%27s_method]] to select a [[Seq]] of nonessential prime implicants
+ * that covers all implicants that are not covered by essential prime implicants.
+ *
+ * @param implicants Nonessential prime implicants
+ * @param minterms Implicants that are not covered by essential prime implicants
+ * @return Selected nonessential prime implicants
+ */
+ private def getCover(implicants: Seq[Implicant], minterms: Seq[Implicant]): Seq[Implicant] = {
+ /** Calculate the implementation cost (using comparators) of a list of implicants, more don't cares is cheaper
+ *
+ * @param cover Implicant list
+ * @return How many comparators need to implement this list of implicants
+ */
+ def getCost(cover: Seq[Implicant]): Int = cover.map(_.bp.mask.bitCount).sum
+
+ /** Determine if one combination of prime implicants is cheaper when implementing as comparators.
+ * Shorter term list is cheaper, term list with more don't cares is cheaper (less comparators)
+ *
+ * @param a Operand a
+ * @param b Operand b
+ * @return `a` < `b`
+ */
+ def cheaper(a: Seq[Implicant], b: Seq[Implicant]): Boolean = {
+ val ca = getCost(a)
+ val cb = getCost(b)
+
+ /** If `a` < `b`
+ *
+ * Like comparing the dictionary order of two strings.
+ * Define `a` < `b` if both `a` and `b` are empty.
+ *
+ * @param a Operand a
+ * @param b Operand b
+ * @return `a` < `b`
+ */
+ @tailrec
+ def listLess(a: Seq[Implicant], b: Seq[Implicant]): Boolean = b.nonEmpty && (a.isEmpty || a.head < b.head || a.head == b.head && listLess(a.tail, b.tail))
+
+ ca < cb || ca == cb && listLess(a.sortWith(_ < _), b.sortWith(_ < _))
+ }
+
+ // if there are no implicant that is not covered by essential prime implicants, which means all implicants are
+ // covered by essential prime implicants, there is no need to apply Petrick's method
+ if (minterms.nonEmpty) {
+ // cover(i): nonessential prime implicants that covers `minterms(i)`
+ val cover = minterms.map(m => implicants.filter(_.covers(m)))
+ // all subsets of `cover`, NP algorithm, O(2 ^ len(cover))
+ val all = cover.tail.foldLeft(cover.head.map(Set(_)))((c0, c1) => c0.flatMap(a => c1.map(a + _)))
+ all.map(_.toList).reduceLeft((a, b) => if (cheaper(a, b)) a else b)
+ } else
+ Seq[Implicant]()
+ }
+
+ def minimize(table: TruthTable): TruthTable = {
+ require(table.table.nonEmpty, "Truth table must not be empty")
+
+ // extract decode table to inputs and outputs
+ val (inputs, outputs) = table.table.unzip
+
+ require(outputs.map(_.getWidth == table.default.getWidth).reduce(_ && _), "All output BitPats and default BitPat must have the same length")
+ require(if (inputs.toSeq.length > 1) inputs.tail.map(_.width == inputs.head.width).reduce(_ && _) else true, "All input BitPats must have the same length")
+
+ // make sure no two inputs specified in the truth table intersect
+ for (t <- inputs.tails; if t.nonEmpty)
+ for (u <- t.tail)
+ require(!t.head.intersects(u), "truth table entries " + t.head + " and " + u + " overlap")
+
+ // number of inputs
+ val n = inputs.head.width
+ // number of outputs
+ val m = outputs.head.getWidth
+
+ // for all outputs
+ val minimized = (0 until m).flatMap(i => {
+ val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i)
+
+ // Minterms, implicants that makes the output to be 1
+ val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ // Maxterms, implicants that makes the output to be 0
+ val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ // Don't cares, implicants that can produce either 0 or 1 as output
+ val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.keys.map(toImplicant).toSeq
+
+ val (implicants, defaultToDc) = table.default match {
+ case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0
+ (mint ++ dc, false)
+ case x if x.mask.testBit(i) && x.value.testBit(i) => // default to 1
+ (maxt ++ dc, false)
+ case x if !x.mask.testBit(i) => // default to ?
+ (mint, true)
+ }
+
+ implicants.foreach(_.isPrime = true)
+ val cols = (0 to n).reverse.map(b => implicants.filter(b == _.bp.mask.bitCount))
+ val mergeTable = cols.map(
+ c => (0 to n).map(
+ b => collection.mutable.Set(c.filter(b == _.bp.value.bitCount):_*)
+ )
+ )
+
+ // O(n ^ 3)
+ for (i <- 0 to n) {
+ for (j <- 0 until n - i) {
+ mergeTable(i)(j).foreach(a => mergeTable(i + 1)(j) ++= mergeTable(i)(j + 1).filter(_ similar a).map(_ merge a))
+ }
+ if (defaultToDc) {
+ for (j <- 0 until n - i) {
+ for (a <- mergeTable(i)(j).filter(_.isPrime)) {
+ if (a.bp.mask.testBit(i) && !a.bp.value.testBit(i)) {
+ // this bit is `0`
+ val t = new BitPat(a.bp.value.setBit(i), a.bp.mask, a.width)
+ if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += t merge a
+ }
+ }
+ for (a <- mergeTable(i)(j + 1).filter(_.isPrime)) {
+ if (a.bp.mask.testBit(i) && a.bp.value.testBit(i)) {
+ // this bit is `1`
+ val t = new BitPat(a.bp.value.clearBit(i), a.bp.mask, a.width)
+ if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += a merge t
+ }
+ }
+ }
+ }
+ }
+
+ val primeImplicants = mergeTable.flatten.flatten.filter(_.isPrime).sortWith(_ < _)
+
+ // O(len(primeImplicants) ^ 4)
+ val (essentialPrimeImplicants, nonessentialPrimeImplicants, uncoveredImplicants) =
+ getEssentialPrimeImplicants(primeImplicants, implicants)
+
+ (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp))
+ })
+
+ minimized.tail.foldLeft(table.copy(table = Map(minimized.head))) { case (tb, t) =>
+ if (tb.table.exists(x => x._1 == t._1)) {
+ tb.copy(table = tb.table.map { case (k, v) =>
+ if (k == t._1) {
+ def ones(bitPat: BitPat) = bitPat.rawString.zipWithIndex.collect{case ('1', x) => x}
+ (k, BitPat("b" + (0 until v.getWidth).map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?").mkString))
+ } else (k, v)
+ })
+ } else {
+ tb.copy(table = tb.table + t)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
new file mode 100644
index 00000000..683de16b
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+
+final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) {
+
+ def inputWidth = table.head._1.getWidth
+
+ def outputWidth = table.head._2.getWidth
+
+ override def toString: String = {
+ def writeRow(map: (BitPat, BitPat)): String =
+ s"${map._1.rawString}->${map._2.rawString}"
+
+ (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).toSeq.sorted.mkString("\n")
+ }
+
+ def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default)
+
+ override def equals(y: Any): Boolean = {
+ y match {
+ case y: TruthTable => toString == y.toString
+ case _ => false
+ }
+ }
+}
+
+object TruthTable {
+ /** Parse TruthTable from its string representation. */
+ def apply(tableString: String): TruthTable = {
+ TruthTable(
+ tableString
+ .split("\n")
+ .filter(_.contains("->"))
+ .map(_.split("->").map(str => BitPat(s"b$str")))
+ .map(bps => bps(0) -> bps(1))
+ .toSeq,
+ BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}")
+ )
+ }
+
+ /** Convert a table and default output into a [[TruthTable]]. */
+ def apply(table: Iterable[(BitPat, BitPat)], default: BitPat): TruthTable = {
+ require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.")
+ require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.")
+ val outputWidth = table.map(_._2.getWidth).head
+ new TruthTable(table.toSeq.groupBy(_._1.toString).map { case (key, values) =>
+ // merge same input inputs.
+ values.head._1 -> BitPat(s"b${
+ Seq.tabulate(outputWidth) { i =>
+ val outputSet = values.map(_._2)
+ .map(_.rawString)
+ .map(_ (i))
+ .toSet
+ .filterNot(_ == '?')
+ require(outputSet.size != 2, s"TruthTable conflict in :\n${values.map { case (i, o) => s"${i.rawString}->${o.rawString}" }.mkString("\n")}")
+ outputSet.headOption.getOrElse('?')
+ }.mkString
+ }")
+ }, default)
+ }
+
+
+ /** consume 1 table, split it into up to 3 tables with the same default bits.
+ *
+ * @return table and its indexes from original bits.
+ * @note
+ * Since most of minimizer(like espresso) cannot handle a multiple default table.
+ * It is useful to split a table into 3 tables based on the default type.
+ */
+ private[decode] def split(
+ table: TruthTable
+ ): Seq[(TruthTable, Seq[Int])] = {
+ def bpFilter(bitPat: BitPat, indexes: Seq[Int]): BitPat =
+ BitPat(s"b${bitPat.rawString.zipWithIndex.filter(b => indexes.contains(b._2)).map(_._1).mkString}")
+
+ def tableFilter(indexes: Seq[Int]): Option[(TruthTable, Seq[Int])] = {
+ if(indexes.nonEmpty) Some((TruthTable(
+ table.table.map { case (in, out) => in -> bpFilter(out, indexes) },
+ bpFilter(table.default, indexes)
+ ), indexes)) else None
+ }
+
+ def index(bitPat: BitPat, bpType: Char): Seq[Int] =
+ bitPat.rawString.zipWithIndex.filter(_._1 == bpType).map(_._2)
+
+ Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t)))
+ }
+
+ /** consume tables, merge it into single table with different default bits.
+ *
+ * @note
+ * Since most of minimizer(like espresso) cannot handle a multiple default table.
+ * It is useful to split a table into 3 tables based on the default type.
+ */
+ private[decode] def merge(
+ tables: Seq[(TruthTable, Seq[Int])]
+ ): TruthTable = {
+ def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] =
+ (table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).rawString.zip(indexes)
+ def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar
+ .sortBy(_._2)
+ .map(_._1)
+ .mkString}")
+ TruthTable(
+ tables
+ .flatMap(_._1.table.keys)
+ .map { key =>
+ key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) })
+ }
+ .toMap,
+ bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) })
+ )
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
new file mode 100644
index 00000000..42e374d1
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3._
+import chisel3.experimental.{ChiselAnnotation, annotate}
+import chisel3.util.{BitPat, pla}
+import chisel3.util.experimental.getAnnotations
+import firrtl.annotations.Annotation
+import logger.LazyLogging
+
+object decoder extends LazyLogging {
+ /** Use a specific [[Minimizer]] to generated decoded signals.
+ *
+ * @param minimizer specific [[Minimizer]], can be [[QMCMinimizer]] or [[EspressoMinimizer]].
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = {
+ val minimizedTable = getAnnotations().collect {
+ case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out)
+ }.toMap.getOrElse(truthTable, minimizer.minimize(truthTable))
+ if (minimizedTable.table.isEmpty) {
+ val outputs = Wire(UInt(minimizedTable.default.getWidth.W))
+ outputs := minimizedTable.default.value.U(minimizedTable.default.getWidth.W)
+ outputs
+ } else {
+ val (plaInput, plaOutput) =
+ pla(minimizedTable.table.toSeq, BitPat(minimizedTable.default.value.U(minimizedTable.default.getWidth.W)))
+
+ annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation =
+ DecodeTableAnnotation(plaOutput.toTarget, truthTable.toString, minimizedTable.toString)
+ })
+
+ plaInput := input
+ plaOutput
+ }
+ }
+
+ /** Use [[EspressoMinimizer]] to generated decoded signals.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def espresso(input: UInt, truthTable: TruthTable): UInt = apply(EspressoMinimizer, input, truthTable)
+
+ /** Use [[QMCMinimizer]] to generated decoded signals.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def qmc(input: UInt, truthTable: TruthTable): UInt = apply(QMCMinimizer, input, truthTable)
+
+ /** try to use [[EspressoMinimizer]] to decode `input` by `truthTable`
+ * if `espresso` not exist in your PATH environment it will fall back to [[QMCMinimizer]], and print a warning.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def apply(input: UInt, truthTable: TruthTable): UInt = {
+ def qmcFallBack(input: UInt, truthTable: TruthTable) = {
+ """fall back to QMC.
+ |Quine-McCluskey is a NP complete algorithm, may run forever or run out of memory in large decode tables.
+ |To get rid of this warning, please use `decoder.qmc` directly, or add espresso to your PATH.
+ |""".stripMargin
+ qmc(input, truthTable)
+ }
+
+ try espresso(input, truthTable) catch {
+ case EspressoNotFoundException =>
+ logger.error(s"espresso is not found in your PATH:\n${sys.env("PATH").split(":").mkString("\n")}".stripMargin)
+ qmcFallBack(input, truthTable)
+ case e: java.io.IOException =>
+ logger.error(s"espresso failed to run with ${e.getMessage}")
+ qmcFallBack(input, truthTable)
+ }
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/getAnnotations.scala b/src/main/scala/chisel3/util/experimental/getAnnotations.scala
new file mode 100644
index 00000000..dc9b75ee
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/getAnnotations.scala
@@ -0,0 +1,9 @@
+package chisel3.util.experimental
+
+import chisel3.internal.Builder
+import firrtl.AnnotationSeq
+
+object getAnnotations {
+ /** Returns the global Annotations */
+ def apply(): AnnotationSeq = Builder.annotationSeq
+}
diff --git a/src/main/scala/chisel3/util/pla.scala b/src/main/scala/chisel3/util/pla.scala
new file mode 100644
index 00000000..c57ca962
--- /dev/null
+++ b/src/main/scala/chisel3/util/pla.scala
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util
+
+import chisel3._
+
+object pla {
+
+ /** Construct a [[https://en.wikipedia.org/wiki/Programmable_logic_array]] from specified table.
+ *
+ * Each position in the input matrix corresponds to an input variable where
+ * `0` implies the corresponding input literal appears complemented in the product term.
+ * `1` implies the input literal appears uncomplemented in the product term
+ * `?` implies the input literal does not appear in the product term.
+ *
+ * For each output
+ * a `1` means this product term makes the function value to `1`
+ * and a `0` or `?` means this product term make the function value to `0`
+ *
+ * @note There is one special case which we call it `? -> 1`. In this scenario, for some of the output functions (bits),
+ * all input terms that make this function value to `1` is purely composed by `?`. In a real pla, this will result in
+ * no connection to the gates in the AND Plane (verilog `z` on gate inputs), which in turn makes the outputs of the
+ * AND Plane undetermined (verilog `x` on outputs). This is not desired behavior in most cases, for example the
+ * minimization result of following truth table:
+ * 0 -> 1
+ * 1 -> 1
+ * which is:
+ * ? -> 1
+ * actually means something other than a verilog `x`. To ease the generation of minimized truth tables, this pla
+ * generation api will hard wire outputs to a `1` on this special case.
+ * This behavior is formally described as: if product terms that make one function value to `1` is solely consisted
+ * of don't-cares (`?`s), then this function is implemented as a constant `1`.
+ *
+ * @param table A [[Seq]] of inputs -> outputs mapping
+ * @param invert A [[BitPat]] specify which bit of the output should be inverted. `1` means the correspond position
+ * of the output should be inverted in the PLA, a `0` or a `?` means direct output from the OR matrix.
+ * @return the (input, output) [[Wire]] of [[UInt]] of the constructed pla.
+ * {{{
+ * // A 1-of-8 decoder (like the 74xx138) can be constructed as follow
+ * val (inputs, outputs) = pla(Seq(
+ * (BitPat("b000"), BitPat("b00000001")),
+ * (BitPat("b001"), BitPat("b00000010")),
+ * (BitPat("b010"), BitPat("b00000100")),
+ * (BitPat("b011"), BitPat("b00001000")),
+ * (BitPat("b100"), BitPat("b00010000")),
+ * (BitPat("b101"), BitPat("b00100000")),
+ * (BitPat("b110"), BitPat("b01000000")),
+ * (BitPat("b111"), BitPat("b10000000")),
+ * ))
+ * }}}
+ */
+ def apply(table: Seq[(BitPat, BitPat)], invert: BitPat = BitPat("b0")): (UInt, UInt) = {
+ require(table.nonEmpty, "pla table must not be empty")
+
+ val (inputTerms, outputTerms) = table.unzip
+ require(
+ inputTerms.map(_.getWidth).distinct.size == 1,
+ "all `BitPat`s in the input part of specified PLA table must have the same width"
+ )
+ require(
+ outputTerms.map(_.getWidth).distinct.size == 1,
+ "all `BitPat`s in the output part of specified PLA table must have the same width"
+ )
+
+ // now all inputs / outputs have the same width
+ val numberOfInputs = inputTerms.head.getWidth
+ val numberOfOutputs = outputTerms.head.getWidth
+
+ val inverterMask = invert.value & invert.mask
+ if (inverterMask.bitCount != 0)
+ require(invert.getWidth == numberOfOutputs,
+ "non-zero inverter mask must have the same width as the output part of specified PLA table"
+ )
+
+ // input wires of the generated PLA
+ val inputs = Wire(UInt(numberOfInputs.W))
+ val invInputs = ~inputs
+
+ // output wires of the generated PLA
+ val outputs = Wire(UInt(numberOfOutputs.W))
+
+ // the AND matrix
+ // use `term -> AND line` map to reuse AND matrix output lines
+ val andMatrixOutputs: Map[String, Bool] = inputTerms.map { t =>
+ val andMatrixInput = Seq
+ .tabulate(numberOfInputs) { i =>
+ if (t.mask.testBit(i)) {
+ Some(
+ if (t.value.testBit(i)) inputs(i)
+ else invInputs(i)
+ )
+ } else {
+ None
+ }
+ }
+ .flatten
+ if (andMatrixInput.nonEmpty) t.toString -> Cat(andMatrixInput).andR() else t.toString -> true.B
+ }.toMap
+
+ // the OR matrix
+ val orMatrixOutputs: UInt = Cat(
+ Seq
+ .tabulate(numberOfOutputs) { i =>
+ val andMatrixLines = table
+ // OR matrix composed by input terms which makes this output bit a `1`
+ .filter {
+ case (_, or) => or.mask.testBit(i) && or.value.testBit(i)
+ }.map {
+ case (inputTerm, _) =>
+ andMatrixOutputs(inputTerm.toString)
+ }
+ if (andMatrixLines.isEmpty) false.B
+ else Cat(andMatrixLines).orR()
+ }
+ .reverse
+ )
+
+ // the INV matrix, useful for decoders
+ val invMatrixOutputs: UInt = Cat(
+ Seq
+ .tabulate(numberOfOutputs) { i =>
+ if (inverterMask.testBit(i)) ~orMatrixOutputs(i)
+ else orMatrixOutputs(i)
+ }
+ .reverse
+ )
+
+ outputs := invMatrixOutputs
+
+ (inputs, outputs)
+ }
+}
diff --git a/src/main/scala/chisel3/verilog.scala b/src/main/scala/chisel3/verilog.scala
new file mode 100644
index 00000000..a91444de
--- /dev/null
+++ b/src/main/scala/chisel3/verilog.scala
@@ -0,0 +1,15 @@
+package chisel3
+
+import chisel3.stage.ChiselStage
+import firrtl.AnnotationSeq
+
+object getVerilogString {
+ def apply(gen: => RawModule): String = ChiselStage.emitVerilog(gen)
+}
+
+object emitVerilog {
+ def apply(gen: => RawModule, args: Array[String] = Array.empty,
+ annotations: AnnotationSeq = Seq.empty): Unit = {
+ (new ChiselStage).emitVerilog(gen, args, annotations)
+ }
+}
diff --git a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala b/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
deleted file mode 100644
index b80d5298..00000000
--- a/src/test/scala/chisel3/stage/phases/DriverCompatibilitySpec.scala
+++ /dev/null
@@ -1,71 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chisel3.stage.phases
-
-
-import chisel3.stage.{NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation}
-
-import firrtl.options.{OutputAnnotationFileAnnotation, StageOptions}
-import firrtl.options.Viewer.view
-import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation
-import org.scalatest.flatspec.AnyFlatSpec
-import org.scalatest.matchers.should.Matchers
-
-class DriverCompatibilitySpec extends AnyFlatSpec with Matchers {
-
- behavior of classOf[DriverCompatibility.AddImplicitOutputFile].toString
-
- it should "do nothing if a ChiselOutputFileAnnotation is present" in {
- val annotations = Seq(
- ChiselOutputFileAnnotation("Foo"),
- TopNameAnnotation("Bar") )
- (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (annotations)
- }
-
- it should "add a ChiselOutputFileAnnotation derived from a TopNameAnnotation" in {
- val annotations = Seq( TopNameAnnotation("Bar") )
- val expected = ChiselOutputFileAnnotation("Bar") +: annotations
- (new DriverCompatibility.AddImplicitOutputFile).transform(annotations).toSeq should be (expected)
- }
-
- behavior of classOf[DriverCompatibility.AddImplicitOutputAnnotationFile].toString
-
- it should "do nothing if an OutputAnnotationFileAnnotation is present" in {
- val annotations = Seq(
- OutputAnnotationFileAnnotation("Foo"),
- TopNameAnnotation("Bar") )
- (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (annotations)
- }
-
- it should "add an OutputAnnotationFileAnnotation derived from a TopNameAnnotation" in {
- val annotations = Seq( TopNameAnnotation("Bar") )
- val expected = OutputAnnotationFileAnnotation("Bar") +: annotations
- (new DriverCompatibility.AddImplicitOutputAnnotationFile).transform(annotations).toSeq should be (expected)
- }
-
- behavior of classOf[DriverCompatibility.DisableFirrtlStage].toString
-
- it should "add a NoRunFirrtlCompilerAnnotation if one does not exist" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- val expected = DriverCompatibility.RunFirrtlCompilerAnnotation +: annos
- (new DriverCompatibility.DisableFirrtlStage).transform(Seq.empty).toSeq should be (expected)
- }
-
- it should "NOT add a NoRunFirrtlCompilerAnnotation if one already exists" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- (new DriverCompatibility.DisableFirrtlStage).transform(annos).toSeq should be (annos)
- }
-
- behavior of classOf[DriverCompatibility.ReEnableFirrtlStage].toString
-
- it should "NOT strip a NoRunFirrtlCompilerAnnotation if NO RunFirrtlCompilerAnnotation is present" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation, DriverCompatibility.RunFirrtlCompilerAnnotation)
- (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (Seq.empty)
- }
-
- it should "strip a NoRunFirrtlCompilerAnnotation if a RunFirrtlCompilerAnnotation is present" in {
- val annos = Seq(NoRunFirrtlCompilerAnnotation)
- (new DriverCompatibility.ReEnableFirrtlStage).transform(annos).toSeq should be (annos)
- }
-
-}
diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala
index 12712bf7..c72c779a 100644
--- a/src/test/scala/chisel3/testers/TestUtils.scala
+++ b/src/test/scala/chisel3/testers/TestUtils.scala
@@ -3,10 +3,22 @@
package chisel3.testers
import TesterDriver.Backend
+import chisel3.{Bundle, RawModule}
+import chisel3.internal.firrtl.Circuit
+import chisel3.stage.ChiselStage
import firrtl.AnnotationSeq
object TestUtils {
// Useful because TesterDriver.Backend is chisel3 package private
def containsBackend(annos: AnnotationSeq): Boolean =
annos.collectFirst { case b: Backend => b }.isDefined
+
+ // Allows us to check that the compiler plugin cloneType is actually working
+ val usingPlugin: Boolean = (new Bundle { def check = _usingPlugin }).check
+ def elaborateNoReflectiveAutoCloneType(f: => RawModule): Circuit = {
+ ChiselStage.elaborate {
+ chisel3.internal.Builder.allowReflectiveAutoCloneType = !usingPlugin
+ f
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/AsyncResetSpec.scala b/src/test/scala/chiselTests/AsyncResetSpec.scala
index a8e62fe8..d49f390c 100644
--- a/src/test/scala/chiselTests/AsyncResetSpec.scala
+++ b/src/test/scala/chiselTests/AsyncResetSpec.scala
@@ -209,7 +209,6 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils {
}
it should "support Fixed regs" in {
- import chisel3.experimental.{withReset => _, _}
assertTesterPasses(new BasicTester {
val reg = withReset(reset.asAsyncReset)(RegNext(-6.0.F(2.BP), 3.F(2.BP)))
val (count, done) = Counter(true.B, 4)
@@ -223,7 +222,7 @@ class AsyncResetSpec extends ChiselFlatSpec with Utils {
}
it should "support Interval regs" in {
- import chisel3.experimental.{withReset => _, _}
+ import chisel3.experimental._
assertTesterPasses(new BasicTester {
val reg = withReset(reset.asAsyncReset) {
val x = RegInit(Interval(range"[0,13]"), 13.I)
diff --git a/src/test/scala/chiselTests/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
index b791297d..fcbc4785 100644
--- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala
+++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala
@@ -3,7 +3,8 @@
package chiselTests
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
+import chisel3.util.QueueIO
class BundleWithIntArg(val i: Int) extends Bundle {
val out = UInt(i.W)
@@ -65,10 +66,19 @@ class NestedAnonymousBundle extends Bundle {
// Not necessarily good style (and not necessarily recommended), but allowed to preserve compatibility.
class BundleWithArgumentField(val x: Data, val y: Data) extends Bundle
+// Needs to be top-level so that reflective autoclonetype works
+class InheritingBundle extends QueueIO(UInt(8.W), 8) {
+ val error = Output(Bool())
+}
+
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
"Bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new BundleWithIntArg(8))
assert(myWire.i == 8)
@@ -76,8 +86,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala implicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithImplicit())
@@ -87,8 +97,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Bundles with Scala explicit and impicit args" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
implicit val implicitInt: Int = 4
val myWire = Wire(new BundleWithArgAndImplicit(8))
@@ -99,16 +109,16 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
}
"Subtyped Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundle(8, 4))
assert(myWire.i == 8)
assert(myWire.i2 == 4)
} }
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
val myWire = Wire(new SubBundleVal(8, 4))
@@ -117,68 +127,88 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
} }
}
- "Subtyped Bundles that don't clone well" should "be caught" in {
- a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
- val io = IO(new Bundle{})
- val myWire = Wire(new SubBundleInvalid(8, 4))
- } }
+ "Autoclonetype" should "work outside of a builder context" in {
+ new BundleWithIntArg(8).cloneType
+ }
+
+ def checkSubBundleInvalid() = {
+ elaborate { new Module {
+ val io = IO(new Bundle{}).suggestName("io")
+ val myWire = Wire(new SubBundleInvalid(8, 4))
+ } }
+ }
+ if (usingPlugin) {
+ "Subtyped Bundles that don't clone well" should "be now be supported!" in {
+ checkSubBundleInvalid()
+ }
+ } else {
+ "Subtyped Bundles that don't clone well" should "be caught" in {
+ a [ChiselException] should be thrownBy extractCause[ChiselException] {
+ checkSubBundleInvalid()
+ }
}
}
"Inner bundles with Scala args" should "not need clonetype" in {
- ChiselStage.elaborate { new ModuleWithInner }
+ elaborate { new ModuleWithInner }
}
"Bundles with arguments as fields" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W))))
+ elaborate { new Module {
+ val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))).suggestName("io")
io.x := 1.U
io.y := 1.U
} }
}
+ it should "also work when giving directions to the fields" in {
+ elaborate { new Module {
+ val io = IO(new BundleWithArgumentField(Input(UInt(8.W)), Output(UInt(8.W)))).suggestName("io")
+ io.y := io.x
+ } }
+ }
+
"Bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.Inner))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.Inner)).suggestName("io")
io.data := 1.U
} }
}
"Parameterized bundles inside companion objects" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8)))
+ elaborate { new Module {
+ val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))).suggestName("io")
io.data := 1.U
} }
}
"Nested directioned anonymous Bundles" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
- val io = IO(new NestedAnonymousBundle)
+ elaborate { new Module {
+ val io = IO(new NestedAnonymousBundle).suggestName("io")
val a = WireDefault(io)
io.a.a := 1.U
} }
}
"3.0 null compatibility" should "not need clonetype" in {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
class InnerClassThing {
def createBundle: Bundle = new Bundle {
val a = Output(UInt(8.W))
}
}
- val io = IO((new InnerClassThing).createBundle)
+ val io = IO((new InnerClassThing).createBundle).suggestName("io")
val a = WireDefault(io)
} }
}
"Aliased fields" should "be caught" in {
a [ChiselException] should be thrownBy extractCause[ChiselException] {
- ChiselStage.elaborate { new Module {
+ elaborate { new Module {
val bundleFieldType = UInt(8.W)
val io = IO(Output(new Bundle {
val a = bundleFieldType
- }))
+ })).suggestName("io")
io.a := 0.U
} }
}
@@ -190,8 +220,8 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val a = typeTuple._1
}
- ChiselStage.elaborate { new Module {
- val io = IO(Output(new BadBundle(UInt(8.W), 1)))
+ elaborate { new Module {
+ val io = IO(Output(new BadBundle(UInt(8.W), 1))).suggestName("io")
io.a := 0.U
} }
}
@@ -204,7 +234,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(3.W))
}))
}
- ChiselStage.elaborate { new TestModule }
+ elaborate { new TestModule }
}
"Wrapped IO construction with parent references" should "not fail for autoclonetype" in {
@@ -216,6 +246,120 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils {
val x = Output(UInt(blah.W))
}))
}
- ChiselStage.elaborate { new TestModule(3) }
+ elaborate { new TestModule(3) }
+ }
+
+ "Autoclonetype" should "support Bundles with if-blocks" in {
+ class MyModule(n: Int) extends MultiIOModule {
+ val io = IO(new Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ if (n > 4) {
+ println("Here we are!")
+ }
+ })
+ io.out := io.in
+ }
+ elaborate(new MyModule(3))
+ }
+
+ behavior of "Compiler Plugin Autoclonetype"
+
+ // New tests from the plugin
+ if (usingPlugin) {
+ it should "NOT break code that extends chisel3.util Bundles if they use the plugin" in {
+ class MyModule extends MultiIOModule {
+ val io = IO(new InheritingBundle)
+ io.deq <> io.enq
+ io.count := 0.U
+ io.error := true.B
+ }
+ elaborate(new MyModule)
+ }
+
+ it should "support Bundles with non-val parameters" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support type-parameterized Bundles" in {
+ class MyBundle[T <: Data](gen: T) extends Bundle {
+ val foo = gen
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(UInt(8.W))))
+ val out = IO(Output(new MyBundle(UInt(8.W))))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with non-val implicit parameters" in {
+ class MyBundle(implicit i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ }
+ elaborate { new MultiIOModule {
+ implicit val x = 8
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(new MyBundle))
+ out := in
+ }}
+ }
+
+ it should "support Bundles with multiple parameter lists" in {
+ class MyBundle(i: Int)(j: Int, jj: Int)(k: UInt) extends Bundle {
+ val foo = UInt((i + j + jj + k.getWidth).W)
+ }
+ elaborate {
+ new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)(8, 8)(UInt(8.W))))
+ val out = IO(Output(new MyBundle(8)(8, 8)(UInt(8.W))))
+ out := in
+ }
+ }
+ }
+
+ it should "support Bundles that implement their own cloneType" in {
+ class MyBundle(i: Int) extends Bundle {
+ val foo = UInt(i.W)
+ override def cloneType = new MyBundle(i).asInstanceOf[this.type]
+ }
+ elaborate { new MultiIOModule {
+ val in = IO(Input(new MyBundle(8)))
+ val out = IO(Output(new MyBundle(8)))
+ out := in
+ }}
+ }
+
+ it should "support Bundles that capture type parameters from their parent scope" in {
+ class MyModule[T <: Data](gen: T) extends MultiIOModule {
+ class MyBundle(n: Int) extends Bundle {
+ val foo = Vec(n, gen)
+ }
+ val in = IO(Input(new MyBundle(4)))
+ val out = IO(Output(new MyBundle(4)))
+ out := in
+ }
+ elaborate(new MyModule(UInt(8.W)))
+ }
+
+ it should "work for higher-kinded types" in {
+ class DataGen[T <: Data](gen: T) {
+ def newType: T = gen.cloneType
+ }
+ class MyBundle[A <: Data, B <: DataGen[A]](gen: B) extends Bundle {
+ val foo = gen.newType
+ }
+ class MyModule extends MultiIOModule {
+ val io = IO(Output(new MyBundle[UInt, DataGen[UInt]](new DataGen(UInt(3.W)))))
+ io.foo := 0.U
+ }
+ elaborate(new MyModule)
+ }
}
}
diff --git a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
index 8e40ad20..401766e2 100644
--- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
+++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala
@@ -1,10 +1,8 @@
// SPDX-License-Identifier: Apache-2.0
package chiselTests
-import Chisel.ChiselException
-import org.scalatest._
import chisel3._
-import chisel3.stage.ChiselStage
+import chisel3.testers.TestUtils
import org.scalatest.matchers.should.Matchers
class BundleWithAnonymousInner(val w: Int) extends Bundle {
@@ -13,11 +11,15 @@ class BundleWithAnonymousInner(val w: Int) extends Bundle {
}
}
+// TODO all `.suggestNames` are due to https://github.com/chipsalliance/chisel3/issues/1802
class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
+ val usingPlugin: Boolean = TestUtils.usingPlugin
+ val elaborate = TestUtils.elaborateNoReflectiveAutoCloneType _
+
behavior of "autoCloneType of inner Bundle in Chisel3"
it should "clone a doubly-nested inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class Outer(val w: Int) extends Module {
class Middle(val w: Int) {
class InnerIOType extends Bundle {
@@ -25,7 +27,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
def getIO: InnerIOType = new InnerIOType
}
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire((new Middle(w)).getIO)
}
new Outer(2)
@@ -33,9 +35,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous inner bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestTop(val w: Int) extends Module {
- val io = IO(new Bundle {})
+ val io = IO(new Bundle {}).suggestName("io")
val myWire = Wire(new Bundle{ val a = UInt(w.W) })
}
new TestTop(2)
@@ -43,18 +45,18 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "pick the correct $outer instance for an anonymous inner bundle" in {
- ChiselStage.elaborate {
+ elaborate {
class Inner(val w: Int) extends Module {
val io = IO(new Bundle{
val in = Input(UInt(w.W))
val out = Output(UInt(w.W))
- })
+ }).suggestName("io")
}
class Outer(val w: Int) extends Module {
val io = IO(new Bundle{
val in = Input(UInt(w.W))
val out = Output(UInt(w.W))
- })
+ }).suggestName("io")
val i = Module(new Inner(w))
val iw = Wire(chiselTypeOf(i.io))
iw <> io
@@ -65,9 +67,9 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, bound, inner bundle of another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
- val io = IO(new BundleWithAnonymousInner(w) )
+ val io = IO(new BundleWithAnonymousInner(w) ).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -76,14 +78,14 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone an anonymous, inner bundle of a Module, bound to another bundle successfully" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule(w: Int) extends Module {
val bun = new Bundle {
val foo = UInt(w.W)
}
val io = IO(new Bundle {
val inner = Input(bun)
- })
+ }).suggestName("io")
val w0 = WireDefault(io)
val w1 = WireDefault(io.inner)
}
@@ -92,31 +94,48 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils {
}
it should "clone a double-nested anonymous Bundle" in {
- ChiselStage.elaborate {
+ elaborate {
class TestModule() extends Module {
val io = IO(new Bundle {
val inner = Input(new Bundle {
val x = UInt(8.W)
})
- })
+ }).suggestName("io")
}
new TestModule()
}
}
- // Test ignored because the compatibility null-inserting autoclonetype doesn't trip this
- ignore should "fail on anonymous doubly-nested inner bundle with clear error" in {
- intercept[ChiselException] { extractCause[ChiselException] { ChiselStage.elaborate {
- class Outer(val w: Int) extends Module {
- class Middle(val w: Int) {
- def getIO: Bundle = new Bundle {
- val in = Input(UInt(w.W))
+ if (usingPlugin) {
+ // This works with the plugin, but is a null pointer exception when using reflective autoclonetype
+ it should "support an anonymous doubly-nested inner bundle" in {
+ elaborate {
+ class Outer(val w: Int) extends Module {
+ class Middle(val w: Int) {
+ def getIO: Bundle = new Bundle {
+ val in = Input(UInt(w.W))
+ }
}
+ val io = IO(new Bundle {}).suggestName("io")
+ val myWire = Wire((new Middle(w)).getIO)
}
- val io = IO(new Bundle {})
- val myWire = Wire((new Middle(w)).getIO)
+ new Outer(2)
}
- new Outer(2)
- }}}.getMessage should include("Unable to determine instance")
+ }
+
+ it should "support anonymous Inner bundles that capture type parameters from outer Bundles" in {
+ elaborate(new MultiIOModule {
+ class MyBundle[T <: Data](n: Int, gen: T) extends Bundle {
+ val foo = new Bundle {
+ val x = Input(Vec(n, gen))
+ }
+ val bar = Output(Option(new { def mkBundle = new Bundle { val x = Vec(n, gen) }}).get.mkBundle)
+ }
+ val io = IO(new MyBundle(4, UInt(8.W)))
+ val myWire = WireInit(io.foo)
+ val myWire2 = WireInit(io.bar)
+ io.bar.x := io.foo.x
+ })
+ }
}
}
diff --git a/src/test/scala/chiselTests/BlackBoxImpl.scala b/src/test/scala/chiselTests/BlackBoxImpl.scala
index f8e16ad7..a9a6fa29 100644
--- a/src/test/scala/chiselTests/BlackBoxImpl.scala
+++ b/src/test/scala/chiselTests/BlackBoxImpl.scala
@@ -8,6 +8,7 @@ import chisel3._
import chisel3.util.{HasBlackBoxInline, HasBlackBoxResource, HasBlackBoxPath}
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import firrtl.FirrtlExecutionSuccess
+import firrtl.transforms.BlackBoxNotFoundException
import org.scalacheck.Test.Failed
import org.scalatest.Succeeded
import org.scalatest.freespec.AnyFreeSpec
@@ -88,6 +89,15 @@ class UsesBlackBoxMinusViaPath extends Module {
io.out := mod0.io.out
}
+class BlackBoxResourceNotFound extends HasBlackBoxResource {
+ val io = IO(new Bundle{})
+ addResource("/missing.resource")
+}
+
+class UsesMissingBlackBoxResource extends RawModule {
+ val foo = Module(new BlackBoxResourceNotFound)
+}
+
class BlackBoxImplSpec extends AnyFreeSpec with Matchers {
val targetDir = "test_run_dir"
val stage = new ChiselStage
@@ -114,5 +124,10 @@ class BlackBoxImplSpec extends AnyFreeSpec with Matchers {
verilogOutput.delete()
Succeeded
}
+ "Resource files that do not exist produce Chisel errors" in {
+ assertThrows[BlackBoxNotFoundException]{
+ ChiselStage.emitChirrtl(new UsesMissingBlackBoxResource)
+ }
+ }
}
}
diff --git a/src/test/scala/chiselTests/BundleLiteralSpec.scala b/src/test/scala/chiselTests/BundleLiteralSpec.scala
index 2a3ce2c9..b4adde4a 100644
--- a/src/test/scala/chiselTests/BundleLiteralSpec.scala
+++ b/src/test/scala/chiselTests/BundleLiteralSpec.scala
@@ -6,9 +6,8 @@ import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.experimental.BundleLiterals._
-import chisel3.experimental.BundleLiteralException
-import chisel3.experimental.ChiselEnum
-import chisel3.experimental.FixedPoint
+import chisel3.experimental.VecLiterals.AddVecLiteralConstructor
+import chisel3.experimental.{BundleLiteralException, ChiselEnum, ChiselRange, FixedPoint, Interval}
class BundleLiteralSpec extends ChiselFlatSpec with Utils {
object MyEnum extends ChiselEnum {
@@ -76,6 +75,24 @@ class BundleLiteralSpec extends ChiselFlatSpec with Utils {
} }
}
+ "bundle literals of vec literals" should "work" in {
+ assertTesterPasses(new BasicTester {
+ val range = range"[0,4].2"
+ val bundleWithVecs = new Bundle {
+ val a = Vec(2, UInt(4.W))
+ val b = Vec(2, Interval(range))
+ }.Lit(
+ _.a -> Vec(2, UInt(4.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U),
+ _.b -> Vec(2, Interval(range)).Lit(0 -> (1.5).I(range), 1 -> (0.25).I(range))
+ )
+ chisel3.assert(bundleWithVecs.a(0) === 0xA.U)
+ chisel3.assert(bundleWithVecs.a(1) === 0xB.U)
+ chisel3.assert(bundleWithVecs.b(0) === (1.5).I(range))
+ chisel3.assert(bundleWithVecs.b(1) === (0.25).I(range))
+ stop()
+ })
+ }
+
"partial bundle literals" should "work in RTL" in {
assertTesterPasses{ new BasicTester{
val bundleLit = (new MyBundle).Lit(_.a -> 42.U)
diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala
index 5d3f23ec..1d392f5c 100644
--- a/src/test/scala/chiselTests/BundleSpec.scala
+++ b/src/test/scala/chiselTests/BundleSpec.scala
@@ -129,6 +129,27 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
}).getMessage should include("aliased fields")
}
+ "Bundles" should "not have bound hardware" in {
+ (the[ChiselException] thrownBy extractCause[ChiselException] {
+ ChiselStage.elaborate { new Module {
+ class MyBundle(val foo: UInt) extends Bundle
+ val in = IO(Input(new MyBundle(123.U))) // This should error: value passed in instead of type
+ val out = IO(Output(new MyBundle(UInt(8.W))))
+
+ out := in
+ } }
+ }).getMessage should include("must be a Chisel type, not hardware")
+ }
+ "Bundles" should "not recursively contain aggregates with bound hardware" in {
+ (the[ChiselException] thrownBy extractCause[ChiselException] {
+ ChiselStage.elaborate { new Module {
+ class MyBundle(val foo: UInt) extends Bundle
+ val out = IO(Output(Vec(2, UInt(8.W))))
+ val in = IO(Input(new MyBundle(out(0)))) // This should error: Bound aggregate passed
+ out := in
+ } }
+ }).getMessage should include("must be a Chisel type, not hardware")
+ }
"Unbound bundles sharing a field" should "not error" in {
ChiselStage.elaborate {
new RawModule {
@@ -141,17 +162,4 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils {
}
}
}
-
- "Bound Data" should "have priority in setting ref over unbound Data" in {
- class MyModule extends RawModule {
- val foo = IO(new Bundle {
- val x = Output(UInt(8.W))
- })
- foo.x := 0.U // getRef on foo.x is None.get without fix
- val bar = new Bundle {
- val y = foo.x
- }
- }
- ChiselStage.emitChirrtl(new MyModule)
- }
}
diff --git a/src/test/scala/chiselTests/ChiselSpec.scala b/src/test/scala/chiselTests/ChiselSpec.scala
index 843b3192..8647d903 100644
--- a/src/test/scala/chiselTests/ChiselSpec.scala
+++ b/src/test/scala/chiselTests/ChiselSpec.scala
@@ -2,22 +2,27 @@
package chiselTests
-import org.scalatest._
-import org.scalatest.prop._
-import org.scalatest.flatspec.AnyFlatSpec
-import org.scalacheck._
import chisel3._
-import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
-import chisel3.testers._
-import firrtl.{AnnotationSeq, CommonOptions, EmittedVerilogCircuitAnnotation, ExecutionOptionsManager, FirrtlExecutionFailure, FirrtlExecutionSuccess, HasFirrtlOptions}
-import firrtl.annotations.{Annotation, DeletedAnnotation}
-import firrtl.util.BackendCompilationUtilities
-import java.io.ByteArrayOutputStream
-import java.security.Permission
-
import chisel3.aop.Aspect
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation}
+import chisel3.testers._
+import firrtl.annotations.Annotation
+import firrtl.ir.Circuit
+import firrtl.util.BackendCompilationUtilities
+import firrtl.{AnnotationSeq, EmittedVerilogCircuitAnnotation}
+import _root_.logger.Logger
+import firrtl.stage.FirrtlCircuitAnnotation
+import org.scalacheck._
+import org.scalatest._
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.funspec.AnyFunSpec
+import org.scalatest.propspec.AnyPropSpec
+import org.scalatest.matchers.should.Matchers
import org.scalatestplus.scalacheck.ScalaCheckPropertyChecks
+
+import java.io.{ByteArrayOutputStream, PrintStream}
+import java.security.Permission
import scala.reflect.ClassTag
/** Common utility functions for Chisel unit tests. */
@@ -85,67 +90,47 @@ trait ChiselRunners extends Assertions with BackendCompilationUtilities {
case EmittedVerilogCircuitAnnotation(a) => a.value
}.getOrElse(fail("No Verilog circuit was emitted by the FIRRTL compiler!"))
}
-}
-
-/** Spec base class for BDD-style testers. */
-abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers
-
-class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
- import org.scalatest.exceptions.TestFailedException
- // Who tests the testers?
- "assertKnownWidth" should "error when the expected width is wrong" in {
- val caught = intercept[ChiselException] {
- assertKnownWidth(7) {
- Wire(UInt(8.W))
- }
- }
- assert(caught.getCause.isInstanceOf[TestFailedException])
- }
- it should "error when the width is unknown" in {
- a [ChiselException] shouldBe thrownBy {
- assertKnownWidth(7) {
- Wire(UInt())
- }
+ def elaborateAndGetModule[A <: RawModule](t: => A): A = {
+ var res: Any = null
+ ChiselStage.elaborate {
+ res = t
+ res.asInstanceOf[A]
}
+ res.asInstanceOf[A]
}
- it should "work if the width is correct" in {
- assertKnownWidth(8) {
- Wire(UInt(8.W))
- }
+ /** Compiles a Chisel Module to FIRRTL
+ * NOTE: This uses the "test_run_dir" as the default directory for generated code.
+ * @param t the generator for the module
+ * @return The FIRRTL Circuit and Annotations _before_ FIRRTL compilation
+ */
+ def getFirrtlAndAnnos(t: => RawModule, providedAnnotations: Seq[Annotation] = Nil): (Circuit, Seq[Annotation]) = {
+ val args = Array(
+ "--target-dir",
+ createTestDirectory(this.getClass.getSimpleName).toString,
+ "--no-run-firrtl",
+ "--full-stacktrace"
+ )
+ val annos = (new ChiselStage).execute(args, Seq(ChiselGeneratorAnnotation(() => t)) ++ providedAnnotations)
+ val circuit = annos.collectFirst {
+ case FirrtlCircuitAnnotation(c) => c
+ }.getOrElse(fail("No FIRRTL Circuit found!!"))
+ (circuit, annos)
}
+}
- "assertInferredWidth" should "error if the width is known" in {
- val caught = intercept[ChiselException] {
- assertInferredWidth(8) {
- Wire(UInt(8.W))
- }
- }
- assert(caught.getCause.isInstanceOf[TestFailedException])
- }
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFlatSpec extends AnyFlatSpec with ChiselRunners with Matchers
- it should "error if the expected width is wrong" in {
- a [TestFailedException] shouldBe thrownBy {
- assertInferredWidth(8) {
- val w = Wire(UInt())
- w := 2.U(2.W)
- w
- }
- }
- }
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFreeSpec extends AnyFreeSpec with ChiselRunners with Matchers
- it should "pass if the width is correct" in {
- assertInferredWidth(4) {
- val w = Wire(UInt())
- w := 2.U(4.W)
- w
- }
- }
-}
+/** Spec base class for BDD-style testers. */
+abstract class ChiselFunSpec extends AnyFunSpec with ChiselRunners with Matchers
/** Spec base class for property-based testers. */
-class ChiselPropSpec extends PropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers {
+abstract class ChiselPropSpec extends AnyPropSpec with ChiselRunners with ScalaCheckPropertyChecks with Matchers {
// Constrain the default number of instances generated for every use of forAll.
implicit override val generatorDrivenConfig: PropertyCheckConfiguration =
@@ -222,6 +207,20 @@ trait Utils {
(stdout.toString, stderr.toString, ret)
}
+ /** Run some Scala thunk and return all logged messages as Strings
+ * @param thunk some Scala code
+ * @return a tuple containing LOGGED, and what the thunk returns
+ */
+ def grabLog[T](thunk: => T): (String, T) = {
+ val baos = new ByteArrayOutputStream()
+ val stream = new PrintStream(baos, true, "utf-8")
+ val ret = Logger.makeScope(Nil) {
+ Logger.setOutput(stream)
+ thunk
+ }
+ (baos.toString, ret)
+ }
+
/** Encodes a System.exit exit code
* @param status the exit code
*/
@@ -343,7 +342,7 @@ trait Utils {
exceptions.collectFirst{ case a: A => a } match {
case Some(a) => throw a
case None => exceptions match {
- case Nil => Unit
+ case Nil => ()
case h :: t => throw h
}
}
diff --git a/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
new file mode 100644
index 00000000..40358d11
--- /dev/null
+++ b/src/test/scala/chiselTests/ChiselTestUtilitiesSpec.scala
@@ -0,0 +1,57 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import org.scalatest.exceptions.TestFailedException
+
+class ChiselTestUtilitiesSpec extends ChiselFlatSpec {
+ // Who tests the testers?
+ "assertKnownWidth" should "error when the expected width is wrong" in {
+ intercept[TestFailedException] {
+ assertKnownWidth(7) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error when the width is unknown" in {
+ intercept[ChiselException] {
+ assertKnownWidth(7) {
+ Wire(UInt())
+ }
+ }
+ }
+
+ it should "work if the width is correct" in {
+ assertKnownWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+
+ "assertInferredWidth" should "error if the width is known" in {
+ intercept[TestFailedException] {
+ assertInferredWidth(8) {
+ Wire(UInt(8.W))
+ }
+ }
+ }
+
+ it should "error if the expected width is wrong" in {
+ a [TestFailedException] shouldBe thrownBy {
+ assertInferredWidth(8) {
+ val w = Wire(UInt())
+ w := 2.U(2.W)
+ w
+ }
+ }
+ }
+
+ it should "pass if the width is correct" in {
+ assertInferredWidth(4) {
+ val w = Wire(UInt())
+ w := 2.U(4.W)
+ w
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/CloneModuleSpec.scala b/src/test/scala/chiselTests/CloneModuleSpec.scala
index e54ef1c2..8359bc28 100644
--- a/src/test/scala/chiselTests/CloneModuleSpec.scala
+++ b/src/test/scala/chiselTests/CloneModuleSpec.scala
@@ -4,7 +4,7 @@ package chiselTests
import chisel3._
import chisel3.stage.ChiselStage
-import chisel3.util.{Queue, EnqIO, DeqIO, QueueIO, log2Ceil}
+import chisel3.util.{Decoupled, Queue, EnqIO, DeqIO, QueueIO, log2Ceil}
import chisel3.experimental.{CloneModuleAsRecord, IO}
import chisel3.testers.BasicTester
@@ -57,6 +57,26 @@ class QueueCloneTester(x: Int, multiIO: Boolean = false) extends BasicTester {
}
}
+class CloneModuleAsRecordAnnotate extends Module {
+ override def desiredName = "Top"
+ val in = IO(Flipped(Decoupled(UInt(8.W))))
+ val out = IO(Decoupled(UInt(8.W)))
+
+ val q1 = Module(new Queue(UInt(8.W), 4))
+ val q2 = CloneModuleAsRecord(q1)
+ val q2_io = q2("io").asInstanceOf[q1.io.type]
+ // Also make a wire to check that cloning works, can be connected to, and annotated
+ val q2_wire = {
+ val w = Wire(chiselTypeOf(q2))
+ w <> q2
+ w
+ }
+ // But connect to the original (using last connect semantics to override connects to wire
+ q1.io.enq <> in
+ q2_io.enq <> q1.io.deq
+ out <> q2_io.deq
+}
+
class CloneModuleSpec extends ChiselPropSpec {
val xVals = Table(
@@ -87,4 +107,48 @@ class CloneModuleSpec extends ChiselPropSpec {
assert(c.modules.length == 3)
}
+ property("Cloned Modules should annotate correctly") {
+ // Hackily get the actually Module object out
+ var mod: CloneModuleAsRecordAnnotate = null
+ val res = ChiselStage.convert {
+ mod = new CloneModuleAsRecordAnnotate
+ mod
+ }
+ // ********** Checking the output of CloneModuleAsRecord **********
+ // Note that we overrode desiredName so that Top is named "Top"
+ mod.q1.io.enq.toTarget.serialize should be ("~Top|Queue>io.enq")
+ mod.q2_io.deq.toTarget.serialize should be ("~Top|Queue>io.deq")
+ mod.q1.io.enq.toAbsoluteTarget.serialize should be ("~Top|Top/q1:Queue>io.enq")
+ mod.q2_io.deq.toAbsoluteTarget.serialize should be ("~Top|Top/q2:Queue>io.deq")
+ // Legacy APIs that nevertheless were tricky to get right
+ mod.q1.io.enq.toNamed.serialize should be ("Top.Queue.io.enq")
+ mod.q2_io.deq.toNamed.serialize should be ("Top.Queue.io.deq")
+ mod.q1.io.enq.instanceName should be ("io.enq")
+ mod.q2_io.deq.instanceName should be ("io.deq")
+ mod.q1.io.enq.pathName should be ("Top.q1.io.enq")
+ mod.q2_io.deq.pathName should be ("Top.q2.io.deq")
+ mod.q1.io.enq.parentPathName should be ("Top.q1")
+ mod.q2_io.deq.parentPathName should be ("Top.q2")
+ mod.q1.io.enq.parentModName should be ("Queue")
+ mod.q2_io.deq.parentModName should be ("Queue")
+
+ // ********** Checking the wire cloned from the output of CloneModuleAsRecord **********
+ val wire_io = mod.q2_wire("io").asInstanceOf[QueueIO[UInt]]
+ mod.q2_wire.toTarget.serialize should be ("~Top|Top>q2_wire")
+ wire_io.enq.toTarget.serialize should be ("~Top|Top>q2_wire.io.enq")
+ mod.q2_wire.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire")
+ wire_io.enq.toAbsoluteTarget.serialize should be ("~Top|Top>q2_wire.io.enq")
+ // Legacy APIs
+ mod.q2_wire.toNamed.serialize should be ("Top.Top.q2_wire")
+ wire_io.enq.toNamed.serialize should be ("Top.Top.q2_wire.io.enq")
+ mod.q2_wire.instanceName should be ("q2_wire")
+ wire_io.enq.instanceName should be ("q2_wire.io.enq")
+ mod.q2_wire.pathName should be ("Top.q2_wire")
+ wire_io.enq.pathName should be ("Top.q2_wire.io.enq")
+ mod.q2_wire.parentPathName should be ("Top")
+ wire_io.enq.parentPathName should be ("Top")
+ mod.q2_wire.parentModName should be ("Top")
+ wire_io.enq.parentModName should be ("Top")
+ }
+
}
diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
index cfcc4608..1795cc1f 100644
--- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala
@@ -289,5 +289,75 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec {
}
}
}
+
+ "A chisel3 Bundle that instantiates a Chisel Bundle" should "bulk connect correctly" in {
+ compile {
+ object Compat {
+ import Chisel._
+ class BiDir extends Bundle {
+ val a = Input(UInt(8.W))
+ val b = Output(UInt(8.W))
+ }
+ class Struct extends Bundle {
+ val a = UInt(8.W)
+ }
+ }
+ import chisel3._
+ import Compat._
+ class Bar extends Bundle {
+ val bidir1 = new BiDir
+ val bidir2 = Flipped(new BiDir)
+ val struct1 = Output(new Struct)
+ val struct2 = Input(new Struct)
+ }
+ // Check every connection both ways to see that chisel3 <>'s commutativity holds
+ class Child extends RawModule {
+ val deq = IO(new Bar)
+ val enq = IO(Flipped(new Bar))
+ enq <> deq
+ deq <> enq
+ }
+ new RawModule {
+ val deq = IO(new Bar)
+ val enq = IO(Flipped(new Bar))
+ // Also important to check connections to child ports
+ val c1 = Module(new Child)
+ val c2 = Module(new Child)
+ c1.enq <> enq
+ enq <> c1.enq
+ c2.enq <> c1.deq
+ c1.deq <> c2.enq
+ deq <> c2.deq
+ c2.deq <> deq
+ }
+ }
+ }
+
+ "A unidirectional but flipped Bundle" should "bulk connect in import chisel3._ code correctly" in {
+ object Compat {
+ import Chisel._
+ class MyBundle(extraFlip: Boolean) extends Bundle {
+ private def maybeFlip[T <: Data](t: T): T = if (extraFlip) t.flip else t
+ val foo = maybeFlip(new Bundle {
+ val bar = UInt(INPUT, width = 8)
+ })
+ override def cloneType = (new MyBundle(extraFlip)).asInstanceOf[this.type]
+ }
+ }
+ import chisel3._
+ import Compat._
+ class Top(extraFlip: Boolean) extends RawModule {
+ val port = IO(new MyBundle(extraFlip))
+ val wire = Wire(new MyBundle(extraFlip))
+ port <> DontCare
+ wire <> DontCare
+ port <> wire
+ wire <> port
+ port.foo <> wire.foo
+ wire.foo <> port.foo
+ }
+ compile(new Top(true))
+ compile(new Top(false))
+ }
}
diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala
index 6a77c821..2d4ad517 100644
--- a/src/test/scala/chiselTests/CompatibilitySpec.scala
+++ b/src/test/scala/chiselTests/CompatibilitySpec.scala
@@ -103,7 +103,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
Reverse(wire) shouldBe a [UInt]
Cat(wire, wire) shouldBe a [UInt]
Log2(wire) shouldBe a [UInt]
- unless(Bool(false)) {}
// 'switch' and 'is' are tested below in Risc
Counter(2) shouldBe a [Counter]
DecoupledIO(wire) shouldBe a [DecoupledIO[UInt]]
@@ -353,13 +352,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
info("Deprecated method DC hasn't been removed")
val bp = BitPat.DC(4)
-
- info("BitPat != UInt is a Bool")
- (bp != UInt(4)) shouldBe a [Bool]
-
- /* This test does not work, but I'm not sure it's supposed to? It does *not* work on chisel3. */
- // info("UInt != BitPat is a Bool")
- // (UInt(4) != bp) shouldBe a [Bool]
}
ChiselStage.elaborate(new Foo)
@@ -459,6 +451,18 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
ChiselStage.elaborate(new Foo)
}
+ it should "support data-types of mixed directionality" in {
+ class Foo extends Module {
+ val io = IO(new Bundle {})
+ val tpe = new Bundle { val foo = UInt(OUTPUT, width = 4); val bar = UInt(width = 4) }
+ // NOTE for some reason, the old bug this hit did not occur when `tpe` is inlined
+ val mem = SeqMem(tpe, 8)
+ mem(3.U)
+
+ }
+ ChiselStage.elaborate((new Foo))
+ }
+
behavior of "debug"
it should "still exist" in {
@@ -474,22 +478,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
behavior of "Data methods"
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = IO(new Bundle{})
-
- info("litArg works")
- UInt(width=3).litArg() should be (None)
- UInt(0, width=3).litArg() should be (Some(chisel3.internal.firrtl.ULit(0, 3.W)))
-
- info("toBits works")
- val wire = Wire(UInt(width=4))
- Vec.fill(4)(wire).toBits.getWidth should be (wire.getWidth * 4)
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
behavior of "Wire"
it should "support legacy methods" in {
@@ -542,9 +530,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
val u = UInt(8)
val s = SInt(-4)
- info("toBools works")
- u.toBools shouldBe a [Seq[Bool]]
-
info("asBits works")
s.asBits shouldBe a [Bits]
@@ -553,35 +538,6 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck
info("toUInt works")
s.toUInt shouldBe a [UInt]
-
- info("toBool works")
- UInt(1).toBool shouldBe a [Bool]
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
- behavior of "UInt"
-
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = new Bundle{}
-
- info("!= works")
- (UInt(1) != UInt(1)) shouldBe a [Bool]
- }
-
- ChiselStage.elaborate(new Foo)
- }
-
- behavior of "SInt"
-
- it should "support legacy methods" in {
- class Foo extends Module {
- val io = new Bundle{}
-
- info("!= works")
- (SInt(-1) != SInt(-1)) shouldBe a [Bool]
}
ChiselStage.elaborate(new Foo)
diff --git a/src/test/scala/chiselTests/CustomBundle.scala b/src/test/scala/chiselTests/CustomBundle.scala
new file mode 100644
index 00000000..b04dcc59
--- /dev/null
+++ b/src/test/scala/chiselTests/CustomBundle.scala
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.{DataMirror, requireIsChiselType}
+import scala.collection.immutable.ListMap
+
+// An example of how Record might be extended
+// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data)
+// it is a possible implementation of a programmatic "Bundle"
+// (and can by connected to MyBundle below)
+final class CustomBundle(elts: (String, Data)*) extends Record {
+ val elements = ListMap(elts map { case (field, elt) =>
+ requireIsChiselType(elt)
+ field -> elt
+ }: _*)
+ def apply(elt: String): Data = elements(elt)
+ override def cloneType: this.type = {
+ val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) }
+ (new CustomBundle(cloned: _*)).asInstanceOf[this.type]
+ }
+}
+
diff --git a/src/test/scala/chiselTests/DriverSpec.scala b/src/test/scala/chiselTests/DriverSpec.scala
deleted file mode 100644
index 3a78683b..00000000
--- a/src/test/scala/chiselTests/DriverSpec.scala
+++ /dev/null
@@ -1,101 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chiselTests
-
-import java.io.File
-
-import chisel3._
-import firrtl.FirrtlExecutionSuccess
-import org.scalacheck.Test.Failed
-import org.scalatest.Succeeded
-import org.scalatest.freespec.AnyFreeSpec
-import org.scalatest.matchers.should.Matchers
-
-class DummyModule extends Module {
- val io = IO(new Bundle {
- val in = Input(UInt(1.W))
- val out = Output(UInt(1.W))
- })
- io.out := io.in
-}
-
-class TypeErrorModule extends chisel3.Module {
- val in = IO(Input(UInt(1.W)))
- val out = IO(Output(SInt(1.W)))
- out := in
-}
-
-class DriverSpec extends AnyFreeSpec with Matchers with chiselTests.Utils {
- "Driver's execute methods are used to run chisel and firrtl" - {
- "options can be picked up from comand line with no args" in {
- // NOTE: Since we don't provide any arguments (notably, "--target-dir"),
- // the generated files will be created in the current directory.
- val targetDir = "."
- Driver.execute(Array.empty[String], () => new DummyModule) match {
- case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) =>
- val exts = List("anno.json", "fir", "v")
- for (ext <- exts) {
- val dummyOutput = new File(targetDir, "DummyModule" + "." + ext)
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
- Succeeded
- case _ =>
- Failed
- }
- }
-
- "options can be picked up from comand line setting top name" in {
- val targetDir = "local-build"
- Driver.execute(Array("-tn", "dm", "-td", targetDir), () => new DummyModule) match {
- case ChiselExecutionSuccess(_, _, Some(_: FirrtlExecutionSuccess)) =>
- val exts = List("anno.json", "fir", "v")
- for (ext <- exts) {
- val dummyOutput = new File(targetDir, "dm" + "." + ext)
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
- Succeeded
- case _ =>
- Failed
- }
-
- }
-
- "execute returns a chisel execution result" in {
- val targetDir = "test_run_dir"
- val args = Array("--compiler", "low", "--target-dir", targetDir)
-
- info("Driver returned a ChiselExecutionSuccess")
- val result = Driver.execute(args, () => new DummyModule)
- result shouldBe a[ChiselExecutionSuccess]
-
- info("emitted circuit included 'circuit DummyModule'")
- val successResult = result.asInstanceOf[ChiselExecutionSuccess]
- successResult.emitted should include ("circuit DummyModule")
-
- val dummyOutput = new File(targetDir, "DummyModule.lo.fir")
- info(s"${dummyOutput.toString} exists")
- dummyOutput.exists() should be(true)
- dummyOutput.delete()
- }
-
- "user errors show a trimmed stack trace" in {
- val targetDir = "test_run_dir"
- val args = Array("--compiler", "low", "--target-dir", targetDir)
-
- val (stdout, stderr, result) = grabStdOutErr { Driver.execute(args, () => new TypeErrorModule) }
-
- info("stdout shows a trimmed stack trace")
- stdout should include ("Stack trace trimmed to user code only")
-
- info("stdout does not include FIRRTL information")
- stdout should not include ("firrtl.")
-
- info("Driver returned a ChiselExecutionFailure")
- result shouldBe a [ChiselExecutionFailure]
- }
- }
-}
diff --git a/src/test/scala/chiselTests/ExtModule.scala b/src/test/scala/chiselTests/ExtModule.scala
index 0c3a0633..161b6f5f 100644
--- a/src/test/scala/chiselTests/ExtModule.scala
+++ b/src/test/scala/chiselTests/ExtModule.scala
@@ -9,7 +9,7 @@ import chisel3.testers.{BasicTester, TesterDriver}
// Avoid collisions with regular BlackBox tests by putting ExtModule blackboxes
// in their own scope.
-package ExtModule {
+package extmoduletests {
import chisel3.experimental.ExtModule
@@ -25,8 +25,8 @@ package ExtModule {
}
class ExtModuleTester extends BasicTester {
- val blackBoxPos = Module(new ExtModule.BlackBoxInverter)
- val blackBoxNeg = Module(new ExtModule.BlackBoxInverter)
+ val blackBoxPos = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxNeg = Module(new extmoduletests.BlackBoxInverter)
blackBoxPos.in := 1.U
blackBoxNeg.in := 0.U
@@ -42,10 +42,10 @@ class ExtModuleTester extends BasicTester {
*/
class MultiExtModuleTester extends BasicTester {
- val blackBoxInvPos = Module(new ExtModule.BlackBoxInverter)
- val blackBoxInvNeg = Module(new ExtModule.BlackBoxInverter)
- val blackBoxPassPos = Module(new ExtModule.BlackBoxPassthrough)
- val blackBoxPassNeg = Module(new ExtModule.BlackBoxPassthrough)
+ val blackBoxInvPos = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxInvNeg = Module(new extmoduletests.BlackBoxInverter)
+ val blackBoxPassPos = Module(new extmoduletests.BlackBoxPassthrough)
+ val blackBoxPassNeg = Module(new extmoduletests.BlackBoxPassthrough)
blackBoxInvPos.in := 1.U
blackBoxInvNeg.in := 0.U
@@ -71,7 +71,7 @@ class ExtModuleSpec extends ChiselFlatSpec {
"DataMirror.modulePorts" should "work with ExtModule" in {
ChiselStage.elaborate(new Module {
val io = IO(new Bundle { })
- val m = Module(new ExtModule.BlackBoxPassthrough)
+ val m = Module(new extmoduletests.BlackBoxPassthrough)
assert(DataMirror.modulePorts(m) == Seq(
"in" -> m.in, "out" -> m.out))
})
diff --git a/src/test/scala/chiselTests/ExtModuleImpl.scala b/src/test/scala/chiselTests/ExtModuleImpl.scala
index f71a1335..c6cd4a9f 100644
--- a/src/test/scala/chiselTests/ExtModuleImpl.scala
+++ b/src/test/scala/chiselTests/ExtModuleImpl.scala
@@ -8,11 +8,11 @@ import chisel3._
import chisel3.experimental.ExtModule
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import chisel3.util.{HasExtModuleInline, HasExtModulePath, HasExtModuleResource}
-import firrtl.FirrtlExecutionSuccess
import firrtl.options.TargetDirAnnotation
import firrtl.stage.FirrtlCircuitAnnotation
-import org.scalacheck.Test.Failed
-import org.scalatest.{FreeSpec, Matchers, Succeeded}
+import firrtl.transforms.BlackBoxNotFoundException
+import org.scalatest.freespec.AnyFreeSpec
+import org.scalatest.matchers.should.Matchers
//scalastyle:off magic.number
@@ -92,7 +92,16 @@ class UsesExtModuleMinusViaPath extends Module {
io.out := mod0.io.out
}
-class ExtModuleImplSpec extends FreeSpec with Matchers {
+class ExtModuleResourceNotFound extends HasExtModuleResource {
+ val io = IO(new Bundle{})
+ addResource("/missing.resource")
+}
+
+class UsesMissingExtModuleResource extends RawModule {
+ val foo = Module(new ExtModuleResourceNotFound)
+}
+
+class ExtModuleImplSpec extends AnyFreeSpec with Matchers {
"ExtModule can have verilator source implementation" - {
"Implementations can be contained in-line" in {
@@ -137,5 +146,11 @@ class ExtModuleImplSpec extends FreeSpec with Matchers {
verilogOutput.exists() should be(true)
verilogOutput.delete()
}
+
+ "Resource files that do not exist produce Chisel errors" in {
+ assertThrows[BlackBoxNotFoundException]{
+ ChiselStage.emitChirrtl(new UsesMissingExtModuleResource)
+ }
+ }
}
}
diff --git a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
index 3be649e1..8a998496 100644
--- a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
+++ b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala
@@ -6,7 +6,7 @@ import java.io.File
import chisel3._
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
-import chisel3.util.experimental.loadMemoryFromFile
+import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline}
import chisel3.util.log2Ceil
import firrtl.FirrtlExecutionSuccess
import firrtl.annotations.MemoryLoadFileType
@@ -33,6 +33,26 @@ class UsesThreeMems(memoryDepth: Int, memoryType: Data) extends Module {
io.value3 := memory3(io.address)
}
+class UsesThreeMemsInline(memoryDepth: Int, memoryType: Data, memoryFile: String, hexOrBinary: MemoryLoadFileType.FileType) extends Module {
+ val io = IO(new Bundle {
+ val address = Input(UInt(memoryType.getWidth.W))
+ val value1 = Output(memoryType)
+ val value2 = Output(memoryType)
+ val value3 = Output(memoryType)
+ })
+
+ val memory1 = Mem(memoryDepth, memoryType)
+ val memory2 = Mem(memoryDepth, memoryType)
+ val memory3 = Mem(memoryDepth, memoryType)
+ loadMemoryFromFileInline(memory1, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory2, memoryFile, hexOrBinary)
+ loadMemoryFromFileInline(memory3, memoryFile, hexOrBinary)
+
+ io.value1 := memory1(io.address)
+ io.value2 := memory2(io.address)
+ io.value3 := memory3(io.address)
+}
+
class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
val io = IO(new Bundle {
val address = Input(UInt(memoryType.getWidth.W))
@@ -205,4 +225,35 @@ class LoadMemoryFromFileSpec extends AnyFreeSpec with Matchers {
file.delete()
}
+ "Module with more than one hex memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "./testmem.h", MemoryLoadFileType.Hex)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemh("./testmem.h", memory1);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory2);""")
+ fileText should include (s"""$$readmemh("./testmem.h", memory3);""")
+ }
+
+ "Module with more than one bin memory inline should work" in {
+ val testDirName = "test_run_dir/load_three_memory_spec_inline"
+
+ val result = (new ChiselStage).execute(
+ args = Array("-X", "verilog", "--target-dir", testDirName),
+ annotations = Seq(ChiselGeneratorAnnotation(() => new UsesThreeMemsInline(memoryDepth = 8, memoryType = UInt(16.W), "testmem.bin", MemoryLoadFileType.Binary)))
+ )
+ val dir = new File(testDirName)
+ val file = new File(dir, s"UsesThreeMemsInline.v")
+ file.exists() should be (true)
+ val fileText = io.Source.fromFile(file).getLines().mkString("\n")
+ fileText should include (s"""$$readmemb("testmem.bin", memory1);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory2);""")
+ fileText should include (s"""$$readmemb("testmem.bin", memory3);""")
+ }
}
diff --git a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala b/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
deleted file mode 100644
index 28673495..00000000
--- a/src/test/scala/chiselTests/MissingCloneBindingExceptionSpec.scala
+++ /dev/null
@@ -1,55 +0,0 @@
-// SPDX-License-Identifier: Apache-2.0
-
-package chiselTests
-import Chisel.ChiselException
-import chisel3.stage.ChiselStage
-import org.scalatest._
-import org.scalatest.matchers.should.Matchers
-
-class MissingCloneBindingExceptionSpec extends ChiselFlatSpec with Matchers with Utils {
- behavior of "missing cloneType in Chisel3"
- ( the [ChiselException] thrownBy extractCause[ChiselException] {
- import chisel3._
-
- class Test extends Module {
- class TestIO(w: Int) extends Bundle {
- val a = Input(Vec(4, UInt(w.W)))
- }
-
- val io = IO(new TestIO(32))
- }
-
- class TestTop extends Module {
- val io = IO(new Bundle {})
-
- val subs = VecInit(Seq.fill(2) {
- Module(new Test).io
- })
- }
-
- ChiselStage.elaborate(new TestTop)
- }).getMessage should include("make all parameters immutable")
-
- behavior of "missing cloneType in Chisel2"
- ( the [ChiselException] thrownBy extractCause[ChiselException] {
- import Chisel._
-
- class Test extends Module {
- class TestIO(w: Int) extends Bundle {
- val a = Vec(4, UInt(width = w)).asInput
- }
-
- val io = IO(new TestIO(32))
- }
-
- class TestTop extends Module {
- val io = IO(new Bundle {})
-
- val subs = Vec.fill(2) {
- Module(new Test).io
- }
- }
-
- ChiselStage.elaborate(new TestTop)
- }).getMessage should include("make all parameters immutable")
-}
diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala
index 932c94a5..7703e876 100644
--- a/src/test/scala/chiselTests/Module.scala
+++ b/src/test/scala/chiselTests/Module.scala
@@ -3,8 +3,12 @@
package chiselTests
import chisel3._
-import chisel3.stage.ChiselStage
import chisel3.experimental.DataMirror
+import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, NoRunFirrtlCompilerAnnotation}
+import firrtl.annotations.NoTargetAnnotation
+import firrtl.options.Unserializable
+
+import scala.io.Source
class SimpleIO extends Bundle {
val in = Input(UInt(32.W))
@@ -140,6 +144,17 @@ class ModuleSpec extends ChiselPropSpec with Utils {
assert(checkModule(this))
})
}
+
+ property("object chisel3.util.experimental.getAnnotations should return current annotations.") {
+ case class DummyAnnotation() extends NoTargetAnnotation with Unserializable
+ (new ChiselStage).transform(Seq(
+ ChiselGeneratorAnnotation(() => new RawModule {
+ assert(chisel3.util.experimental.getAnnotations().contains(DummyAnnotation()))
+ }),
+ DummyAnnotation(),
+ NoRunFirrtlCompilerAnnotation))
+ }
+
property("DataMirror.modulePorts should work") {
ChiselStage.elaborate(new Module {
val io = IO(new Bundle { })
@@ -174,4 +189,15 @@ class ModuleSpec extends ChiselPropSpec with Utils {
}
ChiselStage.elaborate(new RawModule with Foo)
}
+
+ property("getVerilogString(new PlusOne() should produce a valid Verilog string") {
+ val s = getVerilogString(new PlusOne())
+ assert(s.contains("assign io_out = io_in + 32'h1"))
+ }
+
+ property("emitVerilog((new PlusOne()..) shall produce a valid Verilog file in a subfolder") {
+ emitVerilog(new PlusOne(), Array("--target-dir", "generated"))
+ val s = Source.fromFile("generated/PlusOne.v").mkString("")
+ assert(s.contains("assign io_out = io_in + 32'h1"))
+ }
}
diff --git a/src/test/scala/chiselTests/OneHotMuxSpec.scala b/src/test/scala/chiselTests/OneHotMuxSpec.scala
index 887843d4..7608a3e7 100644
--- a/src/test/scala/chiselTests/OneHotMuxSpec.scala
+++ b/src/test/scala/chiselTests/OneHotMuxSpec.scala
@@ -37,26 +37,18 @@ class OneHotMuxSpec extends AnyFreeSpec with Matchers with ChiselRunners {
}
}
"simple one hot mux with all fixed width bundles but with different bundles should Not work" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new DifferentBundleOneHotTester)
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
"UIntToOH with output width greater than 2^(input width)" in {
assertTesterPasses(new UIntToOHTester)
}
"UIntToOH should not accept width of zero (until zero-width wires are fixed" in {
- try {
+ intercept[IllegalArgumentException] {
assertTesterPasses(new BasicTester {
val out = UIntToOH(0.U, 0)
})
- } catch {
- case a: ChiselException => a.getCause match {
- case _: IllegalArgumentException =>
- }
}
}
diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala
index c76b26de..25b54966 100644
--- a/src/test/scala/chiselTests/PrintableSpec.scala
+++ b/src/test/scala/chiselTests/PrintableSpec.scala
@@ -3,11 +3,33 @@
package chiselTests
import chisel3._
+import chisel3.experimental.{BaseSim, ChiselAnnotation}
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
+import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation}
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers
+import java.io.File
+
+/** Dummy [[printf]] annotation.
+ * @param target target of component to be annotated
+ */
+case class PrintfAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
+ def duplicate(n: ReferenceTarget): PrintfAnnotation = this.copy(target = n)
+}
+
+object PrintfAnnotation {
+ /** Create annotation for a given [[printf]].
+ * @param c component to be annotated
+ */
+ def annotate(c: BaseSim): Unit = {
+ chisel3.experimental.annotate(new ChiselAnnotation {
+ def toFirrtl: PrintfAnnotation = PrintfAnnotation(c.toTarget)
+ })
+ }
+}
+
/* Printable Tests */
class PrintableSpec extends AnyFlatSpec with Matchers {
// This regex is brittle, it specifically finds the clock and enable signals followed by commas
@@ -128,7 +150,6 @@ class PrintableSpec extends AnyFlatSpec with Matchers {
printf(p"${FullName(myInst.io.fizz)}")
}
val firrtl = ChiselStage.emitChirrtl(new MyModule)
- println(firrtl)
getPrintfs(firrtl) match {
case Seq(Printf("foo", Seq()),
Printf("myWire.foo", Seq()),
@@ -194,4 +215,48 @@ class PrintableSpec extends AnyFlatSpec with Matchers {
case e => fail()
}
}
+ it should "get emitted with a name and annotated" in {
+
+ /** Test circuit containing annotated and renamed [[printf]]s. */
+ class PrintfAnnotationTest extends Module {
+ val myBun = Wire(new Bundle {
+ val foo = UInt(32.W)
+ val bar = UInt(32.W)
+ })
+ myBun.foo := 0.U
+ myBun.bar := 0.U
+ val howdy = printf(p"hello ${myBun}")
+ PrintfAnnotation.annotate(howdy)
+ PrintfAnnotation.annotate(printf(p"goodbye $myBun"))
+ PrintfAnnotation.annotate(printf(p"adieu $myBun").suggestName("farewell"))
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "PrintfAnnotationTest")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new PrintfAnnotationTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "PrintfAnnotationTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected annotations
+ exactly(3, annoLines) should include ("chiselTests.PrintfAnnotation")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>farewell")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>SIM")
+ exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>howdy")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "PrintfAnnotationTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "hello AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : howdy""")
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : SIM""")
+ exactly(1, firLines) should include ("""printf(clock, UInt<1>("h1"), "adieu AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : farewell""")
+ }
}
diff --git a/src/test/scala/chiselTests/QueueFlushSpec.scala b/src/test/scala/chiselTests/QueueFlushSpec.scala
new file mode 100644
index 00000000..11a411a8
--- /dev/null
+++ b/src/test/scala/chiselTests/QueueFlushSpec.scala
@@ -0,0 +1,259 @@
+package chiselTests
+
+import org.scalacheck._
+
+import chisel3._
+import chisel3.testers.{BasicTester, TesterDriver}
+import chisel3.util._
+import chisel3.util.random.LFSR
+import treadle.WriteVcdAnnotation
+
+/** Test elements can be enqueued and dequeued when flush is tied to false
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class ThingsPassThroughFlushQueueTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends ThingsPassThroughTester(elements, queueDepth, bitWidth, tap, useSyncReadMem, hasFlush = true)
+
+/** Generic flush queue tester base class
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+abstract class FlushQueueTesterBase(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, hasFlush = true))
+ val elems = VecInit(elements.map(_.U))
+ val inCnt = Counter(elements.length + 1)
+ val outCnt = RegInit(0.U(log2Ceil(elements.length).W))
+ val currQCnt = RegInit(0.U(log2Ceil(5).W))
+
+ val flush: Bool = WireInit(false.B)
+ val flushRegister = RegNext(flush, init = false.B)
+ q.io.flush.get := flush
+ q.io.enq.valid := (inCnt.value < elements.length.U)
+ q.io.deq.ready := LFSR(16)(tap)
+
+ q.io.enq.bits := elems(inCnt.value)
+ when(q.io.enq.fire()) {
+ inCnt.inc()
+ currQCnt := currQCnt + 1.U //counts how many items have been enqueued
+ }
+ when(q.io.deq.fire()) {
+ assert(flushRegister === false.B) //check queue isn't flushed (can't dequeue an empty queue)
+ }
+ when(flushRegister) { //Internal signal maybe_full is a register so some signals update on the next cycle
+ //check that queue gets flushed when queue is full
+ assert(q.io.count === 0.U)
+ assert(!q.io.deq.valid, "Expected to not be able to dequeue when flush is asserted the previous cycle")
+ assert(q.io.enq.ready, "Expected enqueue to be ready when flush was asserted the previous cycle because queue should be empty")
+ }
+ when(inCnt.value === elements.length.U) { //stop when all entries are enqueued
+ stop()
+ }
+}
+
+/** Test queue can flush at random times
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class QueueGetsFlushedTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ flush := LFSR(16)((tap + 3) % 16) //testing a flush when flush is called randomly
+ val halfCnt = (queueDepth + 1)/2
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ when (currQCnt > 0.U) {
+ currQCnt := Mux(q.io.enq.fire(), currQCnt, (currQCnt - 1.U))
+ }
+ }
+ when(flush) {
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ outCnt := outCnt + Mux(q.io.enq.fire(), (currQCnt + 1.U), currQCnt)
+ currQCnt := 0.U //resets the number of items currently inside queue
+ }
+}
+
+/** Test queue can flush when empty
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class EmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ val cycleCounter = Counter(elements.length + 1)
+ cycleCounter.inc() //counts every cycle
+
+ //testing a flush when queue is empty
+ flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued
+ q.io.enq.valid := (inCnt.value < elements.length.U) && !flush
+
+ when(q.io.deq.fire()) {
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ }
+}
+
+/** Test queue can enqueue during a flush
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class EnqueueEmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ val cycleCounter = Counter(elements.length + 1)
+ val outCounter = Counter(elements.length + 1)
+
+ //testing an enqueue during a flush
+ flush := (cycleCounter.value === 0.U && inCnt.value === 0.U) //flushed only before anything is enqueued
+ cycleCounter.inc() //counts every cycle
+
+ when(q.io.deq.fire()) {
+ //flush and enqueue were both active on the first cycle,
+ //so that element is flushed immediately which makes outCnt off by one
+ assert(elems(outCounter.value + 1.U) === q.io.deq.bits) //ensure that what comes out is what comes in
+ outCounter.inc()
+ }
+}
+
+/** Test queue can flush when full
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class FullQueueFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+
+ //testing a flush when queue is full
+ flush := (currQCnt === queueDepth.U)
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ outCnt := outCnt + 1.U
+ when (currQCnt > 0.U) {
+ currQCnt := currQCnt - 1.U
+ }
+ }
+ when(flush) {
+ outCnt := outCnt + currQCnt
+ currQCnt := 0.U //resets the number of items currently inside queue
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ }
+}
+
+/** Test queue can dequeue on the same cycle as a flush
+ *
+ * @param elements The sequence of elements used in the queue
+ * @param queueDepth The max number of entries in the queue
+ * @param bitWidth Integer size of the data type used in the queue
+ * @param tap Integer tap('seed') for the LFSR
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element
+ */
+class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends FlushQueueTesterBase(elements, queueDepth, bitWidth, tap, useSyncReadMem) {
+ //Queue should be able to dequeue when queue is not empty and flush is high
+
+ //testing a flush when dequeue is called
+ flush := currQCnt === (queueDepth/2).U
+ q.io.enq.valid := !flushRegister
+ q.io.deq.ready := flush
+
+ when(q.io.deq.fire()) {
+ //ensure that what comes out is what comes in
+ assert(currQCnt <= queueDepth.U)
+ assert(elems(outCnt) === q.io.deq.bits)
+ assert(currQCnt > 0.U)
+ }
+ when(flush) {
+ //The outcount register is one count behind because the dequeue happens at the same time as the flush
+ outCnt := outCnt + currQCnt + 1.U
+ currQCnt := 0.U //resets the number of items currently inside queue
+ assert(currQCnt === 0.U || q.io.deq.valid)
+ }
+ when(flushRegister) {
+ //check that queue gets flushed when queue is full
+ assert(q.io.deq.fire() === false.B)
+ }
+
+}
+
+class QueueFlushSpec extends ChiselPropSpec {
+ // Disable shrinking on error.
+ implicit val noShrinkListVal = Shrink[List[Int]](_ => Stream.empty)
+ implicit val noShrinkInt = Shrink[Int](_ => Stream.empty)
+
+ property("Queue should have things pass through") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new ThingsPassThroughFlushQueueTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue should flush when requested") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new QueueGetsFlushedTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue flush when queue is empty") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new EmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Test queue can enqueue during a flush") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new EnqueueEmptyFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue flush when queue is full") {
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses {
+ new FullQueueFlushEdgecaseTester(se._2, depth, se._1, tap, isSync)
+ }
+ }
+ }
+ }
+ property("Queue should be able to dequeue when flush is high") {
+ forAll(Gen.choose(3, 5), safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
+ whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
+ assertTesterPasses (
+ new DequeueFullQueueEdgecaseTester(se._2, depth, se._1, tap, isSync),
+ annotations = Seq(WriteVcdAnnotation)
+ )
+ }
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala
index 9dc7f120..51b899cb 100644
--- a/src/test/scala/chiselTests/QueueSpec.scala
+++ b/src/test/scala/chiselTests/QueueSpec.scala
@@ -9,8 +9,8 @@ import chisel3.testers.BasicTester
import chisel3.util._
import chisel3.util.random.LFSR
-class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean, hasFlush: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem, hasFlush = hasFlush))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -19,7 +19,7 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int
q.io.enq.valid := (inCnt.value < elements.length.U)
q.io.deq.ready := LFSR(16)(tap)
-
+ q.io.flush.foreach { _ := false.B } //Flush behavior is tested in QueueFlushSpec
q.io.enq.bits := elems(inCnt.value)
when(q.io.enq.fire()) {
inCnt.inc()
@@ -34,8 +34,8 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int
}
}
-class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -62,8 +62,8 @@ class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: I
}
}
-class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth))
+class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -89,8 +89,8 @@ class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, t
}
}
-class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true))
+class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), 1, pipe = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -115,8 +115,8 @@ class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int) extends
}
}
-class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true))
+class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, pipe = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt(bitWidth.W)
})
@@ -141,8 +141,8 @@ class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I
}
}
-class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
- val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true))
+class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
+ val q = Module(new Queue(UInt(bitWidth.W), queueDepth, flow = true, useSyncReadMem = useSyncReadMem))
val elems = VecInit(elements.map {
_.asUInt()
})
@@ -169,9 +169,9 @@ class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I
}
}
-class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int) extends BasicTester {
+class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: Int, useSyncReadMem: Boolean) extends BasicTester {
val enq = Wire(Decoupled(UInt(bitWidth.W)))
- val deq = Queue(enq, queueDepth)
+ val deq = Queue(enq, queueDepth, useSyncReadMem = useSyncReadMem)
val elems = VecInit(elements.map {
_.asUInt()
@@ -202,70 +202,70 @@ class QueueSpec extends ChiselPropSpec {
implicit val noShrinkInt = Shrink[Int](_ => Stream.empty)
property("Queue should have things pass through") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new ThingsPassThroughTester(se._2, depth, se._1, tap)
+ new ThingsPassThroughTester(se._2, depth, se._1, tap, isSync, false)
}
}
}
}
property("Queue should have reasonable ready/valid") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueReasonableReadyValid(se._2, depth, se._1, tap)
+ new QueueReasonableReadyValid(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue should have correct count") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new CountIsCorrectTester(se._2, depth, se._1, tap)
+ new CountIsCorrectTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue pipe should work for 1-element queues") {
- forAll(safeUIntN(20), Gen.choose(0, 15)) { (se, tap) =>
+ forAll(safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (se, tap, isSync) =>
whenever(se._1 >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueSinglePipeTester(se._2, se._1, tap)
+ new QueueSinglePipeTester(se._2, se._1, tap, isSync)
}
}
}
}
property("Queue pipe should work for more general queues") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueuePipeTester(se._2, depth, se._1, tap)
+ new QueuePipeTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue flow should work") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && depth >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueFlowTester(se._2, depth, se._1, tap)
+ new QueueFlowTester(se._2, depth, se._1, tap, isSync)
}
}
}
}
property("Queue companion object factory method should work") {
- forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15)) { (depth, se, tap) =>
+ forAll(vecSizes, safeUIntN(20), Gen.choose(0, 15), Gen.oneOf(true, false)) { (depth, se, tap, isSync) =>
whenever(se._1 >= 1 && se._2.nonEmpty) {
assertTesterPasses {
- new QueueFactoryTester(se._2, depth, se._1, tap)
+ new QueueFactoryTester(se._2, depth, se._1, tap, isSync)
}
}
}
diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala
index c34c2bf4..c21d455c 100644
--- a/src/test/scala/chiselTests/RecordSpec.scala
+++ b/src/test/scala/chiselTests/RecordSpec.scala
@@ -6,24 +6,7 @@ import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.util.{Counter, Queue}
-import chisel3.experimental.{DataMirror, requireIsChiselType}
-import scala.collection.immutable.ListMap
-
-// An example of how Record might be extended
-// In this case, CustomBundle is a Record constructed from a Tuple of (String, Data)
-// it is a possible implementation of a programmatic "Bundle"
-// (and can by connected to MyBundle below)
-final class CustomBundle(elts: (String, Data)*) extends Record {
- val elements = ListMap(elts map { case (field, elt) =>
- requireIsChiselType(elt)
- field -> elt
- }: _*)
- def apply(elt: String): Data = elements(elt)
- override def cloneType: this.type = {
- val cloned = elts.map { case (n, d) => n -> DataMirror.internal.chiselTypeClone(d) }
- (new CustomBundle(cloned: _*)).asInstanceOf[this.type]
- }
-}
+import chisel3.experimental.DataMirror
trait RecordSpecUtils {
class MyBundle extends Bundle {
diff --git a/src/test/scala/chiselTests/Reg.scala b/src/test/scala/chiselTests/Reg.scala
index d86fe8b4..a02e6fa5 100644
--- a/src/test/scala/chiselTests/Reg.scala
+++ b/src/test/scala/chiselTests/Reg.scala
@@ -7,6 +7,7 @@ import chisel3.util._
import chisel3.experimental.DataMirror
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
+import org.scalacheck.Gen
class RegSpec extends ChiselFlatSpec {
"Reg" should "be of the same type and width as t" in {
@@ -55,17 +56,35 @@ class ShiftResetTester(n: Int) extends BasicTester {
val start = 23.U
val sr = ShiftRegister(cntVal + 23.U, n, 1.U, true.B)
when(done) {
- assert(sr === 1.U)
+ assert(sr === (if(n == 0) cntVal + 23.U else 1.U))
stop()
}
}
class ShiftRegisterSpec extends ChiselPropSpec {
property("ShiftRegister should shift") {
- forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } }
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftTester(shift) } }
}
property("ShiftRegister should reset all values inside") {
- forAll(smallPosInts) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } }
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftResetTester(shift) } }
+ }
+}
+
+class ShiftsTester(n: Int) extends BasicTester {
+ val (cntVal, done) = Counter(true.B, n)
+ val start = 23.U
+ val srs = ShiftRegisters(cntVal + start, n)
+ when(RegNext(done)) {
+ srs.zipWithIndex.foreach{ case (data, index) =>
+ assert(data === (23 + n - 1 - index).U)
+ }
+ stop()
+ }
+}
+
+class ShiftRegistersSpec extends ChiselPropSpec {
+ property("ShiftRegisters should shift") {
+ forAll(Gen.choose(0, 4)) { (shift: Int) => assertTesterPasses{ new ShiftsTester(shift) } }
}
}
diff --git a/src/test/scala/chiselTests/ResetSpec.scala b/src/test/scala/chiselTests/ResetSpec.scala
index 0e535964..7a5d444d 100644
--- a/src/test/scala/chiselTests/ResetSpec.scala
+++ b/src/test/scala/chiselTests/ResetSpec.scala
@@ -44,6 +44,26 @@ class ResetSpec extends ChiselFlatSpec with Utils {
ChiselStage.elaborate(new AbstractResetDontCareModule)
}
+ it should "be able to drive Bool" in {
+ ChiselStage.emitVerilog(new RawModule {
+ val in = IO(Input(Bool()))
+ val out = IO(Output(Bool()))
+ val w = Wire(Reset())
+ w := in
+ out := w
+ })
+ }
+
+ it should "be able to drive AsyncReset" in {
+ ChiselStage.emitVerilog(new RawModule {
+ val in = IO(Input(AsyncReset()))
+ val out = IO(Output(AsyncReset()))
+ val w = Wire(Reset())
+ w := in
+ out := w
+ })
+ }
+
it should "allow writing modules that are reset agnostic" in {
val sync = compile(new Module {
val io = IO(new Bundle {
diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala
index 9aacc378..f2e238e9 100644
--- a/src/test/scala/chiselTests/SIntOps.scala
+++ b/src/test/scala/chiselTests/SIntOps.scala
@@ -116,4 +116,36 @@ class SIntOpsSpec extends ChiselPropSpec with Utils {
assertTesterPasses(new SIntLitExtractTester)
}
+ // We use WireDefault with 2 arguments because of
+ // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html
+ // Single Argument case 2
+ property("modulo divide should give min width of arguments") {
+ assertKnownWidth(4) {
+ val x = WireDefault(SInt(8.W), DontCare)
+ val y = WireDefault(SInt(4.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(SInt(4.W), DontCare)
+ val y = WireDefault(SInt(8.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
+
+ property("division should give the width of the numerator + 1") {
+ assertKnownWidth(9) {
+ val x = WireDefault(SInt(8.W), DontCare)
+ val y = WireDefault(SInt(4.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(5) {
+ val x = WireDefault(SInt(4.W), DontCare)
+ val y = WireDefault(SInt(8.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala
index bf0eb2fe..d7dea571 100644
--- a/src/test/scala/chiselTests/StrongEnum.scala
+++ b/src/test/scala/chiselTests/StrongEnum.scala
@@ -74,6 +74,18 @@ class CastFromNonLit extends Module {
io.valid := io.out.isValid
}
+class SafeCastFromNonLit extends Module {
+ val io = IO(new Bundle {
+ val in = Input(UInt(EnumExample.getWidth.W))
+ val out = Output(EnumExample())
+ val valid = Output(Bool())
+ })
+
+ val (enum, valid) = EnumExample.safe(io.in)
+ io.out := enum
+ io.valid := valid
+}
+
class CastFromNonLitWidth(w: Option[Int] = None) extends Module {
val width = if (w.isDefined) w.get.W else UnknownWidth()
@@ -191,6 +203,28 @@ class CastFromNonLitTester extends BasicTester {
stop()
}
+class SafeCastFromNonLitTester extends BasicTester {
+ for ((enum,lit) <- EnumExample.all zip EnumExample.litValues) {
+ val mod = Module(new SafeCastFromNonLit)
+ mod.io.in := lit
+ assert(mod.io.out === enum)
+ assert(mod.io.valid === true.B)
+ }
+
+ val invalid_values = (1 until (1 << EnumExample.getWidth)).
+ filter(!EnumExample.litValues.map(_.litValue).contains(_)).
+ map(_.U)
+
+ for (invalid_val <- invalid_values) {
+ val mod = Module(new SafeCastFromNonLit)
+ mod.io.in := invalid_val
+
+ assert(mod.io.valid === false.B)
+ }
+
+ stop()
+}
+
class CastToInvalidEnumTester extends BasicTester {
val invalid_value: UInt = EnumExample.litValues.last + 1.U
Module(new CastFromLit(invalid_value))
@@ -270,6 +304,41 @@ class StrongEnumFSMTester extends BasicTester {
}
}
+class IsOneOfTester extends BasicTester {
+ import EnumExample._
+
+ // is one of itself
+ assert(e0.isOneOf(e0))
+
+ // is one of Seq of itself
+ assert(e0.isOneOf(Seq(e0)))
+ assert(e0.isOneOf(Seq(e0, e0, e0, e0)))
+ assert(e0.isOneOf(e0, e0, e0, e0))
+
+ // is one of Seq of multiple elements
+ val subset = Seq(e0, e1, e2)
+ assert(e0.isOneOf(subset))
+ assert(e1.isOneOf(subset))
+ assert(e2.isOneOf(subset))
+
+ // is not element not in subset
+ assert(!e100.isOneOf(subset))
+ assert(!e101.isOneOf(subset))
+
+ // test multiple elements with variable number of arguments
+ assert(e0.isOneOf(e0, e1, e2))
+ assert(e1.isOneOf(e0, e1, e2))
+ assert(e2.isOneOf(e0, e1, e2))
+ assert(!e100.isOneOf(e0, e1, e2))
+ assert(!e101.isOneOf(e0, e1, e2))
+
+ // is not another value
+ assert(!e0.isOneOf(e1))
+ assert(!e2.isOneOf(e101))
+
+ stop()
+}
+
class StrongEnumSpec extends ChiselFlatSpec with Utils {
import chisel3.internal.ChiselException
@@ -320,6 +389,10 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils {
assertTesterPasses(new CastFromNonLitTester)
}
+ it should "safely cast non-literal UInts to enums correctly and detect illegal casts" in {
+ assertTesterPasses(new SafeCastFromNonLitTester)
+ }
+
it should "prevent illegal literal casts to enums" in {
a [ChiselException] should be thrownBy extractCause[ChiselException] {
ChiselStage.elaborate(new CastToInvalidEnumTester)
@@ -377,6 +450,69 @@ class StrongEnumSpec extends ChiselFlatSpec with Utils {
"StrongEnum FSM" should "work" in {
assertTesterPasses(new StrongEnumFSMTester)
}
+
+ "Casting a UInt to an Enum" should "warn if the UInt can express illegal states" in {
+ object MyEnum extends ChiselEnum {
+ val e0, e1, e2 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(MyEnum()))
+ out := MyEnum(in)
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should include ("warn")
+ log should include ("Casting non-literal UInt")
+ }
+
+ it should "NOT warn if the Enum is total" in {
+ object TotalEnum extends ChiselEnum {
+ val e0, e1, e2, e3 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(TotalEnum()))
+ out := TotalEnum(in)
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ "Casting a UInt to an Enum with .safe" should "NOT warn" in {
+ object MyEnum extends ChiselEnum {
+ val e0, e1, e2 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(MyEnum()))
+ out := MyEnum.safe(in)._1
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ it should "NOT generate any validity logic if the Enum is total" in {
+ object TotalEnum extends ChiselEnum {
+ val e0, e1, e2, e3 = Value
+ }
+
+ class MyModule extends Module {
+ val in = IO(Input(UInt(2.W)))
+ val out = IO(Output(TotalEnum()))
+ val (res, valid) = TotalEnum.safe(in)
+ assert(valid.litToBoolean, "It should be true.B")
+ out := res
+ }
+ val (log, _) = grabLog(ChiselStage.elaborate(new MyModule))
+ log should not include ("warn")
+ }
+
+ it should "correctly check if the enumeration is one of the values in a given sequence" in {
+ assertTesterPasses(new IsOneOfTester)
+ }
}
class StrongEnumAnnotator extends Module {
diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala
index bba06d11..62d00de2 100644
--- a/src/test/scala/chiselTests/UIntOps.scala
+++ b/src/test/scala/chiselTests/UIntOps.scala
@@ -153,4 +153,37 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils {
io.out := io.in.asBools()(2)
})
}
+
+ // We use WireDefault with 2 arguments because of
+ // https://www.chisel-lang.org/api/3.4.1/chisel3/WireDefault$.html
+ // Single Argument case 2
+ property("modulo divide should give min width of arguments") {
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(8.W), DontCare)
+ val y = WireDefault(UInt(4.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(4.W), DontCare)
+ val y = WireDefault(UInt(8.W), DontCare)
+ val op = x % y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
+
+ property("division should give the width of the numerator") {
+ assertKnownWidth(8) {
+ val x = WireDefault(UInt(8.W), DontCare)
+ val y = WireDefault(UInt(4.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ assertKnownWidth(4) {
+ val x = WireDefault(UInt(4.W), DontCare)
+ val y = WireDefault(UInt(8.W), DontCare)
+ val op = x / y
+ WireDefault(chiselTypeOf(op), op)
+ }
+ }
}
diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala
index 1327913d..97aea909 100644
--- a/src/test/scala/chiselTests/Vec.scala
+++ b/src/test/scala/chiselTests/Vec.scala
@@ -2,11 +2,14 @@
package chiselTests
+import org.scalacheck._
+
import chisel3._
import chisel3.stage.ChiselStage
import chisel3.testers.BasicTester
import chisel3.util._
import org.scalacheck.Shrink
+import scala.annotation.tailrec
class LitTesterMod(vecSize: Int) extends Module {
val io = IO(new Bundle {
@@ -104,6 +107,136 @@ class TabulateTester(n: Int) extends BasicTester {
stop()
}
+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")
+ stop()
+}
+
+object VecMultiDimTester {
+
+ @tailrec
+ private def assert2DIsCorrect(n: Int, arr: Vec[Vec[UInt]], compArr: Seq[Seq[Int]]): Unit = {
+ val compareRow = arr(n) zip compArr(n)
+ compareRow.foreach (x => assert(x._1 === x._2.U))
+ if (n != 0) assert2DIsCorrect(n-1, arr, compArr)
+ }
+
+ @tailrec
+ private def assert3DIsCorrect(n: Int, m: Int, arr: Vec[Vec[Vec[UInt]]], compArr: Seq[Seq[Seq[Int]]]): Unit = {
+ assert2DIsCorrect(m-1, arr(n), compArr(n))
+ if (n != 0) assert3DIsCorrect(n-1, m, arr, compArr)
+ }
+
+ class TabulateTester2D(n: Int, m: Int) extends BasicTester {
+ def gen(x: Int, y: Int): UInt = (x+y).asUInt
+ def genCompVec(x: Int, y:Int): Int = x+y
+ val vec = VecInit.tabulate(n, m){ gen }
+ val compArr = Seq.tabulate(n,m){ genCompVec }
+
+ assert2DIsCorrect(n-1, vec, compArr)
+ stop()
+ }
+
+ class TabulateTester3D(n: Int, m: Int, p: Int) extends BasicTester {
+ def gen(x: Int, y: Int, z: Int): UInt = (x+y+z).asUInt
+ def genCompVec(x: Int, y:Int, z: Int): Int = x+y+z
+ val vec = VecInit.tabulate(n, m, p){ gen }
+ val compArr = Seq.tabulate(n, m, p){ genCompVec }
+
+ assert3DIsCorrect(n-1, m, vec, compArr)
+ stop()
+ }
+
+ class Fill2DTester(n: Int, m: Int, value: Int) extends BasicTester {
+ val u = VecInit.fill(n,m)(value.U)
+ val compareArr = Seq.fill(n,m)(value)
+
+ assert2DIsCorrect(n-1, u, compareArr)
+ stop()
+ }
+
+ class Fill3DTester(n: Int, m: Int, p: Int, value: Int) extends BasicTester {
+ val u = VecInit.fill(n,m,p)(value.U)
+ val compareArr = Seq.fill(n,m,p)(value)
+
+ assert3DIsCorrect(n-1, m, u, compareArr)
+ stop()
+ }
+
+ class BidirectionalTester2DFill(n: Int, m: Int) extends BasicTester {
+ val mod = Module(new PassthroughModule)
+ val vec2D = VecInit.fill(n, m)(mod.io)
+ for {
+ vec1D <- vec2D
+ module <- vec1D
+ } yield {
+ module <> Module(new PassthroughModuleTester).io
+ }
+ stop()
+ }
+
+ class BidirectionalTester3DFill(n: Int, m: Int, p: Int) extends BasicTester {
+ val mod = Module(new PassthroughModule)
+ val vec3D = VecInit.fill(n, m, p)(mod.io)
+
+ for {
+ vec2D <- vec3D
+ vec1D <- vec2D
+ module <- vec1D
+ } yield {
+ module <> (Module(new PassthroughModuleTester).io)
+ }
+ stop()
+ }
+
+ class TabulateModuleTester(value: Int) extends Module {
+ val io = IO(Flipped(new PassthroughModuleIO))
+ // This drives the input of a PassthroughModule
+ io.in := value.U
+ }
+
+ class BidirectionalTester2DTabulate(n: Int, m: Int) extends BasicTester {
+ val vec2D = VecInit.tabulate(n, m) { (x, y) => Module(new TabulateModuleTester(x + y + 1)).io}
+
+ for {
+ x <- 0 until n
+ y <- 0 until m
+ } yield {
+ val value = x + y + 1
+ val receiveMod = Module(new PassthroughModule).io
+ vec2D(x)(y) <> receiveMod
+ assert(receiveMod.out === value.U)
+ }
+ stop()
+ }
+
+ class BidirectionalTester3DTabulate(n: Int, m: Int, p: Int) extends BasicTester {
+ val vec3D = VecInit.tabulate(n, m, p) { (x, y, z) => Module(new TabulateModuleTester(x + y + z + 1)).io }
+
+ for {
+ x <- 0 until n
+ y <- 0 until m
+ z <- 0 until p
+ } yield {
+ val value = x + y + z + 1
+ val receiveMod = Module(new PassthroughModule).io
+ vec3D(x)(y)(z) <> receiveMod
+ assert(receiveMod.out === value.U)
+ }
+ stop()
+ }
+}
+
+class IterateTester(start: Int, len: Int)(f: UInt => UInt) extends BasicTester {
+ val controlVec = VecInit(Seq.iterate(start.U, len)(f))
+ 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")
+ stop()
+}
+
class ShiftRegisterTester(n: Int) extends BasicTester {
val (cnt, wrap) = Counter(true.B, n*2)
val shifter = Reg(Vec(n, UInt((log2Ceil(n) max 1).W)))
@@ -160,9 +293,8 @@ class PassthroughModuleTester extends Module {
assert(io.out === 123.U)
}
-
class ModuleIODynamicIndexTester(n: Int) extends BasicTester {
- val duts = VecInit(Seq.fill(n)(Module(new PassthroughModule).io))
+ val duts = VecInit.fill(n)(Module(new PassthroughModule).io)
val tester = Module(new PassthroughModuleTester)
val (cycle, done) = Counter(true.B, n)
@@ -219,10 +351,50 @@ class VecSpec extends ChiselPropSpec with Utils {
}
}
- property("Vecs should tabulate correctly") {
+ property("VecInit should tabulate correctly") {
forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new TabulateTester(n) } }
}
+ property("VecInit should tabulate 2D vec correctly") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses { new VecMultiDimTester.TabulateTester2D(n, m) } }
+ }
+
+ property("VecInit should tabulate 3D vec correctly") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.TabulateTester3D(n, m, p) } }
+ }
+
+ property("VecInit should fill correctly") {
+ forAll(smallPosInts, Gen.choose(0, 50)) { (n: Int, value: Int) => assertTesterPasses{ new FillTester(n, value) } }
+ }
+
+ property("VecInit should fill 2D vec correctly") {
+ forAll(smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill2DTester(n, m, value) } }
+ }
+
+ property("VecInit should fill 3D vec correctly") {
+ forAll(smallPosInts, smallPosInts, smallPosInts, Gen.choose(0, 50)) { (n: Int, m: Int, p: Int, value: Int) => assertTesterPasses{ new VecMultiDimTester.Fill3DTester(n, m, p, value) } }
+ }
+
+ property("VecInit should support 2D fill bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DFill(n, m) }}
+ }
+
+ property("VecInit should support 3D fill bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DFill(n, m, p) }}
+ }
+
+ property("VecInit should support 2D tabulate bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts) { (n: Int, m: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester2DTabulate(n, m) }}
+ }
+
+ property("VecInit should support 3D tabulate bidirectional wire connection") {
+ forAll(smallPosInts, smallPosInts, smallPosInts) { (n: Int, m: Int, p: Int) => assertTesterPasses{ new VecMultiDimTester.BidirectionalTester3DTabulate(n, m, p) }}
+ }
+
+ property("VecInit should iterate correctly") {
+ forAll(Gen.choose(1, 10), smallPosInts) { (start: Int, len: Int) => assertTesterPasses{ new IterateTester(start, len)(x => x + 50.U)}}
+ }
+
property("Regs of vecs should be usable as shift registers") {
forAll(smallPosInts) { (n: Int) => assertTesterPasses{ new ShiftRegisterTester(n) } }
}
diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala
new file mode 100644
index 00000000..d91cd2f4
--- /dev/null
+++ b/src/test/scala/chiselTests/VecLiteralSpec.scala
@@ -0,0 +1,526 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+
+import chisel3._
+import chisel3.experimental.BundleLiterals.AddBundleLiteralConstructor
+import chisel3.experimental.VecLiterals._
+import chisel3.experimental.{ChiselEnum, FixedPoint, VecLiteralException}
+import chisel3.stage.ChiselStage
+import chisel3.testers.BasicTester
+import chisel3.util.Counter
+import scala.language.reflectiveCalls
+
+class VecLiteralSpec extends ChiselFreeSpec with Utils {
+ object MyEnum extends ChiselEnum {
+ val sA, sB, sC = Value
+ }
+ object MyEnumB extends ChiselEnum {
+ val sA, sB = Value
+ }
+
+ "Vec literals should work with chisel Enums" in {
+ val enumVec = Vec(3, MyEnum()).Lit(0 -> MyEnum.sA, 1 -> MyEnum.sB, 2-> MyEnum.sC)
+ enumVec(0).toString should include (MyEnum.sA.toString)
+ enumVec(1).toString should include (MyEnum.sB.toString)
+ enumVec(2).toString should include (MyEnum.sC.toString)
+ }
+
+ "improperly constructed vec literals should be detected" - {
+ "indices in vec literal muse be greater than zero and less than length" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 3 -> 4.U, -2 -> 7.U)
+ }
+ e.getMessage should include (
+ "VecLiteral: The following indices (2,3,-2) are less than zero or greater or equal to than Vec length"
+ )
+ }
+
+ "indices in vec literals must not be repeated" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U, 2 -> 3.U, 2 -> 3.U, 3 -> 4.U)
+ }
+ e.getMessage should include("VecLiteral: has duplicated indices 2(3 times)")
+ }
+ "lits must fit in vec element width" in {
+ val e = intercept[VecLiteralException] {
+ Vec(2, SInt(4.W)).Lit(0 -> 0xab.S, 1 -> 0xbc.S)
+ }
+ e.getMessage should include(
+ "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: " +
+ "0 -> SInt<9>(171),1 -> SInt<9>(188)"
+ )
+ }
+
+ "all lits must be the same type but width can be equal or smaller than the Vec's element width" in {
+ val v = Vec(2, SInt(4.W)).Lit(0 -> 1.S, 1 -> -2.S)
+ v(0).toString should include(1.S(4.W).toString)
+ v(1).toString should include((-2).S(4.W).toString)
+ v.toString should include ("SInt<4>[2](0=SLit(1,<4>), 1=SLit(-2,<4>)")
+ }
+
+ "all lits must be the same type but width cannot be greater than Vec's element width" in {
+ val e = intercept[VecLiteralException] {
+ val v = Vec(2, SInt(4.W)).Lit(0 -> 11.S, 1 -> -0xffff.S)
+ }
+ e.getMessage should include(
+ "VecLiteral: Vec[SInt<4>] has the following incorrectly typed or sized initializers: 0 -> SInt<5>(11),1 -> SInt<17>(-65535)"
+ )
+ }
+ }
+
+ //NOTE: I had problems where this would not work if this class declaration was inside test scope
+ class HasVecInit extends Module {
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ }
+
+ "Vec literals should work when used to initialize a reg of vec" in {
+ val firrtl = (new ChiselStage).emitFirrtl(new HasVecInit, args = Array("--full-stacktrace"))
+ firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""")
+ firrtl should include("""_y_WIRE[1] <= UInt<8>("hcd")""")
+ firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""")
+ firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""")
+ firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin)
+ }
+
+ //NOTE: I had problems where this would not work if this class declaration was inside test scope
+ class HasPartialVecInit extends Module {
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ }
+
+ "Vec literals should work when used to partially initialize a reg of vec" in {
+ val firrtl = (new ChiselStage).emitFirrtl(new HasPartialVecInit, args = Array("--full-stacktrace"))
+ firrtl should include("""_y_WIRE[0] <= UInt<8>("hab")""")
+ firrtl should include("""_y_WIRE[1] is invalid""")
+ firrtl should include("""_y_WIRE[2] <= UInt<8>("hef")""")
+ firrtl should include("""_y_WIRE[3] <= UInt<8>("hff")""")
+ firrtl should include(""" reset => (reset, _y_WIRE)""".stripMargin)
+ }
+
+ class ResetRegWithPartialVecLiteral extends Module {
+ val in = IO(Input(Vec(4, UInt(8.W))))
+ val out = IO(Output(Vec(4, UInt(8.W))))
+ val initValue = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ val y = RegInit(initValue)
+ when(in(1) > 0.U) {
+ y(1) := in(1)
+ }
+ when(in(2) > 0.U) {
+ y(2) := in(2)
+ }
+ out := y
+ }
+
+ "Vec literals should only init specified fields when used to partially initialize a reg of vec" in {
+ println(ChiselStage.emitFirrtl(new ResetRegWithPartialVecLiteral))
+ assertTesterPasses(new BasicTester {
+ val m = Module(new ResetRegWithPartialVecLiteral)
+ val (counter, wrapped) = Counter(true.B, 8)
+ m.in := DontCare
+ when(counter < 2.U) {
+ m.in(1) := 0xff.U
+ m.in(2) := 0xff.U
+ }.elsewhen(counter === 2.U) {
+ chisel3.assert(m.out(1) === 0xff.U)
+ chisel3.assert(m.out(2) === 0xff.U)
+ }.elsewhen(counter === 3.U) {
+ m.in(1) := 0.U
+ m.in(2) := 0.U
+ m.reset := true.B
+ }.elsewhen(counter > 2.U) {
+ // m.out(1) should not be reset, m.out(2) should be reset
+ chisel3.assert(m.out(1) === 0xff.U)
+ chisel3.assert(m.out(2) === 0xEF.U)
+ }
+ when(wrapped) {
+ stop()
+ }
+ })
+ }
+
+ "lowest of vec literal contains least significant bits and " in {
+ val y = Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W))
+ y.litValue() should be(BigInt("FFEFCDAB", 16))
+ }
+
+ "the order lits are specified does not matter" in {
+ val y = Vec(4, UInt(8.W)).Lit(3 -> 0xFF.U(8.W), 2 -> 0xEF.U(8.W), 1 -> 0xCD.U(8.W), 0 -> 0xAB.U(8.W))
+ y.litValue() should be(BigInt("FFEFCDAB", 16))
+ }
+
+ "regardless of the literals widths, packing should be done based on the width of the Vec's gen" in {
+ val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 2 -> 0x2.U, 3 -> 0x3.U)
+ z.litValue() should be(BigInt("03020202", 16))
+ }
+
+ "packing sparse vec lits should not pack, litOption returns None" in {
+ // missing sub-listeral for index 2
+ val z = Vec(4, UInt(8.W)).Lit(0 -> 0x2.U, 1 -> 0x2.U, 3 -> 0x3.U)
+
+ z.litOption should be(None)
+ }
+
+ "registers can be initialized with a Vec literal" in {
+ assertTesterPasses(new BasicTester {
+ val y = RegInit(Vec(4, UInt(8.W)).Lit(0 -> 0xAB.U(8.W), 1 -> 0xCD.U(8.W), 2 -> 0xEF.U(8.W), 3 -> 0xFF.U(8.W)))
+ chisel3.assert(y.asUInt === BigInt("FFEFCDAB", 16).U)
+ stop()
+ })
+ }
+
+ "how does asUInt work" in {
+ assertTesterPasses(new BasicTester {
+ val vec1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ val vec2 = VecInit(Seq(0xDD.U, 0xCC.U, 0xBB.U, 0xAA.U))
+ printf("vec1 %x\n", vec1.asUInt())
+ printf("vec2 %x\n", vec2.asUInt())
+ stop()
+ })
+ }
+
+ "Vec literals uint conversion" in {
+ class M1 extends Module {
+ val out1 = IO(Output(UInt(64.W)))
+ val out2 = IO(Output(UInt(64.W)))
+
+ val v1 = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+ out1 := v1.asUInt
+
+ val v2 = VecInit(0xDD.U(16.W), 0xCC.U, 0xBB.U, 0xAA.U)
+ out2 := v2.asUInt
+ }
+
+ assertTesterPasses(new BasicTester {
+ val m = Module(new M1)
+ chisel3.assert(m.out1 === m.out2)
+ stop()
+ })
+ }
+
+ "VecLits should work properly with .asUInt" in {
+ val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ assertTesterPasses {
+ new BasicTester {
+ chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)")
+ stop()
+ }
+ }
+ }
+
+ "bundle literals should work in RTL" in {
+ val outsideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+
+ assertTesterPasses {
+ new BasicTester {
+ chisel3.assert(outsideVecLit(0) === 0xDD.U, s"v(0)")
+ chisel3.assert(outsideVecLit(1) === 0xCC.U)
+ chisel3.assert(outsideVecLit(2) === 0xBB.U)
+ chisel3.assert(outsideVecLit(3) === 0xAA.U)
+
+ chisel3.assert(outsideVecLit.litValue().U === outsideVecLit.asUInt())
+
+ val insideVecLit = Vec(4, UInt(16.W)).Lit(0 -> 0xDD.U, 1 -> 0xCC.U, 2 -> 0xBB.U, 3 -> 0xAA.U)
+ chisel3.assert(insideVecLit(0) === 0xDD.U)
+ chisel3.assert(insideVecLit(1) === 0xCC.U)
+ chisel3.assert(insideVecLit(2) === 0xBB.U)
+ chisel3.assert(insideVecLit(3) === 0xAA.U)
+
+ chisel3.assert(insideVecLit(0) === outsideVecLit(0))
+ chisel3.assert(insideVecLit(1) === outsideVecLit(1))
+ chisel3.assert(insideVecLit(2) === outsideVecLit(2))
+ chisel3.assert(insideVecLit(3) === outsideVecLit(3))
+
+ val vecWire1 = Wire(Vec(4, UInt(16.W)))
+ vecWire1 := outsideVecLit
+
+ chisel3.assert(vecWire1(0) === 0xDD.U)
+ chisel3.assert(vecWire1(1) === 0xCC.U)
+ chisel3.assert(vecWire1(2) === 0xBB.U)
+ chisel3.assert(vecWire1(3) === 0xAA.U)
+
+ val vecWire2 = Wire(Vec(4, UInt(16.W)))
+ vecWire2 := insideVecLit
+
+ chisel3.assert(vecWire2(0) === 0xDD.U)
+ chisel3.assert(vecWire2(1) === 0xCC.U)
+ chisel3.assert(vecWire2(2) === 0xBB.U)
+ chisel3.assert(vecWire2(3) === 0xAA.U)
+
+ stop()
+ }
+ }
+ }
+
+ "partial vec literals should work in RTL" in {
+ assertTesterPasses{ new BasicTester{
+ val vecLit = Vec(4, UInt(8.W)).Lit(0 -> 42.U, 2 -> 5.U)
+ chisel3.assert(vecLit(0) === 42.U)
+ chisel3.assert(vecLit(2) === 5.U)
+
+ val vecWire = Wire(Vec(4, UInt(8.W)))
+ vecWire := vecLit
+
+ chisel3.assert(vecWire(0) === 42.U)
+ chisel3.assert(vecWire(2) === 5.U)
+
+ stop()
+ }}
+ }
+
+ "nested vec literals should be constructable" in {
+ val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit(
+ 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U),
+ 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U)
+ )
+
+ outerVec.litValue() should be (BigInt("654321", 16))
+ outerVec(0).litValue() should be (BigInt("321", 16))
+ outerVec(1).litValue() should be (BigInt("654", 16))
+ outerVec(0)(0).litValue() should be (BigInt(1))
+ outerVec(0)(1).litValue() should be (BigInt(2))
+ outerVec(0)(2).litValue() should be (BigInt(3))
+ outerVec(1)(0).litValue() should be (BigInt(4))
+ outerVec(1)(1).litValue() should be (BigInt(5))
+ outerVec(1)(2).litValue() should be (BigInt(6))
+ }
+
+ "contained vecs should work" in {
+ assertTesterPasses{ new BasicTester {
+ val outerVec = Vec(2, Vec(3, UInt(4.W))).Lit(
+ 0 -> Vec(3, UInt(4.W)).Lit(0 -> 1.U, 1 -> 2.U, 2 -> 3.U),
+ 1 -> Vec(3, UInt(4.W)).Lit(0 -> 4.U, 1 -> 5.U, 2 -> 6.U)
+ )
+
+ chisel3.assert(outerVec(0)(0) === 1.U)
+ chisel3.assert(outerVec(0)(1) === 2.U)
+ chisel3.assert(outerVec(0)(2) === 3.U)
+ chisel3.assert(outerVec(1)(0) === 4.U)
+ chisel3.assert(outerVec(1)(1) === 5.U)
+ chisel3.assert(outerVec(1)(2) === 6.U)
+
+ val v0 = outerVec(0)
+ val v1 = outerVec(1)
+ chisel3.assert(v0(0) === 1.U)
+ chisel3.assert(v0(1) === 2.U)
+ chisel3.assert(v0(2) === 3.U)
+ chisel3.assert(v1(0) === 4.U)
+ chisel3.assert(v1(1) === 5.U)
+ chisel3.assert(v1(2) === 6.U)
+
+ stop()
+ }}
+ }
+
+ //TODO: decide what behavior here should be
+ "This doesn't work should it" ignore {
+ assertTesterPasses {
+ new BasicTester {
+ def vecFactory = Vec(2, FixedPoint(8.W, 4.BP))
+
+ val vecWire1 = Wire(Output(vecFactory))
+ val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP))
+ val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP))
+
+ vecWire1 := vecLit1
+ vecWire1 := vecLit2
+ printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt())
+ chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP))
+ chisel3.assert(vecWire1(1) === (3.25).F(8.W, 4.BP))
+ stop()
+ }
+ }
+ }
+
+ "partially initialized Vec literals should assign" in {
+ assertTesterPasses {
+ new BasicTester {
+ def vecFactory = Vec(2, FixedPoint(8.W, 4.BP))
+
+ val vecWire1 = Wire(Output(vecFactory))
+ val vecWire2 = Wire(Output(vecFactory))
+ val vecLit1 = vecFactory.Lit(0 -> (1.5).F(8.W, 4.BP))
+ val vecLit2 = vecFactory.Lit(1 -> (3.25).F(8.W, 4.BP))
+
+ vecWire1 := vecLit1
+ vecWire2 := vecLit2
+ vecWire1(1) := (0.5).F(8.W, 4.BP)
+ printf("vw1(0) %x vw1(1) %x\n", vecWire1(0).asUInt(), vecWire1(1).asUInt())
+ chisel3.assert(vecWire1(0) === (1.5).F(8.W, 4.BP))
+ chisel3.assert(vecWire1(1) === (0.5).F(8.W, 4.BP)) // Last connect won
+ chisel3.assert(vecWire2(1) === (3.25).F(8.W, 4.BP))
+ stop()
+ }
+ }
+ }
+
+ "Vec literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 1 -> 0xB.U, 2 -> 0xC.U))
+ r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop
+
+ // check reset values on first cycle out of reset
+ chisel3.assert(r(0) === 0xA.U)
+ chisel3.assert(r(1) === 0xB.U)
+ chisel3.assert(r(2) === 0xC.U)
+ stop()
+ }
+ }
+ }
+
+ "partially initialized Vec literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U))
+ r := (r.asUInt + 1.U).asTypeOf(Vec(3, UInt(11.W))) // prevent constprop
+ // check reset values on first cycle out of reset
+ chisel3.assert(r(0) === 0xA.U)
+ chisel3.assert(r(2) === 0xC.U)
+ stop()
+ }
+ }
+ }
+
+ "Fields extracted from Vec Literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, UInt(11.W)).Lit(0 -> 0xA.U, 2 -> 0xC.U).apply(0))
+ r := r + 1.U // prevent const prop
+ chisel3.assert(r === 0xA.U) // coming out of reset
+ stop()
+ }
+ }
+ }
+
+ "DontCare fields extracted from Vec Literals should work as register reset values" in {
+ assertTesterPasses {
+ new BasicTester {
+ val r = RegInit(Vec(3, Bool()).Lit(0 -> true.B).apply(2))
+ r := reset.asBool
+ printf(p"r = $r\n") // Can't assert because reset value is DontCare
+ stop()
+ }
+ }
+ }
+
+ "DontCare fields extracted from Vec Literals should work in other Expressions" in {
+ assertTesterPasses {
+ new BasicTester {
+ val x = Vec(3, Bool()).Lit(0 -> true.B).apply(2) || true.B
+ chisel3.assert(x === true.B)
+ stop()
+ }
+ }
+ }
+
+ "vec literals with non-literal values should fail" in {
+ val exc = intercept[VecLiteralException] {
+ extractCause[VecLiteralException] {
+ ChiselStage.elaborate {
+ new RawModule {
+ (Vec(3, UInt(11.W)).Lit(0 -> UInt()))
+ }
+ }
+ }
+ }
+ exc.getMessage should include("field 0 specified with non-literal value UInt")
+ }
+
+ "vec literals are instantiated on connect" in {
+ class VecExample5 extends RawModule {
+ val out = IO(Output(Vec(2, UInt(4.W))))
+ val bundle = Vec(2, UInt(4.W)).Lit(
+ 0 -> 0xa.U,
+ 1 -> 0xb.U
+ )
+ out := bundle
+ }
+
+ val firrtl = (new chisel3.stage.ChiselStage).emitFirrtl(new VecExample5, args = Array("--full-stacktrace"))
+ firrtl should include("""out[0] <= UInt<4>("ha")""")
+ firrtl should include("""out[1] <= UInt<4>("hb")""")
+ }
+
+ class SubBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(4.W)
+ }
+
+ class VecExample extends RawModule {
+ val out = IO(Output(Vec(2, new SubBundle)))
+ val bundle = Vec(2, new SubBundle).Lit(
+ 0 -> (new SubBundle).Lit(_.foo -> 42.U, _.bar -> 22.U),
+ 1 -> (new SubBundle).Lit(_.foo -> 7.U, _.bar -> 3.U)
+ )
+ out := bundle
+ }
+
+ "vec literals can contain bundles" in {
+ val chirrtl = (new chisel3.stage.ChiselStage).emitChirrtl(new VecExample, args = Array("--full-stacktrace"))
+ chirrtl should include("""out[0].bar <= UInt<5>("h16")""")
+ chirrtl should include("""out[0].foo <= UInt<6>("h2a")""")
+ chirrtl should include("""out[1].bar <= UInt<2>("h3")""")
+ chirrtl should include("""out[1].foo <= UInt<3>("h7")""")
+
+ }
+
+ "vec literals can have bundle children" in {
+ val vec = Vec(2, new SubBundle).Lit(
+ 0 -> (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U),
+ 1 -> (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U)
+ )
+ vec.litValue().toString(16) should be("defabc")
+ }
+
+ "vec literals can have bundle children assembled incrementally" in {
+ val bundle1 = (new SubBundle).Lit(_.foo -> 0xab.U, _.bar -> 0xc.U)
+ val bundle2 = (new SubBundle).Lit(_.foo -> 0xde.U, _.bar -> 0xf.U)
+
+ bundle1.litValue().toString(16) should be("abc")
+ bundle2.litValue().toString(16) should be("def")
+
+ val vec = Vec(2, new SubBundle).Lit(0 -> bundle1, 1 -> bundle2)
+
+ vec.litValue().toString(16) should be("defabc")
+ }
+
+ "bundles can contain vec lits" in {
+ val vec1 = Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U)
+ val vec2 = Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U)
+ val bundle = (new Bundle {
+ val foo = Vec(3, UInt(4.W))
+ val bar = Vec(2, UInt(4.W))
+ }).Lit(_.foo -> vec1, _.bar -> vec2)
+ bundle.litValue().toString(16) should be("cbaed")
+ }
+
+ "bundles can contain vec lits in-line" in {
+ val bundle = (new Bundle {
+ val foo = Vec(3, UInt(4.W))
+ val bar = Vec(2, UInt(4.W))
+ }).Lit(
+ _.foo -> Vec(3, UInt(4.W)).Lit(0 -> 0xa.U, 1 -> 0xb.U, 2 -> 0xc.U),
+ _.bar -> Vec(2, UInt(4.W)).Lit(0 -> 0xd.U, 1 -> 0xe.U)
+ )
+ bundle.litValue().toString(16) should be("cbaed")
+ }
+
+ "Vec.Lit is a trivial Vec literal factory" in {
+ val vec = Vec.Lit(0xa.U, 0xb.U)
+ vec(0).litValue() should be(0xa)
+ vec(1).litValue() should be(0xb)
+ }
+
+ "Vec.Lit bases it's element width on the widest literal supplied" in {
+ val vec = Vec.Lit(0xa.U, 0xbbbb.U)
+ vec(0).litValue() should be(0xa)
+ vec(1).litValue() should be(0xbbbb)
+ vec.length should be(2)
+ vec.getWidth should be(16 * 2)
+ vec.litValue() should be(BigInt("bbbb000a", 16))
+ }
+}
diff --git a/src/test/scala/chiselTests/WidthSpec.scala b/src/test/scala/chiselTests/WidthSpec.scala
index 2a33c1d6..34159214 100644
--- a/src/test/scala/chiselTests/WidthSpec.scala
+++ b/src/test/scala/chiselTests/WidthSpec.scala
@@ -186,3 +186,62 @@ class RegInitWidthSpec extends WireDefaultRegInitSpecImpl {
def builder2[T <: Data](x: T, y: T): T = RegInit(x, y)
}
+class OpWidthSpec extends ChiselFlatSpec {
+ import firrtl._
+ import firrtl.ir._
+
+ val maxWidth = 5
+ val uIntOps: Seq[((UInt, UInt) => UInt, PrimOp)] =
+ Seq(
+ (_ +& _, PrimOps.Add),
+ (_ -& _, PrimOps.Sub),
+ (_ * _, PrimOps.Mul),
+ (_ / _, PrimOps.Div),
+ (_ % _, PrimOps.Rem),
+ (_ << _, PrimOps.Dshl),
+ (_ >> _, PrimOps.Dshr)
+ )
+
+ assertTesterPasses(new chisel3.testers.BasicTester {
+ for (i <- 0 to maxWidth) {
+ for (j <- 0 to maxWidth) {
+ for ((cOp, fOp) <- uIntOps) {
+ val args = Seq(i, j).map(w => Reference("", UIntType(IntWidth(w))))
+ fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match {
+ case UIntType(IntWidth(w)) =>
+ val x = 0.U(maxWidth.W).head(i)
+ val y = 0.U(maxWidth.W).head(j)
+ assert(w == cOp(x, y).getWidth)
+ }
+ }
+ }
+ }
+ stop()
+ })
+
+ val sIntOps: Seq[((SInt, SInt) => SInt, PrimOp)] =
+ Seq(
+ (_ +& _, PrimOps.Add),
+ (_ -& _, PrimOps.Sub),
+ (_ * _, PrimOps.Mul),
+ (_ / _, PrimOps.Div),
+ (_ % _, PrimOps.Rem)
+ )
+
+ assertTesterPasses(new chisel3.testers.BasicTester {
+ for (i <- 0 to maxWidth) {
+ for (j <- 0 to maxWidth) {
+ for ((cOp, fOp) <- sIntOps) {
+ val args = Seq(i, j).map(w => Reference("", SIntType(IntWidth(w))))
+ fOp.propagateType(DoPrim(fOp, args, Nil, UnknownType)) match {
+ case SIntType(IntWidth(w)) =>
+ val x = 0.U(maxWidth.W).head(i).asSInt
+ val y = 0.U(maxWidth.W).head(j).asSInt
+ assert(w == cOp(x, y).getWidth)
+ }
+ }
+ }
+ }
+ stop()
+ })
+}
diff --git a/src/test/scala/chiselTests/aop/InjectionSpec.scala b/src/test/scala/chiselTests/aop/InjectionSpec.scala
index c9fa2e5e..a28501a5 100644
--- a/src/test/scala/chiselTests/aop/InjectionSpec.scala
+++ b/src/test/scala/chiselTests/aop/InjectionSpec.scala
@@ -5,6 +5,7 @@ package chiselTests.aop
import chisel3.testers.{BasicTester, TesterDriver}
import chiselTests.{ChiselFlatSpec, Utils}
import chisel3._
+import chisel3.aop.Select
import chisel3.aop.injecting.InjectingAspect
import logger.{LogLevel, LogLevelAnnotation}
@@ -14,6 +15,11 @@ object InjectionHierarchy {
val moduleSubmoduleA = Module(new SubmoduleA)
}
+ class MultiModuleInjectionTester extends BasicTester {
+ val subA0 = Module(new SubmoduleA)
+ val subA1 = Module(new SubmoduleA)
+ }
+
class SubmoduleA extends Module {
val io = IO(new Bundle {
val out = Output(Bool())
@@ -104,6 +110,17 @@ class InjectionSpec extends ChiselFlatSpec with Utils {
}
)
+ val multiModuleInjectionAspect = InjectingAspect(
+ { top: MultiModuleInjectionTester =>
+ Select.collectDeep(top) { case m: SubmoduleA => m }
+ },
+ { m: Module =>
+ val wire = Wire(Bool())
+ wire := m.reset.asBool()
+ dontTouch(wire)
+ stop()
+ }
+ )
"Test" should "pass if inserted the correct values" in {
assertTesterPasses{ new AspectTester(Seq(0, 1, 2)) }
@@ -142,4 +159,12 @@ class InjectionSpec extends ChiselFlatSpec with Utils {
Seq(addingExternalModules) ++ TesterDriver.verilatorOnly
)
}
+
+ "Injection into multiple submodules of the same class" should "work" in {
+ assertTesterPasses(
+ {new MultiModuleInjectionTester},
+ Nil,
+ Seq(multiModuleInjectionAspect) ++ TesterDriver.verilatorOnly
+ )
+ }
}
diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala
index 91353f5a..e09e78c8 100644
--- a/src/test/scala/chiselTests/aop/SelectSpec.scala
+++ b/src/test/scala/chiselTests/aop/SelectSpec.scala
@@ -22,14 +22,16 @@ class SelectTester(results: Seq[Int]) extends BasicTester {
val nreset = reset.asBool() === false.B
val selected = values(counter)
val zero = 0.U + 0.U
+ var p: printf.Printf = null
when(overflow) {
counter := zero
stop()
}.otherwise {
when(nreset) {
assert(counter === values(counter))
- printf("values(%d) = %d\n", counter, selected)
+ p = printf("values(%d) = %d\n", counter, selected)
}
+
}
}
@@ -81,17 +83,18 @@ class SelectSpec extends ChiselFlatSpec {
"Test" should "pass if selecting correct printfs" in {
execute(
() => new SelectTester(Seq(0, 1, 2)),
- { dut: SelectTester => Seq(Select.printfs(dut).last) },
+ { dut: SelectTester => Seq(Select.printfs(dut).last.toString) },
{ dut: SelectTester =>
Seq(Select.Printf(
+ dut.p,
Seq(
When(Select.ops("eq")(dut).last.asInstanceOf[Bool]),
When(dut.nreset),
WhenNot(dut.overflow)
),
- Printable.pack("values(%d) = %d\n", dut.counter, dut.selected),
+ dut.p.pable,
dut.clock
- ))
+ ).toString)
}
)
}
@@ -153,5 +156,60 @@ class SelectSpec extends ChiselFlatSpec {
assert(bbs.size == 1)
}
+ "CloneModuleAsRecord" should "NOT show up in Select aspects" in {
+ import chisel3.experimental.CloneModuleAsRecord
+ class Child extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class Top extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ val inst0 = Module(new Child)
+ val inst1 = CloneModuleAsRecord(inst0)
+ inst0.in := in
+ inst1("in") := inst0.out
+ out := inst1("out")
+ }
+ val top = ChiselGeneratorAnnotation(() => {
+ new Top()
+ }).elaborate
+ .collectFirst { case DesignAnnotation(design: Top) => design }
+ .get
+ Select.collectDeep(top) { case x => x } should equal (Seq(top, top.inst0))
+ Select.getDeep(top)(x => Seq(x)) should equal (Seq(top, top.inst0))
+ Select.instances(top) should equal (Seq(top.inst0))
+ }
+
+ "Using Definition/Instance with Injecting Aspects" should "throw an error" in {
+ import chisel3.experimental.CloneModuleAsRecord
+ import chisel3.experimental.hierarchy._
+ @instantiable
+ class Child extends RawModule {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class Top extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ val definition = Definition(new Child)
+ val inst0 = Instance(definition)
+ val inst1 = Instance(definition)
+ inst0.in := in
+ inst1.in := inst0.out
+ out := inst1.out
+ }
+ val top = ChiselGeneratorAnnotation(() => {
+ new Top()
+ }).elaborate
+ .collectFirst { case DesignAnnotation(design: Top) => design }
+ .get
+ intercept[Exception] { Select.collectDeep(top) { case x => x } }
+ intercept[Exception] { Select.getDeep(top)(x => Seq(x)) }
+ intercept[Exception] { Select.instances(top) }
+ }
+
}
diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala
new file mode 100644
index 00000000..d1620e88
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataView.scala
@@ -0,0 +1,546 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chiselTests.ChiselFlatSpec
+import chisel3._
+import chisel3.experimental.dataview._
+import chisel3.experimental.DataMirror.internal.chiselTypeClone
+import chisel3.stage.ChiselStage
+import chisel3.util.{Decoupled, DecoupledIO}
+
+object SimpleBundleDataView {
+ class BundleA(val w: Int) extends Bundle {
+ val foo = UInt(w.W)
+ }
+ class BundleB(val w: Int) extends Bundle {
+ val bar = UInt(w.W)
+ }
+ implicit val v1 = DataView[BundleA, BundleB](a => new BundleB(a.w), _.foo -> _.bar)
+ implicit val v2 = v1.invert(b => new BundleA(b.w))
+}
+
+object VecBundleDataView {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+ implicit val v1: DataView[MyBundle, Vec[UInt]] = DataView(_ => Vec(2, UInt(8.W)), _.foo -> _(1), _.bar -> _(0))
+ implicit val v2 = v1.invert(_ => new MyBundle)
+}
+
+object FlatDecoupledDataView {
+ class FizzBuzz extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ class FlatDecoupled extends Bundle {
+ val valid = Output(Bool())
+ val ready = Input(Bool())
+ val fizz = Output(UInt(8.W))
+ val buzz = Output(UInt(8.W))
+ }
+ implicit val view = DataView[FlatDecoupled, DecoupledIO[FizzBuzz]](
+ _ => Decoupled(new FizzBuzz),
+ _.valid -> _.valid,
+ _.ready -> _.ready,
+ _.fizz -> _.bits.fizz,
+ _.buzz -> _.bits.buzz
+ )
+ implicit val view2 = view.invert(_ => new FlatDecoupled)
+}
+
+// This should become part of Chisel in a later PR
+object Tuple2DataProduct {
+ implicit def tuple2DataProduct[A : DataProduct, B : DataProduct] = new DataProduct[(A, B)] {
+ def dataIterator(tup: (A, B), path: String): Iterator[(Data, String)] = {
+ val dpa = implicitly[DataProduct[A]]
+ val dpb = implicitly[DataProduct[B]]
+ val (a, b) = tup
+ dpa.dataIterator(a, s"$path._1") ++ dpb.dataIterator(b, s"$path._2")
+ }
+ }
+}
+
+// This should become part of Chisel in a later PR
+object HWTuple {
+ import Tuple2DataProduct._
+
+ class HWTuple2[A <: Data, B <: Data](val _1: A, val _2: B) extends Bundle
+
+ implicit def view[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data](
+ implicit v1: DataView[T1, V1], v2: DataView[T2, V2]
+ ): DataView[(T1, T2), HWTuple2[V1, V2]] =
+ DataView.mapping(
+ { case (a, b) => new HWTuple2(a.viewAs[V1].cloneType, b.viewAs[V2].cloneType)},
+ { case ((a, b), hwt) =>
+ Seq(a.viewAs[V1] -> hwt._1,
+ b.viewAs[V2] -> hwt._2)
+ }
+ )
+
+ implicit def tuple2hwtuple[T1 : DataProduct, T2 : DataProduct, V1 <: Data, V2 <: Data](
+ tup: (T1, T2))(implicit v1: DataView[T1, V1], v2: DataView[T2, V2]
+ ): HWTuple2[V1, V2] = tup.viewAs[HWTuple2[V1, V2]]
+}
+
+// This should become part of Chisel in a later PR
+object SeqDataProduct {
+ // Should we special case Seq[Data]?
+ implicit def seqDataProduct[A : DataProduct]: DataProduct[Seq[A]] = new DataProduct[Seq[A]] {
+ def dataIterator(a: Seq[A], path: String): Iterator[(Data, String)] = {
+ val dpa = implicitly[DataProduct[A]]
+ a.iterator
+ .zipWithIndex
+ .flatMap { case (elt, idx) =>
+ dpa.dataIterator(elt, s"$path[$idx]")
+ }
+ }
+ }
+}
+
+object SeqToVec {
+ import SeqDataProduct._
+
+ // TODO this would need a better way to determine the prototype for the Vec
+ implicit def seqVec[A : DataProduct, B <: Data](implicit dv: DataView[A, B]): DataView[Seq[A], Vec[B]] =
+ DataView.mapping[Seq[A], Vec[B]](
+ xs => Vec(xs.size, chiselTypeClone(xs.head.viewAs[B])), // xs.head is not correct in general
+ { case (s, v) => s.zip(v).map { case (a, b) => a.viewAs[B] -> b } }
+ )
+
+ implicit def seq2Vec[A : DataProduct, B <: Data](xs: Seq[A])(implicit dv: DataView[A, B]): Vec[B] =
+ xs.viewAs[Vec[B]]
+}
+
+class DataViewSpec extends ChiselFlatSpec {
+
+ behavior of "DataView"
+
+ it should "support simple Bundle viewing" in {
+ import SimpleBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA(8)))
+ val out = IO(Output(new BundleB(8)))
+ out := in.viewAs[BundleB]
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out.bar <= in.foo")
+ }
+
+ it should "be a bidirectional mapping" in {
+ import SimpleBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA(8)))
+ val out = IO(Output(new BundleB(8)))
+ out.viewAs[BundleA] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out.bar <= in.foo")
+ }
+
+ it should "handle viewing UInts as UInts" in {
+ class MyModule extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val foo = IO(Output(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ foo := in.viewAs[UInt]
+ bar.viewAs[UInt] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("foo <= in")
+ chirrtl should include("bar <= in")
+ }
+
+ it should "handle viewing Bundles as their same concrete type" in {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val in = IO(Input(new MyBundle))
+ val fizz = IO(Output(new MyBundle))
+ val buzz = IO(Output(new MyBundle))
+ fizz := in.viewAs[MyBundle]
+ buzz.viewAs[MyBundle] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("fizz.foo <= in.foo")
+ chirrtl should include("buzz.foo <= in.foo")
+ }
+
+ it should "handle viewing Vecs as their same concrete type" in {
+ class MyModule extends Module {
+ val in = IO(Input(Vec(1, UInt(8.W))))
+ val fizz = IO(Output(Vec(1, UInt(8.W))))
+ val buzz = IO(Output(Vec(1, UInt(8.W))))
+ fizz := in.viewAs[Vec[UInt]]
+ buzz.viewAs[Vec[UInt]] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("fizz[0] <= in[0]")
+ chirrtl should include("buzz[0] <= in[0]")
+ }
+
+ it should "handle viewing Vecs as Bundles and vice versa" in {
+ import VecBundleDataView._
+ class MyModule extends Module {
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(Vec(2, UInt(8.W))))
+ val out2 = IO(Output(Vec(2, UInt(8.W))))
+ out := in.viewAs[Vec[UInt]]
+ out2.viewAs[MyBundle] := in
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("out[0] <= in.bar")
+ chirrtl should include("out[1] <= in.foo")
+ chirrtl should include("out2[0] <= in.bar")
+ chirrtl should include("out2[1] <= in.foo")
+ }
+
+ it should "work with bidirectional connections for nested types" in {
+ import FlatDecoupledDataView._
+ class MyModule extends Module {
+ val enq = IO(Flipped(Decoupled(new FizzBuzz)))
+ val deq = IO(new FlatDecoupled)
+ val deq2 = IO(new FlatDecoupled)
+ deq <> enq.viewAs[FlatDecoupled]
+ deq2.viewAs[DecoupledIO[FizzBuzz]] <> enq
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include("deq.valid <= enq.valid")
+ chirrtl should include("enq.ready <= deq.ready")
+ chirrtl should include("deq.fizz <= enq.bits.fizz")
+ chirrtl should include("deq.buzz <= enq.bits.buzz")
+ chirrtl should include("deq2.valid <= enq.valid")
+ chirrtl should include("enq.ready <= deq2.ready")
+ chirrtl should include("deq2.fizz <= enq.bits.fizz")
+ chirrtl should include("deq2.buzz <= enq.bits.buzz")
+ }
+
+ it should "support viewing a Bundle as a Parent Bundle type" in {
+ class Foo extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class Bar extends Foo {
+ val bar = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val fooIn = IO(Input(new Foo))
+ val barOut = IO(Output(new Bar))
+ barOut.viewAsSupertype(new Foo) := fooIn
+
+ val barIn = IO(Input(new Bar))
+ val fooOut = IO(Output(new Foo))
+ fooOut := barIn.viewAsSupertype(new 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 {
+ val foo = UInt(8.W)
+ }
+ class Bar extends Foo {
+ val bar = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val barIn = IO(Input(new Bar))
+ val fooOut = IO(Output(new Foo))
+ fooOut.viewAs(new Bar) := barIn
+ }
+ """)
+ }
+
+ it should "work in UInt operations" in {
+ class MyBundle extends Bundle {
+ val value = UInt(8.W)
+ }
+ class MyModule extends Module {
+ val a = IO(Input(UInt(8.W)))
+ val b = IO(Input(new MyBundle))
+ val cond = IO(Input(Bool()))
+ val and, mux, bitsCat = IO(Output(UInt(8.W)))
+ // Chisel unconditionally emits a node, so name it at least
+ val x = a.viewAs[UInt] & b.viewAs[MyBundle].value
+ and.viewAs[UInt] := x
+
+ val y = Mux(cond.viewAs[Bool], a.viewAs[UInt], b.value.viewAs[UInt])
+ mux.viewAs[UInt] := y
+
+ // TODO should we have a macro so that we don't need .apply?
+ val aBits = a.viewAs[UInt].apply(3, 0)
+ val bBits = b.viewAs[MyBundle].value(3, 0)
+ val abCat = aBits.viewAs[UInt] ## bBits.viewAs[UInt]
+ bitsCat := abCat
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ val expected = List(
+ "node x = and(a, b.value)",
+ "and <= x",
+ "node y = mux(cond, a, b.value)",
+ "mux <= y",
+ "node aBits = bits(a, 3, 0)",
+ "node bBits = bits(b.value, 3, 0)",
+ "node abCat = cat(aBits, bBits)",
+ "bitsCat <= abCat"
+ )
+ for (line <- expected) {
+ chirrtl should include(line)
+ }
+ }
+
+ it should "support .asUInt of Views" in {
+ import VecBundleDataView._
+ class MyModule extends Module {
+ val barIn = IO(Input(new MyBundle))
+ val fooOut = IO(Output(UInt()))
+ val cat = barIn.viewAs[Vec[UInt]].asUInt
+ fooOut := cat
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("node cat = cat(barIn.foo, barIn.bar)")
+ chirrtl should include ("fooOut <= cat")
+ }
+
+ it should "be composable" in {
+ // Given DataView[A, B] and DataView[B, C], derive DataView[A, C]
+ class Foo(val foo: UInt) extends Bundle
+ class Bar(val bar: UInt) extends Bundle
+ class Fizz(val fizz: UInt) extends Bundle
+
+ implicit val foo2bar = DataView[Foo, Bar](f => new Bar(chiselTypeClone(f.foo)), _.foo -> _.bar)
+ implicit val bar2fizz = DataView[Bar, Fizz](b => new Fizz(chiselTypeClone(b.bar)), _.bar -> _.fizz)
+
+ implicit val foo2fizz: DataView[Foo, Fizz] = foo2bar.andThen(bar2fizz)
+
+ class MyModule extends Module {
+ val a, b = IO(Input(new Foo(UInt(8.W))))
+ val y, z = IO(Output(new Fizz(UInt(8.W))))
+ y := a.viewAs[Fizz]
+ z := b.viewAs[Bar].viewAs[Fizz]
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("y.fizz <= a.foo")
+ chirrtl should include ("z.fizz <= b.foo")
+ }
+
+ // This example should be turned into a built-in feature
+ it should "enable implementing \"HardwareTuple\"" in {
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val sel = IO(Input(Bool()))
+ val y, z = IO(Output(UInt(8.W)))
+ (y, z) := Mux(sel, (a, b), (c, d))
+ }
+ // Verilog instead of CHIRRTL because the optimizations make it much prettier
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign y = sel ? a : c;")
+ verilog should include ("assign z = sel ? b : d;")
+ }
+
+ it should "support nesting of tuples" in {
+ import Tuple2DataProduct._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val w, x, y, z = IO(Output(UInt(8.W)))
+ ((w, x), (y, z)) := ((a, b), (c, d))
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("w <= a")
+ chirrtl should include ("x <= b")
+ chirrtl should include ("y <= c")
+ chirrtl should include ("z <= d")
+ }
+
+ // This example should be turned into a built-in feature
+ it should "enable viewing Seqs as Vecs" in {
+ import SeqToVec._
+
+ class MyModule extends Module {
+ val a, b, c = IO(Input(UInt(8.W)))
+ val x, y, z = IO(Output(UInt(8.W)))
+ Seq(x, y, z) := VecInit(a, b, c)
+ }
+ // Verilog instead of CHIRRTL because the optimizations make it much prettier
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign x = a;")
+ verilog should include ("assign y = b;")
+ verilog should include ("assign z = c;")
+ }
+
+ it should "support recursive composition of views" in {
+ import Tuple2DataProduct._
+ import SeqDataProduct._
+ import SeqToVec._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val w, x, y, z = IO(Output(UInt(8.W)))
+
+ // A little annoying that we need the type annotation on VecInit to get the implicit conversion to work
+ // Note that one can just use the Seq on the RHS so there is an alternative (may lack discoverability)
+ // We could also overload `VecInit` instead of relying on the implicit conversion
+ Seq((w, x), (y, z)) := VecInit[HWTuple2[UInt, UInt]]((a, b), (c, d))
+ }
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign w = a;")
+ verilog should include ("assign x = b;")
+ verilog should include ("assign y = c;")
+ verilog should include ("assign z = d;")
+ }
+
+ it should "error if you try to dynamically index a Vec view" in {
+ import SeqDataProduct._
+ import SeqToVec._
+ import Tuple2DataProduct._
+ import HWTuple._
+
+ class MyModule extends Module {
+ val inA, inB = IO(Input(UInt(8.W)))
+ val outA, outB = IO(Output(UInt(8.W)))
+ val idx = IO(Input(UInt(1.W)))
+
+ val a, b, c, d = RegInit(0.U)
+
+ // Dynamic indexing is more of a "generator" in Chisel3 than an individual node
+ val selected = Seq((a, b), (c, d)).apply(idx)
+ selected := (inA, inB)
+ (outA, outB) := selected
+ }
+ (the [InvalidViewException] thrownBy {
+ ChiselStage.emitChirrtl(new MyModule)
+ }).getMessage should include ("Dynamic indexing of Views is not yet supported")
+ }
+
+ it should "error if the mapping is non-total in the view" in {
+ class MyBundle(val foo: UInt, val bar: UInt) extends Bundle
+ implicit val dv = DataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar)
+ class MyModule extends Module {
+ val tpe = new MyBundle(UInt(8.W), UInt(8.W))
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(tpe))
+ out := in.viewAs[MyBundle]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View field '_.foo' is missing")
+ }
+
+ it should "error if the mapping is non-total in the target" in {
+ import Tuple2DataProduct._
+ implicit val dv = DataView[(UInt, UInt), UInt](_ => UInt(), _._1 -> _)
+ class MyModule extends Module {
+ val a, b = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := (a, b).viewAs[UInt]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("Target field '_._2' is missing")
+ }
+
+ it should "error if the mapping contains Data that are not part of the Target" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz))
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View mapping must only contain Elements within the Target")
+ }
+
+ it should "error if the mapping contains Data that are not part of the View" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val fizz = UInt(8.W)
+ val buzz = UInt(8.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.fizz, (_, b) => (3.U, b.buzz))
+ implicit val dv2 = dv.invert(_ => new BundleA)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out.viewAs[BundleA] := in
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View mapping must only contain Elements within the View")
+ }
+
+ it should "error if a view has a width that does not match the target" in {
+ class BundleA extends Bundle {
+ val foo = UInt(8.W)
+ }
+ class BundleB extends Bundle {
+ val bar = UInt(4.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule)
+ val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width <8>""".r
+ err.getMessage should fullyMatch regex expected
+ }
+
+ it should "error if a view has a known width when the target width is unknown" in {
+ class BundleA extends Bundle {
+ val foo = UInt()
+ }
+ class BundleB extends Bundle {
+ val bar = UInt(4.W)
+ }
+ implicit val dv = DataView[BundleA, BundleB](_ => new BundleB, _.foo -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(new BundleA))
+ val out = IO(Output(new BundleB))
+ out := in.viewAs[BundleB]
+ }
+ val err = the [InvalidViewException] thrownBy ChiselStage.emitChirrtl(new MyModule)
+ val expected = """View field _\.bar UInt<4> has width <4> that is incompatible with target value .+'s width <unknown>""".r
+ err.getMessage should fullyMatch regex expected
+ }
+
+ behavior of "PartialDataView"
+
+ it should "still error if the mapping is non-total in the view" in {
+ class MyBundle(val foo: UInt, val bar: UInt) extends Bundle
+ implicit val dv = PartialDataView[UInt, MyBundle](_ => new MyBundle(UInt(), UInt()), _ -> _.bar)
+ class MyModule extends Module {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(new MyBundle(UInt(8.W), UInt(8.W))))
+ out := in.viewAs[MyBundle]
+ }
+ val err = the [InvalidViewException] thrownBy (ChiselStage.emitVerilog(new MyModule))
+ err.toString should include ("View field '_.foo' is missing")
+ }
+
+ it should "NOT error if the mapping is non-total in the target" in {
+ import Tuple2DataProduct._
+ implicit val dv = PartialDataView[(UInt, UInt), UInt](_ => UInt(), _._2 -> _)
+ class MyModule extends Module {
+ val a, b = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := (a, b).viewAs[UInt]
+ }
+ val verilog = ChiselStage.emitVerilog(new MyModule)
+ verilog should include ("assign out = b;")
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala
new file mode 100644
index 00000000..3f149f75
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataViewIntegrationSpec.scala
@@ -0,0 +1,57 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.{BaseModule, ExtModule}
+import chisel3.experimental.dataview._
+import chisel3.util.{Decoupled, DecoupledIO, Queue, QueueIO, log2Ceil}
+import chiselTests.ChiselFlatSpec
+import firrtl.transforms.DontTouchAnnotation
+
+// Let's put it all together!
+object DataViewIntegrationSpec {
+
+ class QueueIntf[T <: Data](gen: T, entries: Int) extends Bundle {
+ val ports = new QueueIO(gen, entries)
+ // Let's grab a reference to something internal too
+ // Output because can't have directioned and undirectioned stuff
+ val enq_ptr = Output(UInt(log2Ceil(entries).W))
+ }
+
+ // It's not clear if a view of a Module ever _can_ be total since internal nodes are part of the Module
+ implicit def queueView[T <: Data] = PartialDataView[Queue[T], QueueIntf[T]](
+ q => new QueueIntf(q.gen, q.entries),
+ _.io -> _.ports,
+ // Some token internal signal
+ _.enq_ptr.value -> _.enq_ptr
+ )
+
+ object MyQueue {
+ def apply[T <: Data](enq: DecoupledIO[T], n: Int): QueueIntf[T] = {
+ val queue = Module(new Queue[T](enq.bits.cloneType, n))
+ val view = queue.viewAs[QueueIntf[T]]
+ view.ports.enq <> enq
+ view
+ }
+ }
+
+ class MyModule extends Module {
+ val enq = IO(Flipped(Decoupled(UInt(8.W))))
+ val deq = IO(Decoupled(UInt(8.W)))
+
+ val queue = MyQueue(enq, 4)
+ deq <> queue.ports.deq
+ dontTouch(queue.enq_ptr)
+ }
+}
+
+class DataViewIntegrationSpec extends ChiselFlatSpec {
+ import DataViewIntegrationSpec.MyModule
+
+ "Users" should "be able to view and annotate Modules" in {
+ val (_, annos) = getFirrtlAndAnnos(new MyModule)
+ val ts = annos.collect { case DontTouchAnnotation(t) => t.serialize }
+ ts should equal (Seq("~MyModule|Queue>enq_ptr_value"))
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
new file mode 100644
index 00000000..92091631
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala
@@ -0,0 +1,171 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.dataview._
+import chisel3.experimental.{ChiselAnnotation, annotate}
+import chisel3.stage.ChiselStage
+import chiselTests.ChiselFlatSpec
+
+object DataViewTargetSpec {
+ import firrtl.annotations._
+ private case class DummyAnno(target: ReferenceTarget, id: Int) extends SingleTargetAnnotation[ReferenceTarget] {
+ override def duplicate(n: ReferenceTarget) = this.copy(target = n)
+ }
+ private def mark(d: Data, id: Int) = annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation = DummyAnno(d.toTarget, id)
+ })
+ private def markAbs(d: Data, id: Int) = annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation = DummyAnno(d.toAbsoluteTarget, id)
+ })
+}
+
+class DataViewTargetSpec extends ChiselFlatSpec {
+ import DataViewTargetSpec._
+ private val checks: Seq[Data => String] = Seq(
+ _.toTarget.toString,
+ _.toAbsoluteTarget.toString,
+ _.instanceName,
+ _.pathName,
+ _.parentPathName,
+ _.parentModName,
+ )
+
+ // Check helpers
+ private def checkAll(impl: Data, refs: String*): Unit = {
+ refs.size should be (checks.size)
+ for ((check, value) <- checks.zip(refs)) {
+ check(impl) should be (value)
+ }
+ }
+ private def checkSameAs(impl: Data, refs: Data*): Unit =
+ for (ref <- refs) {
+ checkAll(impl, checks.map(_(ref)):_*)
+ }
+
+ behavior of "DataView Naming"
+
+ it should "support views of Elements" in {
+ class MyChild extends Module {
+ val out = IO(Output(UInt(8.W)))
+ val insideView = out.viewAs[UInt]
+ out := 0.U
+ }
+ class MyParent extends Module {
+ val out = IO(Output(UInt(8.W)))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val m = elaborateAndGetModule(new MyParent)
+ val outsideView = m.inst.out.viewAs[UInt]
+ checkSameAs(m.inst.out, m.inst.insideView, outsideView)
+ }
+
+ it should "support 1:1 mappings of Aggregates and their children" in {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bars = Vec(2, UInt(8.W))
+ }
+ implicit val dv = DataView[MyBundle, Vec[UInt]](_ => Vec(3, UInt(8.W)), _.foo -> _(0), _.bars(0) -> _(1), _.bars(1) -> _(2))
+ class MyChild extends Module {
+ val out = IO(Output(new MyBundle))
+ val outView = out.viewAs[Vec[UInt]] // Note different type
+ val outFooView = out.foo.viewAs[UInt]
+ val outBarsView = out.bars.viewAs[Vec[UInt]]
+ val outBars0View = out.bars(0).viewAs[UInt]
+ out := 0.U.asTypeOf(new MyBundle)
+ }
+ class MyParent extends Module {
+ val out = IO(Output(new MyBundle))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val m = elaborateAndGetModule(new MyParent)
+ val outView = m.inst.out.viewAs[Vec[UInt]]// Note different type
+ val outFooView = m.inst.out.foo.viewAs[UInt]
+ val outBarsView = m.inst.out.bars.viewAs[Vec[UInt]]
+ val outBars0View = m.inst.out.bars(0).viewAs[UInt]
+
+ checkSameAs(m.inst.out, m.inst.outView, outView)
+ checkSameAs(m.inst.out.foo, m.inst.outFooView, m.inst.outView(0), outFooView, outView(0))
+ checkSameAs(m.inst.out.bars, m.inst.outBarsView, outBarsView)
+ checkSameAs(m.inst.out.bars(0), m.inst.outBars0View, outBars0View, m.inst.outView(1), outView(1),
+ m.inst.outBarsView(0), outBarsView(0))
+ }
+
+ // Ideally this would work 1:1 but that requires changing the binding
+ it should "support annotation renaming of Aggregate children of Aggregate views" in {
+ class MyBundle extends Bundle {
+ val foo = Vec(2, UInt(8.W))
+ }
+ class MyChild extends Module {
+ val out = IO(Output(new MyBundle))
+ val outView = out.viewAs[MyBundle]
+ mark(out.foo, 0)
+ mark(outView.foo, 1)
+ markAbs(out.foo, 2)
+ markAbs(outView, 3)
+ out := 0.U.asTypeOf(new MyBundle)
+ }
+ class MyParent extends Module {
+ val out = IO(Output(new MyBundle))
+ val inst = Module(new MyChild)
+ out := inst.out
+ }
+ val (_, annos) = getFirrtlAndAnnos(new MyParent)
+ val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sortBy(_._1)
+ val expected = Seq(
+ 0 -> "~MyParent|MyChild>out.foo",
+ // The child of the view that was itself an Aggregate got split because 1:1 is lacking here
+ 1 -> "~MyParent|MyChild>out.foo[0]",
+ 1 -> "~MyParent|MyChild>out.foo[1]",
+ 2 -> "~MyParent|MyParent/inst:MyChild>out.foo",
+ 3 -> "~MyParent|MyParent/inst:MyChild>out"
+ )
+ pairs should equal (expected)
+ }
+
+ it should "support annotating views that cannot be mapped to a single ReferenceTarget" in {
+ import HWTuple._
+ class MyBundle extends Bundle {
+ val a, b = Input(UInt(8.W))
+ val c, d = Output(UInt(8.W))
+ }
+ // Note that each use of a Tuple as Data causes an implicit conversion creating a View
+ class MyChild extends Module {
+ val io = IO(new MyBundle)
+ (io.c, io.d) := (io.a, io.b)
+ // The type annotations create the views via the implicit conversion
+ val view1: Data = (io.a, io.b)
+ val view2: Data = (io.c, io.d)
+ mark(view1, 0)
+ mark(view2, 1)
+ markAbs(view1, 2)
+ markAbs(view2, 3)
+ mark((io.b, io.d), 4) // Mix it up for fun
+ }
+ class MyParent extends Module {
+ val io = IO(new MyBundle)
+ val inst = Module(new MyChild)
+ io <> inst.io
+ }
+ val (_, annos) = getFirrtlAndAnnos(new MyParent)
+ val pairs = annos.collect { case DummyAnno(t, idx) => (idx, t.toString) }.sorted
+ val expected = Seq(
+ 0 -> "~MyParent|MyChild>io.a",
+ 0 -> "~MyParent|MyChild>io.b",
+ 1 -> "~MyParent|MyChild>io.c",
+ 1 -> "~MyParent|MyChild>io.d",
+ 2 -> "~MyParent|MyParent/inst:MyChild>io.a",
+ 2 -> "~MyParent|MyParent/inst:MyChild>io.b",
+ 3 -> "~MyParent|MyParent/inst:MyChild>io.c",
+ 3 -> "~MyParent|MyParent/inst:MyChild>io.d",
+ 4 -> "~MyParent|MyChild>io.b",
+ 4 -> "~MyParent|MyChild>io.d",
+ )
+ pairs should equal (expected)
+ }
+
+ // TODO check these properties when using @instance API (especially preservation of totality)
+}
diff --git a/src/test/scala/chiselTests/experimental/ForceNames.scala b/src/test/scala/chiselTests/experimental/ForceNames.scala
index b3534f11..06f911e6 100644
--- a/src/test/scala/chiselTests/experimental/ForceNames.scala
+++ b/src/test/scala/chiselTests/experimental/ForceNames.scala
@@ -4,7 +4,7 @@ package chiselTests
import firrtl._
import chisel3._
-import chisel3.core.annotate
+import chisel3.experimental.annotate
import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage}
import chisel3.util.experimental.{ForceNameAnnotation, ForceNamesTransform, InlineInstance, forceName}
import firrtl.annotations.{Annotation, ReferenceTarget}
diff --git a/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala
new file mode 100644
index 00000000..78986517
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/ModuleDataProductSpec.scala
@@ -0,0 +1,91 @@
+// See LICENSE for license details.
+
+package chiselTests.experimental
+
+import chisel3._
+import chisel3.experimental.{BaseModule, ExtModule}
+import chisel3.experimental.dataview.DataProduct
+import chiselTests.ChiselFlatSpec
+
+object ModuleDataProductSpec {
+ class MyBundle extends Bundle {
+ val foo = UInt(8.W)
+ val bar = UInt(8.W)
+ }
+ trait MyIntf extends BaseModule {
+ val in = IO(Input(new MyBundle))
+ val out = IO(Output(new MyBundle))
+ }
+ class Passthrough extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt(8.W)))
+ out := in
+ }
+ class MyUserModule extends Module with MyIntf {
+ val inst = Module(new Passthrough)
+ inst.in := in.foo
+ val r = RegNext(in)
+ out := r
+ }
+
+ class MyExtModule extends ExtModule with MyIntf
+ class MyExtModuleWrapper extends RawModule with MyIntf {
+ val inst = Module(new MyExtModule)
+ inst.in := in
+ out := inst.out
+ }
+}
+
+class ModuleDataProductSpec extends ChiselFlatSpec {
+ import ModuleDataProductSpec._
+
+ behavior of "DataProduct"
+
+ it should "work for UserModules (recursively)" in {
+ val m = elaborateAndGetModule(new MyUserModule)
+ val expected = Seq(
+ m.clock -> "m.clock",
+ m.reset -> "m.reset",
+ m.in -> "m.in",
+ m.in.foo -> "m.in.foo",
+ m.in.bar -> "m.in.bar",
+ m.out -> "m.out",
+ m.out.foo -> "m.out.foo",
+ m.out.bar -> "m.out.bar",
+ m.r -> "m.r",
+ m.r.foo -> "m.r.foo",
+ m.r.bar -> "m.r.bar",
+ m.inst.in -> "m.inst.in",
+ m.inst.out -> "m.inst.out"
+ )
+
+ val impl = implicitly[DataProduct[MyUserModule]]
+ val set = impl.dataSet(m)
+ for ((d, _) <- expected) {
+ set(d) should be (true)
+ }
+ val it = impl.dataIterator(m, "m")
+ it.toList should contain theSameElementsAs (expected)
+ }
+
+ it should "work for (wrapped) ExtModules" in {
+ val m = elaborateAndGetModule(new MyExtModuleWrapper).inst
+ val expected = Seq(
+ m.in -> "m.in",
+ m.in.foo -> "m.in.foo",
+ m.in.bar -> "m.in.bar",
+ m.out -> "m.out",
+ m.out.foo -> "m.out.foo",
+ m.out.bar -> "m.out.bar"
+ )
+
+ val impl = implicitly[DataProduct[MyExtModule]]
+ val set = impl.dataSet(m)
+ for ((d, _) <- expected) {
+ set(d) should be (true)
+ }
+ val it = impl.dataIterator(m, "m")
+ it.toList should contain theSameElementsAs (expected)
+ }
+
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
new file mode 100644
index 00000000..43111fdd
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala
@@ -0,0 +1,28 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import _root_.firrtl.annotations._
+import chisel3.experimental.{annotate, BaseModule}
+import chisel3.Data
+import chisel3.experimental.hierarchy.{Instance, Definition}
+
+object Annotations {
+ case class MarkAnnotation(target: IsMember, tag: String) extends SingleTargetAnnotation[IsMember] {
+ def duplicate(n: IsMember): Annotation = this.copy(target = n)
+ }
+ case class MarkChiselInstanceAnnotation[B <: BaseModule](d: Instance[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = MarkAnnotation(d.toTarget, tag)
+ }
+ case class MarkChiselDefinitionAnnotation[B <: BaseModule](d: Definition[B], tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = MarkAnnotation(d.toTarget, tag)
+ }
+ case class MarkChiselAnnotation(d: Data, tag: String, isAbsolute: Boolean) extends chisel3.experimental.ChiselAnnotation {
+ def toFirrtl = if(isAbsolute) MarkAnnotation(d.toAbsoluteTarget, tag) else MarkAnnotation(d.toTarget, tag)
+ }
+ def mark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, false))
+ def mark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, false))
+ def mark[B <: BaseModule](d: Definition[B], tag: String): Unit = annotate(MarkChiselDefinitionAnnotation(d, tag, false))
+ def amark(d: Data, tag: String): Unit = annotate(MarkChiselAnnotation(d, tag, true))
+ def amark[B <: BaseModule](d: Instance[B], tag: String): Unit = annotate(MarkChiselInstanceAnnotation(d, tag, true))
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala
new file mode 100644
index 00000000..19261c36
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala
@@ -0,0 +1,493 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+package experimental.hierarchy
+
+import chisel3._
+import chisel3.experimental.BaseModule
+import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public}
+
+// TODO/Notes
+// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5
+// - CircuitTarget for annotations on the definition are wrong - needs to be fixed.
+class DefinitionSpec extends ChiselFunSpec with Utils {
+ import Annotations._
+ import Examples._
+ describe("0: Definition instantiation") {
+ it("0.0: module name of a definition should be correct") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("module AddOne :")
+ }
+ it("0.2: accessing internal fields through non-generated means is hard to do") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ //definition.lookup(_.in) // Uncommenting this line will give the following error:
+ //"You are trying to access a macro-only API. Please use the @public annotation instead."
+ definition.in
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("module AddOne :")
+ }
+ it("0.2: reset inference is not defaulted to Bool for definitions") {
+ class Top extends Module with RequireAsyncReset {
+ val definition = Definition(new HasUninferredReset)
+ val i0 = Instance(definition)
+ i0.in := 0.U
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of HasUninferredReset")
+ }
+ }
+ describe("1: Annotations on definitions in same chisel compilation") {
+ it("1.0: should work on a single definition, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ mark(definition, "mark")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "mark"))
+ }
+ it("1.1: should work on a single definition, annotating an inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ mark(definition.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.2: should work on a two nested definitions, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.definition, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0"))
+ }
+ it("1.2: should work on an instance in a definition, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.2: should work on a definition in an instance, annotating the definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0 = Instance(definition)
+ mark(i0.definition, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne".mt, "i0.i0"))
+ }
+ it("1.3: should work on a wire in an instance in a definition") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ mark(definition.i0.innerWire, "i0.i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire"))
+ }
+ it("1.4: should work on a nested module in a definition, annotating the module") {
+ class Top extends Module {
+ val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules)
+ mark(definition.i1, "i0.i1")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2".it, "i0.i1"))
+ }
+ // Can you define an instantiable container? I think not.
+ // Instead, we can test the instantiable container in a definition
+ it("1.5: should work on an instantiable container, annotating a wire in the defintion") {
+ class Top extends Module {
+ val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire)
+ mark(definition.wireContainer.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.6: should work on an instantiable container, annotating a module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableModule)
+ mark(definition.moduleContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.7: should work on an instantiable container, annotating an instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstance)
+ mark(definition.instanceContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.8: should work on an instantiable container, annotating an instantiable container's module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ mark(definition.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.9: should work on public member which references public member of another instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ mark(definition.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.10: should work for targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAnnotation)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("2: Annotations on designs not in the same chisel compilation") {
+ it("2.0: should work on an innerWire, marked in a different compilation") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, false, true))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first"))
+ }
+ it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, true, false))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second"))
+ }
+ it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Definition(new ViewerParent(x, false, false))
+ mark(parent.viewer.x.i0.innerWire, "third")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain(MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third"))
+ }
+ }
+ describe("3: @public") {
+ it("3.0: should work on multi-vals") {
+ class Top() extends Module {
+ val mv = Definition(new MultiVal())
+ mark(mv.x, "mv.x")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|MultiVal>x".rt, "mv.x"))
+ }
+ it("3.1: should work on lazy vals") {
+ class Top() extends Module {
+ val lv = Definition(new LazyVal())
+ mark(lv.x, lv.y)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|LazyVal>x".rt, "Hi"))
+ }
+ it("3.2: should work on islookupables") {
+ class Top() extends Module {
+ val p = Parameters("hi", 0)
+ val up = Definition(new UsesParameters(p))
+ mark(up.x, up.y.string + up.y.int)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|UsesParameters>x".rt, "hi0"))
+ }
+ it("3.3: should work on lists") {
+ class Top() extends Module {
+ val i = Definition(new HasList())
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasList>x_1".rt, "2"))
+ }
+ it("3.4: should work on seqs") {
+ class Top() extends Module {
+ val i = Definition(new HasSeq())
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasSeq>x_1".rt, "2"))
+ }
+ it("3.5: should work on options") {
+ class Top() extends Module {
+ val i = Definition(new HasOption())
+ i.x.map(x => mark(x, "x"))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasOption>x".rt, "x"))
+ }
+ it("3.6: should work on vecs") {
+ class Top() extends Module {
+ val i = Definition(new HasVec())
+ mark(i.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasVec>x".rt, "blah"))
+ }
+ it("3.7: should work on statically indexed vectors external to module") {
+ class Top() extends Module {
+ val i = Definition(new HasVec())
+ mark(i.x(1), "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasVec>x[1]".rt, "blah"))
+ }
+ it("3.8: should work on statically indexed vectors internal to module") {
+ class Top() extends Module {
+ val i = Definition(new HasIndexedVec())
+ mark(i.y, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasIndexedVec>x[1]".rt, "blah"))
+ }
+ ignore("3.9: should work on vals in constructor arguments") {
+ class Top() extends Module {
+ val i = Definition(new HasPublicConstructorArgs(10))
+ //mark(i.x, i.int.toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|HasPublicConstructorArgs>x".rt, "10"))
+ }
+ }
+ describe("4: toDefinition") {
+ it("4.0: should work on modules") {
+ class Top() extends Module {
+ val i = Module(new AddOne())
+ f(i.toDefinition)
+ }
+ def f(i: Definition[AddOne]): Unit = mark(i.innerWire, "blah")
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on seqs of modules") {
+ class Top() extends Module {
+ val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toDefinition)
+ mark(f(is), "blah")
+ }
+ def f(i: Seq[Definition[AddTwo]]): Data = i.head.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on options of modules") {
+ class Top() extends Module {
+ val is: Option[Definition[AddTwo]] = Some(Module(new AddTwo())).map(_.toDefinition)
+ mark(f(is), "blah")
+ }
+ def f(i: Option[Definition[AddTwo]]): Data = i.get.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ }
+ describe("5: Absolute Targets should work as expected") {
+ it("5.0: toAbsoluteTarget on a port of a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwo())
+ amark(i.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo>in".rt, "blah"))
+ }
+ it("5.1: toAbsoluteTarget on a subinstance's data within a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwo())
+ amark(i.i0.innerWire, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("5.2: toAbsoluteTarget on a submodule's data within a definition") {
+ class Top() extends Module {
+ val i = Definition(new AddTwoMixedModules())
+ amark(i.i1.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2>in".rt, "blah"))
+ }
+ it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within a definition") {
+ class Top() extends Module {
+ val i = Definition(new InstantiatesHasVec())
+ amark(i.i1.x.head, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah"))
+ }
+ }
+ describe("6: @instantiable traits should work as expected") {
+ class MyBundle extends Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ }
+ @instantiable
+ trait ModuleIntf extends BaseModule {
+ @public val io = IO(new MyBundle)
+ }
+ @instantiable
+ class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf {
+ override def desiredName: String = super.desiredName + suffix
+ @public val sum = io.in + 1.U
+
+ io.out := sum
+ }
+ class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf
+
+ it("6.0: A Module that implements an @instantiable trait should be definable as that trait") {
+ class Top extends Module {
+ val i: Definition[ModuleIntf] = Definition(new ModuleWithCommonIntf)
+ mark(i.io.in, "gotcha")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|ModuleWithCommonIntf".mt -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") {
+ class Top extends Module {
+ val i: Definition[ModuleWithCommonIntf] = Definition(new ModuleWithCommonIntf)
+ mark(i.io.in, "gotcha")
+ mark(i.sum, "also this")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|ModuleWithCommonIntf>sum".rt -> "also this",
+ "~Top|ModuleWithCommonIntf".mt -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val m: ModuleIntf = Module(new BlackBoxWithCommonIntf)
+ val d: Definition[ModuleIntf] = m.toDefinition
+ mark(d.io.in, "gotcha")
+ mark(d, "module")
+ }
+ val expected = List(
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha",
+ "~Top|BlackBoxWithCommonIntf".mt -> "module"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") {
+ class Top extends Module {
+ val definition = Definition(new ModuleWithCommonIntf("X"))
+ val insts: Seq[Definition[ModuleIntf]] = Vector(
+ Module(new ModuleWithCommonIntf("Y")).toDefinition,
+ Module(new BlackBoxWithCommonIntf).toDefinition,
+ definition
+ )
+ mark(insts(0).io.in, "foo")
+ mark(insts(1).io.in, "bar")
+ mark(insts(2).io.in, "fizz")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo",
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "bar",
+ "~Top|ModuleWithCommonIntfX>io.in".rt -> "fizz"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+ describe("7: @instantiable and @public should compose with DataView") {
+ import chisel3.experimental.dataview._
+ ignore("7.0: should work on simple Views") {
+ @instantiable
+ class MyModule extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ val sum = in + 1.U
+ out := sum + 1.U
+ @public val foo = in.viewAs[UInt]
+ @public val bar = sum.viewAs[UInt]
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val d = Definition(new MyModule)
+ val i = Instance(d)
+ i.foo := foo
+ bar := i.out
+ mark(d.out, "out")
+ mark(d.foo, "foo")
+ mark(d.bar, "bar")
+ }
+ val expectedAnnos = List(
+ "~Top|MyModule>out".rt -> "out",
+ "~Top|MyModule>in".rt -> "foo",
+ "~Top|MyModule>sum".rt -> "bar"
+ )
+ val expectedLines = List(
+ "i.in <= foo",
+ "bar <= i.out"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ ignore("7.1: should work on Aggregate Views that are mapped 1:1") {
+ import chiselTests.experimental.SimpleBundleDataView._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(new BundleA(8)))
+ private val b = IO(Output(new BundleA(8)))
+ @public val in = a.viewAs[BundleB]
+ @public val out = b.viewAs[BundleB]
+ out := in
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(new BundleB(8)))
+ val bar = IO(Output(new BundleB(8)))
+ val d = Definition(new MyModule)
+ val i = Instance(d)
+ i.in := foo
+ bar.bar := i.out.bar
+ mark(d.in, "in")
+ mark(d.in.bar, "in_bar")
+ }
+ val expectedAnnos = List(
+ "~Top|MyModule>a".rt -> "in",
+ "~Top|MyModule>a.foo".rt -> "in_bar",
+ )
+ val expectedLines = List(
+ "i.a <= foo",
+ "bar <= i.b.foo"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
new file mode 100644
index 00000000..23b8c9c0
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala
@@ -0,0 +1,186 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import chisel3._
+import chisel3.util.Valid
+import chisel3.experimental.hierarchy._
+import chisel3.experimental.BaseModule
+
+object Examples {
+ import Annotations._
+ @instantiable
+ class AddOne extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddOneWithAnnotation extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ mark(innerWire, "innerWire")
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddOneWithAbsoluteAnnotation extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val innerWire = Wire(UInt(32.W))
+ amark(innerWire, "innerWire")
+ innerWire := in + 1.U
+ out := innerWire
+ }
+ @instantiable
+ class AddTwo extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val definition = Definition(new AddOne)
+ @public val i0: Instance[AddOne] = Instance(definition)
+ @public val i1: Instance[AddOne] = Instance(definition)
+ i0.in := in
+ i1.in := i0.out
+ out := i1.out
+ }
+ @instantiable
+ class AddTwoMixedModules extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ val definition = Definition(new AddOne)
+ @public val i0: Instance[AddOne] = Instance(definition)
+ @public val i1 = Module(new AddOne)
+ i0.in := in
+ i1.in := i0.out
+ out := i1.out
+ }
+ @instantiable
+ class AggregatePortModule extends Module {
+ @public val io = IO(new Bundle {
+ val in = Input(UInt(32.W))
+ val out = Output(UInt(32.W))
+ })
+ io.out := io.in
+ }
+ @instantiable
+ class WireContainer {
+ @public val innerWire = Wire(UInt(32.W))
+ }
+ @instantiable
+ class AddOneWithInstantiableWire extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val wireContainer = new WireContainer()
+ wireContainer.innerWire := in + 1.U
+ out := wireContainer.innerWire
+ }
+ @instantiable
+ class AddOneContainer {
+ @public val i0 = Module(new AddOne)
+ }
+ @instantiable
+ class AddOneWithInstantiableModule extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val moduleContainer = new AddOneContainer()
+ moduleContainer.i0.in := in
+ out := moduleContainer.i0.out
+ }
+ @instantiable
+ class AddOneInstanceContainer {
+ val definition = Definition(new AddOne)
+ @public val i0 = Instance(definition)
+ }
+ @instantiable
+ class AddOneWithInstantiableInstance extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val instanceContainer = new AddOneInstanceContainer()
+ instanceContainer.i0.in := in
+ out := instanceContainer.i0.out
+ }
+ @instantiable
+ class AddOneContainerContainer {
+ @public val container = new AddOneContainer
+ }
+ @instantiable
+ class AddOneWithInstantiableInstantiable extends Module {
+ @public val in = IO(Input(UInt(32.W)))
+ @public val out = IO(Output(UInt(32.W)))
+ @public val containerContainer = new AddOneContainerContainer()
+ containerContainer.container.i0.in := in
+ out := containerContainer.container.i0.out
+ }
+ @instantiable
+ class Viewer(val y: AddTwo, markPlease: Boolean) {
+ @public val x = y
+ if(markPlease) mark(x.i0.innerWire, "first")
+ }
+ @instantiable
+ class ViewerParent(val x: AddTwo, markHere: Boolean, markThere: Boolean) extends Module {
+ @public val viewer = new Viewer(x, markThere)
+ if(markHere) mark(viewer.x.i0.innerWire, "second")
+ }
+ @instantiable
+ class MultiVal() extends Module {
+ @public val (x, y) = (Wire(UInt(3.W)), Wire(UInt(3.W)))
+ }
+ @instantiable
+ class LazyVal() extends Module {
+ @public val x = Wire(UInt(3.W))
+ @public lazy val y = "Hi"
+ }
+ case class Parameters(string: String, int: Int) extends IsLookupable
+ @instantiable
+ class UsesParameters(p: Parameters) extends Module {
+ @public val y = p
+ @public val x = Wire(UInt(3.W))
+ }
+ @instantiable
+ class HasList() extends Module {
+ @public val y = List(1, 2, 3)
+ @public val x = List.fill(3)(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasSeq() extends Module {
+ @public val y = Seq(1, 2, 3)
+ @public val x = Seq.fill(3)(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasOption() extends Module {
+ @public val x: Option[UInt] = Some(Wire(UInt(3.W)))
+ }
+ @instantiable
+ class HasVec() extends Module {
+ @public val x = VecInit(1.U, 2.U, 3.U)
+ }
+ @instantiable
+ class HasIndexedVec() extends Module {
+ val x = VecInit(1.U, 2.U, 3.U)
+ @public val y = x(1)
+ }
+ @instantiable
+ class HasSubFieldAccess extends Module {
+ val in = IO(Input(Valid(UInt(8.W))))
+ @public val valid = in.valid
+ @public val bits = in.bits
+ }
+ @instantiable
+ class HasPublicConstructorArgs(@public val int: Int) extends Module {
+ @public val x = Wire(UInt(3.W))
+ }
+ @instantiable
+ class InstantiatesHasVec() extends Module {
+ @public val i0 = Instance(Definition(new HasVec()))
+ @public val i1 = Module(new HasVec())
+ }
+ @instantiable
+ class HasUninferredReset() extends Module {
+ @public val in = IO(Input(UInt(3.W)))
+ @public val out = IO(Output(UInt(3.W)))
+ out := RegNext(in)
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
new file mode 100644
index 00000000..3866bf87
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala
@@ -0,0 +1,709 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests
+package experimental.hierarchy
+
+import chisel3._
+import chisel3.experimental.BaseModule
+import chisel3.experimental.hierarchy.{Definition, Instance, instantiable, public}
+import chisel3.util.{DecoupledIO, Valid}
+
+
+// TODO/Notes
+// - In backport, clock/reset are not automatically assigned. I think this is fixed in 3.5
+// - CircuitTarget for annotations on the definition are wrong - needs to be fixed.
+class InstanceSpec extends ChiselFunSpec with Utils {
+ import Annotations._
+ import Examples._
+ describe("0: Instance instantiation") {
+ it("0.0: name of an instance should be correct") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ val i0 = Instance(definition)
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddOne")
+ }
+ it("0.1: name of an instanceclone should not error") {
+ class Top extends Module {
+ val definition = Definition(new AddTwo)
+ val i0 = Instance(definition)
+ val i = i0.i0 // This should not error
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddTwo")
+ }
+ it("0.2: accessing internal fields through non-generated means is hard to do") {
+ class Top extends Module {
+ val definition = Definition(new AddOne)
+ val i0 = Instance(definition)
+ //i0.lookup(_.in) // Uncommenting this line will give the following error:
+ //"You are trying to access a macro-only API. Please use the @public annotation instead."
+ i0.in
+ }
+ val (chirrtl, _) = getFirrtlAndAnnos(new Top)
+ chirrtl.serialize should include ("inst i0 of AddOne")
+ }
+ }
+ describe("1: Annotations on instances in same chisel compilation") {
+ it("1.0: should work on a single instance, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ val i0: Instance[AddOne] = Instance(definition)
+ mark(i0, "i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOne".it, "i0"))
+ }
+ it("1.1: should work on a single instance, annotating an inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddOne] = Definition(new AddOne)
+ val i0: Instance[AddOne] = Instance(definition)
+ mark(i0.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOne>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.2: should work on a two nested instances, annotating the instance") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0: Instance[AddTwo] = Instance(definition)
+ mark(i0.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.3: should work on a two nested instances, annotating the inner wire") {
+ class Top extends Module {
+ val definition: Definition[AddTwo] = Definition(new AddTwo)
+ val i0: Instance[AddTwo] = Instance(definition)
+ mark(i0.i0.innerWire, "i0.i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwo/i0:AddOne>innerWire".rt, "i0.i0.innerWire"))
+ }
+ it("1.4: should work on a nested module in an instance, annotating the module") {
+ class Top extends Module {
+ val definition: Definition[AddTwoMixedModules] = Definition(new AddTwoMixedModules)
+ val i0: Instance[AddTwoMixedModules] = Instance(definition)
+ mark(i0.i1, "i0.i1")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_2".it, "i0.i1"))
+ }
+ it("1.5: should work on an instantiable container, annotating a wire") {
+ class Top extends Module {
+ val definition: Definition[AddOneWithInstantiableWire] = Definition(new AddOneWithInstantiableWire)
+ val i0: Instance[AddOneWithInstantiableWire] = Instance(definition)
+ mark(i0.wireContainer.innerWire, "i0.innerWire")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableWire>innerWire".rt, "i0.innerWire"))
+ }
+ it("1.6: should work on an instantiable container, annotating a module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableModule)
+ val i0 = Instance(definition)
+ mark(i0.moduleContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableModule/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.7: should work on an instantiable container, annotating an instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstance)
+ val i0 = Instance(definition)
+ mark(i0.instanceContainer.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstance/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.8: should work on an instantiable container, annotating an instantiable container's module") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ val i0 = Instance(definition)
+ mark(i0.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.9: should work on public member which references public member of another instance") {
+ class Top extends Module {
+ val definition = Definition(new AddOneWithInstantiableInstantiable)
+ val i0 = Instance(definition)
+ mark(i0.containerContainer.container.i0, "i0.i0")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/i0:AddOneWithInstantiableInstantiable/i0:AddOne".it, "i0.i0"))
+ }
+ it("1.10: should work for targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAnnotation)
+ val i0 = Instance(definition)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("2: Annotations on designs not in the same chisel compilation") {
+ it("2.0: should work on an innerWire, marked in a different compilation") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, false, true)))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "first"))
+ }
+ it("2.1: should work on an innerWire, marked in a different compilation, in instanced instantiable") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, true, false)))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "second"))
+ }
+ it("2.2: should work on an innerWire, marked in a different compilation, in instanced module") {
+ val first = elaborateAndGetModule(new AddTwo)
+ class Top(x: AddTwo) extends Module {
+ val parent = Instance(Definition(new ViewerParent(x, false, false)))
+ mark(parent.viewer.x.i0.innerWire, "third")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top(first))
+ annos should contain (MarkAnnotation("~AddTwo|AddTwo/i0:AddOne>innerWire".rt, "third"))
+ }
+ }
+ describe("3: @public") {
+ it("3.0: should work on multi-vals") {
+ class Top() extends Module {
+ val mv = Instance(Definition(new MultiVal()))
+ mark(mv.x, "mv.x")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/mv:MultiVal>x".rt, "mv.x"))
+ }
+ it("3.1: should work on lazy vals") {
+ class Top() extends Module {
+ val lv = Instance(Definition(new LazyVal()))
+ mark(lv.x, lv.y)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain (MarkAnnotation("~Top|Top/lv:LazyVal>x".rt, "Hi"))
+ }
+ it("3.2: should work on islookupables") {
+ class Top() extends Module {
+ val p = Parameters("hi", 0)
+ val up = Instance(Definition(new UsesParameters(p)))
+ mark(up.x, up.y.string + up.y.int)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/up:UsesParameters>x".rt, "hi0"))
+ }
+ it("3.3: should work on lists") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasList()))
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasList>x_1".rt, "2"))
+ }
+ it("3.4: should work on seqs") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasSeq()))
+ mark(i.x(1), i.y(1).toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasSeq>x_1".rt, "2"))
+ }
+ it("3.5: should work on options") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasOption()))
+ i.x.map(x => mark(x, "x"))
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasOption>x".rt, "x"))
+ }
+ it("3.6: should work on vecs") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasVec()))
+ mark(i.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x".rt, "blah"))
+ }
+ it("3.7: should work on statically indexed vectors external to module") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasVec()))
+ mark(i.x(1), "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasVec>x[1]".rt, "blah"))
+ }
+ it("3.8: should work on statically indexed vectors internal to module") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasIndexedVec()))
+ mark(i.y, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasIndexedVec>x[1]".rt, "blah"))
+ }
+ it("3.9: should work on accessed subfields of aggregate ports") {
+ class Top extends Module {
+ val input = IO(Input(Valid(UInt(8.W))))
+ val i = Instance(Definition(new HasSubFieldAccess))
+ i.valid := input.valid
+ i.bits := input.bits
+ mark(i.valid, "valid")
+ mark(i.bits, "bits")
+ }
+ val expected = List(
+ "~Top|Top/i:HasSubFieldAccess>in.valid".rt -> "valid",
+ "~Top|Top/i:HasSubFieldAccess>in.bits".rt -> "bits"
+ )
+ val lines = List(
+ "i.in.valid <= input.valid",
+ "i.in.bits <= input.bits"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ ignore("3.10: should work on vals in constructor arguments") {
+ class Top() extends Module {
+ val i = Instance(Definition(new HasPublicConstructorArgs(10)))
+ //mark(i.x, i.int.toString)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:HasPublicConstructorArgs>x".rt, "10"))
+ }
+ }
+ describe("4: toInstance") {
+ it("4.0: should work on modules") {
+ class Top() extends Module {
+ val i = Module(new AddOne())
+ f(i.toInstance)
+ }
+ def f(i: Instance[AddOne]): Unit = mark(i.innerWire, "blah")
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOne>innerWire".rt, "blah"))
+ }
+ it("4.1: should work on isinstantiables") {
+ class Top() extends Module {
+ val i = Module(new AddTwo())
+ val v = new Viewer(i, false)
+ mark(f(v.toInstance), "blah")
+ }
+ def f(i: Instance[Viewer]): Data = i.x.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on seqs of modules") {
+ class Top() extends Module {
+ val is = Seq(Module(new AddTwo()), Module(new AddTwo())).map(_.toInstance)
+ mark(f(is), "blah")
+ }
+ def f(i: Seq[Instance[AddTwo]]): Data = i.head.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.3: should work on seqs of isInstantiables") {
+ class Top() extends Module {
+ val i = Module(new AddTwo())
+ val vs = Seq(new Viewer(i, false), new Viewer(i, false)).map(_.toInstance)
+ mark(f(vs), "blah")
+ }
+ def f(i: Seq[Instance[Viewer]]): Data = i.head.x.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("4.2: should work on options of modules") {
+ class Top() extends Module {
+ val is: Option[Instance[AddTwo]] = Some(Module(new AddTwo())).map(_.toInstance)
+ mark(f(is), "blah")
+ }
+ def f(i: Option[Instance[AddTwo]]): Data = i.get.i0.innerWire
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ }
+ describe("5: Absolute Targets should work as expected") {
+ it("5.0: toAbsoluteTarget on a port of an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo>in".rt, "blah"))
+ }
+ it("5.1: toAbsoluteTarget on a subinstance's data within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.i0.innerWire, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i0:AddOne>innerWire".rt, "blah"))
+ }
+ it("5.2: toAbsoluteTarget on a submodule's data within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwoMixedModules()))
+ amark(i.i1.in, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_2>in".rt, "blah"))
+ }
+ it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new InstantiatesHasVec()))
+ amark(i.i1.x.head, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah"))
+ }
+ it("5.4: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance, ILit") {
+ class MyBundle extends Bundle { val x = UInt(3.W) }
+ @instantiable
+ class HasVec() extends Module {
+ @public val x = Wire(Vec(3, new MyBundle()))
+ }
+ @instantiable
+ class InstantiatesHasVec() extends Module {
+ @public val i0 = Instance(Definition(new HasVec()))
+ @public val i1 = Module(new HasVec())
+ }
+ class Top() extends Module {
+ val i = Instance(Definition(new InstantiatesHasVec()))
+ amark(i.i1.x.head.x, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_2>x[0].x".rt, "blah"))
+ }
+ it("5.5: toAbsoluteTarget on a subinstance") {
+ class Top() extends Module {
+ val i = Instance(Definition(new AddTwo()))
+ amark(i.i1, "blah")
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|Top/i:AddTwo/i1:AddOne".it, "blah"))
+ }
+ it("5.6: should work for absolute targets on definition to have correct circuit name"){
+ class Top extends Module {
+ val definition = Definition(new AddOneWithAbsoluteAnnotation)
+ val i0 = Instance(definition)
+ }
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ annos should contain(MarkAnnotation("~Top|AddOneWithAbsoluteAnnotation>innerWire".rt, "innerWire"))
+ }
+ }
+ describe("6: @instantiable traits should work as expected") {
+ class MyBundle extends Bundle {
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ }
+ @instantiable
+ trait ModuleIntf extends BaseModule {
+ @public val io = IO(new MyBundle)
+ }
+ @instantiable
+ class ModuleWithCommonIntf(suffix: String = "") extends Module with ModuleIntf {
+ override def desiredName: String = super.desiredName + suffix
+ @public val sum = io.in + 1.U
+
+ io.out := sum
+ }
+ class BlackBoxWithCommonIntf extends BlackBox with ModuleIntf
+
+ it("6.0: A Module that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val i: Instance[ModuleIntf] = Instance(Definition(new ModuleWithCommonIntf))
+ mark(i.io.in, "gotcha")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|Top/i:ModuleWithCommonIntf".it -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.1 An @instantiable Module that implements an @instantiable trait should be able to use extension methods from both") {
+ class Top extends Module {
+ val i: Instance[ModuleWithCommonIntf] = Instance(Definition(new ModuleWithCommonIntf))
+ mark(i.io.in, "gotcha")
+ mark(i.sum, "also this")
+ mark(i, "inst")
+ }
+ val expected = List(
+ "~Top|Top/i:ModuleWithCommonIntf>io.in".rt -> "gotcha",
+ "~Top|Top/i:ModuleWithCommonIntf>sum".rt -> "also this",
+ "~Top|Top/i:ModuleWithCommonIntf".it -> "inst"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.2 A BlackBox that implements an @instantiable trait should be instantiable as that trait") {
+ class Top extends Module {
+ val i: Instance[ModuleIntf] = Module(new BlackBoxWithCommonIntf).toInstance
+ mark(i.io.in, "gotcha")
+ mark(i, "module")
+ }
+ val expected = List(
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "gotcha",
+ "~Top|BlackBoxWithCommonIntf".mt -> "module"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("6.3 It should be possible to have Vectors of @instantiable traits mixing concrete subclasses") {
+ class Top extends Module {
+ val proto = Definition(new ModuleWithCommonIntf("X"))
+ val insts: Seq[Instance[ModuleIntf]] = Vector(
+ Module(new ModuleWithCommonIntf("Y")).toInstance,
+ Module(new BlackBoxWithCommonIntf).toInstance,
+ Instance(proto)
+ )
+ mark(insts(0).io.in, "foo")
+ mark(insts(1).io.in, "bar")
+ mark(insts(2).io.in, "fizz")
+ }
+ val expected = List(
+ "~Top|ModuleWithCommonIntfY>io.in".rt -> "foo",
+ "~Top|BlackBoxWithCommonIntf>in".rt -> "bar",
+ "~Top|Top/insts_2:ModuleWithCommonIntfX>io.in".rt -> "fizz"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+ // TODO don't forget to test this with heterogeneous Views (eg. viewing a tuple of a port and non-port as a single Bundle)
+ describe("7: @instantiable and @public should compose with DataView") {
+ import chisel3.experimental.dataview._
+ it("7.0: should work on simple Views") {
+ @instantiable
+ class MyModule extends RawModule {
+ val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ val sum = in + 1.U
+ out := sum + 1.U
+ @public val foo = in.viewAs[UInt]
+ @public val bar = sum.viewAs[UInt]
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val i = Instance(Definition(new MyModule))
+ i.foo := foo
+ bar := i.out
+ mark(i.out, "out")
+ mark(i.foo, "foo")
+ mark(i.bar, "bar")
+ }
+ val expectedAnnos = List(
+ "~Top|Top/i:MyModule>out".rt -> "out",
+ "~Top|Top/i:MyModule>in".rt -> "foo",
+ "~Top|Top/i:MyModule>sum".rt -> "bar"
+ )
+ val expectedLines = List(
+ "i.in <= foo",
+ "bar <= i.out"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ ignore("7.1: should work on Aggregate Views") {
+ import chiselTests.experimental.FlatDecoupledDataView._
+ type RegDecoupled = DecoupledIO[FizzBuzz]
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Flipped(new FlatDecoupled))
+ private val b = IO(new FlatDecoupled)
+ @public val enq = a.viewAs[RegDecoupled]
+ @public val deq = b.viewAs[RegDecoupled]
+ @public val enq_valid = enq.valid // Also return a subset of the view
+ deq <> enq
+ }
+ class Top extends RawModule {
+ val foo = IO(Flipped(new RegDecoupled(new FizzBuzz)))
+ val bar = IO(new RegDecoupled(new FizzBuzz))
+ val i = Instance(Definition(new MyModule))
+ i.enq <> foo
+ i.enq_valid := foo.valid // Make sure connections also work for @public on elements of a larger Aggregate
+ i.deq.ready := bar.ready
+ bar.valid := i.deq.valid
+ bar.bits := i.deq.bits
+ mark(i.enq, "enq")
+ mark(i.enq.bits, "enq.bits")
+ mark(i.deq.bits.fizz, "deq.bits.fizz")
+ mark(i.enq_valid, "enq_valid")
+ }
+ val expectedAnnos = List(
+ "~Top|Top/i:MyModule>a".rt -> "enq", // Not split, checks 1:1
+ "~Top|Top/i:MyModule>a.fizz".rt -> "enq.bits", // Split, checks non-1:1 inner Aggregate
+ "~Top|Top/i:MyModule>a.buzz".rt -> "enq.bits",
+ "~Top|Top/i:MyModule>b.fizz".rt -> "deq.bits.fizz", // Checks 1 inner Element
+ "~Top|Top/i:MyModule>a.valid".rt -> "enq_valid"
+ )
+ val expectedLines = List(
+ "i.a.valid <= foo.valid",
+ "foo.ready <= i.a.ready",
+ "i.a.fizz <= foo.bits.fizz",
+ "i.a.buzz <= foo.bits.buzz",
+ "bar.valid <= i.b.valid",
+ "i.b.ready <= bar.ready",
+ "bar.bits.fizz <= i.b.fizz",
+ "bar.bits.buzz <= i.b.buzz",
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- expectedLines) {
+ text should include (line)
+ }
+ for (e <- expectedAnnos.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ it("7.2: should work on views of views") {
+ import chiselTests.experimental.SimpleBundleDataView._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(UInt(8.W)))
+ private val b = IO(Output(new BundleA(8)))
+ @public val in = a.viewAs[UInt].viewAs[UInt]
+ @public val out = b.viewAs[BundleB].viewAs[BundleA].viewAs[BundleB]
+ out.bar := in
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(new BundleB(8)))
+ val i = Instance(Definition(new MyModule))
+ i.in := foo
+ bar := i.out
+ bar.bar := i.out.bar
+ mark(i.in, "in")
+ mark(i.out.bar, "out_bar")
+ }
+ val expected = List(
+ "~Top|Top/i:MyModule>a".rt -> "in",
+ "~Top|Top/i:MyModule>b.foo".rt -> "out_bar",
+ )
+ val lines = List(
+ "i.a <= foo",
+ "bar.bar <= i.b.foo"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+
+ it("7.3: should work with DataView + implicit conversion") {
+ import chiselTests.experimental.SeqToVec._
+ @instantiable
+ class MyModule extends RawModule {
+ private val a = IO(Input(UInt(8.W)))
+ private val b = IO(Output(UInt(8.W)))
+ @public val ports = Seq(a, b)
+ b := a
+ }
+ class Top extends RawModule {
+ val foo = IO(Input(UInt(8.W)))
+ val bar = IO(Output(UInt(8.W)))
+ val i = Instance(Definition(new MyModule))
+ i.ports <> Seq(foo, bar)
+ mark(i.ports, "i.ports")
+ }
+ val expected = List(
+ // Not 1:1 so will get split out
+ "~Top|Top/i:MyModule>a".rt -> "i.ports",
+ "~Top|Top/i:MyModule>b".rt -> "i.ports",
+ )
+ val lines = List(
+ "i.a <= foo",
+ "bar <= i.b"
+ )
+ val (chirrtl, annos) = getFirrtlAndAnnos(new Top)
+ val text = chirrtl.serialize
+ for (line <- lines) {
+ text should include (line)
+ }
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+
+ describe("8: @instantiable and @public should compose with CloneModuleAsRecord") {
+ it("8.0: it should support @public on a CMAR Record in Definitions") {
+ @instantiable
+ class HasCMAR extends Module {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ @public val m = Module(new AggregatePortModule)
+ @public val c = experimental.CloneModuleAsRecord(m)
+ }
+ class Top extends Module {
+ val d = Definition(new HasCMAR)
+ mark(d.c("io"), "c.io")
+ val bun = d.c("io").asInstanceOf[Record]
+ mark(bun.elements("out"), "c.io.out")
+ }
+ val expected = List(
+ "~Top|HasCMAR/c:AggregatePortModule>io".rt -> "c.io",
+ "~Top|HasCMAR/c:AggregatePortModule>io.out".rt -> "c.io.out"
+
+ )
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ it("8.1: it should support @public on a CMAR Record in Instances") {
+ @instantiable
+ class HasCMAR extends Module {
+ @public val in = IO(Input(UInt(8.W)))
+ @public val out = IO(Output(UInt(8.W)))
+ @public val m = Module(new AggregatePortModule)
+ @public val c = experimental.CloneModuleAsRecord(m)
+ }
+ class Top extends Module {
+ val i = Instance(Definition(new HasCMAR))
+ mark(i.c("io"), "i.c.io")
+ val bun = i.c("io").asInstanceOf[Record]
+ mark(bun.elements("out"), "i.c.io.out")
+ }
+ val expected = List(
+ "~Top|Top/i:HasCMAR/c:AggregatePortModule>io".rt -> "i.c.io",
+ "~Top|Top/i:HasCMAR/c:AggregatePortModule>io.out".rt -> "i.c.io.out"
+
+ )
+ val (_, annos) = getFirrtlAndAnnos(new Top)
+ for (e <- expected.map(MarkAnnotation.tupled)) {
+ annos should contain (e)
+ }
+ }
+ }
+}
+
diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala
new file mode 100644
index 00000000..a2e51765
--- /dev/null
+++ b/src/test/scala/chiselTests/experimental/hierarchy/Utils.scala
@@ -0,0 +1,21 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.experimental.hierarchy
+
+import chisel3._
+import _root_.firrtl.annotations._
+import chisel3.stage.{ChiselCircuitAnnotation, CircuitSerializationAnnotation, DesignAnnotation}
+import chiselTests.ChiselRunners
+import firrtl.stage.FirrtlCircuitAnnotation
+import org.scalatest.matchers.should.Matchers
+
+trait Utils extends ChiselRunners with chiselTests.Utils with Matchers {
+ import Annotations._
+ // TODO promote to standard API (in FIRRTL) and perhaps even implement with a macro
+ implicit class Str2RefTarget(str: String) {
+ def rt: ReferenceTarget = Target.deserialize(str).asInstanceOf[ReferenceTarget]
+ def it: InstanceTarget = Target.deserialize(str).asInstanceOf[InstanceTarget]
+ def mt: ModuleTarget = Target.deserialize(str).asInstanceOf[ModuleTarget]
+ def ct: CircuitTarget = Target.deserialize(str).asInstanceOf[CircuitTarget]
+ }
+}
diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
index 52293abb..1e080739 100644
--- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
+++ b/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala
@@ -3,11 +3,15 @@
package chiselTests.experimental.verification
import chisel3._
-import chisel3.experimental.{verification => formal}
+import chisel3.experimental.{ChiselAnnotation, verification => formal}
import chisel3.stage.ChiselStage
import chiselTests.ChiselPropSpec
+import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation}
-class VerificationModule extends Module {
+import java.io.File
+import org.scalatest.matchers.should.Matchers
+
+class SimpleTest extends Module {
val io = IO(new Bundle{
val in = Input(UInt(8.W))
val out = Output(UInt(8.W))
@@ -20,18 +24,131 @@ class VerificationModule extends Module {
}
}
-class VerificationSpec extends ChiselPropSpec {
+/** Dummy verification annotation.
+ * @param target target of component to be annotated
+ */
+case class VerifAnnotation(target: ReferenceTarget) extends SingleTargetAnnotation[ReferenceTarget] {
+ def duplicate(n: ReferenceTarget): VerifAnnotation = this.copy(target = n)
+}
- def assertContains[T](s: Seq[T], x: T): Unit = {
- val containsLine = s.map(_ == x).reduce(_ || _)
+object VerifAnnotation {
+ /** Create annotation for a given verification component.
+ * @param c component to be annotated
+ */
+ def annotate(c: experimental.BaseSim): Unit = {
+ chisel3.experimental.annotate(new ChiselAnnotation {
+ def toFirrtl: VerifAnnotation = VerifAnnotation(c.toTarget)
+ })
+ }
+}
+
+class VerificationSpec extends ChiselPropSpec with Matchers {
+
+ def assertContains(s: Seq[String], x: String): Unit = {
+ val containsLine = s.map(_.contains(x)).reduce(_ || _)
assert(containsLine, s"\n $x\nwas not found in`\n ${s.mkString("\n ")}``")
}
property("basic equality check should work") {
- val fir = ChiselStage.emitFirrtl(new VerificationModule)
+ val fir = ChiselStage.emitChirrtl(new SimpleTest)
val lines = fir.split("\n").map(_.trim)
- assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 16:15]")
- assertContains(lines, "assume(clock, _T_2, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 18:18]")
- assertContains(lines, "assert(clock, _T_3, UInt<1>(\"h1\"), \"\") @[VerificationSpec.scala 19:18]")
+
+ // reset guard around the verification statement
+ assertContains(lines, "when _T_2 : @[VerificationSpec.scala")
+ assertContains(lines, "cover(clock, _T, UInt<1>(\"h1\"), \"\")")
+
+ assertContains(lines, "when _T_6 : @[VerificationSpec.scala")
+ assertContains(lines, "assume(clock, _T_4, UInt<1>(\"h1\"), \"\")")
+
+ assertContains(lines, "when _T_9 : @[VerificationSpec.scala")
+ assertContains(lines, "assert(clock, _T_7, UInt<1>(\"h1\"), \"\")")
+ }
+
+ property("annotation of verification constructs should work") {
+ /** Circuit that contains and annotates verification nodes. */
+ class AnnotationTest extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ })
+ io.out := io.in
+ val cov = formal.cover(io.in === 3.U)
+ val assm = formal.assume(io.in =/= 2.U)
+ val asst = formal.assert(io.out === io.in)
+ VerifAnnotation.annotate(cov)
+ VerifAnnotation.annotate(assm)
+ VerifAnnotation.annotate(asst)
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "VerificationAnnotationTests")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new AnnotationTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "AnnotationTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected verification annotations
+ exactly(3, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>asst")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>assm")
+ exactly(1, annoLines) should include ("~AnnotationTest|AnnotationTest>cov")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "AnnotationTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("cover(clock, _T, UInt<1>(\"h1\"), \"\") : cov")
+ exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : assm")
+ exactly(1, firLines) should include ("assert(clock, _T_6, UInt<1>(\"h1\"), \"\") : asst")
+ }
+
+ property("annotation of verification constructs with suggested name should work") {
+ /** Circuit that annotates a renamed verification nodes. */
+ class AnnotationRenameTest extends Module {
+ val io = IO(new Bundle{
+ val in = Input(UInt(8.W))
+ val out = Output(UInt(8.W))
+ })
+ io.out := io.in
+
+ val goodbye = formal.assert(io.in === 1.U)
+ goodbye.suggestName("hello")
+ VerifAnnotation.annotate(goodbye)
+
+ VerifAnnotation.annotate(formal.assume(io.in =/= 2.U).suggestName("howdy"))
+ }
+
+ // compile circuit
+ val testDir = new File("test_run_dir", "VerificationAnnotationRenameTests")
+ (new ChiselStage).emitSystemVerilog(
+ gen = new AnnotationRenameTest,
+ args = Array("-td", testDir.getPath)
+ )
+
+ // read in annotation file
+ val annoFile = new File(testDir, "AnnotationRenameTest.anno.json")
+ annoFile should exist
+ val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList
+
+ // check for expected verification annotations
+ exactly(2, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation")
+ exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>hello")
+ exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>howdy")
+
+ // read in FIRRTL file
+ val firFile = new File(testDir, "AnnotationRenameTest.fir")
+ firFile should exist
+ val firLines = scala.io.Source.fromFile(firFile).getLines.toList
+
+ // check that verification components have expected names
+ exactly(1, firLines) should include ("assert(clock, _T, UInt<1>(\"h1\"), \"\") : hello")
+ exactly(1, firLines) should include ("assume(clock, _T_3, UInt<1>(\"h1\"), \"\") : howdy")
}
}
diff --git a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
index 0fc42fc6..1634e765 100644
--- a/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselMainSpec.scala
@@ -10,6 +10,8 @@ import chisel3.aop.inspecting.{InspectingAspect, InspectorAspect}
import org.scalatest.GivenWhenThen
import org.scalatest.featurespec.AnyFeatureSpec
import org.scalatest.matchers.should.Matchers
+import org.scalatest.Inside._
+import org.scalatest.EitherValues._
import scala.io.Source
import firrtl.Parser
@@ -32,7 +34,7 @@ object ChiselMainSpec {
/** A module that fails a requirement */
class FailingRequirementModule extends RawModule {
- require(false)
+ require(false, "the user wrote a failing requirement")
}
/** A module that triggers a Builder.error (as opposed to exception) */
@@ -69,14 +71,35 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
args: Array[String],
generator: Option[Class[_ <: RawModule]] = None,
files: Seq[String] = Seq.empty,
- stdout: Option[String] = None,
- stderr: Option[String] = None,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
result: Int = 0,
fileChecks: Map[String, File => Unit] = Map.empty) {
def testName: String = "args" + args.mkString("_")
def argsString: String = args.mkString(" ")
}
+ /** A test of ChiselMain that is going to involve catching an exception.
+ * @param args command line arguments (excluding --module) to pass in
+ * @param generator the module to build (used to generate --module)
+ * @param message snippets of text that should appear (Right) or not appear (Left) in the exception message
+ * @param stdout snippets of text that should appear (Right) or not appear (Left) in STDOUT
+ * @param stderr snippets of text that should appear (Right) or not appear (Left) in STDERR
+ * @param stackTrace snippets of text that should appear (Right) or not appear (Left) in the stack trace
+ * @tparam the type of exception that should occur
+ */
+ case class ChiselMainExceptionTest[A <: Throwable](
+ args: Array[String],
+ generator: Option[Class[_ <: RawModule]] = None,
+ message: Seq[Either[String, String]] = Seq.empty,
+ stdout: Seq[Either[String, String]] = Seq.empty,
+ stderr: Seq[Either[String, String]] = Seq.empty,
+ stackTrace: Seq[Either[String, String]] = Seq.empty
+ ) {
+ def testName: String = "args" + args.mkString("_")
+ def argsString: String = args.mkString(" ")
+ }
+
def runStageExpectFiles(p: ChiselMainTest): Unit = {
Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
val f = new ChiselMainFixture
@@ -85,32 +108,33 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
p.files.foreach( f => new File(td.buildDir + s"/$f").delete() )
When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
val (stdout, stderr, result) =
grabStdOutErr {
catchStatus {
- val module: Array[String] = Array("foo") ++
- (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
- else { Array.empty[String] })
f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
}
}
- p.stdout match {
- case Some(a) =>
+ p.stdout.foreach {
+ case Right(a) =>
Then(s"""STDOUT should include "$a"""")
stdout should include (a)
- case None =>
- Then(s"nothing should print to STDOUT")
- stdout should be (empty)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
}
- p.stderr match {
- case Some(a) =>
- And(s"""STDERR should include "$a"""")
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
stderr should include (a)
- case None =>
- And(s"nothing should print to STDERR")
- stderr should be (empty)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
}
p.result match {
@@ -131,56 +155,128 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
}
}
+ /** Run a ChiselMainExceptionTest and verify that all the properties it spells out hold.
+ * @param p the test to run
+ * @tparam the type of the exception to catch (you shouldn't have to explicitly provide this)
+ */
+ def runStageExpectException[A <: Throwable: scala.reflect.ClassTag](p: ChiselMainExceptionTest[A]): Unit = {
+ Scenario(s"""User runs Chisel Stage with '${p.argsString}'""") {
+ val f = new ChiselMainFixture
+ val td = new TargetDirectoryFixture(p.testName)
+
+ When(s"""the user tries to compile with '${p.argsString}'""")
+ val module: Array[String] =
+ (if (p.generator.nonEmpty) { Array("--module", p.generator.get.getName) }
+ else { Array.empty[String] })
+ val (stdout, stderr, result) =
+ grabStdOutErr {
+ catchStatus {
+ intercept[A] {
+ f.stage.main(Array("-td", td.buildDir.toString) ++ module ++ p.args)
+ }
+ }
+ }
+
+ Then("the expected exception was thrown")
+ result should be a ('right)
+ val exception = result.right.get
+ info(s""" - Exception was a "${exception.getClass.getName}"""")
+
+ val message = exception.getMessage
+ p.message.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ message should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ message should not include (a)
+ }
+
+ p.stdout.foreach {
+ case Right(a) =>
+ Then(s"""STDOUT should include "$a"""")
+ stdout should include (a)
+ case Left(a) =>
+ Then(s"""STDOUT should not include "$a"""")
+ stdout should not include (a)
+ }
+
+ p.stderr.foreach {
+ case Right(a) =>
+ Then(s"""STDERR should include "$a"""")
+ stderr should include (a)
+ case Left(a) =>
+ Then(s"""STDERR should not include "$a"""")
+ stderr should not include (a)
+ }
+
+ val stackTraceString = exception.getStackTrace.mkString("\n")
+ p.stackTrace.foreach {
+ case Left(a) =>
+ And(s"""the stack does not include "$a"""")
+ stackTraceString should not include (a)
+ case Right(a) =>
+ And(s"""the stack trace includes "$a"""")
+ stackTraceString should include (a)
+ }
+
+ }
+ }
+
info("As a Chisel user")
info("I compile a design")
Feature("show elaborating message") {
runStageExpectFiles(
ChiselMainTest(args = Array("-X", "high"),
- generator = Some(classOf[SameTypesModule]),
- stdout = Some("Done elaborating.")
+ generator = Some(classOf[SameTypesModule])
)
)
}
info("I screw up and compile some bad code")
- Feature("Stack trace trimming") {
+ Feature("Stack trace trimming of ChiselException") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("Stack trace trimmed to user code only"),
- result = 1),
- ChiselMainTest(args = Array("-X", "high", "--full-stacktrace"),
- generator = Some(classOf[DifferentTypesModule]),
- stdout = Some("org.scalatest"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Left("java"), Right(classOf[DifferentTypesModule].getName))
+ ),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[DifferentTypesModule]),
+ stackTrace = Seq(Right("java"), Right(classOf[DifferentTypesModule].getName))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Report properly trimmed stack traces") {
+ Feature("Stack trace trimming of user exceptions") {
Seq(
- ChiselMainTest(args = Array("-X", "low"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("requirement failed"),
- result = 1),
- ChiselMainTest(args = Array("-X", "low", "--full-stacktrace"),
- generator = Some(classOf[FailingRequirementModule]),
- stdout = Some("chisel3.internal.ChiselException"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Left("java"))
+ ),
+ ChiselMainExceptionTest[java.lang.IllegalArgumentException](
+ args = Array("-X", "low", "--full-stacktrace"),
+ generator = Some(classOf[FailingRequirementModule]),
+ stackTrace = Seq(Right(classOf[FailingRequirementModule].getName), Right("java"))
+ )
+ ).foreach(runStageExpectException)
}
- Feature("Builder.error source locator") {
+ Feature("Stack trace trimming and Builder.error errors") {
Seq(
- ChiselMainTest(args = Array("-X", "none"),
+ ChiselMainExceptionTest[chisel3.internal.ChiselException](
+ args = Array("-X", "low"),
generator = Some(classOf[BuilderErrorModule]),
- stdout = Some("ChiselMainSpec.scala:41: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"),
- result = 1)
- ).foreach(runStageExpectFiles)
+ message = Seq(Right("Fatal errors during hardware elaboration")),
+ stdout = Seq(Right("ChiselMainSpec.scala:43: Invalid bit range (3,-1) in class chiselTests.stage.ChiselMainSpec$BuilderErrorModule"))
+ )
+ ).foreach(runStageExpectException)
}
Feature("Specifying a custom output file") {
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.fir"),
fileChecks = Map(
"Foo.fir" -> { file =>
@@ -192,7 +288,6 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
runStageExpectFiles(ChiselMainTest(
args = Array("--chisel-output-file", "Foo.pb", "--no-run-firrtl"),
generator = Some(classOf[SameTypesModule]),
- stdout = Some(""),
files = Seq("Foo.pb"),
fileChecks = Map(
"Foo.pb" -> { file =>
@@ -209,10 +304,10 @@ class ChiselMainSpec extends AnyFeatureSpec with GivenWhenThen with Matchers wit
Seq(
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestClassAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect")),
+ stdout = Seq(Right("Ran inspectingAspect"))),
ChiselMainTest(args = Array( "-X", "high", "--with-aspect", "chiselTests.stage.TestObjectAspect" ),
generator = Some(classOf[SameTypesModule]),
- stdout = Some("Ran inspectingAspect"))
+ stdout = Seq(Right("Ran inspectingAspect")))
).foreach(runStageExpectFiles)
}
diff --git a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
index 35e354a6..99c0f7c0 100644
--- a/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselOptionsViewSpec.scala
@@ -4,6 +4,7 @@ package chiselTests.stage
import firrtl.options.Viewer.view
+import firrtl.RenameMap
import chisel3.stage._
import chisel3.internal.firrtl.Circuit
@@ -15,7 +16,7 @@ class ChiselOptionsViewSpec extends AnyFlatSpec with Matchers {
behavior of ChiselOptionsView.getClass.getName
it should "construct a view from an AnnotationSeq" in {
- val bar = Circuit("bar", Seq.empty, Seq.empty)
+ val bar = Circuit("bar", Seq.empty, Seq.empty, RenameMap())
val annotations = Seq(
NoRunFirrtlCompilerAnnotation,
PrintFullStackTraceAnnotation,
diff --git a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
index 98bbb2ea..7b6a2d39 100644
--- a/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
+++ b/src/test/scala/chiselTests/stage/ChiselStageSpec.scala
@@ -30,6 +30,10 @@ object ChiselStageSpec {
out := memory(bar.out)
}
+ class UserExceptionModule extends RawModule {
+ assert(false, "User threw an exception")
+ }
+
}
class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
@@ -40,13 +44,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
val stage = new ChiselStage
}
- behavior of "ChiselStage.emitChirrtl"
+ behavior of "ChiselStage$.emitChirrtl"
it should "return a CHIRRTL string" in {
ChiselStage.emitChirrtl(new Foo) should include ("infer mport")
}
- behavior of "ChiselStage.emitFirrtl"
+ behavior of "ChiselStage$.emitFirrtl"
it should "return a High FIRRTL string" in {
ChiselStage.emitFirrtl(new Foo) should include ("mem memory")
@@ -58,7 +62,7 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
.emitFirrtl(new Foo, args) should include ("module Bar")
}
- behavior of "ChiselStage.emitVerilog"
+ behavior of "ChiselStage$.emitVerilog"
it should "return a Verilog string" in {
ChiselStage.emitVerilog(new Foo) should include ("endmodule")
@@ -84,6 +88,13 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
catchWrites { ChiselStage.convert(new Foo) } shouldBe a[Right[_, _]]
}
+ ignore should "generate a FIRRTL circuit from a CHIRRTL circuit" in {
+ info("no files were written")
+ catchWrites {
+ ChiselStage.convert(ChiselStage.elaborate(new Foo))
+ } shouldBe a[Right[_, _]]
+ }
+
behavior of "ChiselStage$.emitChirrtl"
ignore should "generate a CHIRRTL string from a Chisel module" in {
@@ -142,4 +153,58 @@ class ChiselStageSpec extends AnyFlatSpec with Matchers with Utils {
exactly (1, order) should be (Dependency[chisel3.stage.phases.Elaborate])
}
+ behavior of "ChiselStage$ exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ ChiselStage.emitChirrtl(new UserExceptionModule)
+ }
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is trimmed")
+ exception.getStackTrace.mkString("\n") should not include ("java")
+ }
+
+ behavior of "ChiselStage exception handling"
+
+ it should "truncate a user exception" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule)
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ val stackTrace = exception.getStackTrace.mkString("\n")
+ info("The stack trace is trimmed")
+ stackTrace should not include ("java")
+
+ info("The stack trace include information about running --full-stacktrace")
+ stackTrace should include ("--full-stacktrace")
+ }
+
+ it should """not truncate a user exception with "--full-stacktrace"""" in {
+ info("The user's java.lang.AssertionError was thrown")
+ val exception = intercept[java.lang.AssertionError] {
+ (new ChiselStage).emitChirrtl(new UserExceptionModule, Array("--full-stacktrace"))
+ }
+
+ info(s""" - Exception was a ${exception.getClass.getName}""")
+
+ val message = exception.getMessage
+ info("The exception includes the user's message")
+ message should include ("User threw an exception")
+
+ info("The stack trace is not trimmed")
+ exception.getStackTrace.mkString("\n") should include ("java")
+ }
+
}
diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala
new file mode 100644
index 00000000..0c83493f
--- /dev/null
+++ b/src/test/scala/chiselTests/util/BitPatSpec.scala
@@ -0,0 +1,44 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.util
+
+import chisel3.util.BitPat
+import org.scalatest.flatspec.AnyFlatSpec
+import org.scalatest.matchers.should.Matchers
+
+
+class BitPatSpec extends AnyFlatSpec with Matchers {
+ behavior of classOf[BitPat].toString
+
+ it should "convert a BitPat to readable form" in {
+ val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
+ BitPat("b" + testPattern).toString should be (s"BitPat($testPattern)")
+ }
+
+ it should "convert a BitPat to raw form" in {
+ val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
+ BitPat("b" + testPattern).rawString should be(testPattern)
+ }
+
+ it should "not fail if BitPat width is 0" in {
+ intercept[IllegalArgumentException]{BitPat("b")}
+ }
+
+ it should "contact BitPat via ##" in {
+ (BitPat.Y(4) ## BitPat.dontCare(3) ## BitPat.N(2)).toString should be (s"BitPat(1111???00)")
+ }
+
+ it should "index and return new BitPat" in {
+ val b = BitPat("b1001???")
+ b(0) should be(BitPat.dontCare(1))
+ b(6) should be(BitPat.Y())
+ b(5) should be(BitPat.N())
+ }
+
+ it should "slice and return new BitPat" in {
+ val b = BitPat("b1001???")
+ b(2, 0) should be(BitPat("b???"))
+ b(4, 3) should be(BitPat("b01"))
+ b(6, 6) should be(BitPat("b1"))
+ }
+}
diff --git a/src/test/scala/chiselTests/util/CatSpec.scala b/src/test/scala/chiselTests/util/CatSpec.scala
index 5565ca51..79d2c027 100644
--- a/src/test/scala/chiselTests/util/CatSpec.scala
+++ b/src/test/scala/chiselTests/util/CatSpec.scala
@@ -5,6 +5,7 @@ package chiselTests.util
import chisel3._
import chisel3.stage.ChiselStage
import chisel3.util.Cat
+import chisel3.experimental.noPrefix
import chiselTests.ChiselFlatSpec
@@ -31,4 +32,33 @@ class CatSpec extends ChiselFlatSpec {
}
+ it should "not override the names of its arguments" in {
+ class MyModule extends RawModule {
+ val a, b, c, d = IO(Input(UInt(8.W)))
+ val out = IO(Output(UInt()))
+
+ out := Cat(a, b, c, d)
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ for (name <- Seq("a", "b", "c", "d")) {
+ chirrtl should include (s"input $name : UInt<8>")
+ }
+ }
+
+ it should "have prefixed naming" in {
+ class MyModule extends RawModule {
+ val in = IO(Input(Vec(8, UInt(8.W))))
+ val out = IO(Output(UInt()))
+
+ // noPrefix to avoid `out` as prefix
+ out := noPrefix(Cat(in))
+ }
+ val chirrtl = ChiselStage.emitChirrtl(new MyModule)
+ chirrtl should include ("node lo_lo = cat(in[6], in[7])")
+ chirrtl should include ("node lo_hi = cat(in[4], in[5])")
+ chirrtl should include ("node hi_lo = cat(in[2], in[3])")
+ chirrtl should include ("node hi_hi = cat(in[0], in[1])")
+ }
+
+
}
diff --git a/src/test/scala/chiselTests/util/experimental/PlaSpec.scala b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala
new file mode 100644
index 00000000..8af5c936
--- /dev/null
+++ b/src/test/scala/chiselTests/util/experimental/PlaSpec.scala
@@ -0,0 +1,95 @@
+package chiselTests.util.experimental
+
+import chisel3._
+import chisel3.stage.PrintFullStackTraceAnnotation
+import chisel3.testers.BasicTester
+import chisel3.util.{BitPat, pla}
+import chiselTests.ChiselFlatSpec
+
+class PlaSpec extends ChiselFlatSpec {
+ "A 1-of-8 decoder (eg. 74xx138 without enables)" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b00000001")),
+ (BitPat("b001"), BitPat("b00000010")),
+ (BitPat("b010"), BitPat("b00000100")),
+ (BitPat("b011"), BitPat("b00001000")),
+ (BitPat("b100"), BitPat("b00010000")),
+ (BitPat("b101"), BitPat("b00100000")),
+ (BitPat("b110"), BitPat("b01000000")),
+ (BitPat("b111"), BitPat("b10000000")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(plaOut === o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "An active-low 1-of-8 decoder (eg. inverted 74xx138 without enables)" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b00000001")),
+ (BitPat("b001"), BitPat("b00000010")),
+ (BitPat("b010"), BitPat("b00000100")),
+ (BitPat("b011"), BitPat("b00001000")),
+ (BitPat("b100"), BitPat("b00010000")),
+ (BitPat("b101"), BitPat("b00100000")),
+ (BitPat("b110"), BitPat("b01000000")),
+ (BitPat("b111"), BitPat("b10000000")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table, BitPat("b11111111"))
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(plaOut === ~o.value.U(8.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "#2112" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b000"), BitPat("b?01")),
+ (BitPat("b111"), BitPat("b?01")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(3.W))
+ chisel3.assert(o === plaOut, "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+
+ "A simple PLA" should "be generated correctly" in {
+ assertTesterPasses(new BasicTester {
+ val table = Seq(
+ (BitPat("b0000"), BitPat("b1")),
+ (BitPat("b0001"), BitPat("b1")),
+ (BitPat("b0010"), BitPat("b0")),
+ (BitPat("b0011"), BitPat("b1")),
+ (BitPat("b0100"), BitPat("b1")),
+ (BitPat("b0101"), BitPat("b0")),
+ (BitPat("b0110"), BitPat("b0")),
+ (BitPat("b0111"), BitPat("b0")),
+ (BitPat("b1000"), BitPat("b0")),
+ (BitPat("b1001"), BitPat("b0")),
+ (BitPat("b1010"), BitPat("b1")),
+ (BitPat("b1011"), BitPat("b0")),
+ (BitPat("b1100"), BitPat("b0")),
+ (BitPat("b1101"), BitPat("b1")),
+ (BitPat("b1110"), BitPat("b1")),
+ (BitPat("b1111"), BitPat("b1")),
+ )
+ table.foreach { case (i, o) =>
+ val (plaIn, plaOut) = pla(table)
+ plaIn := WireDefault(i.value.U(4.W))
+ chisel3.assert(plaOut === o.value.U(1.W), "Input " + i.toString + " produced incorrect output BitPat(%b)", plaOut)
+ }
+ stop()
+ })
+ }
+}
diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala
new file mode 100644
index 00000000..743a3cd8
--- /dev/null
+++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala
@@ -0,0 +1,63 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chiselTests.util.experimental
+
+import chisel3.util.BitPat
+import chisel3.util.experimental.decode.TruthTable
+import org.scalatest.flatspec.AnyFlatSpec
+
+class TruthTableSpec extends AnyFlatSpec {
+ val table = 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")
+ )
+ val str = """001->?
+ |010->?
+ |100->1
+ |101->1
+ |111->1
+ |0""".stripMargin
+ "TruthTable" should "serialize" in {
+ assert(table.toString contains "001->?")
+ assert(table.toString contains "010->?")
+ assert(table.toString contains "100->1")
+ assert(table.toString contains "111->1")
+ assert(table.toString contains " 0")
+ }
+ "TruthTable" should "deserialize" in {
+ assert(TruthTable(str) == table)
+ }
+ "TruthTable" should "merge same key" in {
+ assert(
+ TruthTable(
+ """001100->??1
+ |001100->1??
+ |???
+ |""".stripMargin
+ ) == TruthTable(
+ """001100->1?1
+ |???
+ |""".stripMargin
+ )
+ )
+ }
+ "TruthTable" should "crash when merging 0 and 1" in {
+ intercept[IllegalArgumentException] {
+ TruthTable(
+ """0->0
+ |0->1
+ |???
+ |""".stripMargin
+ )
+ }
+ }
+}
diff --git a/src/test/scala/examples/VendingMachineUtils.scala b/src/test/scala/examples/VendingMachineUtils.scala
index 131256f8..6847768a 100644
--- a/src/test/scala/examples/VendingMachineUtils.scala
+++ b/src/test/scala/examples/VendingMachineUtils.scala
@@ -34,6 +34,6 @@ object VendingMachineUtils {
value += incValue
}
}
- outputs
+ outputs.toSeq
}
}