diff options
| author | Jack | 2021-12-18 08:27:38 +0000 |
|---|---|---|
| committer | Jack | 2021-12-18 08:27:38 +0000 |
| commit | dd9ad534771247ac16eaa47eb9794102736b5102 (patch) | |
| tree | d4566d317cb8526b79017de1e438aea8217dd1d4 /src | |
| parent | 440edc4436fb3a8a4175ae425a0d31c4997ee60f (diff) | |
| parent | f50f74f583fba7b98e550c440df091e559ce32b8 (diff) | |
Merge branch 'master' into 3.5-release
Diffstat (limited to 'src')
62 files changed, 2278 insertions, 681 deletions
diff --git a/src/main/scala/chisel3/ChiselExecutionOptions.scala b/src/main/scala/chisel3/ChiselExecutionOptions.scala deleted file mode 100644 index 9f635b19..00000000 --- a/src/main/scala/chisel3/ChiselExecutionOptions.scala +++ /dev/null @@ -1,49 +0,0 @@ -// SPDX-License-Identifier: Apache-2.0 - -package chisel3 - -import chisel3.stage.{NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation} - -import firrtl.{AnnotationSeq, ExecutionOptionsManager, ComposableOptions} - -//TODO: provide support for running firrtl as separate process, could alternatively be controlled by external driver -//TODO: provide option for not saving chirrtl file, instead calling firrtl with in memory chirrtl -/** - * Options that are specific to chisel. - * - * @param runFirrtlCompiler when true just run chisel, when false run chisel then compile its output with firrtl - * @note this extends FirrtlExecutionOptions which extends CommonOptions providing easy access to down chain options - */ -case class ChiselExecutionOptions( - runFirrtlCompiler: Boolean = true, - printFullStackTrace: Boolean = false - // var runFirrtlAsProcess: Boolean = false - ) extends ComposableOptions { - - def toAnnotations: AnnotationSeq = - (if (!runFirrtlCompiler) { Seq(NoRunFirrtlCompilerAnnotation) } else { Seq() }) ++ - (if (printFullStackTrace) { Some(PrintFullStackTraceAnnotation) } else { None }) - -} - -trait HasChiselExecutionOptions { - self: ExecutionOptionsManager => - - var chiselOptions = ChiselExecutionOptions() - - parser.note("chisel3 options") - - parser.opt[Unit]("no-run-firrtl") - .abbr("chnrf") - .foreach { _ => - chiselOptions = chiselOptions.copy(runFirrtlCompiler = false) - } - .text("Stop after chisel emits chirrtl file") - - parser.opt[Unit]("full-stacktrace") - .foreach { _ => - chiselOptions = chiselOptions.copy(printFullStackTrace = true) - } - .text("Do not trim stack trace") -} - diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala index fb564446..aa379629 100644 --- a/src/main/scala/chisel3/Driver.scala +++ b/src/main/scala/chisel3/Driver.scala @@ -2,85 +2,106 @@ package chisel3 -import chisel3.internal.ErrorLog import internal.firrtl._ import firrtl._ -import firrtl.options.{Dependency, Phase, PhaseManager, StageError} -import firrtl.options.phases.DeletedWrapper -import firrtl.options.Viewer.view -import firrtl.annotations.JsonProtocol import firrtl.util.{BackendCompilationUtilities => FirrtlBackendCompilationUtilities} -import chisel3.stage.{ChiselExecutionResultView, ChiselGeneratorAnnotation, ChiselStage} -import chisel3.stage.phases.DriverCompatibility import java.io._ +import _root_.logger.LazyLogging +@deprecated("Use object firrtl.util.BackendCompilationUtilities instead", "Chisel 3.5") +trait BackendCompilationUtilities extends LazyLogging { -/** - * The Driver provides methods to invoke the chisel3 compiler and the firrtl compiler. - * By default firrtl is automatically run after chisel. an [[ExecutionOptionsManager]] - * is needed to manage options. It can parser command line arguments or coordinate - * multiple chisel toolchain tools options. - * - * @example - * {{{ - * val optionsManager = new ExecutionOptionsManager("chisel3") - * with HasFirrtlOptions - * with HasChiselExecutionOptions { - * commonOptions = CommonOption(targetDirName = "my_target_dir") - * chiselOptions = ChiselExecutionOptions(runFirrtlCompiler = false) - * } - * chisel3.Driver.execute(optionsManager, () => new Dut) - * }}} - * or via command line arguments - * @example {{{ - * args = "--no-run-firrtl --target-dir my-target-dir".split(" +") - * chisel3.execute(args, () => new DUT) - * }}} - */ + import scala.sys.process.{ProcessBuilder, ProcessLogger, _} + + // Inlined from old trait firrtl.util.BackendCompilationUtilities + lazy val TestDirectory = FirrtlBackendCompilationUtilities.TestDirectory + def timeStamp: String = FirrtlBackendCompilationUtilities.timeStamp + def loggingProcessLogger: ProcessLogger = FirrtlBackendCompilationUtilities.loggingProcessLogger + def copyResourceToFile(name: String, file: File): Unit = FirrtlBackendCompilationUtilities.copyResourceToFile(name, file) + def createTestDirectory(testName: String): File = FirrtlBackendCompilationUtilities.createTestDirectory(testName) + def makeHarness(template: String => String, post: String)(f: File): File = + FirrtlBackendCompilationUtilities.makeHarness(template, post)(f) + def firrtlToVerilog(prefix: String, dir: File): ProcessBuilder = + FirrtlBackendCompilationUtilities.firrtlToVerilog(prefix, dir) + def verilogToCpp( + dutFile: String, + dir: File, + vSources: Seq[File], + cppHarness: File, + suppressVcd: Boolean = false, + resourceFileName: String = firrtl.transforms.BlackBoxSourceHelper.defaultFileListName + ): ProcessBuilder = { + FirrtlBackendCompilationUtilities.verilogToCpp(dutFile, dir, vSources, cppHarness, suppressVcd, resourceFileName) + } + def cppToExe(prefix: String, dir: File): ProcessBuilder = FirrtlBackendCompilationUtilities.cppToExe(prefix, dir) + def executeExpectingFailure( + prefix: String, + dir: File, + assertionMsg: String = "" + ): Boolean = { + FirrtlBackendCompilationUtilities.executeExpectingFailure(prefix, dir, assertionMsg) + } + def executeExpectingSuccess(prefix: String, dir: File): Boolean = + FirrtlBackendCompilationUtilities.executeExpectingSuccess(prefix, dir) -trait BackendCompilationUtilities extends FirrtlBackendCompilationUtilities { /** Compile Chirrtl to Verilog by invoking Firrtl inside the same JVM * * @param prefix basename of the file * @param dir directory where file lives * @return true if compiler completed successfully */ + @deprecated("Use ChiselStage instead", "Chisel 3.5") def compileFirrtlToVerilog(prefix: String, dir: File): Boolean = { - val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions { - commonOptions = CommonOptions(topName = prefix, targetDirName = dir.getAbsolutePath) - firrtlOptions = FirrtlExecutionOptions(compilerName = "verilog") + + // ====== Implemented by inlining logic from ExecutionsOptionManager.toAnnotations ===== + import firrtl.stage.InfoModeAnnotation + import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation + import _root_.logger.LogLevelAnnotation + val annos: AnnotationSeq = List( + InfoModeAnnotation("append"), + TopNameAnnotation(prefix), + TargetDirAnnotation(dir.getAbsolutePath), + LogLevelAnnotation(_root_.logger.LogLevel.None) + ) + + // ******************* Implemented by inlining firrtl.Driver.execute *************************** + import firrtl.stage.phases.DriverCompatibility + import firrtl.stage.FirrtlStage + import firrtl.options.{Dependency, Phase, PhaseManager} + import firrtl.options.phases.DeletedWrapper + + val phases: Seq[Phase] = { + import DriverCompatibility._ + new PhaseManager( + List( + Dependency[AddImplicitFirrtlFile], + Dependency[AddImplicitAnnotationFile], + Dependency[AddImplicitOutputFile], + Dependency[AddImplicitEmitter], + Dependency[FirrtlStage] + ) + ).transformOrder + .map(DeletedWrapper(_)) } - firrtl.Driver.execute(optionsManager) match { - case _: FirrtlExecutionSuccess => true - case _: FirrtlExecutionFailure => false + val annosx = + try { + phases.foldLeft(annos)((a, p) => p.transform(a)) + } catch { + case _: firrtl.options.OptionsException => return false + } + // ********************************************************************************************* + + val options = annosx + + // ********** Implemented by inlining firrtl.stage.FirrtlExecutionResultView.view ************** + import firrtl.stage.FirrtlCircuitAnnotation + + options.collectFirst { case a: FirrtlCircuitAnnotation => a.circuit } match { + case None => false + case Some(_) => true } + // ********************************************************************************************* } } -/** - * This family provides return values from the chisel3 and possibly firrtl compile steps - */ -@deprecated("This will be removed in Chisel 3.5", "Chisel3 3.4") -trait ChiselExecutionResult - -/** - * - * @param circuitOption Optional circuit, has information like circuit name - * @param emitted The emitted Chirrrl text - * @param firrtlResultOption Optional Firrtl result, @see freechipsproject/firrtl for details - */ -@deprecated("This will be removed in Chisel 3.5", "Chisel 3.4") -case class ChiselExecutionSuccess( - circuitOption: Option[Circuit], - emitted: String, - firrtlResultOption: Option[FirrtlExecutionResult] - ) extends ChiselExecutionResult - -/** - * Getting one of these indicates failure of some sort. - * - * @param message A clue might be provided here. - */ -@deprecated("This will be removed in Chisel 3.5", "Chisel 3.4") -case class ChiselExecutionFailure(message: String) extends ChiselExecutionResult diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 2384c4d3..8f5a2577 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -6,10 +6,12 @@ import chisel3._ import chisel3.internal.{HasId} import chisel3.experimental.BaseModule import chisel3.experimental.FixedPoint -import chisel3.internal.firrtl._ +import chisel3.internal.firrtl.{Definition => DefinitionIR, _} +import chisel3.experimental.hierarchy._ import chisel3.internal.PseudoModule import chisel3.internal.BaseModule.ModuleClone import firrtl.annotations.ReferenceTarget +import scala.reflect.runtime.universe.TypeTag import scala.collection.mutable import chisel3.internal.naming.chiselName @@ -22,7 +24,6 @@ object Select { /** Return just leaf components of expanded node * * @param d Component to find leafs if aggregate typed. Intermediate fields/indicies are not included - * @return */ def getLeafs(d: Data): Seq[Data] = d match { case r: Record => r.getElements.flatMap(getLeafs) @@ -33,7 +34,6 @@ object Select { /** Return all expanded components, including intermediate aggregate nodes * * @param d Component to find leafs if aggregate typed. Intermediate fields/indicies ARE included - * @return */ def getIntermediateAndLeafs(d: Data): Seq[Data] = d match { case r: Record => r +: r.getElements.flatMap(getIntermediateAndLeafs) @@ -41,15 +41,156 @@ object Select { case other => Seq(other) } + /** Selects all instances/modules directly instantiated within given definition + * + * @param parent + */ + def instancesIn(parent: Hierarchy[BaseModule]): Seq[Instance[BaseModule]] = { + check(parent) + implicit val mg = new chisel3.internal.MacroGenerated{} + parent.proto._component.get match { + case d: DefModule => d.commands.collect { + case d: DefInstance => + d.id match { + case p: chisel3.internal.BaseModule.IsClone[_] => + parent._lookup { x => new Instance(Clone(p)).asInstanceOf[Instance[BaseModule]] } + case other: BaseModule => + parent._lookup { x => other } + } + } + case other => Nil + } + } + + /** Selects all Instances of instances/modules directly instantiated within given module, of provided type + * + * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class. + * @param parent hierarchy which instantiates the returned Definitions + */ + def instancesOf[T <: BaseModule : TypeTag](parent: Hierarchy[BaseModule]): Seq[Instance[T]] = { + check(parent) + implicit val mg = new chisel3.internal.MacroGenerated{} + parent.proto._component.get match { + case d: DefModule => d.commands.flatMap { + case d: DefInstance => + d.id match { + case p: chisel3.internal.BaseModule.IsClone[_] => + val i = parent._lookup { x => new Instance(Clone(p)).asInstanceOf[Instance[BaseModule]] } + if(i.isA[T]) Some(i.asInstanceOf[Instance[T]]) else None + case other: BaseModule => + val i = parent._lookup { x => other } + if(i.isA[T]) Some(i.asInstanceOf[Instance[T]]) else None + } + case other => None + } + case other => Nil + } + } + + /** Selects all Instances directly and indirectly instantiated within given root hierarchy, of provided type + * + * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class. + * @param root top of the hierarchy to search for instances/modules of given type + */ + def allInstancesOf[T <: BaseModule : TypeTag](root: Hierarchy[BaseModule]): Seq[Instance[T]] = { + val soFar = if(root.isA[T]) Seq(root.toInstance.asInstanceOf[Instance[T]]) else Nil + val allLocalInstances = instancesIn(root) + soFar ++ (allLocalInstances.flatMap(allInstancesOf[T])) + } + + /** Selects the Definitions of all instances/modules directly instantiated within given module + * + * @param parent + */ + def definitionsIn(parent: Hierarchy[BaseModule]): Seq[Definition[BaseModule]] = { + type DefType = Definition[BaseModule] + implicit val mg = new chisel3.internal.MacroGenerated{} + check(parent) + val defs = parent.proto._component.get match { + case d: DefModule => d.commands.collect { + case i: DefInstance => + i.id match { + case p: chisel3.internal.BaseModule.IsClone[_] => + parent._lookup { x => new Definition(Proto(p.getProto)).asInstanceOf[Definition[BaseModule]] } + case other: BaseModule => + parent._lookup { x => other.toDefinition } + } + } + case other => Nil + } + val (_, defList) = defs.foldLeft((Set.empty[DefType], List.empty[DefType])) { case ((set, list), definition: Definition[BaseModule]) => + if(set.contains(definition)) (set, list) else (set + definition, definition +: list) + } + defList.reverse + } + + + /** Selects all Definitions of instances/modules directly instantiated within given module, of provided type + * + * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class. + * @param parent hierarchy which instantiates the returned Definitions + */ + def definitionsOf[T <: BaseModule : TypeTag](parent: Hierarchy[BaseModule]): Seq[Definition[T]] = { + check(parent) + implicit val mg = new chisel3.internal.MacroGenerated{} + type DefType = Definition[T] + val defs = parent.proto._component.get match { + case d: DefModule => d.commands.flatMap { + case d: DefInstance => + d.id match { + case p: chisel3.internal.BaseModule.IsClone[_] => + val d = parent._lookup { x => new Definition(Clone(p)).asInstanceOf[Definition[BaseModule]] } + if(d.isA[T]) Some(d.asInstanceOf[Definition[T]]) else None + case other: BaseModule => + val d = parent._lookup { x => other.toDefinition } + if(d.isA[T]) Some(d.asInstanceOf[Definition[T]]) else None + } + case other => None + } + } + val (_, defList) = defs.foldLeft((Set.empty[DefType], List.empty[DefType])) { case ((set, list), definition: Definition[T]) => + if(set.contains(definition)) (set, list) else (set + definition, definition +: list) + } + defList.reverse + } + + /** Selects all Definition's directly and indirectly instantiated within given root hierarchy, of provided type + * + * @note IMPORTANT: this function requires summoning a TypeTag[T], which will fail if T is an inner class, i.e. + * a class defined within another class. + * @param root top of the hierarchy to search for definitions of given type + */ + def allDefinitionsOf[T <: BaseModule : TypeTag](root: Hierarchy[BaseModule]): Seq[Definition[T]] = { + type DefType = Definition[T] + val allDefSet = mutable.HashSet[Definition[BaseModule]]() + val defSet = mutable.HashSet[DefType]() + val defList = mutable.ArrayBuffer[DefType]() + def rec(hier: Definition[BaseModule]): Unit = { + if(hier.isA[T] && !defSet.contains(hier.asInstanceOf[DefType])) { + defSet += hier.asInstanceOf[DefType] + defList += hier.asInstanceOf[DefType] + } + allDefSet += hier + val allDefs = definitionsIn(hier) + allDefs.collect { + case d if !allDefSet.contains(d) => rec(d) + } + } + rec(root.toDefinition) + defList.toList + } + /** Collects all components selected by collector within module and all children modules it instantiates * directly or indirectly * Accepts a collector function, rather than a collector partial function (see [[collectDeep]]) + * + * @note This API will not work with the new experimental hierarchy package. Instead, use allInstancesOf or allDefinitionsOf. + * * @param module Module to collect components, as well as all children module it directly and indirectly instantiates * @param collector Collector function to pick, given a module, which components to collect * @param tag Required for generics to work, should ignore this * @tparam T Type of the component that will be collected - * @return */ def getDeep[T](module: BaseModule)(collector: BaseModule => Seq[T]): Seq[T] = { check(module) @@ -63,11 +204,13 @@ object Select { /** Collects all components selected by collector within module and all children modules it instantiates * directly or indirectly * Accepts a collector partial function, rather than a collector function (see [[getDeep]]) + * + * @note This API will not work with the new experimental hierarchy package. Instead, use allInstancesOf or allDefinitionsOf. + * * @param module Module to collect components, as well as all children module it directly and indirectly instantiates * @param collector Collector partial function to pick, given a module, which components to collect * @param tag Required for generics to work, should ignore this * @tparam T Type of the component that will be collected - * @return */ def collectDeep[T](module: BaseModule)(collector: PartialFunction[BaseModule, T]): Iterable[T] = { check(module) @@ -78,9 +221,11 @@ object Select { myItems ++ deepChildrenItems } - /** Selects all instances directly instantiated within given module + /** Selects all modules directly instantiated within given module + * + * @note This API will not work with the new experimental hierarchy package. Instead, use instancesIn or definitionsIn. + * * @param module - * @return */ def instances(module: BaseModule): Seq[BaseModule] = { check(module) @@ -88,7 +233,7 @@ object Select { 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 _: PseudoModule => throw new Exception("instances, collectDeep, and getDeep are currently incompatible with Definition/Instance!") case other => Some(other) } case _ => None @@ -99,7 +244,6 @@ object Select { /** Selects all registers directly instantiated within given module * @param module - * @return */ def registers(module: BaseModule): Seq[Data] = { check(module) @@ -111,7 +255,6 @@ object Select { /** Selects all ios directly contained within given module * @param module - * @return */ def ios(module: BaseModule): Seq[Data] = { check(module) @@ -120,7 +263,6 @@ object Select { /** Selects all SyncReadMems directly contained within given module * @param module - * @return */ def syncReadMems(module: BaseModule): Seq[SyncReadMem[_]] = { check(module) @@ -131,7 +273,6 @@ object Select { /** Selects all Mems directly contained within given module * @param module - * @return */ def mems(module: BaseModule): Seq[Mem[_]] = { check(module) @@ -142,7 +283,6 @@ object Select { /** Selects all arithmetic or logical operators directly instantiated within given module * @param module - * @return */ def ops(module: BaseModule): Seq[(String, Data)] = { check(module) @@ -155,7 +295,6 @@ object Select { * The kind of operators are contained in [[chisel3.internal.firrtl.PrimOp]] * @param opKind the kind of operator, e.g. "mux", "add", or "bits" * @param module - * @return */ def ops(opKind: String)(module: BaseModule): Seq[Data] = { check(module) @@ -166,7 +305,6 @@ object Select { /** Selects all wires in a module * @param module - * @return */ def wires(module: BaseModule): Seq[Data] = { check(module) @@ -177,7 +315,6 @@ object Select { /** Selects all memory ports, including their direction and memory * @param module - * @return */ def memPorts(module: BaseModule): Seq[(Data, MemPortDirection, MemBase[_])] = { check(module) @@ -189,7 +326,6 @@ object Select { /** Selects all memory ports of a given direction, including their memory * @param dir The direction of memory ports to select * @param module - * @return */ def memPorts(dir: MemPortDirection)(module: BaseModule): Seq[(Data, MemBase[_])] = { check(module) @@ -200,7 +336,6 @@ object Select { /** Selects all components who have been set to be invalid, even if they are later connected to * @param module - * @return */ def invalids(module: BaseModule): Seq[Data] = { check(module) @@ -211,7 +346,6 @@ object Select { /** Selects all components who are attached to a given signal, within a module * @param module - * @return */ def attachedTo(module: BaseModule)(signal: Data): Set[Data] = { check(module) @@ -226,7 +360,6 @@ object Select { * E.g. if signal = io.foo.bar, connectionsTo will return all connections to io, io.foo, and io.bar * @param module * @param signal - * @return */ def connectionsTo(module: BaseModule)(signal: Data): Seq[PredicatedConnect] = { check(module) @@ -237,7 +370,7 @@ object Select { var seenDef = isPort searchWhens(module, (cmd: Command, preds) => { cmd match { - case cmd: Definition if cmd.id.isInstanceOf[Data] => + case cmd: DefinitionIR if cmd.id.isInstanceOf[Data] => val x = getIntermediateAndLeafs(cmd.id.asInstanceOf[Data]) if(x.contains(signal)) prePredicates = preds case Connect(_, loc@Node(d: Data), exp) => @@ -263,13 +396,12 @@ object Select { /** Selects all stop statements, and includes the predicates surrounding the stop statement * * @param module - * @return */ def stops(module: BaseModule): Seq[Stop] = { val stops = mutable.ArrayBuffer[Stop]() searchWhens(module, (cmd: Command, preds: Seq[Predicate]) => { cmd match { - case chisel3.internal.firrtl.Stop(_, clock, ret) => stops += Stop(preds, ret, getId(clock).asInstanceOf[Clock]) + case chisel3.internal.firrtl.Stop(_, _, clock, ret) => stops += Stop(preds, ret, getId(clock).asInstanceOf[Clock]) case other => } }) @@ -279,7 +411,6 @@ object Select { /** Selects all printf statements, and includes the predicates surrounding the printf statement * * @param module - * @return */ def printfs(module: BaseModule): Seq[Printf] = { val printfs = mutable.ArrayBuffer[Printf]() @@ -297,6 +428,7 @@ object Select { require(module.isClosed, "Can't use Selector on modules that have not finished construction!") require(module._component.isDefined, "Can't use Selector on modules that don't have components!") } + private def check(hierarchy: Hierarchy[BaseModule]): Unit = check(hierarchy.proto) // Given a loc, return all subcomponents of id that could be assigned to in connect private def getEffected(a: Arg): Seq[Data] = a match { diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 1a476f61..dc7e6487 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -43,7 +43,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( injection: M => Unit ) extends Aspect[T] { final def toAnnotation(top: T): AnnotationSeq = { - val moduleNames = Select.collectDeep(top) { case i => i.name }.toSeq + val moduleNames = Select.allDefinitionsOf[chisel3.experimental.BaseModule](top.toDefinition).map{i => i.toTarget.module }.toSeq toAnnotation(selectRoots(top), top.name, moduleNames) } diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index dde2321d..ccb4ec1f 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -33,7 +33,10 @@ package object Chisel { implicit class AddDirectionToData[T<:Data](target: T) { def asInput: T = Input(target) def asOutput: T = Output(target) - def flip(): T = Flipped(target) + def flip: T = Flipped(target) + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def flip(dummy: Int*): T = flip } implicit class AddDirMethodToData[T<:Data](target: T) { @@ -362,7 +365,8 @@ package object Chisel { implicit class fromBooleanToLiteral(x: Boolean) extends chisel3.fromBooleanToLiteral(x) implicit class fromIntToWidth(x: Int) extends chisel3.fromIntToWidth(x) - type BackendCompilationUtilities = firrtl.util.BackendCompilationUtilities + @deprecated("Use object firrtl.util.BackendCompilationUtilities instead", "Chisel 3.5") + type BackendCompilationUtilities = chisel3.BackendCompilationUtilities val ImplicitConversions = chisel3.util.ImplicitConversions // Deprecated as of Chisel3 @@ -634,6 +638,7 @@ package object Chisel { final def toUInt(implicit compileOptions: CompileOptions): UInt = a.do_asUInt(DeprecatedSourceInfo, compileOptions) + final def toBools(implicit compileOptions: CompileOptions): Seq[Bool] = a.do_asBools(DeprecatedSourceInfo, compileOptions) } } diff --git a/src/main/scala/chisel3/experimental/conversions/package.scala b/src/main/scala/chisel3/experimental/conversions/package.scala new file mode 100644 index 00000000..574f9f96 --- /dev/null +++ b/src/main/scala/chisel3/experimental/conversions/package.scala @@ -0,0 +1,128 @@ + +package chisel3.experimental + +import chisel3._ +import chisel3.experimental.dataview._ +import scala.language.implicitConversions + +/** Implicit conversions from some Scala standard library types and [[Data]] + * + * @note As this leans heavily on the experimental [[DataView]] feature, these APIs are experimental and subject to change + */ +package object conversions { + + /** Implicit conversion between `Seq` and `Vec` */ + implicit def seq2vec[A : DataProduct, B <: Data](xs: Seq[A])(implicit dv: DataView[A, B]): Vec[B] = + xs.viewAs[Vec[B]] + + /** Implicit conversion between [[Tuple2]] and [[HWTuple2]] */ + 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]] + } + + /** Implicit conversion between [[Tuple3]] and [[HWTuple3]] */ + implicit def tuple3hwtuple[T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, V1 <: Data, V2 <: Data, V3 <: Data]( + tup: (T1, T2, T3) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3] + ): HWTuple3[V1, V2, V3] = { + tup.viewAs[HWTuple3[V1, V2, V3]] + } + + /** Implicit conversion between [[Tuple4]] and [[HWTuple4]] */ + implicit def tuple4hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data + ]( + tup: (T1, T2, T3, T4) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4] + ): HWTuple4[V1, V2, V3, V4] = { + tup.viewAs[HWTuple4[V1, V2, V3, V4]] + } + + /** Implicit conversion between [[Tuple5]] and [[HWTuple5]] */ + implicit def tuple5hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data + ]( + tup: (T1, T2, T3, T4, T5) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5] + ): HWTuple5[V1, V2, V3, V4, V5] = { + tup.viewAs[HWTuple5[V1, V2, V3, V4, V5]] + } + + /** Implicit conversion between [[Tuple6]] and [[HWTuple6]] */ + implicit def tuple6hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, T6 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, V6 <: Data + ]( + tup: (T1, T2, T3, T4, T5, T6) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], + v6: DataView[T6, V6] + ): HWTuple6[V1, V2, V3, V4, V5, V6] = { + tup.viewAs[HWTuple6[V1, V2, V3, V4, V5, V6]] + } + + /** Implicit conversion between [[Tuple7]] and [[HWTuple7]] */ + implicit def tuple7hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, V6 <: Data, V7 <: Data + ]( + tup: (T1, T2, T3, T4, T5, T6, T7) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], + v6: DataView[T6, V6], v7: DataView[T7, V7] + ): HWTuple7[V1, V2, V3, V4, V5, V6, V7] = { + tup.viewAs[HWTuple7[V1, V2, V3, V4, V5, V6, V7]] + } + + /** Implicit conversion between [[Tuple8]] and [[HWTuple8]] */ + implicit def tuple8hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, V6 <: Data, V7 <: Data, V8 <: Data + ]( + tup: (T1, T2, T3, T4, T5, T6, T7, T8) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], + v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8] + ): HWTuple8[V1, V2, V3, V4, V5, V6, V7, V8] = { + tup.viewAs[HWTuple8[V1, V2, V3, V4, V5, V6, V7, V8]] + } + + /** Implicit conversion between [[Tuple9]] and [[HWTuple9]] */ + implicit def tuple9hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, T9 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, V6 <: Data, V7 <: Data, V8 <: Data, V9 <: Data + ]( + tup: (T1, T2, T3, T4, T5, T6, T7, T8, T9) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], + v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8], v9: DataView[T9, V9] + ): HWTuple9[V1, V2, V3, V4, V5, V6, V7, V8, V9] = { + tup.viewAs[HWTuple9[V1, V2, V3, V4, V5, V6, V7, V8, V9]] + } + + /** Implicit conversion between [[Tuple10]] and [[HWTuple10]] */ + implicit def tuple10hwtuple[ + T1 : DataProduct, T2 : DataProduct, T3 : DataProduct, T4 : DataProduct, T5 : DataProduct, + T6 : DataProduct, T7 : DataProduct, T8 : DataProduct, T9 : DataProduct, T10 : DataProduct, + V1 <: Data, V2 <: Data, V3 <: Data, V4 <: Data, V5 <: Data, V6 <: Data, V7 <: Data, V8 <: Data, V9 <: Data, V10 <: Data + ]( + tup: (T1, T2, T3, T4, T5, T6, T7, T8, T9, T10) + )( + implicit v1: DataView[T1, V1], v2: DataView[T2, V2], v3: DataView[T3, V3], v4: DataView[T4, V4], v5: DataView[T5, V5], + v6: DataView[T6, V6], v7: DataView[T7, V7], v8: DataView[T8, V8], v9: DataView[T9, V9], v10: DataView[T10, V10] + ): HWTuple10[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10] = { + tup.viewAs[HWTuple10[V1, V2, V3, V4, V5, V6, V7, V8, V9, V10]] + } +} diff --git a/src/main/scala/chisel3/experimental/verification/package.scala b/src/main/scala/chisel3/experimental/verification/package.scala new file mode 100644 index 00000000..a026542d --- /dev/null +++ b/src/main/scala/chisel3/experimental/verification/package.scala @@ -0,0 +1,27 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.experimental + +import chisel3.{Bool, CompileOptions} +import chisel3.internal.sourceinfo.SourceInfo + +package object verification { + + object assert { + @deprecated("Please use chisel3.assert instead. The chisel3.experimental.verification package will be removed.", "Chisel 3.5") + def apply(predicate: Bool, msg: String = "") + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): chisel3.assert.Assert = chisel3.assert(predicate, msg) + } + + object assume { + @deprecated("Please use chisel3.assume instead. The chisel3.experimental.verification package will be removed.", "Chisel 3.5") + def apply(predicate: Bool, msg: String = "") + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): chisel3.assume.Assume = chisel3.assume(predicate, msg) + } + + object cover { + @deprecated("Please use chisel3.cover instead. The chisel3.experimental.verification package will be removed.", "Chisel 3.5") + def apply(predicate: Bool, msg: String = "") + (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): chisel3.cover.Cover = chisel3.cover(predicate, msg) + } +} diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala index 4d6738d6..c307d3ae 100644 --- a/src/main/scala/chisel3/stage/package.scala +++ b/src/main/scala/chisel3/stage/package.scala @@ -24,33 +24,4 @@ package object stage { } } - - private[chisel3] implicit object ChiselExecutionResultView extends OptionsView[ChiselExecutionResult] { - - def view(options: AnnotationSeq): ChiselExecutionResult = { - var chiselCircuit: Option[ChiselCircuit] = None - var chirrtlCircuit: Option[String] = None - - options.foreach { - case a @ ChiselCircuitAnnotation(b) => - chiselCircuit = Some(b) - chirrtlCircuit = { - val anno = CircuitSerializationAnnotation(a.circuit, "", FirrtlFileFormat) - Some(anno.getBytes.map(_.toChar).mkString) - } - case _ => - } - - val fResult = firrtl.stage.phases.DriverCompatibility.firrtlResultView(options) - - (chiselCircuit, chirrtlCircuit) match { - case (None, _) => ChiselExecutionFailure("Failed to elaborate Chisel circuit") - case (Some(_), None) => ChiselExecutionFailure("Failed to convert Chisel circuit to FIRRTL") - case (Some(a), Some(b)) => ChiselExecutionSuccess( Some(a), b, Some(fResult)) - } - - } - - } - } diff --git a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala index 9305c5c9..847b7179 100644 --- a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala +++ b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala @@ -2,19 +2,13 @@ package chisel3.stage.phases -import firrtl.{AnnotationSeq, ExecutionOptionsManager, HasFirrtlOptions} -import firrtl.annotations.NoTargetAnnotation -import firrtl.options.{Dependency, OptionsException, OutputAnnotationFileAnnotation, Phase, Unserializable} -import firrtl.stage.{FirrtlCircuitAnnotation, RunFirrtlTransformAnnotation} -import firrtl.stage.phases.DriverCompatibility.TopNameAnnotation +import firrtl.annotations.Annotation +import firrtl.options.Phase -import chisel3.HasChiselExecutionOptions -import chisel3.stage.{ChiselStage, NoRunFirrtlCompilerAnnotation, ChiselOutputFileAnnotation} - -/** This provides components of a compatibility wrapper around Chisel's deprecated [[chisel3.Driver]]. +/** This formerly provided components of a compatibility wrapper around Chisel's removed `chisel3.Driver`. * - * Primarily, this object includes [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s - * derived from the deprecated [[firrtl.stage.phases.DriverCompatibility.TopNameAnnotation]]. + * This object formerly included [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s + * derived from the deprecated `firrtl.stage.phases.DriverCompatibility.TopNameAnnotation`. */ @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/testers/BasicTester.scala b/src/main/scala/chisel3/testers/BasicTester.scala index d17407ea..99002660 100644 --- a/src/main/scala/chisel3/testers/BasicTester.scala +++ b/src/main/scala/chisel3/testers/BasicTester.scala @@ -21,12 +21,7 @@ class BasicTester extends Module() { * reset). If your definition of reset is not the encapsulating Module's * reset, you will need to gate this externally. */ - def stop()(implicit sourceInfo: SourceInfo) { - // TODO: rewrite this using library-style SourceInfo passing. - when (!reset.asBool) { - pushCommand(Stop(sourceInfo, clock.ref, 0)) - } - } + def stop()(implicit sourceInfo: SourceInfo): Unit = chisel3.stop() /** The finish method provides a hook that subclasses of BasicTester can use to * alter a circuit after their constructor has been called. diff --git a/src/main/scala/chisel3/util/Arbiter.scala b/src/main/scala/chisel3/util/Arbiter.scala index 059bdd14..b68acae1 100644 --- a/src/main/scala/chisel3/util/Arbiter.scala +++ b/src/main/scala/chisel3/util/Arbiter.scala @@ -10,6 +10,7 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because /** IO bundle definition for an Arbiter, which takes some number of ready-valid inputs and outputs * (selects) at most one. + * @groupdesc Signals The actual hardware fields of the Bundle * * @param gen data type * @param n number of inputs @@ -17,8 +18,20 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because class ArbiterIO[T <: Data](private val gen: T, val n: Int) extends Bundle { // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs. +/** Input data, one per potential sender + * + * @group Signals + */ val in = Flipped(Vec(n, Decoupled(gen))) +/** Output data after arbitration + * + * @group Signals + */ val out = Decoupled(gen) +/** One-Hot vector indicating which output was chosen + * + * @group Signals + */ val chosen = Output(UInt(log2Ceil(n).W)) } @@ -47,7 +60,7 @@ abstract class LockingArbiterLike[T <: Data](gen: T, n: Int, count: Int, needsLo val locked = lockCount.value =/= 0.U val wantsLock = needsLock.map(_(io.out.bits)).getOrElse(true.B) - when (io.out.fire() && wantsLock) { + when (io.out.fire && wantsLock) { lockIdx := io.chosen lockCount.inc() } @@ -63,7 +76,7 @@ abstract class LockingArbiterLike[T <: Data](gen: T, n: Int, count: Int, needsLo class LockingRRArbiter[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T => Bool] = None) extends LockingArbiterLike[T](gen, n, count, needsLock) { - lazy val lastGrant = RegEnable(io.chosen, io.out.fire()) + lazy val lastGrant = RegEnable(io.chosen, io.out.fire) lazy val grantMask = (0 until n).map(_.asUInt > lastGrant) lazy val validMask = io.in zip grantMask map { case (in, g) => in.valid && g } diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 0dcb2466..808245de 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -4,11 +4,16 @@ package chisel3.util import scala.language.experimental.macros import chisel3._ -import chisel3.internal.chiselRuntimeDeprecated import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} object BitPat { + + private[chisel3] implicit val bitPatOrder = new Ordering[BitPat] { + import scala.math.Ordered.orderingToOrdered + def compare(x: BitPat, y: BitPat): Int = (x.getWidth, x.value, x.mask) compare (y.getWidth, y.value, y.mask) + } + /** Parses a bit pattern string into (bits, mask, width). * * @return bits the literal value, with don't cares being 0 @@ -90,6 +95,7 @@ object BitPat { * @note the UInt must be a literal */ def apply(x: UInt): BitPat = { + require(x.isLit, s"$x is not a literal, BitPat.apply(x: UInt) only accepts literals") val len = if (x.isWidthKnown) x.getWidth else 0 apply("b" + x.litValue.toString(2).reverse.padTo(len, "0").reverse.mkString) } @@ -111,6 +117,119 @@ object BitPat { } } +package experimental { + object BitSet { + + /** Construct a [[BitSet]] from a sequence of [[BitPat]]. + * All [[BitPat]] must have the same width. + */ + def apply(bitpats: BitPat*): BitSet = { + val bs = new BitSet { def terms = bitpats.flatMap(_.terms).toSet } + // check width + bs.getWidth + bs + } + + /** Empty [[BitSet]]. */ + val empty: BitSet = new BitSet { + def terms = Set() + } + + /** Construct a [[BitSet]] from String. + * each line should be a valid [[BitPat]] string with the same width. + */ + def fromString(str: String): BitSet = { + val bs = new BitSet { def terms = str.split('\n').map(str => BitPat(str)).toSet } + // check width + bs.getWidth + bs + } + } + + /** A Set of [[BitPat]] represents a set of bit vector with mask. */ + sealed trait BitSet { outer => + /** all [[BitPat]] elements in [[terms]] make up this [[BitSet]]. + * all [[terms]] should be have the same width. + */ + def terms: Set[BitPat] + + /** Get specified width of said BitSet */ + def getWidth: Int = { + require(terms.map(_.width).size <= 1, s"All BitPats must be the same size! Got $this") + // set width = 0 if terms is empty. + terms.headOption.map(_.width).getOrElse(0) + } + + import BitPat.bitPatOrder + override def toString: String = terms.toSeq.sorted.mkString("\n") + + /** whether this [[BitSet]] is empty (i.e. no value matches) */ + def isEmpty: Boolean = terms.forall(_.isEmpty) + + /** Check whether this [[BitSet]] overlap with that [[BitSet]], i.e. !(intersect.isEmpty) + * + * @param that [[BitSet]] to be checked. + * @return true if this and that [[BitSet]] have overlap. + */ + def overlap(that: BitSet): Boolean = + !terms.flatMap(a => that.terms.map(b => (a, b))).forall { case (a, b) => !a.overlap(b) } + + /** Check whether this [[BitSet]] covers that (i.e. forall b matches that, b also matches this) + * + * @param that [[BitSet]] to be covered + * @return true if this [[BitSet]] can cover that [[BitSet]] + */ + def cover(that: BitSet): Boolean = + that.subtract(this).isEmpty + + /** Intersect `this` and `that` [[BitSet]]. + * + * @param that [[BitSet]] to be intersected. + * @return a [[BitSet]] containing all elements of `this` that also belong to `that`. + */ + def intersect(that: BitSet): BitSet = + terms + .flatMap(a => that.terms.map(b => a.intersect(b))) + .filterNot(_.isEmpty) + .fold(BitSet.empty)(_.union(_)) + + /** Subtract that from this [[BitSet]]. + * + * @param that subtrahend [[BitSet]]. + * @return a [[BitSet]] containing elements of `this` which are not the elements of `that`. + */ + def subtract(that: BitSet): BitSet = + terms.map { a => + that.terms.map(b => a.subtract(b)).fold(a)(_.intersect(_)) + }.filterNot(_.isEmpty).fold(BitSet.empty)(_.union(_)) + + /** Union this and that [[BitSet]] + * + * @param that [[BitSet]] to union. + * @return a [[BitSet]] containing all elements of `this` and `that`. + */ + def union(that: BitSet): BitSet = new BitSet { + def terms = outer.terms ++ that.terms + } + + /** Test whether two [[BitSet]] matches the same set of value + * + * @note + * This method can be very expensive compared to ordinary == operator between two Objects + * + * @return true if two [[BitSet]] is same. + */ + override def equals(obj: Any): Boolean = { + obj match { + case that: BitSet => this.getWidth == that.getWidth && this.cover(that) && that.cover(this) + case _ => false + } + } + } + +} + + /** Bit patterns are literals with masks, used to represent values with don't * care bits. Equality comparisons will ignore don't care bits. * @@ -120,19 +239,19 @@ object BitPat { * "b10001".U === BitPat("b101??") // evaluates to false.B * }}} */ -sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends SourceInfoDoc { - def getWidth: Int = width +sealed class BitPat(val value: BigInt, val mask: BigInt, val width: Int) extends util.experimental.BitSet with SourceInfoDoc { + import chisel3.util.experimental.BitSet + def terms = Set(this) + + /** + * Get specified width of said BitPat + */ + override 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 = { @@ -161,14 +280,83 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou 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 => + /** Check whether this [[BitPat]] overlap with that [[BitPat]], i.e. !(intersect.isEmpty) + * + * @param that [[BitPat]] to be checked. + * @return true if this and that [[BitPat]] have overlap. + */ + def overlap(that: BitPat): Boolean = ((mask & that.mask) & (value ^ that.value)) == 0 + + /** Check whether this [[BitSet]] covers that (i.e. forall b matches that, b also matches this) + * + * @param that [[BitPat]] to be covered + * @return true if this [[BitSet]] can cover that [[BitSet]] + */ + def cover(that: BitPat): Boolean = (mask & (~that.mask | (value ^ that.value))) == 0 + + /** Intersect `this` and `that` [[BitPat]]. + * + * @param that [[BitPat]] to be intersected. + * @return a [[BitSet]] containing all elements of `this` that also belong to `that`. + */ + def intersect(that: BitPat): BitSet = { + if (!overlap(that)) { + BitSet.empty + } else { + new BitPat(this.value | that.value, this.mask | that.mask, this.width.max(that.width)) + } + } + + /** Subtract a [[BitPat]] from this. + * + * @param that subtrahend [[BitPat]]. + * @return a [[BitSet]] containing elements of `this` which are not the elements of `that`. + */ + def subtract(that: BitPat): BitSet = { + require(width == that.width) + def enumerateBits(mask: BigInt): Seq[BigInt] = { + if (mask == 0) { + Nil + } else { + // bits comes after the first '1' in a number are inverted in its two's complement. + // therefore bit is always the first '1' in x (counting from least significant bit). + val bit = mask & (-mask) + bit +: enumerateBits(mask & ~bit) + } + } + + val intersection = intersect(that) + val omask = this.mask + if (intersection.isEmpty) { + this + } else { + new BitSet { + val terms = + intersection.terms.flatMap { remove => + enumerateBits(~omask & remove.mask).map { bit => + // Only care about higher than current bit in remove + val nmask = (omask | ~(bit - 1)) & remove.mask + val nvalue = (remove.value ^ bit) & nmask + val nwidth = remove.width + new BitPat(nvalue, nmask, nwidth) + } + } + } + } + } + + override def isEmpty: Boolean = false + + /** 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) => "?" + case (true, true) => "1" + case (false, true) => "0" + case (_, false) => "?" + } } - }.mkString + .mkString override def toString = s"BitPat($rawString)" } diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 8909ffe3..4b8b3eeb 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -16,6 +16,7 @@ import chisel3.internal.naming._ // can't use chisel3_ version because of compi * while the consumer uses the flipped interface (inputs bits). * The actual semantics of ready/valid are enforced via the use of concrete subclasses. * @param gen the type of data to be wrapped in Ready/Valid + * @groupdesc Signals The actual hardware fields of the Bundle */ abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle { @@ -26,8 +27,19 @@ abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle case _ => gen } +/** Indicates that the consumer is ready to accept the data this cycle + * @group Signals + */ val ready = Input(Bool()) + +/** Indicates that the producer has put valid data in 'bits' + * @group Signals + */ val valid = Output(Bool()) + +/** The data to be transferred when ready and valid are asserted at the same cycle + * @group Signals + */ val bits = Output(genType) } @@ -37,7 +49,10 @@ object ReadyValidIO { /** Indicates if IO is both ready and valid */ - def fire(): Bool = target.ready && target.valid + def fire: Bool = target.ready && target.valid + + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def fire(dummy: Int = 0): Bool = fire /** Push dat onto the output bits of this interface to let the consumer know it has happened. * @param dat the values to assign to bits. @@ -82,9 +97,6 @@ object ReadyValidIO { * @param gen the type of data to be wrapped in DecoupledIO */ class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen) -{ - override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type] -} /** This factory adds a decoupled handshaking protocol to a data bundle. */ object Decoupled @@ -121,11 +133,9 @@ object Decoupled * Additionally, once 'valid' is raised it will never be lowered until after * 'ready' has also been raised. * @param gen the type of data to be wrapped in IrrevocableIO + * @groupdesc Signals The actual hardware fields of the Bundle */ class IrrevocableIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen) -{ - override def cloneType: this.type = new IrrevocableIO(gen).asInstanceOf[this.type] -} /** Factory adds an irrevocable handshaking protocol to a data bundle. */ object Irrevocable @@ -164,6 +174,7 @@ object DeqIO { * @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 + * @groupdesc Signals The hardware fields of the 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. @@ -172,13 +183,21 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boo * but internally, the queue implementation itself sits on the other side * of the interface so uses the flipped instance. */ - /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped. */ + /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped. + * @group Signals + */ val enq = Flipped(EnqIO(gen)) - /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]*/ + /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]] + * @group Signals + */ val deq = Flipped(DeqIO(gen)) - /** The current amount of data in the queue */ + /** The current amount of data in the queue + * @group Signals + */ 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)*/ + /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue) + * @group Signals + */ val flush = if (hasFlush) Some(Input(Bool())) else None } @@ -228,9 +247,9 @@ class Queue[T <: Data](val gen: T, 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) + 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. @@ -288,20 +307,26 @@ class Queue[T <: Data](val gen: T, } } -/** Factory for a generic hardware queue. - * - * @param enq input (enqueue) interface to the queue, also determines width of queue elements - * @param entries depth (number of elements) of the queue - * - * @return output (dequeue) interface from the queue - * - * @example {{{ - * consumer.io.in <> Queue(producer.io.out, 16) - * }}} - */ +/** Factory for a generic hardware queue. */ object Queue { - /** Create a queue and supply a DecoupledIO containing the product. */ + /** Create a [[Queue]] and supply a [[DecoupledIO]] containing the product. + * + * @param enq input (enqueue) interface to the queue, also determines type of queue elements. + * @param entries depth (number of elements) of the queue + * @param pipe True if a single entry queue can run at full throughput (like a pipeline). The `ready` signals are + * 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 flush Optional [[Bool]] signal, if defined, the [[Queue.hasFlush]] will be true, and connect correspond + * signal to [[Queue]] instance. + * @return output (dequeue) interface from the queue. + * + * @example {{{ + * consumer.io.in <> Queue(producer.io.out, 16) + * }}} + */ @chiselName def apply[T <: Data]( enq: ReadyValidIO[T], @@ -309,7 +334,7 @@ object Queue pipe: Boolean = false, flow: Boolean = false, useSyncReadMem: Boolean = false, - hasFlush: Boolean = false): DecoupledIO[T] = { + flush: Option[Bool] = None): DecoupledIO[T] = { if (entries == 0) { val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits))) deq.valid := enq.valid @@ -317,7 +342,8 @@ object Queue enq.ready := deq.ready deq } else { - val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, hasFlush)) + val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, flush.isDefined)) + q.io.flush.zip(flush).foreach(f => f._1 := f._2) 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 @@ -325,10 +351,25 @@ object Queue } } - /** Create a queue and supply a IrrevocableIO containing the product. - * Casting from Decoupled is safe here because we know the Queue has - * Irrevocable semantics; we didn't want to change the return type of - * apply() for backwards compatibility reasons. + /** Create a queue and supply a [[IrrevocableIO]] containing the product. + * Casting from [[DecoupledIO]] is safe here because we know the [[Queue]] has + * Irrevocable semantics. + * we didn't want to change the return type of apply() for backwards compatibility reasons. + * + * @param enq [[DecoupledIO]] signal to enqueue. + * @param entries The max number of entries in the queue + * @param pipe True if a single entry queue can run at full throughput (like a pipeline). The ''ready'' signals are + * 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 flush Optional [[Bool]] signal, if defined, the [[Queue.hasFlush]] will be true, and connect correspond + * signal to [[Queue]] instance. + * @return a [[DecoupledIO]] signal which should connect to the dequeue signal. + * + * @example {{{ + * consumer.io.in <> Queue(producer.io.out, 16) + * }}} */ @chiselName def irrevocable[T <: Data]( @@ -336,8 +377,9 @@ object Queue entries: Int = 2, pipe: Boolean = false, flow: Boolean = false, - useSyncReadMem: Boolean = false): IrrevocableIO[T] = { - val deq = apply(enq, entries, pipe, flow, useSyncReadMem) + useSyncReadMem: Boolean = false, + flush: Option[Bool] = None): IrrevocableIO[T] = { + val deq = apply(enq, entries, pipe, flow, useSyncReadMem, flush) 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/Enum.scala b/src/main/scala/chisel3/util/Enum.scala index bf150464..4501a2de 100644 --- a/src/main/scala/chisel3/util/Enum.scala +++ b/src/main/scala/chisel3/util/Enum.scala @@ -6,7 +6,6 @@ package chisel3.util import chisel3._ -import chisel3.internal.chiselRuntimeDeprecated /** Defines a set of unique UInt constants * diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala index 6c6d685e..5d80502a 100644 --- a/src/main/scala/chisel3/util/Valid.scala +++ b/src/main/scala/chisel3/util/Valid.scala @@ -17,20 +17,26 @@ import chisel3._ * @tparam T the type of the data * @param gen some data * @see [[Valid$ Valid factory]] for concrete examples + * @groupdesc Signals The actual hardware fields of the Bundle */ class Valid[+T <: Data](gen: T) extends Bundle { - /** A bit that will be asserted when `bits` is valid */ + /** A bit that will be asserted when `bits` is valid + * @group Signals + */ val valid = Output(Bool()) - /** Some data */ + /** The data to be transferred, qualified by `valid` + * @group Signals + */ val bits = Output(gen) /** True when `valid` is asserted * @return a Chisel [[Bool]] true if `valid` is asserted */ - def fire(dummy: Int = 0): Bool = valid + def fire: Bool = valid - override def cloneType: this.type = Valid(gen).asInstanceOf[this.type] + @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5") + def fire(dummy: Int = 0): Bool = valid } /** Factory for generating "valid" interfaces. A "valid" interface is a data-communicating interface between a producer @@ -172,13 +178,18 @@ class Pipe[T <: Data](val gen: T, val latency: Int = 1)(implicit compileOptions: /** Interface for [[Pipe]]s composed of a [[Valid]] input and [[Valid]] output * @define notAQueue + * @groupdesc Signals Hardware fields of the Bundle */ class PipeIO extends Bundle { - /** [[Valid]] input */ + /** [[Valid]] input + * @group Signals + */ val enq = Input(Valid(gen)) - /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`. */ + /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`. + * @group Signals + */ val deq = Output(Valid(gen)) } diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 1d725875..4dcea99e 100644 --- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala @@ -7,11 +7,21 @@ import logger.LazyLogging case object EspressoNotFoundException extends Exception +/** A [[Minimizer]] implementation to use espresso to minimize the [[TruthTable]]. + * + * espresso uses heuristic algorithm providing a sub-optimized) result. + * For implementation details, please refer to: + * [[https://www.springerprofessional.de/en/logic-minimization-algorithms-for-vlsi-synthesis/13780088]] + * + * a espresso executable should be downloaded from [[https://github.com/chipsalliance/espresso]] + * + * If user want to user the this [[Minimizer]], a espresso executable should be added to system PATH environment. + */ 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 = { + private def espresso(table: TruthTable): TruthTable = { def writeTable(table: TruthTable): String = { def invert(string: String) = string .replace('0', 't') @@ -45,7 +55,7 @@ object EspressoMinimizer extends Minimizer with LazyLogging { |""".stripMargin ++ (if (defaultType == '1') invertRawTable else rawTable) } - def readTable(espressoTable: String): Map[BitPat, BitPat] = { + def readTable(espressoTable: String) = { def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?')) espressoTable @@ -53,7 +63,6 @@ object EspressoMinimizer extends Minimizer with LazyLogging { .filterNot(_.startsWith(".")) .map(_.split(' ')) .map(row => bitPat(row(0)) -> bitPat(row(1))) - .toMap } val input = writeTable(table) diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala index c1533f44..59120221 100644 --- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -8,6 +8,15 @@ import scala.annotation.tailrec import scala.math.Ordered.orderingToOrdered import scala.language.implicitConversions +/** A [[Minimizer]] implementation to use Quine-Mccluskey algorithm to minimize the [[TruthTable]]. + * + * This algorithm can always find the best solution, but is a NP-Complete algorithm, + * which means, for large-scale [[TruthTable]] minimization task, it will be really slow, + * and might run out of memory of JVM stack. + * + * In this situation, users should consider switch to [[EspressoMinimizer]], + * which uses heuristic algorithm providing a sub-optimized result. + */ object QMCMinimizer extends Minimizer { private implicit def toImplicant(x: BitPat): Implicant = new Implicant(x) @@ -225,11 +234,11 @@ object QMCMinimizer extends Minimizer { 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 + val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.map(_._1).map(toImplicant) // 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 + val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.map(_._1).map(toImplicant) // 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 dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.map(_._1).map(toImplicant) val (implicants, defaultToDc) = table.default match { case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0 @@ -282,7 +291,7 @@ object QMCMinimizer extends Minimizer { (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp)) }) - minimized.tail.foldLeft(table.copy(table = Map(minimized.head))) { case (tb, t) => + minimized.tail.foldLeft(table.copy(table = Seq(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) { @@ -291,7 +300,7 @@ object QMCMinimizer extends Minimizer { } else (k, v) }) } else { - tb.copy(table = tb.table + t) + tb.copy(table = tb.table :+ t) } } } diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 683de16b..322466f9 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -4,8 +4,7 @@ package chisel3.util.experimental.decode import chisel3.util.BitPat -final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { - +sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default: BitPat, val sort: Boolean) { def inputWidth = table.head._1.getWidth def outputWidth = table.head._2.getWidth @@ -14,10 +13,10 @@ final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { 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") + (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).mkString("\n") } - def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default) + def copy(table: Seq[(BitPat, BitPat)] = this.table, default: BitPat = this.default, sort: Boolean = this.sort) = TruthTable(table, default, sort) override def equals(y: Any): Boolean = { y match { @@ -28,25 +27,12 @@ final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { } 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 = { + def apply(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): 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) => + val mergedTable = table.groupBy(_._1.toString).map { case (key, values) => // merge same input inputs. values.head._1 -> BitPat(s"b${ Seq.tabulate(outputWidth) { i => @@ -59,9 +45,23 @@ object TruthTable { outputSet.headOption.getOrElse('?') }.mkString }") - }, default) + }.toSeq + import BitPat.bitPatOrder + new TruthTable(if(sort) mergedTable.sorted else mergedTable, default, sort) } + /** Parse TruthTable from its string representation. */ + def fromString(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(" ", "")}") + ) + } /** consume 1 table, split it into up to 3 tables with the same default bits. * @@ -99,18 +99,17 @@ object TruthTable { 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) + table.table.map(a => a._1.toString -> a._2).collectFirst{ case (k, v) if k == bitPat.toString => v}.getOrElse(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) + .flatMap(_._1.table.map(_._1)) .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 index 42e374d1..e0bf83b2 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -5,7 +5,7 @@ package chisel3.util.experimental.decode import chisel3._ import chisel3.experimental.{ChiselAnnotation, annotate} import chisel3.util.{BitPat, pla} -import chisel3.util.experimental.getAnnotations +import chisel3.util.experimental.{BitSet, getAnnotations} import firrtl.annotations.Annotation import logger.LazyLogging @@ -19,7 +19,7 @@ object decoder extends LazyLogging { */ def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = { val minimizedTable = getAnnotations().collect { - case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out) + case DecodeTableAnnotation(_, in, out) => TruthTable.fromString(in) -> TruthTable.fromString(out) }.toMap.getOrElse(truthTable, minimizer.minimize(truthTable)) if (minimizedTable.table.isEmpty) { val outputs = Wire(UInt(minimizedTable.default.getWidth.W)) @@ -80,4 +80,35 @@ object decoder extends LazyLogging { qmcFallBack(input, truthTable) } } + + + /** Generate a decoder circuit that matches the input to each bitSet. + * + * The resulting circuit functions like the following but is optimized with a logic minifier. + * {{{ + * when(input === bitSets(0)) { output := b000001 } + * .elsewhen (input === bitSets(1)) { output := b000010 } + * .... + * .otherwise { if (errorBit) output := b100000 else output := DontCare } + * }}} + * + * @param input input to the decoder circuit, width should be equal to bitSets.width + * @param bitSets set of ports to be matched, all width should be the equal + * @param errorBit whether generate an additional decode error bit at MSB of output. + * @return decoded wire + */ + def bitset(input: chisel3.UInt, bitSets: Seq[BitSet], errorBit: Boolean = false): chisel3.UInt = + chisel3.util.experimental.decode.decoder( + input, + chisel3.util.experimental.decode.TruthTable.fromString( + { + bitSets.zipWithIndex.flatMap { + case (bs, i) => + bs.terms.map(bp => + s"${bp.rawString}->${if (errorBit) "0"}${"0" * (bitSets.size - i - 1)}1${"0" * i}" + ) + } ++ Seq(s"${if (errorBit) "1"}${"?" * bitSets.size}") + }.mkString("\n") + ) + ) } diff --git a/src/main/scala/chisel3/util/random/GaloisLFSR.scala b/src/main/scala/chisel3/util/random/GaloisLFSR.scala index 0d407c87..68346e82 100644 --- a/src/main/scala/chisel3/util/random/GaloisLFSR.scala +++ b/src/main/scala/chisel3/util/random/GaloisLFSR.scala @@ -13,7 +13,7 @@ import chisel3._ * * $seedExplanation * - * In the example below, a 4-bit LFSR Fibonacci LFSR is constructed. The tap points are defined as four and three + * In the example below, a 4-bit LFSR Galois LFSR is constructed. The tap points are defined as four and three * (using LFSR convention of indexing from one). This results in the hardware configuration shown in the diagram. * * {{{ @@ -85,7 +85,7 @@ class MaxPeriodGaloisLFSR(width: Int, seed: Option[BigInt] = Some(1), reduction: */ object GaloisLFSR { - /** Return a pseudorandom [[UInt]] generated from a [[FibonacciLFSR]]. + /** Return a pseudorandom [[UInt]] generated from a [[GaloisLFSR]]. * $paramWidth * $paramTaps * $paramIncrement diff --git a/src/main/scala/chisel3/util/random/PRNG.scala b/src/main/scala/chisel3/util/random/PRNG.scala index d94b78e8..3a44385a 100644 --- a/src/main/scala/chisel3/util/random/PRNG.scala +++ b/src/main/scala/chisel3/util/random/PRNG.scala @@ -7,16 +7,23 @@ import chisel3.util.Valid /** Pseudo Random Number Generators (PRNG) interface * @param n the width of the LFSR + * @groupdesc Signals The actual hardware fields of the Bundle */ class PRNGIO(val n: Int) extends Bundle { - /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state) */ + /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state) + * @group Signals + */ val seed: Valid[Vec[Bool]] = Input(Valid(Vec(n, Bool()))) - /** When asserted, the PRNG will increment by one */ + /** When asserted, the PRNG will increment by one + * @group Signals + */ val increment: Bool = Input(Bool()) - /** The current state of the PRNG */ + /** The current state of the PRNG + * @group Signals + */ val out: Vec[Bool] = Output(Vec(n, Bool())) } @@ -62,7 +69,7 @@ abstract class PRNG(val width: Int, val seed: Option[BigInt], step: Int = 1, upd state := nextState(state) } - when (io.seed.fire()) { + when (io.seed.fire) { state := (if (updateSeed) { nextState(io.seed.bits) } else { io.seed.bits }) } diff --git a/src/test/scala/chisel3/testers/TestUtils.scala b/src/test/scala/chisel3/testers/TestUtils.scala index c72c779a..338f9cd4 100644 --- a/src/test/scala/chisel3/testers/TestUtils.scala +++ b/src/test/scala/chisel3/testers/TestUtils.scala @@ -12,13 +12,4 @@ 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/AutoClonetypeSpec.scala b/src/test/scala/chiselTests/AutoClonetypeSpec.scala index fcbc4785..ef58f1ed 100644 --- a/src/test/scala/chiselTests/AutoClonetypeSpec.scala +++ b/src/test/scala/chiselTests/AutoClonetypeSpec.scala @@ -5,6 +5,7 @@ package chiselTests import chisel3._ import chisel3.testers.TestUtils import chisel3.util.QueueIO +import chisel3.stage.ChiselStage.elaborate class BundleWithIntArg(val i: Int) extends Bundle { val out = UInt(i.W) @@ -71,14 +72,11 @@ 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 { elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) val myWire = Wire(new BundleWithIntArg(8)) assert(myWire.i == 8) @@ -87,7 +85,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Bundles with Scala implicit args" should "not need clonetype" in { elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) implicit val implicitInt: Int = 4 val myWire = Wire(new BundleWithImplicit()) @@ -98,7 +96,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Bundles with Scala explicit and impicit args" should "not need clonetype" in { elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) implicit val implicitInt: Int = 4 val myWire = Wire(new BundleWithArgAndImplicit(8)) @@ -110,7 +108,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Subtyped Bundles" should "not need clonetype" in { elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) val myWire = Wire(new SubBundle(8, 4)) @@ -118,7 +116,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { assert(myWire.i2 == 4) } } elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) val myWire = Wire(new SubBundleVal(8, 4)) @@ -131,23 +129,12 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { new BundleWithIntArg(8).cloneType } - def checkSubBundleInvalid() = { + "Subtyped Bundles that don't clone well" should "be now be supported!" in { elaborate { new Module { - val io = IO(new Bundle{}).suggestName("io") + val io = IO(new Bundle{}) 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 { elaborate { new ModuleWithInner } @@ -155,7 +142,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { "Bundles with arguments as fields" should "not need clonetype" in { elaborate { new Module { - val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))).suggestName("io") + val io = IO(Output(new BundleWithArgumentField(UInt(8.W), UInt(8.W)))) io.x := 1.U io.y := 1.U } } @@ -163,28 +150,28 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { 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") + val io = IO(new BundleWithArgumentField(Input(UInt(8.W)), Output(UInt(8.W)))) io.y := io.x } } } "Bundles inside companion objects" should "not need clonetype" in { elaborate { new Module { - val io = IO(Output(new CompanionObjectWithBundle.Inner)).suggestName("io") + val io = IO(Output(new CompanionObjectWithBundle.Inner)) io.data := 1.U } } } "Parameterized bundles inside companion objects" should "not need clonetype" in { elaborate { new Module { - val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))).suggestName("io") + val io = IO(Output(new CompanionObjectWithBundle.ParameterizedInner(8))) io.data := 1.U } } } "Nested directioned anonymous Bundles" should "not need clonetype" in { elaborate { new Module { - val io = IO(new NestedAnonymousBundle).suggestName("io") + val io = IO(new NestedAnonymousBundle) val a = WireDefault(io) io.a.a := 1.U } } @@ -197,7 +184,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val a = Output(UInt(8.W)) } } - val io = IO((new InnerClassThing).createBundle).suggestName("io") + val io = IO((new InnerClassThing).createBundle) val a = WireDefault(io) } } } @@ -208,7 +195,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { val bundleFieldType = UInt(8.W) val io = IO(Output(new Bundle { val a = bundleFieldType - })).suggestName("io") + })) io.a := 0.U } } } @@ -221,7 +208,7 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { } elaborate { new Module { - val io = IO(Output(new BadBundle(UInt(8.W), 1))).suggestName("io") + val io = IO(Output(new BadBundle(UInt(8.W), 1))) io.a := 0.U } } } @@ -265,101 +252,97 @@ class AutoClonetypeSpec extends ChiselFlatSpec with Utils { 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 "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 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 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 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 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 implement their own cloneType" 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 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 + 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) } - elaborate(new MyModule(UInt(8.W))) + 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) + 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 401766e2..258d0823 100644 --- a/src/test/scala/chiselTests/AutoNestedCloneSpec.scala +++ b/src/test/scala/chiselTests/AutoNestedCloneSpec.scala @@ -3,6 +3,7 @@ package chiselTests import chisel3._ import chisel3.testers.TestUtils +import chisel3.stage.ChiselStage.elaborate import org.scalatest.matchers.should.Matchers class BundleWithAnonymousInner(val w: Int) extends Bundle { @@ -11,10 +12,7 @@ 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" @@ -27,7 +25,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } def getIO: InnerIOType = new InnerIOType } - val io = IO(new Bundle {}).suggestName("io") + val io = IO(new Bundle {}) val myWire = Wire((new Middle(w)).getIO) } new Outer(2) @@ -37,7 +35,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { it should "clone an anonymous inner bundle successfully" in { elaborate { class TestTop(val w: Int) extends Module { - val io = IO(new Bundle {}).suggestName("io") + val io = IO(new Bundle {}) val myWire = Wire(new Bundle{ val a = UInt(w.W) }) } new TestTop(2) @@ -50,13 +48,13 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { 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 @@ -69,7 +67,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { it should "clone an anonymous, bound, inner bundle of another bundle successfully" in { elaborate { class TestModule(w: Int) extends Module { - val io = IO(new BundleWithAnonymousInner(w) ).suggestName("io") + val io = IO(new BundleWithAnonymousInner(w)) val w0 = WireDefault(io) val w1 = WireDefault(io.inner) } @@ -85,7 +83,7 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { } val io = IO(new Bundle { val inner = Input(bun) - }).suggestName("io") + }) val w0 = WireDefault(io) val w1 = WireDefault(io.inner) } @@ -100,42 +98,39 @@ class AutoNestedCloneSpec extends ChiselFlatSpec with Matchers with Utils { val inner = Input(new Bundle { val x = UInt(8.W) }) - }).suggestName("io") + }) } new TestModule() } } - 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)) - } + 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) } - new Outer(2) + val io = IO(new Bundle {}) + val myWire = Wire((new Middle(w)).getIO) } + new Outer(2) } + } - 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) + 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 io = IO(new MyBundle(4, UInt(8.W))) - val myWire = WireInit(io.foo) - val myWire2 = WireInit(io.bar) - io.bar.x := io.foo.x - }) - } + 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 a9a6fa29..2fa3d8a6 100644 --- a/src/test/scala/chiselTests/BlackBoxImpl.scala +++ b/src/test/scala/chiselTests/BlackBoxImpl.scala @@ -7,7 +7,6 @@ import java.io.File 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 diff --git a/src/test/scala/chiselTests/BundleSpec.scala b/src/test/scala/chiselTests/BundleSpec.scala index 1d392f5c..d9f82e6d 100644 --- a/src/test/scala/chiselTests/BundleSpec.scala +++ b/src/test/scala/chiselTests/BundleSpec.scala @@ -10,25 +10,20 @@ trait BundleSpecUtils { class BundleFooBar extends Bundle { val foo = UInt(16.W) val bar = UInt(16.W) - override def cloneType: this.type = (new BundleFooBar).asInstanceOf[this.type] } class BundleBarFoo extends Bundle { val bar = UInt(16.W) val foo = UInt(16.W) - override def cloneType: this.type = (new BundleBarFoo).asInstanceOf[this.type] } class BundleFoo extends Bundle { val foo = UInt(16.W) - override def cloneType: this.type = (new BundleFoo).asInstanceOf[this.type] } class BundleBar extends Bundle { val bar = UInt(16.W) - override def cloneType: this.type = (new BundleBar).asInstanceOf[this.type] } class BadSeqBundle extends Bundle { val bar = Seq(UInt(16.W), UInt(8.W), UInt(4.W)) - override def cloneType: this.type = (new BadSeqBundle).asInstanceOf[this.type] } class MyModule(output: Bundle, input: Bundle) extends Module { @@ -116,17 +111,21 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils { } } - "Bundles" should "not have aliased fields" in { + "Bundles" should "with aliased fields, should show a helpful error message" in { + class AliasedBundle extends Bundle { + val a = UInt(8.W) + val b = a + val c = SInt(8.W) + val d = c + } + (the[ChiselException] thrownBy extractCause[ChiselException] { ChiselStage.elaborate { new Module { - val io = IO(Output(new Bundle { - val a = UInt(8.W) - val b = a - })) + val io = IO(Output(new AliasedBundle)) io.a := 0.U io.b := 1.U } } - }).getMessage should include("aliased fields") + }).getMessage should include("contains aliased fields named (a,b),(c,d)") } "Bundles" should "not have bound hardware" in { @@ -162,4 +161,16 @@ class BundleSpec extends ChiselFlatSpec with BundleSpecUtils with Utils { } } } + + // This tests the interaction of override def cloneType and the plugin. + // We are commenting it for now because although this code fails to compile + // as expected when just copied here, the test version is not seeing the failure. + // """ + // class BundleBaz(w: Int) extends Bundle { + // val baz = UInt(w.W) + // // This is a compiler error when using the plugin, which we test below. + // override def cloneType = (new BundleBaz(w)).asInstanceOf[this.type] + // } + // """ shouldNot compile + } diff --git a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala index 1795cc1f..4b03dfa5 100644 --- a/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilityInteroperabilitySpec.scala @@ -12,8 +12,6 @@ object CompatibilityComponents { class ChiselBundle extends Bundle { val a = UInt(width = 32) val b = UInt(width = 32).flip - - override def cloneType: this.type = (new ChiselBundle).asInstanceOf[this.type] } class ChiselRecord extends Record { val elements = ListMap("a" -> UInt(width = 32), "b" -> UInt(width = 32).flip) @@ -48,8 +46,6 @@ object Chisel3Components { class Chisel3Bundle extends Bundle { val a = Output(UInt(32.W)) val b = Input(UInt(32.W)) - - override def cloneType: this.type = (new Chisel3Bundle).asInstanceOf[this.type] } class Chisel3Record extends Record { @@ -341,7 +337,6 @@ class CompatibiltyInteroperabilitySpec extends ChiselFlatSpec { val foo = maybeFlip(new Bundle { val bar = UInt(INPUT, width = 8) }) - override def cloneType = (new MyBundle(extraFlip)).asInstanceOf[this.type] } } import chisel3._ diff --git a/src/test/scala/chiselTests/CompatibilitySpec.scala b/src/test/scala/chiselTests/CompatibilitySpec.scala index 2d4ad517..7ac67b7c 100644 --- a/src/test/scala/chiselTests/CompatibilitySpec.scala +++ b/src/test/scala/chiselTests/CompatibilitySpec.scala @@ -195,11 +195,9 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck class SmallBundle extends Bundle { val f1 = UInt(width = 4) val f2 = UInt(width = 5) - override def cloneType: this.type = (new SmallBundle).asInstanceOf[this.type] } class BigBundle extends SmallBundle { val f3 = UInt(width = 6) - override def cloneType: this.type = (new BigBundle).asInstanceOf[this.type] } "A Module with missing bundle fields when compiled with the Chisel compatibility package" should "not throw an exception" in { @@ -538,6 +536,9 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck info("toUInt works") s.toUInt shouldBe a [UInt] + + info("toBools works") + s.toBools shouldBe a [Seq[Bool]] } ChiselStage.elaborate(new Foo) @@ -566,4 +567,32 @@ class CompatibiltySpec extends ChiselFlatSpec with ScalaCheckDrivenPropertyCheck verilog should include ("assign io_out_0 = io_in_0;") } + it should "ignore .suggestName on field io" in { + class MyModule extends Module { + val io = new Bundle { + val foo = UInt(width = 8).asInput + val bar = UInt(width = 8).asOutput + } + io.suggestName("potato") + io.bar := io.foo + } + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("input [7:0] io_foo") + verilog should include ("output [7:0] io_bar") + } + + it should "properly name field io" in { + class MyModule extends Module { + val io = new Bundle { + val foo = UInt(width = 8).asInput + val bar = UInt(width = 8).asOutput + } + val wire = Wire(init = io.foo) + io.bar := wire + } + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("input [7:0] io_foo") + verilog should include ("output [7:0] io_bar") + } + } diff --git a/src/test/scala/chiselTests/CompileOptionsTest.scala b/src/test/scala/chiselTests/CompileOptionsTest.scala index 9c88c1e0..1ecf97f0 100644 --- a/src/test/scala/chiselTests/CompileOptionsTest.scala +++ b/src/test/scala/chiselTests/CompileOptionsTest.scala @@ -14,11 +14,9 @@ class CompileOptionsSpec extends ChiselFlatSpec with Utils { class SmallBundle extends Bundle { val f1 = UInt(4.W) val f2 = UInt(5.W) - override def cloneType: this.type = (new SmallBundle).asInstanceOf[this.type] } class BigBundle extends SmallBundle { val f3 = UInt(6.W) - override def cloneType: this.type = (new BigBundle).asInstanceOf[this.type] } "A Module with missing bundle fields when compiled with implicit Strict.CompileOption " should "throw an exception" in { diff --git a/src/test/scala/chiselTests/ComplexAssign.scala b/src/test/scala/chiselTests/ComplexAssign.scala index 36fb59c2..222b6373 100644 --- a/src/test/scala/chiselTests/ComplexAssign.scala +++ b/src/test/scala/chiselTests/ComplexAssign.scala @@ -7,10 +7,7 @@ import chisel3.testers.BasicTester import chisel3.util._ import org.scalacheck.Shrink -class Complex[T <: Data](val re: T, val im: T) extends Bundle { - override def cloneType: this.type = - new Complex(re.cloneType, im.cloneType).asInstanceOf[this.type] -} +class Complex[T <: Data](val re: T, val im: T) extends Bundle class ComplexAssign(w: Int) extends Module { val io = IO(new Bundle { diff --git a/src/test/scala/chiselTests/ConnectSpec.scala b/src/test/scala/chiselTests/ConnectSpec.scala index 367864e6..f9ef5946 100644 --- a/src/test/scala/chiselTests/ConnectSpec.scala +++ b/src/test/scala/chiselTests/ConnectSpec.scala @@ -2,6 +2,8 @@ package chiselTests +import org.scalatest._ + import chisel3._ import chisel3.experimental.{Analog, FixedPoint} import chisel3.stage.ChiselStage @@ -126,4 +128,37 @@ class ConnectSpec extends ChiselPropSpec with Utils { property("Pipe internal connections should succeed") { ChiselStage.elaborate( new PipeInternalWires) } + + property("Connect error messages should have meaningful information") { + class InnerExample extends Module { + val myReg = RegInit(0.U(8.W)) + } + + class OuterAssignExample extends Module { + val inner = Module(new InnerExample()) + inner.myReg := false.B // ERROR + } + + val assignError = the [ChiselException] thrownBy {ChiselStage.elaborate { new OuterAssignExample}} + val expectedAssignError = """.*@: myReg in InnerExample cannot be written from module OuterAssignExample.""" + assignError.getMessage should fullyMatch regex expectedAssignError + + class OuterReadExample extends Module { + val myReg = RegInit(0.U(8.W)) + val inner = Module(new InnerExample()) + myReg := inner.myReg // ERROR + } + + val readError = the [ChiselException] thrownBy {ChiselStage.elaborate { new OuterReadExample }} + val expectedReadError = """.*@: myReg in InnerExample cannot be read from module OuterReadExample.""" + readError.getMessage should fullyMatch regex expectedReadError + + val typeMismatchError = the [ChiselException] thrownBy {ChiselStage.elaborate { new RawModule { + val myUInt = Wire(UInt(4.W)) + val mySInt = Wire(SInt(4.W)) + myUInt := mySInt + }}} + val expectedTypeMismatchError = """.*@: Sink \(UInt<4>\) and Source \(SInt<4>\) have different types.""" + typeMismatchError.getMessage should fullyMatch regex expectedTypeMismatchError + } } diff --git a/src/test/scala/chiselTests/DataPrint.scala b/src/test/scala/chiselTests/DataPrint.scala index b5f96c4d..7fb790a8 100644 --- a/src/test/scala/chiselTests/DataPrint.scala +++ b/src/test/scala/chiselTests/DataPrint.scala @@ -20,6 +20,14 @@ class DataPrintSpec extends ChiselFlatSpec with Matchers { val b = Bool() } + class PartialBundleTest extends Bundle { + val a = UInt(8.W) + val b = Bool() + val c = SInt(8.W) + val e = FixedPoint(5.W, 3.BP) + val f = EnumTest.Type() + } + "Data types" should "have a meaningful string representation" in { ChiselStage.elaborate { new RawModule { UInt().toString should be ("UInt") @@ -31,18 +39,20 @@ class DataPrintSpec extends ChiselFlatSpec with Matchers { Vec(3, UInt(2.W)).toString should be ("UInt<2>[3]") EnumTest.Type().toString should be ("EnumTest") (new BundleTest).toString should be ("BundleTest") - } } + new Bundle { val a = UInt(8.W) }.toString should be ("AnonymousBundle") + new Bundle { val a = UInt(8.W) }.a.toString should be ("UInt<8>") + }} } class BoundDataModule extends Module { // not in the test to avoid anon naming suffixes - Wire(UInt()).toString should be("UInt(Wire in BoundDataModule)") - Reg(SInt()).toString should be("SInt(Reg in BoundDataModule)") + Wire(UInt()).toString should be("BoundDataModule.?: Wire[UInt]") + Reg(SInt()).toString should be("BoundDataModule.?: Reg[SInt]") val io = IO(Output(Bool())) // needs a name so elaboration doesn't fail - io.toString should be("Bool(IO in unelaborated BoundDataModule)") + io.toString should be("BoundDataModule.io: IO[Bool]") val m = Mem(4, UInt(2.W)) - m(2).toString should be("UInt<2>(MemPort in BoundDataModule)") - (2.U + 2.U).toString should be("UInt<2>(OpResult in BoundDataModule)") - Wire(Vec(3, UInt(2.W))).toString should be ("UInt<2>[3](Wire in BoundDataModule)") + m(2).toString should be("BoundDataModule.?: MemPort[UInt<2>]") + (2.U + 2.U).toString should be("BoundDataModule.?: OpResult[UInt<2>]") + Wire(Vec(3, UInt(2.W))).toString should be ("BoundDataModule.?: Wire[UInt<2>[3]]") class InnerModule extends Module { val io = IO(Output(new Bundle { @@ -50,8 +60,31 @@ class DataPrintSpec extends ChiselFlatSpec with Matchers { })) } val inner = Module(new InnerModule) - inner.clock.toString should be ("Clock(IO clock in InnerModule)") - inner.io.a.toString should be ("UInt<4>(IO io_a in InnerModule)") + inner.clock.toString should be ("InnerModule.clock: IO[Clock]") + inner.io.a.toString should be ("InnerModule.io.a: IO[UInt<4>]") + + class FooTypeTest extends Bundle { + val foo = Vec(2, UInt(8.W)) + val fizz = UInt(8.W) + } + val tpe = new FooTypeTest + val fooio: FooTypeTest = IO(Input(tpe)) + fooio.foo(0).toString should be ("BoundDataModule.fooio.foo[0]: IO[UInt<8>]") + + class NestedBundle extends Bundle { + val nestedFoo = UInt(8.W) + val nestedFooVec = Vec(2, UInt(8.W)) + } + class NestedType extends Bundle { + val foo = new NestedBundle + } + + val nestedTpe = new NestedType + val nestedio = IO(Input(nestedTpe)) + (nestedio.foo.nestedFoo.toString should be + ("BoundDataModule.nestedio.foo.nestedFoo: IO[UInt<8>]")) + (nestedio.foo.nestedFooVec(0).toString should be + ("BoundDataModule.nestedio.foo.nestedFooVec[0]: IO[UInt<8>]")) } "Bound data types" should "have a meaningful string representation" in { @@ -67,13 +100,12 @@ class DataPrintSpec extends ChiselFlatSpec with Matchers { true.B.toString should be ("Bool(true)") 2.25.F(6.W, 2.BP).toString should be ("FixedPoint<6><<2>>(2.25)") -2.25.F(6.W, 2.BP).toString should be ("FixedPoint<6><<2>>(-2.25)") + Vec(3, UInt(4.W)).toString should be ("UInt<4>[3]") EnumTest.sNone.toString should be ("EnumTest(0=sNone)") EnumTest.sTwo.toString should be ("EnumTest(2=sTwo)") EnumTest(1.U).toString should be ("EnumTest(1=sOne)") (new BundleTest).Lit(_.a -> 2.U, _.b -> false.B).toString should be ("BundleTest(a=UInt<8>(2), b=Bool(false))") - new Bundle { - val a = UInt(8.W) - }.toString should be ("AnonymousBundle") + (new PartialBundleTest).Lit().toString should be ("PartialBundleTest(a=UInt<8>(DontCare), b=Bool(DontCare), c=SInt<8>(DontCare), e=FixedPoint<5><<3>>(DontCare), f=EnumTest(DontCare))") DontCare.toString should be ("DontCare()") } } } diff --git a/src/test/scala/chiselTests/FixedPointSpec.scala b/src/test/scala/chiselTests/FixedPointSpec.scala index a1acdb17..2530bb13 100644 --- a/src/test/scala/chiselTests/FixedPointSpec.scala +++ b/src/test/scala/chiselTests/FixedPointSpec.scala @@ -60,6 +60,9 @@ class FixedPointFromBitsTester extends BasicTester { val negativefp = (-3.5).F(4.BP) val positivefp = 3.5.F(4.BP) + assert(- positivefp === negativefp) + assert(positivefp === -negativefp) + assert(uint2fp === uint_result) assert(sint2fp === sint_result) assert(fp2fp === fp_result) diff --git a/src/test/scala/chiselTests/Harness.scala b/src/test/scala/chiselTests/Harness.scala index 23379498..51576566 100644 --- a/src/test/scala/chiselTests/Harness.scala +++ b/src/test/scala/chiselTests/Harness.scala @@ -4,10 +4,7 @@ package chiselTests import java.io.File -import firrtl.util.BackendCompilationUtilities - -class HarnessSpec extends ChiselPropSpec - with BackendCompilationUtilities { +class HarnessSpec extends ChiselPropSpec { def makeTrivialVerilog: (File => File) = makeHarness((prefix: String) => s""" module ${prefix}; diff --git a/src/test/scala/chiselTests/InlineSpec.scala b/src/test/scala/chiselTests/InlineSpec.scala index 397eac2e..59a1e984 100644 --- a/src/test/scala/chiselTests/InlineSpec.scala +++ b/src/test/scala/chiselTests/InlineSpec.scala @@ -5,7 +5,6 @@ package chiselTests import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.experimental.{InlineInstance, FlattenInstance} -import firrtl.FirrtlExecutionSuccess import firrtl.passes.InlineAnnotation import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlStage} import firrtl.transforms.FlattenAnnotation diff --git a/src/test/scala/chiselTests/IntervalSpec.scala b/src/test/scala/chiselTests/IntervalSpec.scala index abc619e5..c223260d 100644 --- a/src/test/scala/chiselTests/IntervalSpec.scala +++ b/src/test/scala/chiselTests/IntervalSpec.scala @@ -15,7 +15,7 @@ import firrtl.passes.CheckTypes.InvalidConnect import firrtl.passes.CheckWidths.{DisjointSqueeze, InvalidRange} import firrtl.passes.{PassExceptions, WrapWithRemainder} import firrtl.stage.{CompilerAnnotation, FirrtlCircuitAnnotation} -import firrtl.{FIRRTLException, HighFirrtlCompiler, LowFirrtlCompiler, MiddleFirrtlCompiler, MinimumVerilogCompiler, NoneCompiler, SystemVerilogCompiler, VerilogCompiler} +import firrtl.{HighFirrtlCompiler, LowFirrtlCompiler, MiddleFirrtlCompiler, MinimumVerilogCompiler, NoneCompiler, SystemVerilogCompiler, VerilogCompiler} import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers @@ -406,6 +406,13 @@ class IntervalChainedSubTester extends BasicTester { assert(intervalResult1 === 5.I) assert(intervalResult2 === 5.I) + val negativeInterval = (-3.5).I(4.BP) + val positiveInterval = 3.5.I(4.BP) + + assert(negativeInterval =/= positiveInterval) + assert(-negativeInterval === positiveInterval) + assert(negativeInterval === -positiveInterval) + stop() } diff --git a/src/test/scala/chiselTests/InvalidateAPISpec.scala b/src/test/scala/chiselTests/InvalidateAPISpec.scala index b7db33cc..52ad02b4 100644 --- a/src/test/scala/chiselTests/InvalidateAPISpec.scala +++ b/src/test/scala/chiselTests/InvalidateAPISpec.scala @@ -105,7 +105,7 @@ class InvalidateAPISpec extends ChiselPropSpec with Matchers with BackendCompila ChiselStage.elaborate(new ModuleWithDontCareSink) } } - exception.getMessage should include("DontCare cannot be a connection sink (LHS)") + exception.getMessage should include("DontCare cannot be a connection sink") } property("a DontCare cannot be a connection sink (LHS) for <>") { diff --git a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala index 8a998496..74e587bc 100644 --- a/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala +++ b/src/test/scala/chiselTests/LoadMemoryFromFileSpec.scala @@ -8,7 +8,6 @@ import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.experimental.{loadMemoryFromFile,loadMemoryFromFileInline} import chisel3.util.log2Ceil -import firrtl.FirrtlExecutionSuccess import firrtl.annotations.MemoryLoadFileType import org.scalatest.freespec.AnyFreeSpec import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/chiselTests/Module.scala b/src/test/scala/chiselTests/Module.scala index 7703e876..f0d6dbe7 100644 --- a/src/test/scala/chiselTests/Module.scala +++ b/src/test/scala/chiselTests/Module.scala @@ -9,6 +9,7 @@ import firrtl.annotations.NoTargetAnnotation import firrtl.options.Unserializable import scala.io.Source +import scala.annotation.nowarn class SimpleIO extends Bundle { val in = Input(UInt(32.W)) @@ -167,6 +168,71 @@ class ModuleSpec extends ChiselPropSpec with Utils { "a" -> m.a, "b" -> m.b)) }) } + + property("DataMirror.modulePorts should replace deprecated <module>.getPorts") { + class MyModule extends Module { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(Vec(2, UInt(8.W))) + }) + val extra = IO(Input(UInt(8.W))) + val delay = RegNext(io.in) + io.out(0) := delay + io.out(1) := delay + extra + } + var mod: MyModule = null + ChiselStage.elaborate { + mod = new MyModule + mod + } + // Note that this is just top-level ports, Aggregates are not flattened + DataMirror.modulePorts(mod) should contain theSameElementsInOrderAs Seq( + "clock" -> mod.clock, + "reset" -> mod.reset, + "io" -> mod.io, + "extra" -> mod.extra + ) + // Delete this when the deprecated API is deleted + // Note this also uses deprecated Port + import chisel3.internal.firrtl.Port + import SpecifiedDirection.{Input => IN, Unspecified} + mod.getPorts should contain theSameElementsInOrderAs Seq( + Port(mod.clock, IN), + Port(mod.reset, IN), + Port(mod.io, Unspecified), + Port(mod.extra, IN) + ): @nowarn // delete when Port and getPorts become private + } + + property("DataMirror.fullModulePorts should return all ports including children of Aggregates") { + class MyModule extends Module { + val io = IO(new Bundle { + val in = Input(UInt(8.W)) + val out = Output(Vec(2, UInt(8.W))) + }) + val extra = IO(Input(UInt(8.W))) + val delay = RegNext(io.in) + io.out(0) := delay + io.out(1) := delay + extra + } + var mod: MyModule = null + ChiselStage.elaborate { + mod = new MyModule + mod + } + val expected = Seq( + "clock" -> mod.clock, + "reset" -> mod.reset, + "io" -> mod.io, + "io_out" -> mod.io.out, + "io_out_0" -> mod.io.out(0), + "io_out_1" -> mod.io.out(1), + "io_in" -> mod.io.in, + "extra" -> mod.extra + ) + DataMirror.fullModulePorts(mod) should contain theSameElementsInOrderAs expected + } + property("A desiredName parameterized by a submodule should work") { ChiselStage.elaborate(new ModuleWrapper(new ModuleWire)).name should be ("ModuleWireWrapper") } diff --git a/src/test/scala/chiselTests/PrintableSpec.scala b/src/test/scala/chiselTests/PrintableSpec.scala index 25b54966..c7e819ec 100644 --- a/src/test/scala/chiselTests/PrintableSpec.scala +++ b/src/test/scala/chiselTests/PrintableSpec.scala @@ -3,7 +3,7 @@ package chiselTests import chisel3._ -import chisel3.experimental.{BaseSim, ChiselAnnotation} +import chisel3.experimental.ChiselAnnotation import chisel3.stage.ChiselStage import chisel3.testers.BasicTester import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation} @@ -23,7 +23,7 @@ object PrintfAnnotation { /** Create annotation for a given [[printf]]. * @param c component to be annotated */ - def annotate(c: BaseSim): Unit = { + def annotate(c: VerificationStatement): Unit = { chisel3.experimental.annotate(new ChiselAnnotation { def toFirrtl: PrintfAnnotation = PrintfAnnotation(c.toTarget) }) @@ -139,7 +139,6 @@ class PrintableSpec extends AnyFlatSpec with Matchers { } class MyBundle extends Bundle { val foo = UInt(32.W) - override def cloneType: this.type = (new MyBundle).asInstanceOf[this.type] } class MyModule extends BasicTester { override def desiredName: String = "MyModule" @@ -246,7 +245,7 @@ class PrintableSpec extends AnyFlatSpec with Matchers { // 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>printf") exactly(1, annoLines) should include ("~PrintfAnnotationTest|PrintfAnnotationTest>howdy") // read in FIRRTL file @@ -256,7 +255,7 @@ class PrintableSpec extends AnyFlatSpec with Matchers { // 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"), "goodbye AnonymousBundle(foo -> %d, bar -> %d)", myBun.foo, myBun.bar) : printf""") 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 index 11a411a8..9e0c6bb4 100644 --- a/src/test/scala/chiselTests/QueueFlushSpec.scala +++ b/src/test/scala/chiselTests/QueueFlushSpec.scala @@ -40,11 +40,11 @@ abstract class FlushQueueTesterBase(elements: Seq[Int], queueDepth: Int, bitWidt q.io.deq.ready := LFSR(16)(tap) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() currQCnt := currQCnt + 1.U //counts how many items have been enqueued } - when(q.io.deq.fire()) { + 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 @@ -70,18 +70,18 @@ class QueueGetsFlushedTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, flush := LFSR(16)((tap + 3) % 16) //testing a flush when flush is called randomly val halfCnt = (queueDepth + 1)/2 - when(q.io.deq.fire()) { + 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)) + 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) + outCnt := outCnt + Mux(q.io.enq.fire, (currQCnt + 1.U), currQCnt) currQCnt := 0.U //resets the number of items currently inside queue } } @@ -102,7 +102,7 @@ class EmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidth: I 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()) { + when(q.io.deq.fire) { assert(elems(outCnt) === q.io.deq.bits) outCnt := outCnt + 1.U } @@ -124,7 +124,7 @@ class EnqueueEmptyFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitW 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()) { + 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 @@ -145,7 +145,7 @@ class FullQueueFlushEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWidt //testing a flush when queue is full flush := (currQCnt === queueDepth.U) - when(q.io.deq.fire()) { + 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) @@ -177,7 +177,7 @@ class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWi q.io.enq.valid := !flushRegister q.io.deq.ready := flush - when(q.io.deq.fire()) { + 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) @@ -191,7 +191,7 @@ class DequeueFullQueueEdgecaseTester (elements: Seq[Int], queueDepth: Int, bitWi } when(flushRegister) { //check that queue gets flushed when queue is full - assert(q.io.deq.fire() === false.B) + assert(q.io.deq.fire === false.B) } } diff --git a/src/test/scala/chiselTests/QueueSpec.scala b/src/test/scala/chiselTests/QueueSpec.scala index 51b899cb..9eb6c20c 100644 --- a/src/test/scala/chiselTests/QueueSpec.scala +++ b/src/test/scala/chiselTests/QueueSpec.scala @@ -21,10 +21,10 @@ class ThingsPassThroughTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int 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()) { + when(q.io.enq.fire) { inCnt.inc() } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { //ensure that what comes out is what comes in assert(elems(outCnt.value) === q.io.deq.bits) outCnt.inc() @@ -51,10 +51,10 @@ class QueueReasonableReadyValid(elements: Seq[Int], queueDepth: Int, bitWidth: I assert(q.io.deq.valid || q.io.count === 0.U) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { outCnt.inc() } when(outCnt.value === elements.length.U) { @@ -74,11 +74,11 @@ class CountIsCorrectTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, t q.io.deq.ready := LFSR(16)(tap) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() assert(q.io.count === (inCnt.value - outCnt.value)) } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { outCnt.inc() assert(q.io.count === (inCnt.value - outCnt.value)) } @@ -103,10 +103,10 @@ class QueueSinglePipeTester(elements: Seq[Int], bitWidth: Int, tap: Int, useSync assert(q.io.enq.ready || (q.io.count === 1.U && !q.io.deq.ready)) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { outCnt.inc() } @@ -129,10 +129,10 @@ class QueuePipeTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I assert(q.io.enq.ready || (q.io.count === queueDepth.U && !q.io.deq.ready)) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { outCnt.inc() } @@ -155,13 +155,13 @@ class QueueFlowTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap: I q.io.deq.ready := LFSR(16)(tap) //Queue should be empty or valid - assert(q.io.deq.valid || (q.io.count === 0.U && !q.io.enq.fire())) + assert(q.io.deq.valid || (q.io.count === 0.U && !q.io.enq.fire)) q.io.enq.bits := elems(inCnt.value) - when(q.io.enq.fire()) { + when(q.io.enq.fire) { inCnt.inc() } - when(q.io.deq.fire()) { + when(q.io.deq.fire) { outCnt.inc() } when(outCnt.value === elements.length.U) { @@ -183,10 +183,10 @@ class QueueFactoryTester(elements: Seq[Int], queueDepth: Int, bitWidth: Int, tap deq.ready := LFSR(16)(tap) enq.bits := elems(inCnt.value) - when(enq.fire()) { + when(enq.fire) { inCnt.inc() } - when(deq.fire()) { + when(deq.fire) { //ensure that what comes out is what comes in assert(elems(outCnt.value) === deq.bits) outCnt.inc() diff --git a/src/test/scala/chiselTests/RecordSpec.scala b/src/test/scala/chiselTests/RecordSpec.scala index c21d455c..e6986efb 100644 --- a/src/test/scala/chiselTests/RecordSpec.scala +++ b/src/test/scala/chiselTests/RecordSpec.scala @@ -8,11 +8,12 @@ import chisel3.testers.BasicTester import chisel3.util.{Counter, Queue} import chisel3.experimental.DataMirror +import scala.collection.immutable.SeqMap + trait RecordSpecUtils { class MyBundle extends Bundle { val foo = UInt(32.W) val bar = UInt(32.W) - override def cloneType: this.type = (new MyBundle).asInstanceOf[this.type] } // Useful for constructing types from CustomBundle // This is a def because each call to this needs to return a new instance @@ -65,6 +66,11 @@ trait RecordSpecUtils { } } + class AliasedRecord extends Module { + val field = UInt(32.W) + val io = IO(new CustomBundle("in" -> Input(field), "out" -> Output(field))) + } + class RecordIOModule extends Module { val io = IO(new CustomBundle("in" -> Input(UInt(32.W)), "out" -> Output(UInt(32.W)))) io("out") := io("in") @@ -104,6 +110,23 @@ class RecordSpec extends ChiselFlatSpec with RecordSpecUtils with Utils { ChiselStage.elaborate { new MyModule(new MyBundle, fooBarType) } } + they should "not allow aliased fields" in { + class AliasedFieldRecord extends Record { + val foo = UInt(8.W) + val elements = SeqMap("foo" -> foo, "bar" -> foo) + override def cloneType: AliasedFieldRecord.this.type = this + } + + val e = intercept[AliasedAggregateFieldException] { + ChiselStage.elaborate { + new Module { + val io = IO(new AliasedFieldRecord) + } + } + } + e.getMessage should include ("contains aliased fields named (bar,foo)") + } + they should "follow UInt serialization/deserialization API" in { assertTesterPasses { new RecordSerializationTest } } diff --git a/src/test/scala/chiselTests/SIntOps.scala b/src/test/scala/chiselTests/SIntOps.scala index f2e238e9..222d0ba7 100644 --- a/src/test/scala/chiselTests/SIntOps.scala +++ b/src/test/scala/chiselTests/SIntOps.scala @@ -44,7 +44,7 @@ class SIntOps extends Module { io.noteqout := (a =/= b) io.lesseqout := a <= b io.greateqout := a >= b - // io.negout := -a(15, 0).toSInt + io.negout := -a(15, 0).asSInt io.negout := (0.S -% a) } diff --git a/src/test/scala/chiselTests/StrongEnum.scala b/src/test/scala/chiselTests/StrongEnum.scala index d7dea571..404c3f66 100644 --- a/src/test/scala/chiselTests/StrongEnum.scala +++ b/src/test/scala/chiselTests/StrongEnum.scala @@ -257,8 +257,8 @@ class IsLitTester extends BasicTester { for (e <- EnumExample.all) { val wire = WireDefault(e) - assert(e.isLit()) - assert(!wire.isLit()) + assert(e.isLit) + assert(!wire.isLit) } stop() } diff --git a/src/test/scala/chiselTests/TransitNameSpec.scala b/src/test/scala/chiselTests/TransitNameSpec.scala index b21818d6..656c6731 100644 --- a/src/test/scala/chiselTests/TransitNameSpec.scala +++ b/src/test/scala/chiselTests/TransitNameSpec.scala @@ -6,7 +6,6 @@ import chisel3._ import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage} import chisel3.util.TransitName -import firrtl.FirrtlExecutionSuccess import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers diff --git a/src/test/scala/chiselTests/UIntOps.scala b/src/test/scala/chiselTests/UIntOps.scala index 62d00de2..bc6454b8 100644 --- a/src/test/scala/chiselTests/UIntOps.scala +++ b/src/test/scala/chiselTests/UIntOps.scala @@ -6,6 +6,7 @@ import chisel3._ import org.scalatest._ import chisel3.stage.ChiselStage import chisel3.testers.BasicTester +import chisel3.util._ import org.scalacheck.Shrink import org.scalatest.matchers.should.Matchers @@ -22,6 +23,8 @@ class UIntOps extends Module { val modout = Output(UInt(32.W)) val lshiftout = Output(UInt(32.W)) val rshiftout = Output(UInt(32.W)) + val lrotateout = Output(UInt(32.W)) + val rrotateout = Output(UInt(32.W)) val lessout = Output(Bool()) val greatout = Output(Bool()) val eqout = Output(Bool()) @@ -30,6 +33,8 @@ class UIntOps extends Module { val greateqout = Output(Bool()) }) + dontTouch(io) + val a = io.a val b = io.b @@ -42,6 +47,8 @@ class UIntOps extends Module { io.modout := a % b io.lshiftout := (a << b(3, 0))(31, 0) io.rshiftout := a >> b + io.lrotateout := a.rotateLeft(5) + io.rrotateout := a.rotateRight(5) io.lessout := a < b io.greatout := a > b io.eqout := a === b @@ -67,6 +74,14 @@ class UIntOpsTester(a: Long, b: Long) extends BasicTester { assert(dut.io.modout === (a % (b max 1)).U(32.W)) assert(dut.io.lshiftout === (a << (b % 16)).U(32.W)) assert(dut.io.rshiftout === (a >> b).U(32.W)) + assert( + dut.io.lrotateout === s"h${Integer.rotateLeft(a.toInt, 5).toHexString}" + .U(32.W) + ) + assert( + dut.io.rrotateout === s"h${Integer.rotateRight(a.toInt, 5).toHexString}" + .U(32.W) + ) assert(dut.io.lessout === (a < b).B) assert(dut.io.greatout === (a > b).B) assert(dut.io.eqout === (a == b).B) @@ -98,6 +113,65 @@ class NegativeShift(t: => Bits) extends Module { Reg(t) >> -1 } +class BasicRotate extends BasicTester { + val shiftAmount = random.LFSR(4) + val ctr = RegInit(0.U(4.W)) + + + val rotL = 1.U(3.W).rotateLeft(shiftAmount) + val rotR = 1.U(3.W).rotateRight(shiftAmount) + + printf("Shift amount: %d rotateLeft:%b rotateRight:%b\n", shiftAmount, rotL, rotR) + + switch(shiftAmount % 3.U) { + is(0.U, 3.U) { + assert(rotL === "b001".U) + assert(rotR === "b001".U) + } + is(1.U) { + assert(rotL === "b010".U) + assert(rotR === "b100".U) + } + is(2.U) { + assert(rotL === "b100".U) + assert(rotR === "b010".U) + } + } + + ctr := ctr + 1.U + + when (ctr === 15.U){ + stop() + } +} + +/** rotating a w-bit word left by n should be equivalent to rotating it by w - n + * to the left + */ +class MatchedRotateLeftAndRight(w: Int = 13) extends BasicTester { + val initValue = BigInt(w, scala.util.Random) + println(s"Initial value: ${initValue.toString(2)}") + + val maxWidthBits = log2Ceil(w + 1) + val shiftAmount1 = RegInit(0.U(w.W)) + val shiftAmount2 = RegInit(w.U(w.W)) + shiftAmount1 := shiftAmount1 + 1.U + shiftAmount2 := shiftAmount2 - 1.U + + val value = RegInit(initValue.U(w.W)) + + val out1 = value.rotateLeft(shiftAmount1) + val out2 = value.rotateRight(shiftAmount2) + + printf("rotateLeft by %d: %b\n", shiftAmount1, out1) + + assert(out1 === out2) + when(shiftAmount1 === w.U) { + assert(out1 === initValue.U) + stop() + } +} + class UIntLitExtractTester extends BasicTester { assert("b101010".U(2) === false.B) assert("b101010".U(3) === true.B) @@ -140,6 +214,16 @@ class UIntOpsSpec extends ChiselPropSpec with Matchers with Utils { } } + property("rotateLeft and rotateRight should work for dynamic shift values") { + assertTesterPasses(new BasicRotate) + } + + property( + "rotateLeft and rotateRight should be consistent for dynamic shift values" + ) { + assertTesterPasses(new MatchedRotateLeftAndRight) + } + property("Bit extraction on literals should work for all non-negative indices") { assertTesterPasses(new UIntLitExtractTester) } diff --git a/src/test/scala/chiselTests/Vec.scala b/src/test/scala/chiselTests/Vec.scala index 97aea909..24ba0bf8 100644 --- a/src/test/scala/chiselTests/Vec.scala +++ b/src/test/scala/chiselTests/Vec.scala @@ -313,6 +313,36 @@ class ModuleIODynamicIndexTester(n: Int) extends BasicTester { when (done) { stop() } } +class ReduceTreeTester() extends BasicTester { + class FooIO[T <: Data](n: Int, private val gen: T) extends Bundle { + val in = Flipped(Vec(n, new DecoupledIO(gen))) + val out = new DecoupledIO(gen) + } + + class Foo[T <: Data](n: Int, private val gen: T) extends Module { + val io = IO(new FooIO(n, gen)) + + def foo(a: DecoupledIO[T], b: DecoupledIO[T]) = { + a.ready := true.B + b.ready := true.B + val out = Wire(new DecoupledIO(gen)) + + out.valid := true.B + + val regSel = RegInit(false.B) + out.bits := Mux(regSel, a.bits, b.bits) + out.ready := a.ready + out + } + + io.out <> io.in.reduceTree(foo) + } + + val dut = Module(new Foo(5, UInt(5.W))) + dut.io := DontCare + stop() +} + class VecSpec extends ChiselPropSpec with Utils { // Disable shrinking on error. implicit val noShrinkListVal = Shrink[List[Int]](_ => Stream.empty) @@ -456,4 +486,8 @@ class VecSpec extends ChiselPropSpec with Utils { }} } } + + property("reduceTree should preserve input/output type") { + assertTesterPasses { new ReduceTreeTester() } + } } diff --git a/src/test/scala/chiselTests/VecLiteralSpec.scala b/src/test/scala/chiselTests/VecLiteralSpec.scala index d91cd2f4..74d8c005 100644 --- a/src/test/scala/chiselTests/VecLiteralSpec.scala +++ b/src/test/scala/chiselTests/VecLiteralSpec.scala @@ -142,17 +142,17 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { "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)) + 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)) + 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)) + z.litValue should be(BigInt("03020202", 16)) } "packing sparse vec lits should not pack, litOption returns None" in { @@ -221,7 +221,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { chisel3.assert(outsideVecLit(2) === 0xBB.U) chisel3.assert(outsideVecLit(3) === 0xAA.U) - chisel3.assert(outsideVecLit.litValue().U === outsideVecLit.asUInt()) + 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) @@ -277,15 +277,15 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { 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)) + 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 { @@ -473,19 +473,19 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { 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.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") + 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") + vec.litValue.toString(16) should be("defabc") } "bundles can contain vec lits" in { @@ -495,7 +495,7 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { 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") + bundle.litValue.toString(16) should be("cbaed") } "bundles can contain vec lits in-line" in { @@ -506,21 +506,21 @@ class VecLiteralSpec extends ChiselFreeSpec with Utils { _.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") + 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(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(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)) + vec.litValue should be(BigInt("bbbb000a", 16)) } } diff --git a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala b/src/test/scala/chiselTests/VerificationSpec.scala index 1e080739..2d7144df 100644 --- a/src/test/scala/chiselTests/experimental/verification/VerificationSpec.scala +++ b/src/test/scala/chiselTests/VerificationSpec.scala @@ -1,15 +1,14 @@ // SPDX-License-Identifier: Apache-2.0 -package chiselTests.experimental.verification +package chiselTests import chisel3._ import chisel3.experimental.{ChiselAnnotation, verification => formal} import chisel3.stage.ChiselStage -import chiselTests.ChiselPropSpec import firrtl.annotations.{ReferenceTarget, SingleTargetAnnotation} +import org.scalatest.matchers.should.Matchers import java.io.File -import org.scalatest.matchers.should.Matchers class SimpleTest extends Module { val io = IO(new Bundle{ @@ -17,10 +16,10 @@ class SimpleTest extends Module { val out = Output(UInt(8.W)) }) io.out := io.in - formal.cover(io.in === 3.U) + cover(io.in === 3.U) when (io.in === 3.U) { - formal.assume(io.in =/= 2.U) - formal.assert(io.out === io.in) + assume(io.in =/= 2.U) + assert(io.out === io.in) } } @@ -35,7 +34,7 @@ object VerifAnnotation { /** Create annotation for a given verification component. * @param c component to be annotated */ - def annotate(c: experimental.BaseSim): Unit = { + def annotate(c: VerificationStatement): Unit = { chisel3.experimental.annotate(new ChiselAnnotation { def toFirrtl: VerifAnnotation = VerifAnnotation(c.toTarget) }) @@ -60,8 +59,8 @@ class VerificationSpec extends ChiselPropSpec with Matchers { 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\"), \"\")") + assertContains(lines, "when _T_10 : @[VerificationSpec.scala") + assertContains(lines, "assert(clock, _T_8, UInt<1>(\"h1\"), \"\")") } property("annotation of verification constructs should work") { @@ -72,9 +71,9 @@ class VerificationSpec extends ChiselPropSpec with Matchers { 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) + val cov = cover(io.in === 3.U) + val assm = chisel3.assume(io.in =/= 2.U) + val asst = chisel3.assert(io.out === io.in) VerifAnnotation.annotate(cov) VerifAnnotation.annotate(assm) VerifAnnotation.annotate(asst) @@ -93,7 +92,7 @@ class VerificationSpec extends ChiselPropSpec with Matchers { val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList // check for expected verification annotations - exactly(3, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation") + exactly(3, annoLines) should include ("chiselTests.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") @@ -106,7 +105,7 @@ class VerificationSpec extends ChiselPropSpec with Matchers { // 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") + exactly(1, firLines) should include ("assert(clock, _T_7, UInt<1>(\"h1\"), \"\") : asst") } property("annotation of verification constructs with suggested name should work") { @@ -118,11 +117,11 @@ class VerificationSpec extends ChiselPropSpec with Matchers { }) io.out := io.in - val goodbye = formal.assert(io.in === 1.U) + val goodbye = chisel3.assert(io.in === 1.U) goodbye.suggestName("hello") VerifAnnotation.annotate(goodbye) - VerifAnnotation.annotate(formal.assume(io.in =/= 2.U).suggestName("howdy")) + VerifAnnotation.annotate(chisel3.assume(io.in =/= 2.U).suggestName("howdy")) } // compile circuit @@ -138,7 +137,7 @@ class VerificationSpec extends ChiselPropSpec with Matchers { val annoLines = scala.io.Source.fromFile(annoFile).getLines.toList // check for expected verification annotations - exactly(2, annoLines) should include ("chiselTests.experimental.verification.VerifAnnotation") + exactly(2, annoLines) should include ("chiselTests.VerifAnnotation") exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>hello") exactly(1, annoLines) should include ("~AnnotationRenameTest|AnnotationRenameTest>howdy") @@ -149,6 +148,6 @@ class VerificationSpec extends ChiselPropSpec with Matchers { // 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") + exactly(1, firLines) should include ("assume(clock, _T_4, UInt<1>(\"h1\"), \"\") : howdy") } } diff --git a/src/test/scala/chiselTests/aop/SelectSpec.scala b/src/test/scala/chiselTests/aop/SelectSpec.scala index e09e78c8..2b47c6b8 100644 --- a/src/test/scala/chiselTests/aop/SelectSpec.scala +++ b/src/test/scala/chiselTests/aop/SelectSpec.scala @@ -133,11 +133,10 @@ class SelectSpec extends ChiselFlatSpec { { dut: SelectTester => Seq(Select.Stop( Seq( - When(Select.ops("eq")(dut).dropRight(1).last.asInstanceOf[Bool]), - When(dut.nreset), - WhenNot(dut.overflow) + When(Select.ops("eq")(dut)(1).asInstanceOf[Bool]), + When(dut.overflow) ), - 1, + 0, dut.clock )) } diff --git a/src/test/scala/chiselTests/experimental/DataView.scala b/src/test/scala/chiselTests/experimental/DataView.scala index d1620e88..7c5d170b 100644 --- a/src/test/scala/chiselTests/experimental/DataView.scala +++ b/src/test/scala/chiselTests/experimental/DataView.scala @@ -5,7 +5,9 @@ package chiselTests.experimental import chiselTests.ChiselFlatSpec import chisel3._ import chisel3.experimental.dataview._ +import chisel3.experimental.conversions._ import chisel3.experimental.DataMirror.internal.chiselTypeClone +import chisel3.experimental.HWTuple2 import chisel3.stage.ChiselStage import chisel3.util.{Decoupled, DecoupledIO} @@ -50,69 +52,6 @@ object FlatDecoupledDataView { 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" @@ -327,15 +266,14 @@ class DataViewSpec extends ChiselFlatSpec { 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._ - + it should "enable using Seq like Data" in { 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)) + // Unclear why the implicit conversion does not work in this case for Seq + // That being said, it's easy enough to cast via `.viewAs` with or without + Seq(y, z) := Mux(sel, Seq(a, b).viewAs, Seq(c, d).viewAs[Vec[UInt]]) } // Verilog instead of CHIRRTL because the optimizations make it much prettier val verilog = ChiselStage.emitVerilog(new MyModule) @@ -343,25 +281,8 @@ class DataViewSpec extends ChiselFlatSpec { 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))) @@ -376,11 +297,6 @@ class DataViewSpec extends ChiselFlatSpec { } 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))) @@ -397,12 +313,26 @@ class DataViewSpec extends ChiselFlatSpec { 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._ + it should "support dynamic indexing for Vec identity views" in { + class MyModule extends Module { + val dataIn = IO(Input(UInt(8.W))) + val addr = IO(Input(UInt(2.W))) + val dataOut = IO(Output(UInt(8.W))) + + val vec = RegInit(0.U.asTypeOf(Vec(4, UInt(8.W)))) + val view = vec.viewAs[Vec[UInt]] + // Dynamic indexing is more of a "generator" in Chisel3 than an individual node + // This style is not recommended, this is just testing the behavior + val selected = view(addr) + selected := dataIn + dataOut := selected + } + val chirrtl = ChiselStage.emitChirrtl(new MyModule) + chirrtl should include ("vec[addr] <= dataIn") + chirrtl should include ("dataOut <= vec[addr]") + } + it should "error if you try to dynamically index a Vec view that does not correspond to a Vec target" in { class MyModule extends Module { val inA, inB = IO(Input(UInt(8.W))) val outA, outB = IO(Output(UInt(8.W))) @@ -411,6 +341,7 @@ class DataViewSpec extends ChiselFlatSpec { val a, b, c, d = RegInit(0.U) // Dynamic indexing is more of a "generator" in Chisel3 than an individual node + // This style is not recommended, this is just testing the behavior val selected = Seq((a, b), (c, d)).apply(idx) selected := (inA, inB) (outA, outB) := selected @@ -434,7 +365,6 @@ class DataViewSpec extends ChiselFlatSpec { } 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))) @@ -533,7 +463,6 @@ class DataViewSpec extends ChiselFlatSpec { } 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))) diff --git a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala index 92091631..a17b0f40 100644 --- a/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala +++ b/src/test/scala/chiselTests/experimental/DataViewTargetSpec.scala @@ -4,8 +4,8 @@ package chiselTests.experimental import chisel3._ import chisel3.experimental.dataview._ +import chisel3.experimental.conversions._ import chisel3.experimental.{ChiselAnnotation, annotate} -import chisel3.stage.ChiselStage import chiselTests.ChiselFlatSpec object DataViewTargetSpec { @@ -127,7 +127,6 @@ class DataViewTargetSpec extends ChiselFlatSpec { } 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)) diff --git a/src/test/scala/chiselTests/experimental/TraceSpec.scala b/src/test/scala/chiselTests/experimental/TraceSpec.scala new file mode 100644 index 00000000..59548921 --- /dev/null +++ b/src/test/scala/chiselTests/experimental/TraceSpec.scala @@ -0,0 +1,309 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chiselTests + +import chisel3._ +import chisel3.experimental.ChiselEnum +import chisel3.experimental.Trace._ +import chisel3.stage.{ChiselGeneratorAnnotation, ChiselStage, DesignAnnotation} +import chisel3.util.experimental.InlineInstance +import firrtl.AnnotationSeq +import firrtl.annotations.TargetToken.{Instance, OfModule, Ref} +import firrtl.annotations.{CompleteTarget, InstanceTarget, ReferenceTarget} +import org.scalatest.matchers.should.Matchers + +class TraceSpec extends ChiselFlatSpec with Matchers { + + def refTarget(topName: String, ref: String, path: Seq[(Instance, OfModule)] = Seq()) = + ReferenceTarget(topName, topName, path, ref, Seq()) + + def instTarget(topName: String, instance: String, ofModule: String, path: Seq[(Instance, OfModule)] = Seq()) = + InstanceTarget(topName, topName, path, instance, ofModule) + + def compile(testName: String, gen: () => Module): (os.Path, AnnotationSeq) = { + val testDir = os.Path(createTestDirectory(testName).getAbsolutePath) + val annos = (new ChiselStage).execute( + Array("--target-dir", s"$testDir"), + Seq( + ChiselGeneratorAnnotation(gen) + ) + ) + (testDir, annos) + } + + "TraceFromAnnotations" should "be able to get nested name." in { + class Bundle0 extends Bundle { + val a = UInt(8.W) + val b = Bool() + val c = Enum0.Type + } + + class Bundle1 extends Bundle { + val a = new Bundle0 + val b = Vec(4, Vec(4, Bool())) + } + + class Module0 extends Module { + val i = IO(Input(new Bundle1)) + val o = IO(Output(new Bundle1)) + val r = Reg(new Bundle1) + o := r + r := i + + traceName(r) + traceName(i) + traceName(o) + } + + class Module1 extends Module { + val i = IO(Input(new Bundle1)) + val m0 = Module(new Module0) + m0.i := i + m0.o := DontCare + } + + object Enum0 extends ChiselEnum { + val s0, s1, s2 = Value + } + + val (testDir, annos) = compile("TraceFromAnnotaions", () => new Module1) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module1] + // out of Builder. + + val oneTarget = finalTarget(annos)(dut.m0.r.a.a).head + val ioTarget = finalTarget(annos)(dut.m0.i.b(1)(2)).head + + val topName = "Module1" + oneTarget should be(refTarget(topName, "r_a_a", Seq(Instance("m0") -> OfModule("Module0")))) + + ioTarget should be(refTarget(topName, "i_b_1_2", Seq(Instance("m0") -> OfModule("Module0")))) + + // Below codes doesn't needs to be a FIRRTL Transform. + def generateVerilatorConfigFile(data: Seq[Data], annos: AnnotationSeq): String = + """`verilator_config + |lint_off -rule unused + |lint_off -rule declfilename + |""".stripMargin + + data + .flatMap(finalTarget(annos)) + .toSet + .map { target: CompleteTarget => + s"""public_flat_rd -module "${target.tokens.collectFirst { case OfModule(m) => m }.get}" -var "${target.tokens.collectFirst { case Ref(r) => r }.get}"""" + } + .mkString("\n") + "\n" + + def verilatorTemplate(data: Seq[Data], annos: AnnotationSeq): String = { + val vpiNames = data.flatMap(finalTarget(annos)).map { ct => + s"""TOP.${ct.circuit}.${ct.path.map { case (Instance(i), _) => i }.mkString(".")}.${ct.tokens.collectFirst { case Ref(r) => r }.get}""" + } + s""" + |#include "V${topName}.h" + |#include "verilated_vpi.h" + |#include <memory> + |#include <verilated.h> + | + |int vpiGetInt(const char name[]) { + | vpiHandle vh1 = vpi_handle_by_name((PLI_BYTE8 *)name, NULL); + | if (!vh1) + | vl_fatal(__FILE__, __LINE__, "sim_main", "No handle found"); + | s_vpi_value v; + | v.format = vpiIntVal; + | vpi_get_value(vh1, &v); + | return v.value.integer; + |} + | + |int main(int argc, char **argv) { + | const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext}; + | contextp->commandArgs(argc, argv); + | const std::unique_ptr<V$topName> top{new V$topName{contextp.get(), "TOP"}}; + | top->reset = 0; + | top->clock = 0; + | int a_b = 1; + | top->i_a_b = a_b; + | bool started = false; + | int ticks = 20; + | while (ticks--) { + | contextp->timeInc(1); + | top->clock = !top->clock; + | if (!top->clock) { + | if (contextp->time() > 1 && contextp->time() < 10) { + | top->reset = 1; + | } else { + | top->reset = 0; + | started = true; + | } + | a_b = a_b ? 0 : 1; + | top->i_a_b = a_b; + | } + | top->eval(); + | VerilatedVpi::callValueCbs(); + | if (started && !top->clock) { + | const int i = top->i_a_b; + | const int o = vpiGetInt("${vpiNames.head}"); + | if (i == o) + | vl_fatal(__FILE__, __LINE__, "sim_main", "${vpiNames.head} should be the old value of Module1.i_a_b"); + | printf("${vpiNames.head}=%d Module1.m0.o_a_b=%d\\n", i, o); + | } + | } + | top->final(); + | return 0; + |} + |""".stripMargin + } + + val config = os.temp(dir = testDir, contents = generateVerilatorConfigFile(Seq(dut.m0.o.a.b), annos)) + val verilog = testDir / s"$topName.v" + val cpp = os.temp(dir = testDir, suffix = ".cpp", contents = verilatorTemplate(Seq(dut.m0.o.a.b), annos)) + val exe = testDir / "obj_dir" / s"V$topName" + os.proc("verilator", "-Wall", "--cc", "--exe", "--build", "--vpi", s"$cpp", s"$verilog", s"$config").call(stdout = os.Inherit, stderr = os.Inherit, cwd = testDir) + assert(os.proc(s"$exe").call(stdout = os.Inherit, stderr = os.Inherit).exitCode == 0, "verilator should exit peacefully") + } + + "TraceFromCollideBundle" should "work" in { + class CollideModule extends Module { + val a = IO(Input(Vec(2, new Bundle { + val b = Flipped(Bool()) + val c = Vec(2, new Bundle { + val d = UInt(2.W) + val e = Flipped(UInt(3.W)) + }) + val c_1_e = UInt(4.W) + }))) + val a_0_c = IO(Output(UInt(5.W))) + val a__0 = IO(Output(UInt(5.W))) + a_0_c := DontCare + a__0 := DontCare + + traceName(a) + traceName(a_0_c) + traceName(a__0) + } + + val (_, annos) = compile("TraceFromCollideBundle", () => new CollideModule) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[CollideModule] + + val topName = "CollideModule" + + val a0 = finalTarget(annos)(dut.a(0)) + val a__0 = finalTarget(annos)(dut.a__0).head + val a__0_ref = refTarget(topName, "a__0") + a0.foreach(_ shouldNot be(a__0_ref)) + a__0 should be(a__0_ref) + + val a0_c = finalTarget(annos)(dut.a(0).c) + val a_0_c = finalTarget(annos)(dut.a_0_c).head + val a_0_c_ref = refTarget(topName, "a_0_c") + a0_c.foreach(_ shouldNot be(a_0_c_ref)) + a_0_c should be(a_0_c_ref) + + val a0_c1_e = finalTarget(annos)(dut.a(0).c(1).e).head + val a0_c_1_e = finalTarget(annos)(dut.a(0).c_1_e).head + a0_c1_e should be(refTarget(topName, "a_0_c__1_e")) + a0_c_1_e should be(refTarget(topName, "a_0_c_1_e")) + } + + "Inline should work" should "work" in { + class Module0 extends Module { + val i = IO(Input(Bool())) + val o = IO(Output(Bool())) + traceName(i) + o := !i + } + + class Module1 extends Module { + val i = IO(Input(Bool())) + val o = IO(Output(Bool())) + val m0 = Module(new Module0 with InlineInstance) + m0.i := i + o := m0.o + } + + val (_, annos) = compile("Inline", () => new Module1) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module1] + + val m0_i = finalTarget(annos)(dut.m0.i).head + m0_i should be(refTarget("Module1", "m0_i")) + } + + "Constant Propagation" should "be turned off by traceName" in { + class Module0 extends Module { + val i = WireDefault(1.U) + val i0 = i + 1.U + val o = IO(Output(UInt(2.W))) + traceName(i0) + o := i0 + } + + val (_, annos) = compile("ConstantProp", () => new Module0) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[Module0] + + val i0 = finalTarget(annos)(dut.i0).head + i0 should be(refTarget("Module0", "i0")) + } + + "Nested Module" should "work" in { + class Io extends Bundle { + val i = Input(Bool()) + val o = Output(Bool()) + } + + class Not extends Module { + val io = IO(new Io) + io.o := !io.i + } + + class M1 extends Module { + val io = IO(new Io) + val not = Module(new Not) + not.io <> io + } + + class M2 extends Module { + val io = IO(new Io) + val m1 = Module(new M1 with InlineInstance) + val not = Module(new Not) + + m1.io.i := io.i + not.io.i := io.i + + io.o := m1.io.o && not.io.o + } + + class M3 extends Module { + val io = IO(new Io) + val m2 = Module(new M2) + io <> m2.io + traceName(m2.not) + traceName(m2.m1.not) + } + + val (_, annos) = compile("NestedModule", () => new M3) + val m3 = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[M3] + + val m2_m1_not = finalTarget(annos)(m3.m2.m1.not).head + val m2_not = finalTarget(annos)(m3.m2.not).head + + m2_m1_not should be(instTarget("M3", "m1_not", "Not", Seq(Instance("m2") -> OfModule("M2")))) + m2_not should be(instTarget("M3", "not", "Not", Seq(Instance("m2") -> OfModule("M2")))) + } + + "All traced signal" should "generate" in { + class M extends Module { + val a = Wire(Bool()) + val b = Wire(Vec(2, Bool())) + a := DontCare + b := DontCare + Seq(a, b).foreach(traceName) + } + val (_, annos) = compile("NestedModule", () => new M) + val dut = annos.collectFirst { case DesignAnnotation(dut) => dut }.get.asInstanceOf[M] + val allTargets = finalTargetMap(annos) + allTargets(dut.a.toAbsoluteTarget) should be (Seq(refTarget("M", "a"))) + allTargets(dut.b.toAbsoluteTarget) should be (Seq( + refTarget("M", "b_0"), + refTarget("M", "b_1"), + )) + allTargets(dut.b(0).toAbsoluteTarget) should be (Seq(refTarget("M", "b_0"))) + allTargets(dut.b(1).toAbsoluteTarget) should be (Seq(refTarget("M", "b_1"))) + } +} diff --git a/src/test/scala/chiselTests/experimental/Tuple.scala b/src/test/scala/chiselTests/experimental/Tuple.scala new file mode 100644 index 00000000..5f897fbc --- /dev/null +++ b/src/test/scala/chiselTests/experimental/Tuple.scala @@ -0,0 +1,163 @@ +// See LICENSE for license details. + +package chiselTests.experimental + +import chiselTests.ChiselFlatSpec +import chisel3._ +import chisel3.experimental.conversions._ +import chisel3.stage.ChiselStage + +class TupleSpec extends ChiselFlatSpec { + + behavior of "Tuple" + + it should "enable using Tuple2 like Data" in { + 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 { + 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") + } + + it should "enable using Tuple3 like Data" in { + class MyModule extends Module { + val a, b, c = IO(Input(UInt(8.W))) + val f, g, h = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val v, w, x = IO(Output(UInt(8.W))) + (v, w, x) := Mux(sel, (a, b, c), (f, g, h)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign v = sel ? a : f;") + verilog should include ("assign w = sel ? b : g;") + verilog should include ("assign x = sel ? c : h;") + } + + it should "enable using Tuple4 like Data" in { + class MyModule extends Module { + val a, b, c, d = IO(Input(UInt(8.W))) + val f, g, h, i = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val v, w, x, y = IO(Output(UInt(8.W))) + (v, w, x, y) := Mux(sel, (a, b, c, d), (f, g, h, i)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + verilog should include ("assign v = sel ? a : f;") + verilog should include ("assign w = sel ? b : g;") + verilog should include ("assign x = sel ? c : h;") + verilog should include ("assign y = sel ? d : i;") + } + + it should "enable using Tuple5 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4) := Mux(sel, (a0, a1, a2, a3, a4), (b0, b1, b2, b3, b4)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 5) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + + it should "enable using Tuple6 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4, a5 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4, b5 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4, z5 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4, z5) := Mux(sel, (a0, a1, a2, a3, a4, a5), (b0, b1, b2, b3, b4, b5)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 6) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + + it should "enable using Tuple7 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4, a5, a6 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4, b5, b6 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4, z5, z6 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4, z5, z6) := Mux(sel, (a0, a1, a2, a3, a4, a5, a6), (b0, b1, b2, b3, b4, b5, b6)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 7) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + + it should "enable using Tuple8 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4, a5, a6, a7 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4, b5, b6, b7 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4, z5, z6, z7 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4, z5, z6, z7) := Mux(sel, (a0, a1, a2, a3, a4, a5, a6, a7), (b0, b1, b2, b3, b4, b5, b6, b7)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 8) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + + it should "enable using Tuple9 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4, a5, a6, a7, a8 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4, b5, b6, b7, b8 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4, z5, z6, z7, z8 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4, z5, z6, z7, z8) := + Mux(sel, (a0, a1, a2, a3, a4, a5, a6, a7, a8), (b0, b1, b2, b3, b4, b5, b6, b7, b8)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 9) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + + it should "enable using Tuple10 like Data" in { + class MyModule extends Module { + val a0, a1, a2, a3, a4, a5, a6, a7, a8, a9 = IO(Input(UInt(8.W))) + val b0, b1, b2, b3, b4, b5, b6, b7, b8, b9 = IO(Input(UInt(8.W))) + val sel = IO(Input(Bool())) + val z0, z1, z2, z3, z4, z5, z6, z7, z8, z9 = IO(Output(UInt(8.W))) + (z0, z1, z2, z3, z4, z5, z6, z7, z8, z9) := + Mux(sel, (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9), (b0, b1, b2, b3, b4, b5, b6, b7, b8, b9)) + } + // Verilog instead of CHIRRTL because the optimizations make it much prettier + val verilog = ChiselStage.emitVerilog(new MyModule) + for (i <- 0 until 10) { + verilog should include(s"assign z$i = sel ? a$i : b$i;") + } + } + +} diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala index 43111fdd..eba412f1 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/Annotations.scala @@ -5,24 +5,20 @@ package chiselTests.experimental.hierarchy import _root_.firrtl.annotations._ import chisel3.experimental.{annotate, BaseModule} import chisel3.Data -import chisel3.experimental.hierarchy.{Instance, Definition} +import chisel3.experimental.hierarchy.{Instance, Definition, Hierarchy} 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 { + case class MarkChiselHierarchyAnnotation[B <: BaseModule](d: Hierarchy[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 mark[B <: BaseModule](d: Hierarchy[B], tag: String): Unit = annotate(MarkChiselHierarchyAnnotation(d, tag, true)) 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)) + def amark[B <: BaseModule](d: Hierarchy[B], tag: String): Unit = annotate(MarkChiselHierarchyAnnotation(d, tag, true)) } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala index 19261c36..f33f7869 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/DefinitionSpec.scala @@ -40,6 +40,44 @@ class DefinitionSpec extends ChiselFunSpec with Utils { val (chirrtl, _) = getFirrtlAndAnnos(new Top) chirrtl.serialize should include ("inst i0 of HasUninferredReset") } + it("0.3: module names of repeated definition should be sequential") { + class Top extends Module { + val k = Module(new AddTwoParameterized(4, (x: Int) => Seq.tabulate(x){j => + val addOneDef = Definition(new AddOneParameterized(x+j)) + val addOne = Instance(addOneDef) + addOne + })) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOneParameterized :") + chirrtl.serialize should include ("module AddOneParameterized_1 :") + chirrtl.serialize should include ("module AddOneParameterized_2 :") + chirrtl.serialize should include ("module AddOneParameterized_3 :") + } + it("0.4: multiple instantiations should have sequential names") { + class Top extends Module { + val addOneDef = Definition(new AddOneParameterized(4)) + val addOne = Instance(addOneDef) + val otherAddOne = Module(new AddOneParameterized(4)) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOneParameterized :") + chirrtl.serialize should include ("module AddOneParameterized_1 :") + } + it("0.5: nested definitions should have sequential names") { + class Top extends Module { + val k = Module(new AddTwoWithNested(4, (x: Int) => Seq.tabulate(x){j => + val addOneDef = Definition(new AddOneWithNested(x+j)) + val addOne = Instance(addOneDef) + addOne + })) + } + val (chirrtl, _) = getFirrtlAndAnnos(new Top) + chirrtl.serialize should include ("module AddOneWithNested :") + chirrtl.serialize should include ("module AddOneWithNested_1 :") + chirrtl.serialize should include ("module AddOneWithNested_2 :") + chirrtl.serialize should include ("module AddOneWithNested_3 :") + } } describe("1: Annotations on definitions in same chisel compilation") { it("1.0: should work on a single definition, annotating the definition") { @@ -97,7 +135,7 @@ class DefinitionSpec extends ChiselFunSpec with Utils { mark(definition.i1, "i0.i1") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_1".it, "i0.i1")) } // Can you define an instantiable container? I think not. // Instead, we can test the instantiable container in a definition @@ -258,6 +296,17 @@ class DefinitionSpec extends ChiselFunSpec with Utils { val (_, annos) = getFirrtlAndAnnos(new Top) annos should contain(MarkAnnotation("~Top|HasPublicConstructorArgs>x".rt, "10")) } + it("3.10: should work on unimplemented vals in abstract classes/traits") { + class Top() extends Module { + val i = Definition(new ConcreteHasBlah()) + def f(d: Definition[HasBlah]): Unit = { + mark(d, d.blah.toString) + } + f(i) + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain(MarkAnnotation("~Top|ConcreteHasBlah".mt, "10")) + } } describe("4: toDefinition") { it("4.0: should work on modules") { @@ -311,7 +360,7 @@ class DefinitionSpec extends ChiselFunSpec with Utils { amark(i.i1.in, "blah") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + annos should contain(MarkAnnotation("~Top|AddTwoMixedModules/i1:AddOne_1>in".rt, "blah")) } it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within a definition") { class Top() extends Module { @@ -319,7 +368,7 @@ class DefinitionSpec extends ChiselFunSpec with Utils { amark(i.i1.x.head, "blah") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_2>x[0]".rt, "blah")) + annos should contain(MarkAnnotation("~Top|InstantiatesHasVec/i1:HasVec_1>x[0]".rt, "blah")) } } describe("6: @instantiable traits should work as expected") { diff --git a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala index 23b8c9c0..c0f504ff 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/Examples.scala @@ -36,6 +36,19 @@ object Examples { out := innerWire } @instantiable + class AddOneParameterized(width: Int) extends Module { + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + out := in + 1.U + } + class AddOneWithNested(width: Int) extends Module { + @public val in = IO(Input(UInt(width.W))) + @public val out = IO(Output(UInt(width.W))) + val addOneDef = Seq.fill(3)(Definition(new AddOne)) + out := in + 1.U + } + + @instantiable class AddTwo extends Module { @public val in = IO(Input(UInt(32.W))) @public val out = IO(Output(UInt(32.W))) @@ -58,6 +71,33 @@ object Examples { out := i1.out } @instantiable + class AddTwoParameterized(width: Int, makeParameterizedOnes: Int => Seq[Instance[AddOneParameterized]]) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + val addOnes = makeParameterizedOnes(width) + addOnes.head.in := in + out := addOnes.last.out + addOnes.zip(addOnes.tail).foreach{ case (head, tail) => tail.in := head.out} + } + @instantiable + class AddTwoWithNested(width: Int, makeParameterizedOnes: Int => Seq[Instance[AddOneWithNested]]) extends Module { + val in = IO(Input(UInt(width.W))) + val out = IO(Output(UInt(width.W))) + val addOnes = makeParameterizedOnes(width) + } + + @instantiable + class AddFour extends Module { + @public val in = IO(Input(UInt(32.W))) + @public val out = IO(Output(UInt(32.W))) + @public val definition = Definition(new AddTwoMixedModules) + @public val i0 = Instance(definition) + @public val i1 = Instance(definition) + 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)) @@ -183,4 +223,17 @@ object Examples { @public val out = IO(Output(UInt(3.W))) out := RegNext(in) } + @instantiable + abstract class HasBlah() extends Module { + @public val blah: Int + } + + @instantiable + class ConcreteHasBlah() extends HasBlah { + val blah = 10 + } + @instantiable + class HasTypeParams[D <: Data](d: D) extends Module { + @public val blah = Wire(d) + } } diff --git a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala index 3866bf87..9ceb9b40 100644 --- a/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala +++ b/src/test/scala/chiselTests/experimental/hierarchy/InstanceSpec.scala @@ -89,7 +89,7 @@ class InstanceSpec extends ChiselFunSpec with Utils { mark(i0.i1, "i0.i1") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_2".it, "i0.i1")) + annos should contain (MarkAnnotation("~Top|Top/i0:AddTwoMixedModules/i1:AddOne_1".it, "i0.i1")) } it("1.5: should work on an instantiable container, annotating a wire") { class Top extends Module { @@ -144,6 +144,15 @@ class InstanceSpec extends ChiselFunSpec with Utils { val (_, annos) = getFirrtlAndAnnos(new Top) annos should contain (MarkAnnotation("~Top|AddOneWithAnnotation>innerWire".rt, "innerWire")) } + it("1.11: should work on things with type parameters"){ + class Top extends Module { + val definition = Definition(new HasTypeParams[UInt](UInt(3.W))) + val i0 = Instance(definition) + mark(i0.blah, "blah") + } + val (_, annos) = getFirrtlAndAnnos(new Top) + annos should contain (MarkAnnotation("~Top|Top/i0:HasTypeParams>blah".rt, "blah")) + } } describe("2: Annotations on designs not in the same chisel compilation") { it("2.0: should work on an innerWire, marked in a different compilation") { @@ -353,7 +362,7 @@ class InstanceSpec extends ChiselFunSpec with Utils { amark(i.i1.in, "blah") } val (_, annos) = getFirrtlAndAnnos(new Top) - annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_2>in".rt, "blah")) + annos should contain(MarkAnnotation("~Top|Top/i:AddTwoMixedModules/i1:AddOne_1>in".rt, "blah")) } it("5.3: toAbsoluteTarget on a submodule's data, in an aggregate, within an instance") { class Top() extends Module { @@ -361,7 +370,7 @@ class InstanceSpec extends ChiselFunSpec with Utils { 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")) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_1>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) } @@ -379,7 +388,7 @@ class InstanceSpec extends ChiselFunSpec with Utils { 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")) + annos should contain(MarkAnnotation("~Top|Top/i:InstantiatesHasVec/i1:HasVec_1>x[0].x".rt, "blah")) } it("5.5: toAbsoluteTarget on a subinstance") { class Top() extends Module { @@ -620,7 +629,7 @@ class InstanceSpec extends ChiselFunSpec with Utils { } it("7.3: should work with DataView + implicit conversion") { - import chiselTests.experimental.SeqToVec._ + import chisel3.experimental.conversions._ @instantiable class MyModule extends RawModule { private val a = IO(Input(UInt(8.W))) @@ -705,5 +714,142 @@ class InstanceSpec extends ChiselFunSpec with Utils { } } } + describe("9: isA[..]") { + it("9.0: it should work on simple classes") { + class Top extends Module { + val d = Definition(new AddOne) + require(d.isA[AddOne]) + } + getFirrtlAndAnnos(new Top) + } + it("9.1: it should not work on inner classes") { + class InnerClass extends Module + class Top extends Module { + val d = Definition(new InnerClass) + "require(d.isA[Module])" should compile // ensures that the test below is checking something useful + "require(d.isA[InnerClass])" shouldNot compile + } + getFirrtlAndAnnos(new Top) + } + it("9.2: it should work on super classes") { + class InnerClass extends Module + class Top extends Module { + val d = Definition(new InnerClass) + require(d.isA[Module]) + } + getFirrtlAndAnnos(new Top) + } + it("9.2: it should work after casts") { + class Top extends Module { + val d0: Definition[Module] = Definition(new AddOne) + require(d0.isA[AddOne]) + val d1: Definition[Module] = Definition((new AddOne).asInstanceOf[Module]) + require(d1.isA[AddOne]) + val i0: Instance[Module] = Instance(d0) + require(i0.isA[AddOne]) + val i1: Instance[Module] = Instance(d1) + require(i1.isA[AddOne]) + val i2: Instance[Module] = Instance(Definition(new AddOne)) + require(i2.isA[AddOne]) + } + getFirrtlAndAnnos(new Top) + } + } + describe("10: Select APIs") { + it("10.0: instancesOf") { + val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules => + val targets = aop.Select.instancesOf[AddOne](m.toDefinition).map { i: Instance[AddOne] => i.toTarget } + targets should be (Seq( + "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it, + "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_1".it, + )) + }) + getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect)) + } + it("10.1: instancesIn") { + val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules => + val insts = aop.Select.instancesIn(m.toDefinition) + val abs = insts.map { i: Instance[BaseModule] => i.toAbsoluteTarget } + val rel = insts.map { i: Instance[BaseModule] => i.toTarget } + abs should be (Seq( + "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it, + "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_1".it, + )) + rel should be (Seq( + "~AddTwoMixedModules|AddTwoMixedModules/i0:AddOne".it, + "~AddTwoMixedModules|AddTwoMixedModules/i1:AddOne_1".it, + )) + }) + getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect)) + } + it("10.2: allInstancesOf") { + val aspect = aop.inspecting.InspectingAspect({ m: AddFour => + val insts = aop.Select.allInstancesOf[AddOne](m.toDefinition) + val abs = insts.map { i: Instance[AddOne] => i.in.toAbsoluteTarget } + val rel = insts.map { i: Instance[AddOne] => i.in.toTarget } + rel should be (Seq( + "~AddFour|AddFour/i0:AddTwoMixedModules/i0:AddOne>in".rt, + "~AddFour|AddFour/i0:AddTwoMixedModules/i1:AddOne_1>in".rt, + "~AddFour|AddFour/i1:AddTwoMixedModules/i0:AddOne>in".rt, + "~AddFour|AddFour/i1:AddTwoMixedModules/i1:AddOne_1>in".rt, + )) + abs should be (Seq( + "~AddFour|AddFour/i0:AddTwoMixedModules/i0:AddOne>in".rt, + "~AddFour|AddFour/i0:AddTwoMixedModules/i1:AddOne_1>in".rt, + "~AddFour|AddFour/i1:AddTwoMixedModules/i0:AddOne>in".rt, + "~AddFour|AddFour/i1:AddTwoMixedModules/i1:AddOne_1>in".rt, + )) + }) + getFirrtlAndAnnos(new AddFour, Seq(aspect)) + } + it("10.3: definitionsOf") { + val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules => + val targets = aop.Select.definitionsOf[AddOne](m.toDefinition).map { i: Definition[AddOne] => i.in.toTarget } + targets should be (Seq( + "~AddTwoMixedModules|AddOne>in".rt, + "~AddTwoMixedModules|AddOne_1>in".rt, + )) + }) + getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect)) + } + it("10.4: definitionsIn") { + val aspect = aop.inspecting.InspectingAspect({ m: AddTwoMixedModules => + val targets = aop.Select.definitionsIn(m.toDefinition).map { i: Definition[BaseModule] => i.toTarget } + targets should be (Seq( + "~AddTwoMixedModules|AddOne".mt, + "~AddTwoMixedModules|AddOne_1".mt, + )) + }) + getFirrtlAndAnnos(new AddTwoMixedModules, Seq(aspect)) + } + it("10.5: allDefinitionsOf") { + val aspect = aop.inspecting.InspectingAspect({ m: AddFour => + val targets = aop.Select.allDefinitionsOf[AddOne](m.toDefinition).map { i: Definition[AddOne] => i.in.toTarget } + targets should be (Seq( + "~AddFour|AddOne>in".rt, + "~AddFour|AddOne_1>in".rt, + )) + }) + getFirrtlAndAnnos(new AddFour, Seq(aspect)) + } + it("10.6: Select.collectDeep should fail when combined with hierarchy package") { + val aspect = aop.inspecting.InspectingAspect({ m: AddFour => + aop.Select.collectDeep(m) { case m: AddOne => m.toTarget } + }) + intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) } + } + it("10.7: Select.getDeep should fail when combined with hierarchy package") { + val aspect = aop.inspecting.InspectingAspect({ m: AddFour => + aop.Select.getDeep(m) { m: BaseModule => Nil } + }) + intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) } + } + it("10.8: Select.instances should fail when combined with hierarchy package") { + val aspect = aop.inspecting.InspectingAspect({ m: AddFour => + aop.Select.instances(m) + }) + intercept[Exception] { getFirrtlAndAnnos(new AddFour, Seq(aspect)) } + } + } } diff --git a/src/test/scala/chiselTests/util/BitPatSpec.scala b/src/test/scala/chiselTests/util/BitPatSpec.scala index 0c83493f..549e8bca 100644 --- a/src/test/scala/chiselTests/util/BitPatSpec.scala +++ b/src/test/scala/chiselTests/util/BitPatSpec.scala @@ -24,10 +24,18 @@ class BitPatSpec extends AnyFlatSpec with Matchers { intercept[IllegalArgumentException]{BitPat("b")} } - it should "contact BitPat via ##" in { + it should "concat BitPat via ##" in { (BitPat.Y(4) ## BitPat.dontCare(3) ## BitPat.N(2)).toString should be (s"BitPat(1111???00)") } + it should "throw when BitPat apply to a Hardware" in { + intercept[java.lang.IllegalArgumentException]{ + chisel3.stage.ChiselStage.emitChirrtl(new chisel3.Module { + BitPat(chisel3.Reg(chisel3.Bool())) + }) + } + } + it should "index and return new BitPat" in { val b = BitPat("b1001???") b(0) should be(BitPat.dontCare(1)) diff --git a/src/test/scala/chiselTests/util/BitSetSpec.scala b/src/test/scala/chiselTests/util/BitSetSpec.scala new file mode 100644 index 00000000..8120cc97 --- /dev/null +++ b/src/test/scala/chiselTests/util/BitSetSpec.scala @@ -0,0 +1,119 @@ +package chiselTests.util + +import chisel3.util.experimental.BitSet +import chisel3.util.BitPat +import org.scalatest.flatspec.AnyFlatSpec +import org.scalatest.matchers.should.Matchers + +class BitSetSpec extends AnyFlatSpec with Matchers { + behavior of classOf[BitSet].toString + + it should "reject unequal width when constructing a BitSet" in { + intercept[IllegalArgumentException] { + BitSet.fromString( + """b0010 + |b00010 + |""".stripMargin) + } + } + + it should "return empty subtraction result correctly" in { + val aBitPat = BitPat("b10?") + val bBitPat = BitPat("b1??") + + aBitPat.subtract(bBitPat).isEmpty should be (true) + } + + it should "return nonempty subtraction result correctly" in { + val aBitPat = BitPat("b10?") + val bBitPat = BitPat("b1??") + val cBitPat = BitPat("b11?") + val dBitPat = BitPat("b100") + + val diffBitPat = bBitPat.subtract(aBitPat) + bBitPat.cover(diffBitPat) should be (true) + diffBitPat.equals(cBitPat) should be (true) + + val largerdiffBitPat = bBitPat.subtract(dBitPat) + aBitPat.cover(dBitPat) should be (true) + largerdiffBitPat.cover(diffBitPat) should be (true) + } + + it should "be able to handle complex subtract between BitSet" in { + val aBitSet = BitSet.fromString( + """b?01?0 + |b11111 + |b00000 + |""".stripMargin) + val bBitSet = BitSet.fromString( + """b?1111 + |b?0000 + |""".stripMargin + ) + val expected = BitPat("b?01?0") + + expected.equals(aBitSet.subtract(bBitSet)) should be (true) + } + + it should "be generated from BitPat union" in { + val aBitSet = BitSet.fromString( + """b001?0 + |b000??""".stripMargin) + val aBitPat = BitPat("b000??") + val bBitPat = BitPat("b001?0") + val cBitPat = BitPat("b00000") + aBitPat.cover(cBitPat) should be (true) + aBitSet.cover(bBitPat) should be (true) + + aBitSet.equals(aBitPat.union(bBitPat)) should be (true) + } + + it should "be generated from BitPat subtraction" in { + val aBitSet = BitSet.fromString( + """b001?0 + |b000??""".stripMargin) + val aBitPat = BitPat("b00???") + val bBitPat = BitPat("b001?1") + + aBitSet.equals(aBitPat.subtract(bBitPat)) should be (true) + } + + it should "union two BitSet together" in { + val aBitSet = BitSet.fromString( + """b001?0 + |b001?1 + |""".stripMargin) + val bBitSet = BitSet.fromString( + """b000?? + |b01??? + |""".stripMargin + ) + val cBitPat = BitPat("b0????") + cBitPat.equals(aBitSet.union(bBitSet)) should be (true) + } + + it should "be decoded" in { + import chisel3._ + import chisel3.util.experimental.decode.decoder + // [0 - 256] part into: [0 - 31], [32 - 47, 64 - 127], [192 - 255] + // "0011????" "10??????" is empty to error + chisel3.stage.ChiselStage.emitSystemVerilog(new Module { + val in = IO(Input(UInt(8.W))) + val out = IO(Output(UInt(4.W))) + out := decoder.bitset(in, Seq( + BitSet.fromString( + "b000?????" + ), + BitSet.fromString( + """b0010???? + |b01?????? + |""".stripMargin + ), + BitSet.fromString( + "b11??????" + ) + ), true) + }) + } + +} diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala index 743a3cd8..255effaf 100644 --- a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -2,8 +2,9 @@ package chiselTests.util.experimental +import chisel3._ import chisel3.util.BitPat -import chisel3.util.experimental.decode.TruthTable +import chisel3.util.experimental.decode.{TruthTable, decoder} import org.scalatest.flatspec.AnyFlatSpec class TruthTableSpec extends AnyFlatSpec { @@ -34,16 +35,16 @@ class TruthTableSpec extends AnyFlatSpec { assert(table.toString contains " 0") } "TruthTable" should "deserialize" in { - assert(TruthTable(str) == table) + assert(TruthTable.fromString(str) == table) } "TruthTable" should "merge same key" in { assert( - TruthTable( + TruthTable.fromString( """001100->??1 |001100->1?? |??? |""".stripMargin - ) == TruthTable( + ) == TruthTable.fromString( """001100->1?1 |??? |""".stripMargin @@ -52,7 +53,7 @@ class TruthTableSpec extends AnyFlatSpec { } "TruthTable" should "crash when merging 0 and 1" in { intercept[IllegalArgumentException] { - TruthTable( + TruthTable.fromString( """0->0 |0->1 |??? @@ -60,4 +61,24 @@ class TruthTableSpec extends AnyFlatSpec { ) } } + "TruthTable" should "be reproducible" in { + class Foo extends Module { + + val io = IO(new Bundle{ + val in = Input(UInt(4.W)) + val out = Output(UInt(16.W)) + }) + + + val table = TruthTable( + (0 until 16).map{ + i => BitPat(i.U(4.W)) -> BitPat((1<<i).U(16.W)) + }, + BitPat.dontCare(16) + ) + + io.out := decoder.qmc(io.in, table) + } + assert(chisel3.stage.ChiselStage.emitChirrtl(new Foo) == chisel3.stage.ChiselStage.emitChirrtl(new Foo)) + } } |
