From 654db98e92c0ea96d89f0307c043951fdc73a257 Mon Sep 17 00:00:00 2001 From: SoyaOhnishi Date: Wed, 27 Jan 2021 11:29:47 +0900 Subject: Fix some typo and using foreach instead of map in BoringUtils (#1755) If a method passed to higher function does not return any value, it is prefer to use `foreach` instead of `map`. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- .../chisel3/util/experimental/BoringUtils.scala | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/BoringUtils.scala b/src/main/scala/chisel3/util/experimental/BoringUtils.scala index 18551da8..f2a3e757 100644 --- a/src/main/scala/chisel3/util/experimental/BoringUtils.scala +++ b/src/main/scala/chisel3/util/experimental/BoringUtils.scala @@ -19,7 +19,7 @@ class BoringUtilsException(message: String) extends Exception(message) /** Utilities for generating synthesizable cross module references that "bore" through the hierarchy. The underlying * cross module connects are handled by FIRRTL's Wiring Transform. * - * Consider the following exmple where you want to connect a component in one module to a component in another. Module + * Consider the following example where you want to connect a component in one module to a component in another. Module * `Constant` has a wire tied to `42` and `Expect` will assert unless connected to `42`: * {{{ * class Constant extends Module { @@ -45,8 +45,8 @@ class BoringUtilsException(message: String) extends Exception(message) * * ===Hierarchical Boring=== * - * Hierarchcical boring involves connecting one sink instance to another source instance in a parent module. Below, - * module `Top` contains an instance of `Cosntant` and `Expect`. Using [[BoringUtils.bore]], we can connect + * Hierarchical boring involves connecting one sink instance to another source instance in a parent module. Below, + * module `Top` contains an instance of `Constant` and `Expect`. Using [[BoringUtils.bore]], we can connect * `constant.x` to `expect.y`. * * {{{ @@ -89,7 +89,7 @@ class BoringUtilsException(message: String) extends Exception(message) * ==Comments== * * Both hierarchical and non-hierarchical boring emit FIRRTL annotations that describe sources and sinks. These are - * matched by a `name` key that indicates they should be wired together. Hierarhical boring safely generates this name + * matched by a `name` key that indicates they should be wired together. Hierarchical boring safely generates this name * automatically. Non-hierarchical boring unsafely relies on user input to generate this name. Use of non-hierarchical * naming may result in naming conflicts that the user must handle. * @@ -115,7 +115,7 @@ object BoringUtils { /** Add a named source cross module reference * @param component source circuit component * @param name unique identifier for this source - * @param disableDedup disable dedupblication of this source component (this should be true if you are trying to wire + * @param disableDedup disable deduplication of this source component (this should be true if you are trying to wire * from specific identical sources differently) * @param uniqueName if true, this will use a non-conflicting name from the global namespace * @return the name used @@ -137,7 +137,7 @@ object BoringUtils { def transformClass = classOf[WiringTransform] }, new ChiselAnnotation { def toFirrtl = DontTouchAnnotation(component.toNamed) } ) ++ maybeDedup - annotations.map(annotate(_)) + annotations.foreach(annotate(_)) id } @@ -146,8 +146,8 @@ object BoringUtils { * @param name unique identifier for this sink that must resolve to * @param disableDedup disable deduplication of this sink component (this should be true if you are trying to wire * specific, identical sinks differently) - * @param forceExists if true, require that the provided `name` paramater already exists in the global namespace - * @throws BoringUtilsException if name is expected to exist and itdoesn't + * @param forceExists if true, require that the provided `name` parameter already exists in the global namespace + * @throws BoringUtilsException if name is expected to exist and it doesn't */ def addSink( component: InstanceId, @@ -169,7 +169,7 @@ object BoringUtils { Seq(new ChiselAnnotation with RunFirrtlTransform { def toFirrtl = SinkAnnotation(component.toNamed, name) def transformClass = classOf[WiringTransform] }) ++ maybeDedup - annotations.map(annotate(_)) + annotations.foreach(annotate(_)) } /** Connect a source to one or more sinks @@ -187,7 +187,7 @@ object BoringUtils { case _: Exception => "bore" } val genName = addSource(source, boringName, true, true) - sinks.map(addSink(_, genName, true, true)) + sinks.foreach(addSink(_, genName, true, true)) genName } -- cgit v1.2.3 From f45216effc573d33d4aa4e525cff955ab332efbd Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 4 Feb 2021 00:36:12 +0000 Subject: Remove Deprecated APIs (#1730) --- src/main/scala/chisel3/Driver.scala | 200 --------------------- src/main/scala/chisel3/compatibility.scala | 3 - .../scala/chisel3/internal/firrtl/Emitter.scala | 2 +- src/main/scala/chisel3/util/BitPat.scala | 13 -- src/main/scala/chisel3/util/BlackBoxUtils.scala | 3 - src/main/scala/chisel3/util/Conditional.scala | 9 - 6 files changed, 1 insertion(+), 229 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala index 998f5ca0..fb564446 100644 --- a/src/main/scala/chisel3/Driver.scala +++ b/src/main/scala/chisel3/Driver.scala @@ -84,203 +84,3 @@ case class ChiselExecutionSuccess( */ @deprecated("This will be removed in Chisel 3.5", "Chisel 3.4") case class ChiselExecutionFailure(message: String) extends ChiselExecutionResult - -@deprecated("Please switch to chisel3.stage.ChiselStage", "3.2.4") -object Driver extends BackendCompilationUtilities { - - /** - * Elaborate the Module specified in the gen function into a Chisel IR Circuit. - * - * @param gen A function that creates a Module hierarchy. - * @return The resulting Chisel IR in the form of a Circuit. (TODO: Should be FIRRTL IR) - */ - @deprecated("Use ChiselStage.elaborate or use a ChiselStage class. This will be removed in 3.4.", "3.2.4") - def elaborate[T <: RawModule](gen: () => T): Circuit = internal.Builder.build(Module(gen()))._1 - - /** - * Convert the given Chisel IR Circuit to a FIRRTL Circuit. - * - * @param ir Chisel IR Circuit, generated e.g. by elaborate(). - */ - @deprecated("Use ChiselStage.convert or use a ChiselStage class. This will be removed in 3.4.", "3.2.4") - def toFirrtl(ir: Circuit): firrtl.ir.Circuit = Converter.convert(ir) - - /** - * Emit the Module specified in the gen function directly as a FIRRTL string without - * invoking FIRRTL. - * - * @param gen A function that creates a Module hierarchy. - */ - @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl. This will be removed in 3.4.", "3.2.2") - def emit[T <: RawModule](gen: () => T): String = Driver.emit(elaborate(gen)) - - /** - * Emit the given Chisel IR Circuit as a FIRRTL string, without invoking FIRRTL. - * - * @param ir Chisel IR Circuit, generated e.g. by elaborate(). - */ - @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl", "3.2.2") - def emit[T <: RawModule](ir: Circuit): String = Emitter.emit(ir) - - /** - * Elaborate the Module specified in the gen function into Verilog. - * - * @param gen A function that creates a Module hierarchy. - * @return A String containing the design in Verilog. - */ - @deprecated("Use (new chisel3.stage.ChiselStage).emitVerilog. This will be removed in 3.4.", "3.2.2") - def emitVerilog[T <: RawModule](gen: => T): String = { - execute(Array[String](), { () => gen }) match { - case ChiselExecutionSuccess(_, _, Some(firrtl.FirrtlExecutionSuccess(_, verilog))) => verilog - case _ => sys.error("Cannot get Verilog!") - } - } - - /** - * Dump the elaborated Chisel IR Circuit as a FIRRTL String, without invoking FIRRTL. - * - * If no File is given as input, it will dump to a default filename based on the name of the - * top Module. - * - * @param c Elaborated Chisel Circuit. - * @param optName File to dump to. If unspecified, defaults to ".fir". - * @return The File the circuit was dumped to. - */ - @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4") - def dumpFirrtl(ir: Circuit, optName: Option[File]): File = { - val f = optName.getOrElse(new File(ir.name + ".fir")) - val w = new FileWriter(f) - w.write(Driver.emit(ir)) - w.close() - f - } - - /** - * Emit the annotations of a circuit - * - * @param ir The circuit containing annotations to be emitted - * @param optName An optional filename (will use s"\${ir.name}.json" otherwise) - */ - @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4") - def dumpAnnotations(ir: Circuit, optName: Option[File]): File = { - val f = optName.getOrElse(new File(ir.name + ".anno.json")) - val w = new FileWriter(f) - w.write(JsonProtocol.serialize(ir.annotations.map(_.toFirrtl))) - w.close() - f - } - - /** - * Dump the elaborated Circuit to ProtoBuf. - * - * If no File is given as input, it will dump to a default filename based on the name of the - * top Module. - * - * @param c Elaborated Chisel Circuit. - * @param optFile Optional File to dump to. If unspecified, defaults to ".pb". - * @return The File the circuit was dumped to. - */ - @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4") - def dumpProto(c: Circuit, optFile: Option[File]): File = { - val f = optFile.getOrElse(new File(c.name + ".pb")) - val ostream = new java.io.FileOutputStream(f) - // Lazily convert modules to make intermediate objects garbage collectable - val modules = c.components.map(m => () => Converter.convert(m)) - firrtl.proto.ToProto.writeToStreamFast(ostream, ir.NoInfo, modules, c.name) - f - } - - private var target_dir: Option[String] = None - @deprecated("Use chisel3.stage.ChiselStage with '--target-directory'. This will be removed in 3.4.", "3.2.2") - def parseArgs(args: Array[String]): Unit = { - for (i <- 0 until args.size) { - if (args(i) == "--targetDir") { - target_dir = Some(args(i + 1)) - } - } - } - - @deprecated("This has no effect on Chisel3 Driver! This will be removed in 3.4.", "3.2.2") - def targetDir(): String = { target_dir getOrElse new File(".").getCanonicalPath } - - /** - * Run the chisel3 compiler and possibly the firrtl compiler with options specified - * - * @param optionsManager The options specified - * @param dut The device under test - * @return An execution result with useful stuff, or failure with message - */ - @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2") - def execute( - optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions, - dut: () => RawModule): ChiselExecutionResult = { - - val annos: AnnotationSeq = - Seq(DriverCompatibility.OptionsManagerAnnotation(optionsManager), ChiselGeneratorAnnotation(dut)) ++ - optionsManager.chiselOptions.toAnnotations ++ - optionsManager.firrtlOptions.toAnnotations ++ - optionsManager.commonOptions.toAnnotations - - val targets = - Seq( Dependency[DriverCompatibility.AddImplicitOutputFile], - Dependency[DriverCompatibility.AddImplicitOutputAnnotationFile], - Dependency[DriverCompatibility.DisableFirrtlStage], - Dependency[ChiselStage], - Dependency[DriverCompatibility.MutateOptionsManager], - Dependency[DriverCompatibility.ReEnableFirrtlStage], - Dependency[DriverCompatibility.FirrtlPreprocessing], - Dependency[chisel3.stage.phases.MaybeFirrtlStage] ) - val currentState = - Seq( Dependency[firrtl.stage.phases.DriverCompatibility.AddImplicitFirrtlFile], - Dependency[chisel3.stage.phases.Convert] ) - - val phases: Seq[Phase] = new PhaseManager(targets, currentState) { - override val wrappers = Seq( DeletedWrapper(_: Phase) ) - }.transformOrder - - val annosx = try { - phases.foldLeft(annos)( (a, p) => p.transform(a) ) - } catch { - /* ChiselStage and FirrtlStage can throw StageError. Since Driver is not a StageMain, it cannot catch these. While - * Driver is deprecated and removed in 3.2.1+, the Driver catches all errors. - */ - case e: StageError => annos - } - - view[ChiselExecutionResult](annosx) - } - - /** - * Run the chisel3 compiler and possibly the firrtl compiler with options specified via an array of Strings - * - * @param args The options specified, command line style - * @param dut The device under test - * @return An execution result with useful stuff, or failure with message - */ - @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2") - def execute(args: Array[String], dut: () => RawModule): ChiselExecutionResult = { - val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions - - optionsManager.parse(args) match { - case true => - execute(optionsManager, dut) - case _ => - ChiselExecutionFailure("could not parse results") - } - } - - /** - * This is just here as command line way to see what the options are - * It will not successfully run - * TODO: Look into dynamic class loading as way to make this main useful - * - * @param args unused args - */ - @deprecated("Use chisel3.stage.ChiselMain. This will be removed in 3.4.", "3.2.2") - def main(args: Array[String]) { - execute(Array("--help"), null) - } - - val version = BuildInfo.version - val chiselVersionString = BuildInfo.toString -} diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index e62cba7d..3b9a3dcc 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -363,8 +363,6 @@ package object Chisel { implicit class fromIntToWidth(x: Int) extends chisel3.fromIntToWidth(x) type BackendCompilationUtilities = firrtl.util.BackendCompilationUtilities - @deprecated("Please switch to chisel3.stage.ChiselStage", "3.4") - val Driver = chisel3.Driver val ImplicitConversions = chisel3.util.ImplicitConversions // Deprecated as of Chisel3 @@ -455,7 +453,6 @@ package object Chisel { val Log2 = chisel3.util.Log2 - val unless = chisel3.util.unless type SwitchContext[T <: Bits] = chisel3.util.SwitchContext[T] val is = chisel3.util.is val switch = chisel3.util.switch diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 354be0c0..ad4df80a 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -29,7 +29,7 @@ private class Emitter(circuit: Circuit) { case d: Clock => "Clock" case _: AsyncReset => "AsyncReset" case _: ResetType => "Reset" - case d: chisel3.core.EnumType => s"UInt${d.width}" + case d: chisel3.experimental.EnumType => s"UInt${d.width}" case d: UInt => s"UInt${d.width}" case d: SInt => s"SInt${d.width}" case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}" diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 40563e23..12a421a0 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -91,12 +91,6 @@ object BitPat { /** @group SourceInfoTransformMacro */ def do_=/= (that: BitPat) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that =/= x - - final def != (that: BitPat): Bool = macro SourceInfoTransform.thatArg - @chiselRuntimeDeprecated - @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") - def do_!= (that: BitPat) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that != x } } @@ -125,11 +119,4 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou !(this === that) } - def != (that: UInt): Bool = macro SourceInfoTransform.thatArg - @chiselRuntimeDeprecated - @deprecated("Use '=/=', which avoids potential precedence problems", "3.0") - def do_!= (that: UInt) - (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { - this =/= that - } } diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala index 21bd4dfa..3cd704b3 100644 --- a/src/main/scala/chisel3/util/BlackBoxUtils.scala +++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala @@ -9,9 +9,6 @@ import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInline trait HasBlackBoxResource extends BlackBox { self: BlackBox => - @deprecated("Use addResource instead", "3.2") - def setResource(blackBoxResource: String): Unit = addResource(blackBoxResource) - /** Copies a resource file to the target directory * * Resource files are located in project_root/src/main/resources/. diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala index b934f27f..1ac94bfe 100644 --- a/src/main/scala/chisel3/util/Conditional.scala +++ b/src/main/scala/chisel3/util/Conditional.scala @@ -12,15 +12,6 @@ import scala.reflect.macros.blackbox._ import chisel3._ import chisel3.internal.sourceinfo.SourceInfo -@deprecated("The unless conditional is deprecated, use when(!condition){...} instead", "3.2") -object unless { - /** Does the same thing as [[when$ when]], but with the condition inverted. - */ - def apply(c: Bool)(block: => Any) { - when (!c) { block } - } -} - /** Implementation details for [[switch]]. See [[switch]] and [[chisel3.util.is is]] for the * user-facing API. * @note DO NOT USE. This API is subject to change without warning. -- cgit v1.2.3 From 2ed343e2305b7c22000f3f46fa81d73a369907eb Mon Sep 17 00:00:00 2001 From: Vladimir Milovanović Date: Mon, 8 Feb 2021 20:55:48 +0100 Subject: Parametrized Mem- & SyncReadMem-based implementation of the Queue class (#1740) * Added SyncReadMem-based implementation of the Queue class * Rework of the parametrized Queue class SyncReadMem-based implementation * Modification of the parametrized Queue class SyncReadMem-based implementation * Limiting the visibility of the read address for SyncReadMem-based Queue Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/Decoupled.scala | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 032d731d..006001ec 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -186,6 +186,7 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle * combinationally coupled. * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately). * The ''valid'' signals are coupled. + * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element. * * @example {{{ * val q = Module(new Queue(UInt(), 16)) @@ -197,7 +198,8 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle class Queue[T <: Data](val gen: T, val entries: Int, val pipe: Boolean = false, - val flow: Boolean = false) + val flow: Boolean = false, + val useSyncReadMem: Boolean = false) (implicit compileOptions: chisel3.CompileOptions) extends Module() { require(entries > -1, "Queue must have non-negative number of entries") @@ -215,7 +217,7 @@ class Queue[T <: Data](val gen: T, val io = IO(new QueueIO(genType, entries)) - val ram = Mem(entries, genType) + val ram = if (useSyncReadMem) SyncReadMem(entries, genType, SyncReadMem.WriteFirst) else Mem(entries, genType) val enq_ptr = Counter(entries) val deq_ptr = Counter(entries) val maybe_full = RegInit(false.B) @@ -239,7 +241,15 @@ class Queue[T <: Data](val gen: T, io.deq.valid := !empty io.enq.ready := !full - io.deq.bits := ram(deq_ptr.value) + + if (useSyncReadMem) { + val deq_ptr_next = Mux(deq_ptr.value === (entries.U - 1.U), 0.U, deq_ptr.value + 1.U) + val r_addr = WireDefault(Mux(do_deq, deq_ptr_next, deq_ptr.value)) + io.deq.bits := ram.read(r_addr) + } + else { + io.deq.bits := ram(deq_ptr.value) + } if (flow) { when (io.enq.valid) { io.deq.valid := true.B } @@ -285,7 +295,8 @@ object Queue enq: ReadyValidIO[T], entries: Int = 2, pipe: Boolean = false, - flow: Boolean = false): DecoupledIO[T] = { + flow: Boolean = false, + useSyncReadMem: Boolean = false): DecoupledIO[T] = { if (entries == 0) { val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits))) deq.valid := enq.valid @@ -293,7 +304,7 @@ object Queue enq.ready := deq.ready deq } else { - val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow)) + val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem)) q.io.enq.valid := enq.valid // not using <> so that override is allowed q.io.enq.bits := enq.bits enq.ready := q.io.enq.ready @@ -311,8 +322,9 @@ object Queue enq: ReadyValidIO[T], entries: Int = 2, pipe: Boolean = false, - flow: Boolean = false): IrrevocableIO[T] = { - val deq = apply(enq, entries, pipe, flow) + flow: Boolean = false, + useSyncReadMem: Boolean = false): IrrevocableIO[T] = { + val deq = apply(enq, entries, pipe, flow, useSyncReadMem) require(entries > 0, "Zero-entry queues don't guarantee Irrevocability") val irr = Wire(new IrrevocableIO(chiselTypeOf(deq.bits))) irr.bits := deq.bits -- cgit v1.2.3 From 2b5466c7773c8cd7a08c48aa00d9365cbb205fd2 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 10 Feb 2021 22:19:09 -0500 Subject: Fix stack trace trimming across Driver/ChiselStage (#1771) * Handle MemTypeBinding in Analog Signed-off-by: Schuyler Eldridge * Fix stack trace trimming across ChiselStage Fix bug in stack trace trimming behavior. Now, the following is what happens: 1. The Builder, if catching accumulated errors, will now throw a ChiselException with a Scala-trimmed Stack trace. Previously, this would throw the full excpetion. 2. The Elaborate phase handles stack trace trimming. By default, any Throwable thrown during elaboration will have its stack trace *mutably* trimmed and is rethrown. A logger.error is printed stating that there was an error during elaboration and how the user can turn on the full stack trace. If the --full-stacktrace option is on, then the Throwable is not caught and only the first logger.error (saying that elaboration failed) will be printed. 3. ChiselStage (the class), ChiselStage$ (the object), and ChiselMain all inherit the behavior of (2). Mutable stack trace trimming behavior is moved into an implicit class (previously this was defined on ChiselException only) so this can be applied to any Throwable. No StageErrors are now thrown anymore. However, StageErrors may still be caught by ChiselMain (since it is a StageMain). Testing is added for ChiselMain, ChiselStage, and ChiselStage$ to test all this behavior. Signed-off-by: Schuyler Eldridge --- .../scala/chisel3/stage/ChiselAnnotations.scala | 6 +----- src/main/scala/chisel3/stage/ChiselStage.scala | 22 +++------------------- .../scala/chisel3/stage/phases/Elaborate.scala | 15 +++++++++++++-- 3 files changed, 17 insertions(+), 26 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index bbe86ab4..04246fc5 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -56,13 +56,9 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot /** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]] */ - def elaborate: AnnotationSeq = try { + def elaborate: AnnotationSeq = { val (circuit, dut) = Builder.build(Module(gen())) Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut)) - } catch { - case e @ (_: OptionsException | _: ChiselException) => throw e - case e: Throwable => - throw new ChiselException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e) } } diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala index aae7ad8d..1bcf5124 100644 --- a/src/main/scala/chisel3/stage/ChiselStage.scala +++ b/src/main/scala/chisel3/stage/ChiselStage.scala @@ -13,7 +13,7 @@ import firrtl.{ VerilogEmitter, SystemVerilogEmitter } -import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageError, StageMain} +import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageMain} import firrtl.options.phases.DeletedWrapper import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlCli, RunFirrtlTransformAnnotation} import firrtl.options.Viewer.view @@ -28,7 +28,7 @@ class ChiselStage extends Stage { override def prerequisites = Seq.empty override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq.empty + override def optionalPrerequisiteOf = Seq(Dependency[firrtl.stage.FirrtlStage]) override def invalidates(a: Phase) = false val shell: Shell = new Shell("chisel") with ChiselCli with FirrtlCli @@ -42,23 +42,7 @@ class ChiselStage extends Stage { } } - def run(annotations: AnnotationSeq): AnnotationSeq = try { - phaseManager.transform(annotations) - } catch { - case ce: ChiselException => - val stackTrace = if (!view[ChiselOptions](annotations).printFullStackTrace) { - ce.chiselStackTrace - } else { - val sw = new StringWriter - ce.printStackTrace(new PrintWriter(sw)) - sw.toString - } - Predef - .augmentString(stackTrace) - .lines - .foreach(line => println(s"${ErrorLog.errTag} $line")) - throw new StageError(cause=ce) - } + def run(annotations: AnnotationSeq): AnnotationSeq = phaseManager.transform(annotations) /** Convert a Chisel module to a CHIRRTL string * @param gen a call-by-name Chisel module diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala index 04cfc33e..df39b66b 100644 --- a/src/main/scala/chisel3/stage/phases/Elaborate.scala +++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala @@ -6,6 +6,7 @@ import java.io.{PrintWriter, StringWriter} import chisel3.ChiselException import chisel3.internal.ErrorLog +import chisel3.internal.ExceptionHelpers.ThrowableHelpers import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions} import firrtl.AnnotationSeq import firrtl.options.Viewer.view @@ -21,8 +22,18 @@ class Elaborate extends Phase { override def invalidates(a: Phase) = false def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap { - case a: ChiselGeneratorAnnotation => a.elaborate - case a => Some(a) + case a: ChiselGeneratorAnnotation => try { + a.elaborate + } catch { + /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace + */ + case scala.util.control.NonFatal(a) => + if (!view[ChiselOptions](annotations).printFullStackTrace) { + a.trimStackTraceToUserCode() + } + throw(a) + } + case a => Some(a) } } -- cgit v1.2.3 From 923ccbde1353e37f0948d3c5d94b49965dc6d950 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sat, 27 Feb 2021 05:01:10 +0800 Subject: Expose AnnotationSeq to Module. (#1731) --- .../scala/chisel3/aop/injecting/InjectingAspect.scala | 2 +- src/main/scala/chisel3/stage/ChiselAnnotations.scala | 6 +----- src/main/scala/chisel3/stage/phases/AspectPhase.scala | 2 +- src/main/scala/chisel3/stage/phases/Elaborate.scala | 15 +++++++-------- .../scala/chisel3/util/experimental/getAnnotations.scala | 9 +++++++++ 5 files changed, 19 insertions(+), 15 deletions(-) create mode 100644 src/main/scala/chisel3/util/experimental/getAnnotations.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 39590b93..170bfbad 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -54,7 +54,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( * @return */ final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = { - val dynamicContext = new DynamicContext() + val dynamicContext = new DynamicContext(annotationsInAspect) // Add existing module names into the namespace. If injection logic instantiates new modules // which would share the same name, they will get uniquified accordingly moduleNames.foreach { n => diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index 04246fc5..b071c60f 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -56,11 +56,7 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot /** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]] */ - def elaborate: AnnotationSeq = { - val (circuit, dut) = Builder.build(Module(gen())) - Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut)) - } - + def elaborate: AnnotationSeq = (new chisel3.stage.phases.Elaborate).transform(Seq(this)) } object ChiselGeneratorAnnotation extends HasShellOptions { diff --git a/src/main/scala/chisel3/stage/phases/AspectPhase.scala b/src/main/scala/chisel3/stage/phases/AspectPhase.scala index 1a27b8fa..72965861 100644 --- a/src/main/scala/chisel3/stage/phases/AspectPhase.scala +++ b/src/main/scala/chisel3/stage/phases/AspectPhase.scala @@ -29,7 +29,7 @@ class AspectPhase extends Phase { case other => Seq(other) } if(dut.isDefined) { - val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get) } + val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get, remainingAnnotations) } remainingAnnotations ++ newAnnotations } else annotations } diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala index df39b66b..e8f2623e 100644 --- a/src/main/scala/chisel3/stage/phases/Elaborate.scala +++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala @@ -2,15 +2,13 @@ package chisel3.stage.phases -import java.io.{PrintWriter, StringWriter} - -import chisel3.ChiselException -import chisel3.internal.ErrorLog +import chisel3.Module import chisel3.internal.ExceptionHelpers.ThrowableHelpers -import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions} +import chisel3.internal.{Builder, DynamicContext} +import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselOptions, DesignAnnotation} import firrtl.AnnotationSeq +import firrtl.options.Phase import firrtl.options.Viewer.view -import firrtl.options.{OptionsException, Phase} /** Elaborate all [[chisel3.stage.ChiselGeneratorAnnotation]]s into [[chisel3.stage.ChiselCircuitAnnotation]]s. */ @@ -22,8 +20,9 @@ class Elaborate extends Phase { override def invalidates(a: Phase) = false def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap { - case a: ChiselGeneratorAnnotation => try { - a.elaborate + case ChiselGeneratorAnnotation(gen) => try { + val (circuit, dut) = Builder.build(Module(gen()), new DynamicContext(annotations)) + Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut)) } catch { /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace */ diff --git a/src/main/scala/chisel3/util/experimental/getAnnotations.scala b/src/main/scala/chisel3/util/experimental/getAnnotations.scala new file mode 100644 index 00000000..dc9b75ee --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/getAnnotations.scala @@ -0,0 +1,9 @@ +package chisel3.util.experimental + +import chisel3.internal.Builder +import firrtl.AnnotationSeq + +object getAnnotations { + /** Returns the global Annotations */ + def apply(): AnnotationSeq = Builder.annotationSeq +} -- cgit v1.2.3 From 43de37fdd48bcb6bbc861553715b06be3c67f6bf Mon Sep 17 00:00:00 2001 From: Jerry Zhao Date: Mon, 1 Mar 2021 11:39:33 -0800 Subject: Fix conversions between DecoupledIO and IrrevocableIO (#1781) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/Decoupled.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 006001ec..33682020 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -108,7 +108,7 @@ object Decoupled @chiselName def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = { require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.") - val d = Wire(new DecoupledIO(irr.bits)) + val d = Wire(new DecoupledIO(chiselTypeOf(irr.bits))) d.bits := irr.bits d.valid := irr.valid irr.ready := d.ready @@ -139,7 +139,7 @@ object Irrevocable */ def apply[T <: Data](dec: DecoupledIO[T]): IrrevocableIO[T] = { require(DataMirror.directionOf(dec.bits) == Direction.Input, "Only safe to cast consumed Decoupled bits to Irrevocable.") - val i = Wire(new IrrevocableIO(dec.bits)) + val i = Wire(new IrrevocableIO(chiselTypeOf(dec.bits))) dec.bits := i.bits dec.valid := i.valid i.ready := dec.ready -- cgit v1.2.3 From 9ea57e031f834841609a30031ddab08fe4f47029 Mon Sep 17 00:00:00 2001 From: Carlos Eduardo Date: Thu, 11 Mar 2021 18:25:54 -0300 Subject: Import memory files inline for Verilog generation (#1805) This annotation adds memory import with inline generation for the emmiter. Supports both readmemh and readmemb statements based on argument.--- .../util/experimental/LoadMemoryTransform.scala | 82 +++++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala index d91d97b7..93981485 100644 --- a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala +++ b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala @@ -40,7 +40,7 @@ case class ChiselLoadMemoryAnnotation[T <: Data]( } -/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file. This relies on +/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file as a bind module. This relies on * Verilator and Verilog's `\$readmemh` or `\$readmemb`. The [[https://github.com/freechipsproject/treadle Treadle * backend]] can also recognize this annotation and load memory at run-time. * @@ -116,6 +116,86 @@ object loadMemoryFromFile { } } + +/** [[loadMemoryFromFileInline]] is an annotation generator that helps with loading a memory from a text file inlined in + * the Verilog module. This relies on Verilator and Verilog's `\$readmemh` or `\$readmemb`. + * The [[https://github.com/freechipsproject/treadle Treadlebackend]] can also recognize this annotation and load memory at run-time. + * + * This annotation, when the FIRRTL compiler runs, triggers the [[MemoryFileInlineAnnotation]] that will add Verilog + * directives inlined to the module enabling the specified memories to be initialized from files. + * The module supports both `hex` and `bin` files by passing the appropriate [[MemoryLoadFileType.FileType]] argument with + * [[MemoryLoadFileType.Hex]] or [[MemoryLoadFileType.Binary]]. Hex is the default. + * + * ==Example module== + * + * Consider a simple Module containing a memory: + * {{{ + * import chisel3._ + * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module { + * val io = IO(new Bundle { + * val address = Input(UInt(memoryType.getWidth.W)) + * val value = Output(memoryType) + * }) + * val memory = Mem(memoryDepth, memoryType) + * io.value := memory(io.address) + * } + * }}} + * + * ==Above module with annotation== + * + * To load this memory from the file `/workspace/workdir/mem1.hex.txt` just add an import and annotate the memory: + * {{{ + * import chisel3._ + * import chisel3.util.experimental.loadMemoryFromFileInline // <<-- new import here + * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module { + * val io = IO(new Bundle { + * val address = Input(UInt(memoryType.getWidth.W)) + * val value = Output(memoryType) + * }) + * val memory = Mem(memoryDepth, memoryType) + * io.value := memory(io.address) + * loadMemoryFromFileInline(memory, "/workspace/workdir/mem1.hex.txt") // <<-- Note the annotation here + * } + * }}} + * + * ==Example file format== + * + * A memory file should consist of ASCII text in either hex or binary format. The following example shows such a + * file formatted to use hex: + * {{{ + * 0 + * 7 + * d + * 15 + * }}} + * + * A binary file can be similarly constructed. + * Chisel does not validate the file format or existence. It is supposed to be in a path accessible by the synthesis + * tool together with the generated Verilog. + * + * @see Chisel3 Wiki entry on + * [[https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories#loading-memories-in-simulation "Loading Memories + * in Simulation"]] + */ +object loadMemoryFromFileInline { + + + /** Annotate a memory such that it can be initialized inline using a file + * @param memory the memory + * @param fileName the file used for initialization + * @param hexOrBinary whether the file uses a hex or binary number representation + */ + def apply[T <: Data]( + memory: MemBase[T], + fileName: String, + hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex + ): Unit = { + annotate(new ChiselAnnotation { + override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, fileName, hexOrBinary) + }) + } +} + /** This transform only is activated if Verilog is being generated (determined by presence of the proper emit * annotation) when activated it creates additional Verilog files that contain modules bound to the modules that * contain an initializable memory -- cgit v1.2.3 From ac094a5e8f7fb8237b54d4d6b9e0b7b759b44ef7 Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 18 Mar 2021 12:28:09 -0400 Subject: Don't toggle top.cpp clock and reset on same cycle (#1820) Change top.cpp to deassert reset one time unit before the clock asserts. This avoids a Verilator simultation issue in top.cpp where the eval() function is only called once per simultation loop. If the clock and reset are both changed and eval() is only called once, then any combinational update due to a change in reset is not visible to the sequential logic. This avoids issues where the downstream compilation utilities move synchronous reset logic outside of an always block that describes a synchronous reset flip flop. Reset now deasserts on time unit 10 and the clock ticks on time unit 11. h/t @albert-magyar Signed-off-by: Schuyler Eldridge --- src/main/resources/chisel3/top.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'src/main') diff --git a/src/main/resources/chisel3/top.cpp b/src/main/resources/chisel3/top.cpp index 4e9c1433..a3475e2f 100644 --- a/src/main/resources/chisel3/top.cpp +++ b/src/main/resources/chisel3/top.cpp @@ -47,8 +47,15 @@ int main(int argc, char** argv) { cout << "Starting simulation!\n"; while (!Verilated::gotFinish() && main_time < timeout) { - if (main_time > 10) { - top->reset = 0; // Deassert reset + // Deassert reset on timestep 10. This needs to occur before the clock + // asserts on timestep 11 because there is a single call to top->eval() in + // this loop. Verilator evaluates sequential logic (always blocks) before + // combinational logic during top->eval(). Staggering the reset update is + // necessary to produce the same simulation behavior independent of whether + // or not the generated Verilog puts synchronous reset logic inside or + // outside its associated always block. + if (main_time == 10) { + top->reset = 0; } if ((main_time % 10) == 1) { top->clock = 1; // Toggle clock @@ -92,4 +99,3 @@ int main(int argc, char** argv) { if (tfp) tfp->close(); #endif } - -- cgit v1.2.3 From 2a56c6540e914611ac12647e157aec4c5c595758 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Fri, 19 Mar 2021 01:28:56 +0800 Subject: Add toString method to BitPat (#1819) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/BitPat.scala | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 12a421a0..5a40d312 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -119,4 +119,11 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou !(this === that) } + override def toString = { + "BitPat(" + + (0 until width).map(i => + if (((mask >> i) & 1) == 1) if (((value >> i) & 1) == 1) "1" else "0" else "?" + ).reverse.reduce(_ + _) + + ")" + } } -- cgit v1.2.3 From 2c7264a6d923e2d1dc645c8b7dec2add7fb6cfbc Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Wed, 21 Apr 2021 14:47:03 -0700 Subject: fixing context bug (#1874) --- .../scala/chisel3/aop/injecting/InjectingAspect.scala | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 170bfbad..768680ed 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -54,13 +54,14 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( * @return */ final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = { - val dynamicContext = new DynamicContext(annotationsInAspect) - // Add existing module names into the namespace. If injection logic instantiates new modules - // which would share the same name, they will get uniquified accordingly - moduleNames.foreach { n => - dynamicContext.globalNamespace.name(n) - } RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module => + val dynamicContext = new DynamicContext(annotationsInAspect) + // Add existing module names into the namespace. If injection logic instantiates new modules + // which would share the same name, they will get uniquified accordingly + moduleNames.foreach { n => + dynamicContext.globalNamespace.name(n) + } + val (chiselIR, _) = Builder.build(Module(new ModuleAspect(module) { module match { case x: Module => withClockAndReset(x.clock, x.reset) { injection(module) } @@ -75,11 +76,15 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( val annotations = chiselIR.annotations.map(_.toFirrtl).filterNot{ a => a.isInstanceOf[DesignAnnotation[_]] } + /** Statements to be injected via aspect. */ val stmts = mutable.ArrayBuffer[ir.Statement]() + /** Modules to be injected via aspect. */ val modules = Aspect.getFirrtl(chiselIR.copy(components = comps)).modules.flatMap { + // for "container" modules, inject their statements case m: firrtl.ir.Module if m.name == module.name => stmts += m.body Nil + // for modules to be injected case other: firrtl.ir.DefModule => Seq(other) } -- cgit v1.2.3 From c5861176887bfa529277e686df09a42aeceb6cd7 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 29 Apr 2021 16:18:06 -0700 Subject: Scala 2.13 support (#1751) --- src/main/scala/chisel3/aop/Select.scala | 8 ++++---- src/main/scala/chisel3/aop/injecting/InjectingAspect.scala | 2 +- src/main/scala/chisel3/compatibility.scala | 2 +- src/main/scala/chisel3/util/MixedVec.scala | 4 ++++ 4 files changed, 10 insertions(+), 6 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index e2689f39..b9ad808b 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -248,7 +248,7 @@ object Select { case other => } }) - predicatedConnects + predicatedConnects.toSeq } /** Selects all stop statements, and includes the predicates surrounding the stop statement @@ -264,7 +264,7 @@ object Select { case other => } }) - stops + stops.toSeq } /** Selects all printf statements, and includes the predicates surrounding the printf statement @@ -280,7 +280,7 @@ object Select { case other => } }) - printfs + printfs.toSeq } // Checks that a module has finished its construction @@ -321,7 +321,7 @@ object Select { } } catch { case e: ChiselException => i.getOptionRef.get match { - case l: LitArg => l.num.intValue().toString + case l: LitArg => l.num.intValue.toString } } diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index 768680ed..c540fc83 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -89,7 +89,7 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( Seq(other) } - InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts), modules, annotations) + InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts.toSeq), modules, annotations) }.toSeq } } diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala index 3b9a3dcc..dde2321d 100644 --- a/src/main/scala/chisel3/compatibility.scala +++ b/src/main/scala/chisel3/compatibility.scala @@ -398,8 +398,8 @@ package object Chisel { } // Deprecated as of Chsiel3 - @throws(classOf[Exception]) object throwException { + @throws(classOf[Exception]) def apply(s: String, t: Throwable = null): Nothing = { val xcpt = new Exception(s, t) throw xcpt diff --git a/src/main/scala/chisel3/util/MixedVec.scala b/src/main/scala/chisel3/util/MixedVec.scala index a632ec3a..14d6be38 100644 --- a/src/main/scala/chisel3/util/MixedVec.scala +++ b/src/main/scala/chisel3/util/MixedVec.scala @@ -91,6 +91,10 @@ final class MixedVec[T <: Data](private val eltsIn: Seq[T]) extends Record with eltsIn.foreach(e => requireIsChiselType(e)) } + // In Scala 2.13, this is protected in IndexedSeq, must override as public because it's public in + // Record + override def className: String = "MixedVec" + // Clone the inputs so that we have our own references. private val elts: IndexedSeq[T] = eltsIn.map(_.cloneTypeFull).toIndexedSeq -- cgit v1.2.3 From 7dd2d7db355d8dd9e1fc49ed7cd479ce5273b691 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Fri, 30 Apr 2021 05:12:24 +0000 Subject: add helper function to convert chirrtl to firrtl. (#1854) * add convert(chirrtl: cir.Circuit): fir.Circuit to convert chirrtl to firrtl. * add scaladoc. * add test. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/stage/ChiselStage.scala | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala index 1bcf5124..989b3a17 100644 --- a/src/main/scala/chisel3/stage/ChiselStage.scala +++ b/src/main/scala/chisel3/stage/ChiselStage.scala @@ -172,6 +172,26 @@ object ChiselStage { .get } + /** Return a [[firrtl.ir.Circuit]] for a [[chisel3.internal.firrtl.Circuit]](aka chirrtl) + * @param chirrtl [[chisel3.internal.firrtl.Circuit]] which need to be converted to [[firrtl.ir.Circuit]] + */ + def convert(chirrtl: cir.Circuit): fir.Circuit = { + val phase = new ChiselPhase { + override val targets = Seq( + Dependency[chisel3.stage.phases.AddImplicitOutputFile], + Dependency[chisel3.stage.phases.AddImplicitOutputAnnotationFile], + Dependency[chisel3.stage.phases.MaybeAspectPhase], + Dependency[chisel3.stage.phases.Convert] ) + } + + phase + .transform(Seq(ChiselCircuitAnnotation(chirrtl))) + .collectFirst { + case FirrtlCircuitAnnotation(a) => a + } + .get + } + /** Return a CHIRRTL string for a Chisel module * @param gen a call-by-name Chisel module */ -- cgit v1.2.3 From 365a51a8ce692c85df60427e0562e89945d9797d Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Wed, 5 May 2021 13:18:57 -0400 Subject: Remove chisel3.stage.phases.DriverCompatibility (#1772) --- .../chisel3/stage/phases/DriverCompatibility.scala | 152 +-------------------- 1 file changed, 2 insertions(+), 150 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala index 659914ae..9305c5c9 100644 --- a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala +++ b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala @@ -16,153 +16,5 @@ import chisel3.stage.{ChiselStage, NoRunFirrtlCompilerAnnotation, ChiselOutputFi * Primarily, this object includes [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s * derived from the deprecated [[firrtl.stage.phases.DriverCompatibility.TopNameAnnotation]]. */ -object DriverCompatibility { - - /** Adds a [[ChiselOutputFileAnnotation]] derived from a [[TopNameAnnotation]] if no [[ChiselOutputFileAnnotation]] - * already exists. If no [[TopNameAnnotation]] exists, then no [[firrtl.stage.OutputFileAnnotation]] is added. ''This is not a - * replacement for [[chisel3.stage.phases.AddImplicitOutputFile AddImplicitOutputFile]] as this only adds an output - * file based on a discovered top name and not on a discovered elaborated circuit.'' Consequently, this will provide - * the correct behavior before a circuit has been elaborated. - * @note the output suffix is unspecified and will be set by the underlying [[firrtl.EmittedComponent]] - */ - private [chisel3] class AddImplicitOutputFile extends Phase { - - override def prerequisites = Seq.empty - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage]) - override def invalidates(a: Phase) = false - - def transform(annotations: AnnotationSeq): AnnotationSeq = { - val hasOutputFile = annotations - .collectFirst{ case a: ChiselOutputFileAnnotation => a } - .isDefined - lazy val top = annotations.collectFirst{ case TopNameAnnotation(a) => a } - - if (!hasOutputFile && top.isDefined) { - ChiselOutputFileAnnotation(top.get) +: annotations - } else { - annotations - } - } - } - - /** If a [[firrtl.options.OutputAnnotationFileAnnotation]] does not exist, this adds one derived from a - * [[TopNameAnnotation]]. ''This is not a replacement for [[chisel3.stage.phases.AddImplicitOutputAnnotationFile]] as - * this only adds an output annotation file based on a discovered top name.'' Consequently, this will provide the - * correct behavior before a circuit has been elaborated. - * @note the output suffix is unspecified and will be set by [[firrtl.options.phases.WriteOutputAnnotations]] - */ - private[chisel3] class AddImplicitOutputAnnotationFile extends Phase { - - override def prerequisites = Seq.empty - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage]) - override def invalidates(a: Phase) = false - - def transform(annotations: AnnotationSeq): AnnotationSeq = - annotations - .collectFirst{ case _: OutputAnnotationFileAnnotation => annotations } - .getOrElse{ - val top = annotations.collectFirst{ case TopNameAnnotation(a) => a } - if (top.isDefined) { - OutputAnnotationFileAnnotation(top.get) +: annotations - } else { - annotations - } - } - } - - private[chisel3] case object RunFirrtlCompilerAnnotation extends NoTargetAnnotation - - /** Disables the execution of [[firrtl.stage.FirrtlStage]]. This can be used to call [[chisel3.stage.ChiselStage]] and - * guarantee that the FIRRTL compiler will not run. This is necessary for certain [[chisel3.Driver]] compatibility - * situations where you need to do something between Chisel compilation and FIRRTL compilations, e.g., update a - * mutable data structure. - */ - private[chisel3] class DisableFirrtlStage extends Phase { - - override def prerequisites = Seq.empty - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq(Dependency[ChiselStage]) - override def invalidates(a: Phase) = false - - def transform(annotations: AnnotationSeq): AnnotationSeq = annotations - .collectFirst { case NoRunFirrtlCompilerAnnotation => annotations } - .getOrElse { Seq(RunFirrtlCompilerAnnotation, NoRunFirrtlCompilerAnnotation) ++ annotations } - } - - private[chisel3] class ReEnableFirrtlStage extends Phase { - - override def prerequisites = Seq(Dependency[DisableFirrtlStage], Dependency[ChiselStage]) - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq.empty - override def invalidates(a: Phase) = false - - def transform(annotations: AnnotationSeq): AnnotationSeq = annotations - .collectFirst { case RunFirrtlCompilerAnnotation => - val a: AnnotationSeq = annotations.filter { - case NoRunFirrtlCompilerAnnotation | RunFirrtlCompilerAnnotation => false - case _ => true - } - a - } - .getOrElse{ annotations } - - } - - private[chisel3] case class OptionsManagerAnnotation( - manager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions) - extends NoTargetAnnotation with Unserializable - - /** Mutate an input [[firrtl.ExecutionOptionsManager]] based on information encoded in an [[firrtl.AnnotationSeq]]. - * This is intended to be run between [[chisel3.stage.ChiselStage ChiselStage]] and [[firrtl.stage.FirrtlStage]] if - * you want to have backwards compatibility with an [[firrtl.ExecutionOptionsManager]]. - */ - private[chisel3] class MutateOptionsManager extends Phase { - - override def prerequisites = Seq(Dependency[chisel3.stage.ChiselStage]) - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq(Dependency[ReEnableFirrtlStage]) - override def invalidates(a: Phase) = false - - def transform(annotations: AnnotationSeq): AnnotationSeq = { - - val optionsManager = annotations - .collectFirst{ case OptionsManagerAnnotation(a) => a } - .getOrElse{ throw new OptionsException( - "An OptionsManagerException must exist for Chisel Driver compatibility mode") } - - val firrtlCircuit = annotations.collectFirst{ case FirrtlCircuitAnnotation(a) => a } - optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy( - firrtlCircuit = firrtlCircuit, - annotations = optionsManager.firrtlOptions.annotations ++ annotations, - customTransforms = optionsManager.firrtlOptions.customTransforms ++ - annotations.collect{ case RunFirrtlTransformAnnotation(a) => a } ) - - annotations - - } - - } - - /** A [[Phase]] that lets us run - * @todo a better solution than the current state hack below may be needed - */ - private [chisel3] class FirrtlPreprocessing extends Phase { - - override def prerequisites = Seq(Dependency[ChiselStage], Dependency[MutateOptionsManager], Dependency[ReEnableFirrtlStage]) - override def optionalPrerequisites = Seq.empty - override def optionalPrerequisiteOf = Seq(Dependency[MaybeFirrtlStage]) - override def invalidates(a: Phase) = false - - private val phases = - Seq( new firrtl.stage.phases.DriverCompatibility.AddImplicitOutputFile, - new firrtl.stage.phases.DriverCompatibility.AddImplicitEmitter ) - - override def transform(annotations: AnnotationSeq): AnnotationSeq = - phases - .foldLeft(annotations)( (a, p) => p.transform(a) ) - - } - -} +@deprecated("This object contains no public members. This will be removed in Chisel 3.6.", "Chisel 3.5") +object DriverCompatibility -- cgit v1.2.3 From 361e4433ac6f0db8564415f07258ae151a48affe Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 17:58:35 +0000 Subject: add ShiftRegisters to expose register inside ShiftRegister. (#1723) * add ShiftRegisters to expose register inside ShiftRegister. * use Seq.iter for oneline implementation.--- src/main/scala/chisel3/util/Reg.scala | 43 ++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 16 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala index 982d80d0..1be6ea85 100644 --- a/src/main/scala/chisel3/util/Reg.scala +++ b/src/main/scala/chisel3/util/Reg.scala @@ -42,14 +42,7 @@ object ShiftRegister * val regDelayTwo = ShiftRegister(nextVal, 2, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = { - // The order of tests reflects the expected use cases. - if (n != 0) { - RegEnable(apply(in, n-1, en), en) - } else { - in - } - } + def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).last /** Returns the n-cycle delayed version of the input signal with reset initialization. * @@ -62,12 +55,30 @@ object ShiftRegister * val regDelayTwoReset = ShiftRegister(nextVal, 2, 0.U, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = { - // The order of tests reflects the expected use cases. - if (n != 0) { - RegEnable(apply(in, n-1, resetData, en), resetData, en) - } else { - in - } - } + def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = ShiftRegisters(in, n, resetData, en).last +} + + +object ShiftRegisters +{ + /** Returns a sequence of delayed input signal registers from 1 to n. + * + * @param in input to delay + * @param n number of cycles to delay + * @param en enable the shift + * + */ + def apply[T <: Data](in: T, n: Int, en: Bool = true.B): Seq[T] = + Seq.iterate(in, n + 1)(util.RegEnable(_, en)).drop(1) + + /** Returns delayed input signal registers with reset initialization from 1 to n. + * + * @param in input to delay + * @param n number of cycles to delay + * @param resetData reset value for each register in the shift + * @param en enable the shift + * + */ + def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): Seq[T] = + Seq.iterate(in, n + 1)(util.RegEnable(_, resetData, en)).drop(1) } -- cgit v1.2.3 From c118facb82e43c010a6333c5281b956a7c9c7e20 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sun, 9 May 2021 13:32:48 +0000 Subject: Fix ShiftRegister with 0 delay. (#1903) * Add test to check ShiftRegister(s) with delay is 0. This should break ShiftRegister(x, 0) since last is not exist in a empty Seq. Originally, test only test 1 to 4, which missed a potential bug from #1723. * Fix ShiftRegister with 0 delay. if ShiftRegisters is empty, java will complain: ``` java.util.NoSuchElementException scala.collection.LinearSeqOptimized.last(LinearSeqOptimized.scala:150) ``` This fix this issue and return `in` directly when ShiftRegister size is 0. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/Reg.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala index 1be6ea85..e2b5d172 100644 --- a/src/main/scala/chisel3/util/Reg.scala +++ b/src/main/scala/chisel3/util/Reg.scala @@ -42,7 +42,7 @@ object ShiftRegister * val regDelayTwo = ShiftRegister(nextVal, 2, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).last + def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).lastOption.getOrElse(in) /** Returns the n-cycle delayed version of the input signal with reset initialization. * @@ -55,7 +55,7 @@ object ShiftRegister * val regDelayTwoReset = ShiftRegister(nextVal, 2, 0.U, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = ShiftRegisters(in, n, resetData, en).last + def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = ShiftRegisters(in, n, resetData, en).lastOption.getOrElse(in) } -- cgit v1.2.3 From 994e8780f41cf64c1b9758745c6f81c7e3dd375c Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Mon, 10 May 2021 19:04:23 +0000 Subject: implement equal to BitPat. (#1867) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/BitPat.scala | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 5a40d312..88e46e3c 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -108,6 +108,13 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou def === (that: UInt): Bool = macro SourceInfoTransform.thatArg def =/= (that: UInt): Bool = 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_=== (that: UInt) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { -- cgit v1.2.3 From 1c1a4d7217574a938ad0ce529803fb991f9903f0 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 20 May 2021 06:50:10 +0000 Subject: Implement PLA (#1912) * implement pla * implement test for pla * implement inverter matrix of PLA generator * fix for review. Co-authored-by: Boyang Han --- src/main/scala/chisel3/util/pla.scala | 119 ++++++++++++++++++++++++++++++++++ 1 file changed, 119 insertions(+) create mode 100644 src/main/scala/chisel3/util/pla.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/pla.scala b/src/main/scala/chisel3/util/pla.scala new file mode 100644 index 00000000..bb2b7996 --- /dev/null +++ b/src/main/scala/chisel3/util/pla.scala @@ -0,0 +1,119 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util + +import chisel3._ + +object pla { + + /** Construct a [[https://en.wikipedia.org/wiki/Programmable_logic_array]] from specified table. + * + * Each position in the input matrix corresponds to an input variable where + * `0` implies the corresponding input literal appears complemented in the product term. + * `1` implies the input literal appears uncomplemented in the product term + * `?` implies the input literal does not appear in the product term. + * + * For each output + * a `1` means this product term makes the function value to `1` + * and a `0` or `?` means this product term make the function value to `0` + * + * @param table A [[Seq]] of inputs -> outputs mapping + * @param invert A [[BitPat]] specify which bit of the output should be inverted. `1` means the correspond position + * of the output should be inverted in the PLA, a `0` or a `?` means direct output from the OR matrix. + * @return the (input, output) [[Wire]] of [[UInt]] of the constructed pla. + * {{{ + * // A 1-of-8 decoder (like the 74xx138) can be constructed as follow + * val (inputs, outputs) = pla(Seq( + * (BitPat("b000"), BitPat("b00000001")), + * (BitPat("b001"), BitPat("b00000010")), + * (BitPat("b010"), BitPat("b00000100")), + * (BitPat("b011"), BitPat("b00001000")), + * (BitPat("b100"), BitPat("b00010000")), + * (BitPat("b101"), BitPat("b00100000")), + * (BitPat("b110"), BitPat("b01000000")), + * (BitPat("b111"), BitPat("b10000000")), + * )) + * }}} + */ + def apply(table: Seq[(BitPat, BitPat)], invert: BitPat = BitPat("b0")): (UInt, UInt) = { + require(table.nonEmpty, "pla table must not be empty") + + val (inputTerms, outputTerms) = table.unzip + require( + inputTerms.map(_.getWidth).distinct.size == 1, + "all `BitPat`s in the input part of specified PLA table must have the same width" + ) + require( + outputTerms.map(_.getWidth).distinct.size == 1, + "all `BitPat`s in the output part of specified PLA table must have the same width" + ) + + // now all inputs / outputs have the same width + val numberOfInputs = inputTerms.head.getWidth + val numberOfOutputs = outputTerms.head.getWidth + + val inverterMask = invert.value & invert.mask + if (inverterMask.bitCount != 0) + require(invert.getWidth == numberOfOutputs, + "non-zero inverter mask must have the same width as the output part of specified PLA table" + ) + + // input wires of the generated PLA + val inputs = Wire(UInt(numberOfInputs.W)) + val invInputs = ~inputs + + // output wires of the generated PLA + val outputs = Wire(UInt(numberOfOutputs.W)) + + // the AND matrix + // use `term -> AND line` map to reuse AND matrix output lines + val andMatrixOutputs: Map[String, Bool] = inputTerms.map { t => + t.toString -> Cat( + Seq + .tabulate(numberOfInputs) { i => + if (t.mask.testBit(i)) { + Some( + if (t.value.testBit(i)) inputs(i) + else invInputs(i) + ) + } else { + None + } + } + .flatten + ).andR() + }.toMap + + // the OR matrix + val orMatrixOutputs: UInt = Cat( + Seq + .tabulate(numberOfOutputs) { i => + val andMatrixLines = table + // OR matrix composed by input terms which makes this output bit a `1` + .filter { + case (_, or) => or.mask.testBit(i) && or.value.testBit(i) + }.map { + case (inputTerm, _) => + andMatrixOutputs(inputTerm.toString) + } + if (andMatrixLines.isEmpty) false.B + else Cat(andMatrixLines).orR() + } + .reverse + ) + + // the INV matrix, useful for decoders + val invMatrixOutputs: UInt = Cat( + Seq + .tabulate(numberOfOutputs) { i => + if (inverterMask.testBit(i)) ~orMatrixOutputs(i) + else orMatrixOutputs(i) + } + .reverse + ) + + outputs := invMatrixOutputs + + (inputs, outputs) + } +} -- cgit v1.2.3 From 6ccff4848fcf55cb1e865738a67f2bec25988ac8 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Wed, 26 May 2021 02:35:34 +0800 Subject: throw exception if BitPat width is 0 (#1920) * spot a bug when BitPat width is 0 * fix #1919 Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/util/BitPat.scala | 1 + 1 file changed, 1 insertion(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 88e46e3c..985d22da 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -23,6 +23,7 @@ object BitPat { // If ? parsing is to be exposed, the return API needs further scrutiny // (especially with things like mask polarity). require(x.head == 'b', "BitPats must be in binary and be prefixed with 'b'") + require(x.length > 1, "BitPat width cannot be 0.") var bits = BigInt(0) var mask = BigInt(0) var count = 0 -- cgit v1.2.3 From 820200b75242dde2a66c8103fd53eb10afc7ff6b Mon Sep 17 00:00:00 2001 From: Schuyler Eldridge Date: Thu, 10 Jun 2021 17:32:08 -0400 Subject: Stop Emitting BlackBoxResourceAnno (#1954) * Change HasBlackBoxResource to Resolve Resources Change HasBlackBoxResource to resolve resources immediately and emit BlackBoxInlineAnno instead of a BlackBoxResourceAnno. This removes the need for a FIRRTL compiler to grok the Java Resource API in order to handle BlackBoxResourceAnno. Emit BlackBoxInlineAnno from HasExtModuleResource instead of BlackBoxResourceAnno. Signed-off-by: Schuyler Eldridge --- src/main/scala/chisel3/util/BlackBoxUtils.scala | 33 +++++++++++++++++++++--- src/main/scala/chisel3/util/ExtModuleUtils.scala | 7 +++-- 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala index 3cd704b3..443d7f3e 100644 --- a/src/main/scala/chisel3/util/BlackBoxUtils.scala +++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala @@ -4,12 +4,39 @@ package chisel3.util import chisel3._ import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform} -import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper} +import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper, + BlackBoxNotFoundException} +import firrtl.annotations.ModuleName +import logger.LazyLogging + +private[util] object BlackBoxHelpers { + + implicit class BlackBoxInlineAnnoHelpers(anno: BlackBoxInlineAnno.type) extends LazyLogging { + /** Generate a BlackBoxInlineAnno from a Java Resource and a module name. */ + def fromResource(resourceName: String, moduleName: ModuleName) = try { + val blackBoxFile = os.resource / os.RelPath(resourceName.dropWhile(_ == '/')) + val contents = os.read(blackBoxFile) + if (contents.size > BigInt(2).pow(20)) { + val message = + s"Black box resource $resourceName, which will be converted to an inline annotation, is greater than 1 MiB." + + "This may affect compiler performance. Consider including this resource via a black box path." + logger.warn(message) + } + BlackBoxInlineAnno(moduleName, blackBoxFile.last, contents) + } catch { + case e: os.ResourceNotFoundException => + throw new BlackBoxNotFoundException(resourceName, e.getMessage) + } + } +} + +import BlackBoxHelpers._ trait HasBlackBoxResource extends BlackBox { self: BlackBox => - /** Copies a resource file to the target directory + /** Copies a Java resource containing some text into the output directory. This is typically used to copy a Verilog file + * to the final output directory, but may be used to copy any Java resource (e.g., a C++ testbench). * * Resource files are located in project_root/src/main/resources/. * Example of adding the resource file project_root/src/main/resources/blackbox.v: @@ -19,7 +46,7 @@ trait HasBlackBoxResource extends BlackBox { */ def addResource(blackBoxResource: String): Unit = { val anno = new ChiselAnnotation with RunFirrtlTransform { - def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource) + def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed) def transformClass = classOf[BlackBoxSourceHelper] } chisel3.experimental.annotate(anno) diff --git a/src/main/scala/chisel3/util/ExtModuleUtils.scala b/src/main/scala/chisel3/util/ExtModuleUtils.scala index 831639be..62f384bc 100644 --- a/src/main/scala/chisel3/util/ExtModuleUtils.scala +++ b/src/main/scala/chisel3/util/ExtModuleUtils.scala @@ -3,7 +3,10 @@ package chisel3.util import chisel3.experimental.{ChiselAnnotation, ExtModule, RunFirrtlTransform} -import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper} +import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper, + BlackBoxNotFoundException} + +import BlackBoxHelpers._ trait HasExtModuleResource extends ExtModule { self: ExtModule => @@ -18,7 +21,7 @@ trait HasExtModuleResource extends ExtModule { */ def addResource(blackBoxResource: String): Unit = { val anno = new ChiselAnnotation with RunFirrtlTransform { - def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource) + def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed) def transformClass = classOf[BlackBoxSourceHelper] } chisel3.experimental.annotate(anno) -- cgit v1.2.3 From 71575609ae7242585ed1008b8473acae1a42165e Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:18:58 +0000 Subject: implement TruthTable to represent a decode table. --- .../util/experimental/decode/TruthTable.scala | 102 +++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/main/scala/chisel3/util/experimental/decode/TruthTable.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala new file mode 100644 index 00000000..084403ac --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -0,0 +1,102 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +import chisel3.util.BitPat + +class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { + require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.") + require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.") + + def inputWidth = table.head._1.getWidth + + def outputWidth = table.head._2.getWidth + + override def toString: String = { + def writeRow(map: (BitPat, BitPat)): String = + s"${TruthTable.bpStr(map._1)}->${TruthTable.bpStr(map._2)}" + + (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${TruthTable.bpStr(default)}")).toSeq.sorted.mkString("\n") + } + + def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default) + + override def equals(y: Any): Boolean = { + y match { + case y: TruthTable => toString == y.toString + case _ => false + } + } +} + +object TruthTable { + /** Parse TruthTable from its string representation. */ + def apply(tableString: String): TruthTable = { + TruthTable( + tableString + .split("\n") + .filter(_.contains("->")) + .map(_.split("->").map(str => BitPat(s"b$str"))) + .map(bps => bps(0) -> bps(1)) + .toMap, + BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}") + ) + } + + def apply(table: Map[BitPat, BitPat], default: BitPat) = new TruthTable(table, default) + + + /** consume 1 table, split it into up to 3 tables with the same default bits. + * + * @return table and its indexes from original bits. + * @note + * Since most of minimizer(like espresso) cannot handle a multiple default table. + * It is useful to split a table into 3 tables based on the default type. + */ + private[decode] def split( + table: TruthTable + ): Seq[(TruthTable, Seq[Int])] = { + def bpFilter(bitPat: BitPat, indexes: Seq[Int]): BitPat = + BitPat(s"b${bpStr(bitPat).zipWithIndex.filter(b => indexes.contains(b._2)).map(_._1).mkString}") + + def tableFilter(indexes: Seq[Int]): Option[(TruthTable, Seq[Int])] = { + if(indexes.nonEmpty) Some((TruthTable( + table.table.map { case (in, out) => in -> bpFilter(out, indexes) }, + bpFilter(table.default, indexes) + ), indexes)) else None + } + + def index(bitPat: BitPat, bpType: Char): Seq[Int] = + bpStr(bitPat).zipWithIndex.filter(_._1 == bpType).map(_._2) + + Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t))) + } + + /** consume tables, merge it into single table with different default bits. + * + * @note + * Since most of minimizer(like espresso) cannot handle a multiple default table. + * It is useful to split a table into 3 tables based on the default type. + */ + private[decode] def merge( + tables: Seq[(TruthTable, Seq[Int])] + ): TruthTable = { + def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] = + bpStr(table.table.getOrElse(bitPat, BitPat.dontCare(indexes.size))).zip(indexes) + def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar + .sortBy(_._2) + .map(_._1) + .mkString}") + TruthTable( + tables + .flatMap(_._1.table.keys) + .map { key => + key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) }) + } + .toMap, + bitPat(tables.flatMap { case (table, indexes) => bpStr(table.default).zip(indexes) }) + ) + } + + private def bpStr(bitPat: BitPat) = bitPat.toString.drop(7).dropRight(1) +} -- cgit v1.2.3 From 1c9163bb05ff7e50885d1560a9df088ff1f2b49d Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:15:14 +0000 Subject: implement DecodeTableAnnotation for decode table caching. --- .../util/experimental/decode/DecodeTableAnnotation.scala | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala new file mode 100644 index 00000000..3a6957e2 --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala @@ -0,0 +1,13 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation} + +case class DecodeTableAnnotation( + target: ReferenceTarget, + truthTable: TruthTable, + minimizedTable: TruthTable) + extends SingleTargetAnnotation[ReferenceTarget] { + override def duplicate(n: ReferenceTarget): Annotation = this.copy(target = n) +} -- cgit v1.2.3 From 28eef17430d8bbca2765b5a2b0ab0337f7484840 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sun, 23 May 2021 07:55:48 +0000 Subject: TruthTable can merge same inputs now. --- .../util/experimental/decode/TruthTable.scala | 27 ++++++++++++++++++---- 1 file changed, 22 insertions(+), 5 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 084403ac..37bfec77 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -4,9 +4,7 @@ package chisel3.util.experimental.decode import chisel3.util.BitPat -class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { - require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.") - require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.") +sealed class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { def inputWidth = table.head._1.getWidth @@ -38,12 +36,31 @@ object TruthTable { .filter(_.contains("->")) .map(_.split("->").map(str => BitPat(s"b$str"))) .map(bps => bps(0) -> bps(1)) - .toMap, + .toSeq, BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}") ) } - def apply(table: Map[BitPat, BitPat], default: BitPat) = new TruthTable(table, default) + /** Convert a table and default output into a [[TruthTable]]. */ + def apply(table: Iterable[(BitPat, BitPat)], default: BitPat) = { + 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).map { case (key, values) => + // merge same input inputs. + key -> BitPat(s"b${ + Seq.tabulate(outputWidth) { i => + val outputSet = values.map(_._2) + .map(bpStr) + .map(_ (i)) + .toSet + .filterNot(_ == '?') + require(outputSet.size != 2, s"TruthTable conflict in :\n${values.map { case (i, o) => s"${bpStr(i)}->${bpStr(o)}" }.mkString("\n")}") + outputSet.headOption.getOrElse('?') + }.mkString + }") + }, default) + } /** consume 1 table, split it into up to 3 tables with the same default bits. -- cgit v1.2.3 From f3318e52e4338dc558de8a900f93bc5757223642 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Sun, 23 May 2021 13:35:54 +0000 Subject: fix for 2.13 --- src/main/scala/chisel3/util/experimental/decode/TruthTable.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 37bfec77..cde9cf14 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -46,9 +46,9 @@ object 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).map { case (key, values) => + new TruthTable(table.toSeq.groupBy(_._1.toString).map { case (key, values) => // merge same input inputs. - key -> BitPat(s"b${ + values.head._1 -> BitPat(s"b${ Seq.tabulate(outputWidth) { i => val outputSet = values.map(_._2) .map(bpStr) -- cgit v1.2.3 From 819e806664da117172b6d46aa1adf83d39042d48 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:16:52 +0000 Subject: implement abstract Minimizer as a general API. --- .../util/experimental/decode/Minimizer.scala | 29 ++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/main/scala/chisel3/util/experimental/decode/Minimizer.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala new file mode 100644 index 00000000..86847710 --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala @@ -0,0 +1,29 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +abstract class Minimizer { + /** Minimize a multi-input multi-output logic function given by the truth table `table`, with function output values + * on unspecified inputs treated as `default`, and return a minimized PLA-like representation of the function. + * + * Each bit of `table[]._1` encodes one 1-bit input variable of the logic function, and each bit of `default` and + * `table[]._2` represents one 1-bit output value of the function. + * + * @param table Truth table, can have don't cares in both inputs and outputs, specified as [(inputs, outputs), ...] + * @return Minimized truth table, [(inputs, outputs), ...] + * + * @example {{{ + * minimize(BitPat("b?"), Seq( + * (BitPat("b000"), BitPat("b0")), + * // (BitPat("b001"), BitPat("b?")), // same as default, can be omitted + * // (BitPat("b010"), BitPat("b?")), // same as default, can be omitted + * (BitPat("b011"), BitPat("b0")), + * (BitPat("b100"), BitPat("b1")), + * (BitPat("b101"), BitPat("b1")), + * (BitPat("b110"), BitPat("b0")), + * (BitPat("b111"), BitPat("b1")), + * )) + * }}} + */ + def minimize(table: TruthTable): TruthTable +} \ No newline at end of file -- cgit v1.2.3 From 3691fe61cc43beb0e7d0f813723d4150daa79372 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 6 May 2021 16:20:50 +0000 Subject: add a simple decoder API. --- .../decode/DecodeTableAnnotation.scala | 4 +- .../chisel3/util/experimental/decode/decoder.scala | 53 ++++++++++++++++++++++ 2 files changed, 55 insertions(+), 2 deletions(-) create mode 100644 src/main/scala/chisel3/util/experimental/decode/decoder.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala index 3a6957e2..cd289a5d 100644 --- a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala +++ b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala @@ -6,8 +6,8 @@ import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation} case class DecodeTableAnnotation( target: ReferenceTarget, - truthTable: TruthTable, - minimizedTable: TruthTable) + truthTable: String, + minimizedTable: String) extends SingleTargetAnnotation[ReferenceTarget] { override def duplicate(n: ReferenceTarget): Annotation = this.copy(target = n) } diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala new file mode 100644 index 00000000..2ef474e1 --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +import chisel3._ +import chisel3.experimental.{ChiselAnnotation, annotate} +import chisel3.util.{BitPat, pla} +import chisel3.util.experimental.getAnnotations +import firrtl.annotations.Annotation +import logger.LazyLogging + +object decoder extends LazyLogging { + def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = { + val minimizedTable = getAnnotations().collect { + case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out) + }.toMap.getOrElse( + { + logger.trace(s"""Decoder Cache Hit! + |${truthTable.table} + |""".stripMargin) + truthTable + }, { + val startTime = System.nanoTime() + val minimizedTable = minimizer.minimize(truthTable) + val totalTime = System.nanoTime() - startTime + val totalTimeInSeconds = totalTime / 1e9 + val info = f"Logic Minimize with $minimizer finished in ${totalTimeInSeconds} second" + if (totalTimeInSeconds > 5) + logger.error( + s"$info spends too long, consider using chisel3.util.experimental.DecodeTableAnnotation to cache decode result or switch to EspressoMinimizer." + ) + else logger.trace(info) + minimizedTable + } + ) + if (minimizedTable.table.isEmpty) { + val outputs = Wire(UInt(minimizedTable.default.getWidth.W)) + outputs := minimizedTable.default.value.U(minimizedTable.default.getWidth.W) + outputs + } else { + val (plaInput, plaOutput) = + pla(minimizedTable.table.toSeq, BitPat(minimizedTable.default.value.U(minimizedTable.default.getWidth.W))) + + annotate(new ChiselAnnotation { + override def toFirrtl: Annotation = + DecodeTableAnnotation(plaOutput.toTarget, truthTable.toString, minimizedTable.toString) + }) + + plaInput := input + plaOutput + } + } +} -- cgit v1.2.3 From d2c71fbf64a4728914414bbf27803d7cf03f94ad Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Tue, 25 May 2021 04:54:14 +0000 Subject: async decoder with 5 seconds timeout. --- .../chisel3/util/experimental/decode/decoder.scala | 25 ++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala index 2ef474e1..87e839d3 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -9,27 +9,34 @@ import chisel3.util.experimental.getAnnotations import firrtl.annotations.Annotation import logger.LazyLogging +import scala.concurrent.ExecutionContext.Implicits.global +import scala.concurrent.duration.Duration +import scala.concurrent.{Await, Future} + object decoder extends LazyLogging { - def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = { + def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable, timeout: Int = 5): UInt = { val minimizedTable = getAnnotations().collect { case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out) }.toMap.getOrElse( { - logger.trace(s"""Decoder Cache Hit! + logger.warn(s"""Decoder Cache Hit! |${truthTable.table} |""".stripMargin) truthTable }, { val startTime = System.nanoTime() - val minimizedTable = minimizer.minimize(truthTable) + val future = Future(minimizer.minimize(truthTable)) + var now = System.nanoTime() + while (!future.isCompleted) { + val elapsed = (System.nanoTime() - now) / 1e9 + now = System.nanoTime() + if(elapsed > timeout) logger.error(s"Minimizer has been executed for ${(System.nanoTime() - startTime) / 1e9} seconds.") + } + val minimizedTable = Await.result(future, Duration.Inf) val totalTime = System.nanoTime() - startTime val totalTimeInSeconds = totalTime / 1e9 - val info = f"Logic Minimize with $minimizer finished in ${totalTimeInSeconds} second" - if (totalTimeInSeconds > 5) - logger.error( - s"$info spends too long, consider using chisel3.util.experimental.DecodeTableAnnotation to cache decode result or switch to EspressoMinimizer." - ) - else logger.trace(info) + val info = f"Logic Minimize with $minimizer finished in $totalTimeInSeconds second" + logger.warn(info) minimizedTable } ) -- cgit v1.2.3 From 88b7c48c18267e55c755f4fe6615c2faf2910906 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Tue, 25 May 2021 07:22:32 +0000 Subject: remove all timeouts by review. --- .../chisel3/util/experimental/decode/decoder.scala | 30 ++-------------------- 1 file changed, 2 insertions(+), 28 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala index 87e839d3..8168824f 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -9,37 +9,11 @@ import chisel3.util.experimental.getAnnotations import firrtl.annotations.Annotation import logger.LazyLogging -import scala.concurrent.ExecutionContext.Implicits.global -import scala.concurrent.duration.Duration -import scala.concurrent.{Await, Future} - object decoder extends LazyLogging { - def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable, timeout: Int = 5): UInt = { + def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = { val minimizedTable = getAnnotations().collect { case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out) - }.toMap.getOrElse( - { - logger.warn(s"""Decoder Cache Hit! - |${truthTable.table} - |""".stripMargin) - truthTable - }, { - val startTime = System.nanoTime() - val future = Future(minimizer.minimize(truthTable)) - var now = System.nanoTime() - while (!future.isCompleted) { - val elapsed = (System.nanoTime() - now) / 1e9 - now = System.nanoTime() - if(elapsed > timeout) logger.error(s"Minimizer has been executed for ${(System.nanoTime() - startTime) / 1e9} seconds.") - } - val minimizedTable = Await.result(future, Duration.Inf) - val totalTime = System.nanoTime() - startTime - val totalTimeInSeconds = totalTime / 1e9 - val info = f"Logic Minimize with $minimizer finished in $totalTimeInSeconds second" - logger.warn(info) - minimizedTable - } - ) + }.toMap.getOrElse(truthTable, minimizer.minimize(truthTable)) if (minimizedTable.table.isEmpty) { val outputs = Wire(UInt(minimizedTable.default.getWidth.W)) outputs := minimizedTable.default.value.U(minimizedTable.default.getWidth.W) -- cgit v1.2.3 From c11fdf33981365927ea085b7aee095933a4a5ddf Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Mon, 14 Jun 2021 14:58:01 +0000 Subject: add documentation for DecodeTableAnnotation. --- .../chisel3/util/experimental/decode/DecodeTableAnnotation.scala | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala index cd289a5d..2b50ef90 100644 --- a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala +++ b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala @@ -4,6 +4,15 @@ package chisel3.util.experimental.decode import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation} +/** DecodeTableAnnotation used to store a decode result for a specific [[TruthTable]]. + * This is useful for saving large [[TruthTable]] during a elaboration time. + * + * @note user should manage the correctness of [[minimizedTable]]. + * + * @param target output wire of a decoder. + * @param truthTable input [[TruthTable]] encoded in a serialized [[TruthTable]]. + * @param minimizedTable minimized [[truthTable]] encoded in a serialized [[TruthTable]]. + */ case class DecodeTableAnnotation( target: ReferenceTarget, truthTable: String, -- cgit v1.2.3 From 48548463c6c5c6bc9076c80924cb4683fc737f11 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Mon, 14 Jun 2021 22:39:42 +0800 Subject: Apply Jack's Review 1. `TruthTable` is final now. 2. add return type for `TruthTable` Co-authored-by: Jack Koenig --- src/main/scala/chisel3/util/experimental/decode/TruthTable.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index cde9cf14..ca0ff8b4 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -4,7 +4,7 @@ package chisel3.util.experimental.decode import chisel3.util.BitPat -sealed class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { +final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { def inputWidth = table.head._1.getWidth @@ -42,7 +42,7 @@ object TruthTable { } /** Convert a table and default output into a [[TruthTable]]. */ - def apply(table: Iterable[(BitPat, BitPat)], default: BitPat) = { + def apply(table: Iterable[(BitPat, BitPat)], default: BitPat): TruthTable = { require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.") require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.") val outputWidth = table.map(_._2.getWidth).head -- cgit v1.2.3 From f48b4abd9b2e10a5243cadf3803f7f42d4ce25fc Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Thu, 6 May 2021 16:27:11 +0000 Subject: implement QMC. --- .../util/experimental/decode/QMCMinimizer.scala | 282 +++++++++++++++++++++ 1 file changed, 282 insertions(+) create mode 100644 src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala new file mode 100644 index 00000000..bc5ea351 --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -0,0 +1,282 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +import chisel3.util.BitPat + +import scala.annotation.tailrec +import scala.math.Ordered.orderingToOrdered +import scala.language.implicitConversions + +object QMCMinimizer extends Minimizer { + private implicit def toImplicant(x: BitPat): Implicant = new Implicant(x) + + private class Implicant(val bp: BitPat) { + var isPrime: Boolean = true + + def width = bp.getWidth + + override def equals(that: Any): Boolean = that match { + case x: Implicant => bp.value == x.bp.value && bp.mask == x.bp.mask + case _ => false + } + + override def hashCode = bp.value.toInt + + /** Check whether two implicants have the same value on all of the cared bits (intersection). + * + * {{{ + * value ^^ x.value // bits that are different + * (bits that are different) & x.mask // bits that are different and `this` care + * (bits that are different and `this` care) & y.mask // bits that are different and `both` care + * (bits that are different and both care) == 0 // no (bits that are different and we both care) exists + * no (bits that are different and we both care) exists // all cared bits are the same, two terms intersect + * }}} + * + * @param y Implicant to be checked with + * @return Whether two implicants intersect + */ + def intersects(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask & y.bp.mask) == 0 + + /** Check whether two implicants are similar. + * Two implicants are "similar" when they satisfy all the following rules: + * 1. have the same mask ('?'s are at the same positions) + * 1. values only differ by one bit + * 1. the bit at the differed position of this term is '1' (that of the other term is '0') + * + * @example this = 11?0, x = 10?0 -> similar + * @example this = 11??, x = 10?0 -> not similar, violated rule 1 + * @example this = 11?1, x = 10?0 -> not similar, violated rule 2 + * @example this = 10?0, x = 11?0 -> not similar, violated rule 3 + * @param y Implicant to be checked with + * @return Whether this term is similar to the other + */ + def similar(y: Implicant): Boolean = { + val diff = bp.value - y.bp.value + bp.mask == y.bp.mask && bp.value > y.bp.value && (diff & diff - 1) == 0 + } + + /** Merge two similar implicants + * Rule of merging: '0' and '1' merge to '?' + * + * @param y Term to be merged with + * @return A new term representing the merge result + */ + def merge(y: Implicant): Implicant = { + require(similar(y), s"merge is only reasonable when $this is similar to $y") + + // if two term can be merged, then they both are not prime implicants. + isPrime = false + y.isPrime = false + val bit = bp.value - y.bp.value + new BitPat(bp.value &~ bit, bp.mask &~ bit, width) + } + + /** Check all bits in `x` cover the correspond position in `y`. + * + * Rule to define coverage relationship among `0`, `1` and `?`: + * 1. '?' covers '0' and '1', '0' covers '0', '1' covers '1' + * 1. '1' doesn't cover '?', '1' doesn't cover '0' + * 1. '0' doesn't cover '?', '0' doesn't cover '1' + * + * For all bits that `x` don't care, `y` can be `0`, `1`, `?` + * For all bits that `x` care, `y` must be the same value and not masked. + * {{{ + * (~x.mask & -1) | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1 + * -> ~x.mask | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1 + * -> ~x.mask | ((x.value xnor y.value) & y.mask) = -1 + * -> x.mask & ~((x.value xnor y.value) & y.mask) = 0 + * -> x.mask & (~(x.value xnor y.value) | ~y.mask) = 0 + * -> x.mask & ((x.value ^ y.value) | ~y.mask) = 0 + * -> ((x.value ^ y.value) & x.mask | ~y.mask & x.mask) = 0 + * }}} + * + * @param y to check is covered by `x` or not. + * @return Whether `x` covers `y` + */ + def covers(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask | ~y.bp.mask & bp.mask) == 0 + + override def toString = (if (!isPrime) "Non" else "") + "Prime" + bp.toString.replace("BitPat", "Implicant") + } + + /** + * If two terms have different value, then their order is determined by the value, or by the mask. + */ + private implicit def ordering: Ordering[Implicant] = new Ordering[Implicant] { + override def compare(x: Implicant, y: Implicant): Int = + if (x.bp.value < y.bp.value || x.bp.value == y.bp.value && x.bp.mask > y.bp.mask) -1 else 1 + } + + /** Calculate essential prime implicants based on previously calculated prime implicants and all implicants. + * + * @param primes Prime implicants + * @param minterms All implicants + * @return (a, b, c) + * a: essential prime implicants + * b: nonessential prime implicants + * c: implicants that are not cover by any of the essential prime implicants + */ + private def getEssentialPrimeImplicants(primes: Seq[Implicant], minterms: Seq[Implicant]): (Seq[Implicant], Seq[Implicant], Seq[Implicant]) = { + // primeCovers(i): implicants that `prime(i)` covers + val primeCovers = primes.map(p => minterms.filter(p.covers)) + // eliminate prime implicants that can be covered by other prime implicants + for (((icover, pi), i) <- (primeCovers zip primes).zipWithIndex) { + for (((jcover, pj), _) <- (primeCovers zip primes).zipWithIndex.drop(i + 1)) { + // we prefer prime implicants with wider implicants coverage + if (icover.size > jcover.size && jcover.forall(pi.covers)) { + // calculate essential prime implicants with `pj` eliminated from prime implicants table + return getEssentialPrimeImplicants(primes.filter(_ != pj), minterms) + } + } + } + + // implicants that only one prime implicant covers + val essentiallyCovered = minterms.filter(t => primes.count(_.covers(t)) == 1) + // essential prime implicants, prime implicants that covers only one implicant + val essential = primes.filter(p => essentiallyCovered.exists(p.covers)) + // {nonessential} = {prime implicants} - {essential prime implicants} + val nonessential = primes.filterNot(essential contains _) + // implicants that no essential prime implicants covers + val uncovered = minterms.filterNot(t => essential.exists(_.covers(t))) + if (essential.isEmpty || uncovered.isEmpty) + (essential, nonessential, uncovered) + else { + // now there are implicants (`uncovered`) that are covered by multiple nonessential prime implicants (`nonessential`) + // need to reduce prime implicants + val (a, b, c) = getEssentialPrimeImplicants(nonessential, uncovered) + (essential ++ a, b, c) + } + } + + /** Use [[https://en.wikipedia.org/wiki/Petrick%27s_method]] to select a [[Seq]] of nonessential prime implicants + * that covers all implicants that are not covered by essential prime implicants. + * + * @param implicants Nonessential prime implicants + * @param minterms Implicants that are not covered by essential prime implicants + * @return Selected nonessential prime implicants + */ + private def getCover(implicants: Seq[Implicant], minterms: Seq[Implicant]): Seq[Implicant] = { + /** Calculate the implementation cost (using comparators) of a list of implicants, more don't cares is cheaper + * + * @param cover Implicant list + * @return How many comparators need to implement this list of implicants + */ + def getCost(cover: Seq[Implicant]): Int = cover.map(_.bp.mask.bitCount).sum + + /** Determine if one combination of prime implicants is cheaper when implementing as comparators. + * Shorter term list is cheaper, term list with more don't cares is cheaper (less comparators) + * + * @param a Operand a + * @param b Operand b + * @return `a` < `b` + */ + def cheaper(a: Seq[Implicant], b: Seq[Implicant]): Boolean = { + val ca = getCost(a) + val cb = getCost(b) + + /** If `a` < `b` + * + * Like comparing the dictionary order of two strings. + * Define `a` < `b` if both `a` and `b` are empty. + * + * @param a Operand a + * @param b Operand b + * @return `a` < `b` + */ + @tailrec + def listLess(a: Seq[Implicant], b: Seq[Implicant]): Boolean = b.nonEmpty && (a.isEmpty || a.head < b.head || a.head == b.head && listLess(a.tail, b.tail)) + + ca < cb || ca == cb && listLess(a.sortWith(_ < _), b.sortWith(_ < _)) + } + + // if there are no implicant that is not covered by essential prime implicants, which means all implicants are + // covered by essential prime implicants, there is no need to apply Petrick's method + if (minterms.nonEmpty) { + // cover(i): nonessential prime implicants that covers `minterms(i)` + val cover = minterms.map(m => implicants.filter(_.covers(m))) + val all = cover.tail.foldLeft(cover.head.map(Set(_)))((c0, c1) => c0.flatMap(a => c1.map(a + _))) + all.map(_.toList).reduceLeft((a, b) => if (cheaper(a, b)) a else b) + } else + Seq[Implicant]() + } + + def minimize(table: TruthTable): TruthTable = { + require(table.table.nonEmpty, "Truth table must not be empty") + + // extract decode table to inputs and outputs + val (inputs, outputs) = table.table.unzip + + require(outputs.map(_.getWidth == table.default.getWidth).reduce(_ && _), "All output BitPats and default BitPat must have the same length") + require(if (inputs.toSeq.length > 1) inputs.tail.map(_.width == inputs.head.width).reduce(_ && _) else true, "All input BitPats must have the same length") + + // make sure no two inputs specified in the truth table intersect + for (t <- inputs.tails; if t.nonEmpty) + for (u <- t.tail) + require(!t.head.intersects(u), "truth table entries " + t.head + " and " + u + " overlap") + + // number of inputs + val n = inputs.head.width + // number of outputs + val m = outputs.head.getWidth + + // for all outputs + table.copy(table = (0 until m).flatMap(i => { + val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i) + + // Minterms, implicants that makes the output to be 1 + val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.map(_._1).map(toImplicant).toSeq + // Maxterms, implicants that makes the output to be 0 + val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.map(_._1).map(toImplicant).toSeq + // Don't cares, implicants that can produce either 0 or 1 as output + val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.map(_._1).map(toImplicant).toSeq + + val (implicants, defaultToDc) = table.default match { + case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0 + (mint ++ dc, false) + case x if x.mask.testBit(i) && x.value.testBit(i) => // default to 1 + (maxt ++ dc, false) + case x if !x.mask.testBit(i) => // default to ? + (mint, true) + } + + implicants.foreach(_.isPrime = true) + val cols = (0 to n).reverse.map(b => implicants.filter(b == _.bp.mask.bitCount)) + val mergeTable = cols.map( + c => (0 to n).map( + b => collection.mutable.Set(c.filter(b == _.bp.value.bitCount):_*) + ) + ) + + for (i <- 0 to n) { + for (j <- 0 until n - i) { + mergeTable(i)(j).foreach(a => mergeTable(i + 1)(j) ++= mergeTable(i)(j + 1).filter(_ similar a).map(_ merge a)) + } + if (defaultToDc) { + for (j <- 0 until n - i) { + for (a <- mergeTable(i)(j).filter(_.isPrime)) { + if (a.bp.mask.testBit(i) && !a.bp.value.testBit(i)) { + // this bit is `0` + val t = new BitPat(a.bp.value.setBit(i), a.bp.mask, a.width) + if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += t merge a + } + } + for (a <- mergeTable(i)(j + 1).filter(_.isPrime)) { + if (a.bp.mask.testBit(i) && a.bp.value.testBit(i)) { + // this bit is `1` + val t = new BitPat(a.bp.value.clearBit(i), a.bp.mask, a.width) + if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += a merge t + } + } + } + } + } + + val primeImplicants = mergeTable.flatten.flatten.filter(_.isPrime).sortWith(_ < _) + + val (essentialPrimeImplicants, nonessentialPrimeImplicants, uncoveredImplicants) = + getEssentialPrimeImplicants(primeImplicants, implicants) + + (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp)) + }).toMap) + } +} \ No newline at end of file -- cgit v1.2.3 From 93e9e2ba8058ef89afacc00c16e138b0243587ba Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Sun, 23 May 2021 04:04:42 -0700 Subject: Merge minimized table before return as a TruthTable --- .../chisel3/util/experimental/decode/QMCMinimizer.scala | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala index bc5ea351..3fa5a70c 100644 --- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -220,7 +220,7 @@ object QMCMinimizer extends Minimizer { val m = outputs.head.getWidth // for all outputs - table.copy(table = (0 until m).flatMap(i => { + val minimized = (0 until m).flatMap(i => { val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i) // Minterms, implicants that makes the output to be 1 @@ -277,6 +277,19 @@ object QMCMinimizer extends Minimizer { getEssentialPrimeImplicants(primeImplicants, implicants) (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp)) - }).toMap) + }) + + minimized.tail.foldLeft(table.copy(table = Map(minimized.head))) { case (tb, t) => + if (tb.table.exists(x => x._1 == t._1)) { + tb.copy(table = tb.table.map { case (k, v) => + if (k == t._1) { + def ones(bitPat: BitPat) = bitPat.toString.drop(7).dropRight(1).zipWithIndex.collect{case ('1', x) => x} + (k, BitPat("b" + (0 until v.getWidth).map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?").mkString)) + } else (k, v) + }) + } else { + tb.copy(table = tb.table + t) + } + } } } \ No newline at end of file -- cgit v1.2.3 From 6bcedfdc01e51079c258c5cc576356ad561e2555 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Tue, 11 May 2021 06:01:25 -0700 Subject: Refactor to a more `scala` form --- src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala index 3fa5a70c..b6b97daf 100644 --- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -224,11 +224,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) }.map(_._1).map(toImplicant).toSeq + val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.keys.map(toImplicant).toSeq // Maxterms, implicants that makes the output to be 0 - val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.map(_._1).map(toImplicant).toSeq + val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.keys.map(toImplicant).toSeq // Don't cares, implicants that can produce either 0 or 1 as output - val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.map(_._1).map(toImplicant).toSeq + val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.keys.map(toImplicant).toSeq val (implicants, defaultToDc) = table.default match { case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0 -- cgit v1.2.3 From 31821aabdb8222dfeae76bac24858dd2ec271aa9 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Tue, 11 May 2021 06:23:30 -0700 Subject: Add computational complexity analysis --- src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala index b6b97daf..54faa734 100644 --- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -194,6 +194,7 @@ object QMCMinimizer extends Minimizer { if (minterms.nonEmpty) { // cover(i): nonessential prime implicants that covers `minterms(i)` val cover = minterms.map(m => implicants.filter(_.covers(m))) + // all subsets of `cover`, NP algorithm, O(2 ^ len(cover)) val all = cover.tail.foldLeft(cover.head.map(Set(_)))((c0, c1) => c0.flatMap(a => c1.map(a + _))) all.map(_.toList).reduceLeft((a, b) => if (cheaper(a, b)) a else b) } else @@ -247,6 +248,7 @@ object QMCMinimizer extends Minimizer { ) ) + // O(n ^ 3) for (i <- 0 to n) { for (j <- 0 until n - i) { mergeTable(i)(j).foreach(a => mergeTable(i + 1)(j) ++= mergeTable(i)(j + 1).filter(_ similar a).map(_ merge a)) @@ -273,6 +275,7 @@ object QMCMinimizer extends Minimizer { val primeImplicants = mergeTable.flatten.flatten.filter(_.isPrime).sortWith(_ < _) + // O(len(primeImplicants) ^ 4) val (essentialPrimeImplicants, nonessentialPrimeImplicants, uncoveredImplicants) = getEssentialPrimeImplicants(primeImplicants, implicants) -- cgit v1.2.3 From a3ddd4b98049b624080422717c6822ec9ab43e07 Mon Sep 17 00:00:00 2001 From: Martin Schoeberl Date: Wed, 16 Jun 2021 19:33:26 +0200 Subject: getVerilog in Chisel3 (#1921) --- src/main/scala/chisel3/verilog.scala | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 src/main/scala/chisel3/verilog.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/verilog.scala b/src/main/scala/chisel3/verilog.scala new file mode 100644 index 00000000..a91444de --- /dev/null +++ b/src/main/scala/chisel3/verilog.scala @@ -0,0 +1,15 @@ +package chisel3 + +import chisel3.stage.ChiselStage +import firrtl.AnnotationSeq + +object getVerilogString { + def apply(gen: => RawModule): String = ChiselStage.emitVerilog(gen) +} + +object emitVerilog { + def apply(gen: => RawModule, args: Array[String] = Array.empty, + annotations: AnnotationSeq = Seq.empty): Unit = { + (new ChiselStage).emitVerilog(gen, args, annotations) + } +} -- cgit v1.2.3 From f8053db3d20b733e0119b77595f0cdfcdab71057 Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Thu, 24 Jun 2021 14:03:28 -0700 Subject: create and extend annotatable BaseSim class for verification nodes (#1968) * prototype annotating verif constructs * switch to final class * name emissions * moving BaseSim to experimental * adding name tests * fixing quotation escapes * emitting names, but everything has a default name * only name things with provided/suggested names * name every BaseSim node * removing msg, unused imports * fixing file exist calls--- src/main/scala/chisel3/internal/firrtl/Emitter.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index ad4df80a..53d5c6ce 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -80,8 +80,8 @@ private class Emitter(circuit: Circuit) { val printfArgs = Seq(e.clock.fullName(ctx), "UInt<1>(1)", "\"" + printf.format(fmt) + "\"") ++ args printfArgs mkString ("printf(", ", ", ")") - case e: Verification => s"${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, " + - s"UInt<1>(1), " + "\"" + s"${printf.format(e.message)}" + "\")" + case e: Verification[_] => + s"""${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, UInt<1>(1), "${printf.format(e.message)}") : ${e.name}""" case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid" case e: DefInstance => s"inst ${e.name} of ${e.id.name}" case w: WhenBegin => -- cgit v1.2.3 From 0531cb53d3cedaff33c2a280e34418f6af5bc6a1 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 29 Jun 2021 15:34:18 -0700 Subject: Restore aop.Select behavior for CloneModuleAsRecord --- src/main/scala/chisel3/aop/Select.scala | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index b9ad808b..08cf40ff 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -6,6 +6,7 @@ import chisel3._ import chisel3.experimental.{BaseModule, FixedPoint} import chisel3.internal.HasId import chisel3.internal.firrtl._ +import chisel3.internal.BaseModule.ModuleClone import firrtl.annotations.ReferenceTarget import scala.collection.mutable @@ -82,7 +83,10 @@ object Select { check(module) module._component.get match { case d: DefModule => d.commands.collect { - case i: DefInstance => i.id + case i: DefInstance => i.id match { + case clone: ModuleClone => clone._proto + case other => other + } } case other => Nil } -- cgit v1.2.3 From 25a84b5667614ea3f437b656f1939caba57e6f66 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 29 Jun 2021 16:39:45 -0700 Subject: Change behavior of aop.Select to not include CloneModuleAsRecord Previously, CloneModuleAsRecord clones would result in the same BaseModule object coming up multiple times when using APIs like .instances, .collectDeep, and .getDeep. This was not the intended behavior and can lead to very subtle bugs. --- src/main/scala/chisel3/aop/Select.scala | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 08cf40ff..a16c415c 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -82,11 +82,12 @@ object Select { def instances(module: BaseModule): Seq[BaseModule] = { check(module) module._component.get match { - case d: DefModule => d.commands.collect { + case d: DefModule => d.commands.flatMap { case i: DefInstance => i.id match { - case clone: ModuleClone => clone._proto - case other => other + case _: ModuleClone => None + case other => Some(other) } + case _ => None } case other => Nil } -- cgit v1.2.3 From 503ae520e7f997bcbc639b79869c9a4214d402ed Mon Sep 17 00:00:00 2001 From: Deborah Soung Date: Tue, 6 Jul 2021 14:40:59 -0700 Subject: Make printf return BaseSim subclass so it can be named/annotated (#1992) --- src/main/scala/chisel3/aop/Select.scala | 4 ++-- src/main/scala/chisel3/internal/firrtl/Emitter.scala | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index a16c415c..078422bb 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -281,7 +281,7 @@ object Select { val printfs = mutable.ArrayBuffer[Printf]() searchWhens(module, (cmd: Command, preds: Seq[Predicate]) => { cmd match { - case chisel3.internal.firrtl.Printf(_, clock, pable) => printfs += Printf(preds, pable, getId(clock).asInstanceOf[Clock]) + case chisel3.internal.firrtl.Printf(id, _, clock, pable) => printfs += Printf(id, preds, pable, getId(clock).asInstanceOf[Clock]) case other => } }) @@ -418,7 +418,7 @@ object Select { * @param pable * @param clock */ - case class Printf(preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable { + case class Printf(id: printf.Printf, preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable { def serialize: String = { s"printf when(${preds.map(_.serialize).mkString(" & ")}) on ${getName(clock)}: $pable" } diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 53d5c6ce..47849d91 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -75,11 +75,11 @@ private class Emitter(circuit: Circuit) { case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}" case e: Attach => e.locs.map(_.fullName(ctx)).mkString("attach (", ", ", ")") case e: Stop => s"stop(${e.clock.fullName(ctx)}, UInt<1>(1), ${e.ret})" - case e: Printf => + case e: chisel3.internal.firrtl.Printf => val (fmt, args) = e.pable.unpack(ctx) val printfArgs = Seq(e.clock.fullName(ctx), "UInt<1>(1)", "\"" + printf.format(fmt) + "\"") ++ args - printfArgs mkString ("printf(", ", ", ")") + (printfArgs mkString ("printf(", ", ", ")")) + s": ${e.name}" case e: Verification[_] => s"""${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, UInt<1>(1), "${printf.format(e.message)}") : ${e.name}""" case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid" -- cgit v1.2.3 From 695864f5716626a15a7798dae048d8301940a2db Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Thu, 15 Jul 2021 02:32:06 +0800 Subject: Espresso Decoder (#1964) Co-authored-by: Haoran Yuan Co-authored-by: Boyang Han --- .../experimental/decode/EspressoMinimizer.scala | 75 ++++++++++++++++++++++ .../util/experimental/decode/TruthTable.scala | 2 +- .../chisel3/util/experimental/decode/decoder.scala | 49 ++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala new file mode 100644 index 00000000..0351a46a --- /dev/null +++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: Apache-2.0 + +package chisel3.util.experimental.decode + +import chisel3.util.BitPat +import logger.LazyLogging + +case object EspressoNotFoundException extends Exception + +object EspressoMinimizer extends Minimizer with LazyLogging { + def minimize(table: TruthTable): TruthTable = + TruthTable.merge(TruthTable.split(table).map{case (table, indexes) => (espresso(table), indexes)}) + + def espresso(table: TruthTable): TruthTable = { + def writeTable(table: TruthTable): String = { + def invert(string: String) = string + .replace('0', 't') + .replace('1', '0') + .replace('t', '1') + val defaultType: Char = { + val t = table.default.toString.drop(7).dropRight(1).toCharArray.distinct + require(t.length == 1, "Internal Error: espresso only accept unified default type.") + t.head + } + val tableType: String = defaultType match { + case '?' => "fr" + case _ => "fd" + } + val rawTable = table + .toString + .split("\n") + .filter(_.contains("->")) + .mkString("\n") + .replace("->", " ") + .replace('?', '-') + // invert all output, since espresso cannot handle default is on. + val invertRawTable = rawTable + .split("\n") + .map(_.split(" ")) + .map(row => s"${row(0)} ${invert(row(1))}") + .mkString("\n") + s""".i ${table.inputWidth} + |.o ${table.outputWidth} + |.type ${tableType} + |""".stripMargin ++ (if (defaultType == '1') invertRawTable else rawTable) + } + + def readTable(espressoTable: String): Map[BitPat, BitPat] = { + def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?')) + + espressoTable + .split("\n") + .filterNot(_.startsWith(".")) + .map(_.split(' ')) + .map(row => bitPat(row(0)) -> bitPat(row(1))) + .toMap + } + + // Since Espresso don't implements pipe, we use a temp file to do so. + val input = writeTable(table) + logger.trace(s"""espresso input table: + |$input + |""".stripMargin) + val f = os.temp(input) + val o = try { + os.proc("espresso", f).call().out.chunks.mkString + } catch { + case e: java.io.IOException if e.getMessage.contains("error=2, No such file or directory") => throw EspressoNotFoundException + } + logger.trace(s"""espresso output table: + |$o + |""".stripMargin) + TruthTable(readTable(o), table.default) + } +} diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index ca0ff8b4..f4f200ce 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -99,7 +99,7 @@ object TruthTable { tables: Seq[(TruthTable, Seq[Int])] ): TruthTable = { def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] = - bpStr(table.table.getOrElse(bitPat, BitPat.dontCare(indexes.size))).zip(indexes) + bpStr(table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).zip(indexes) def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar .sortBy(_._2) .map(_._1) diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala index 8168824f..42e374d1 100644 --- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala +++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala @@ -10,6 +10,13 @@ import firrtl.annotations.Annotation import logger.LazyLogging object decoder extends LazyLogging { + /** Use a specific [[Minimizer]] to generated decoded signals. + * + * @param minimizer specific [[Minimizer]], can be [[QMCMinimizer]] or [[EspressoMinimizer]]. + * @param input input signal that contains decode table input + * @param truthTable [[TruthTable]] to decode user input. + * @return decode table output. + */ def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = { val minimizedTable = getAnnotations().collect { case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out) @@ -31,4 +38,46 @@ object decoder extends LazyLogging { plaOutput } } + + /** Use [[EspressoMinimizer]] to generated decoded signals. + * + * @param input input signal that contains decode table input + * @param truthTable [[TruthTable]] to decode user input. + * @return decode table output. + */ + def espresso(input: UInt, truthTable: TruthTable): UInt = apply(EspressoMinimizer, input, truthTable) + + /** Use [[QMCMinimizer]] to generated decoded signals. + * + * @param input input signal that contains decode table input + * @param truthTable [[TruthTable]] to decode user input. + * @return decode table output. + */ + def qmc(input: UInt, truthTable: TruthTable): UInt = apply(QMCMinimizer, input, truthTable) + + /** try to use [[EspressoMinimizer]] to decode `input` by `truthTable` + * if `espresso` not exist in your PATH environment it will fall back to [[QMCMinimizer]], and print a warning. + * + * @param input input signal that contains decode table input + * @param truthTable [[TruthTable]] to decode user input. + * @return decode table output. + */ + def apply(input: UInt, truthTable: TruthTable): UInt = { + def qmcFallBack(input: UInt, truthTable: TruthTable) = { + """fall back to QMC. + |Quine-McCluskey is a NP complete algorithm, may run forever or run out of memory in large decode tables. + |To get rid of this warning, please use `decoder.qmc` directly, or add espresso to your PATH. + |""".stripMargin + qmc(input, truthTable) + } + + try espresso(input, truthTable) catch { + case EspressoNotFoundException => + logger.error(s"espresso is not found in your PATH:\n${sys.env("PATH").split(":").mkString("\n")}".stripMargin) + qmcFallBack(input, truthTable) + case e: java.io.IOException => + logger.error(s"espresso failed to run with ${e.getMessage}") + qmcFallBack(input, truthTable) + } + } } -- cgit v1.2.3 From da923f317ff325a93cee6289552ccfa413c35f98 Mon Sep 17 00:00:00 2001 From: anniej-sifive Date: Tue, 3 Aug 2021 16:09:19 -0700 Subject: Added flush capability to Queue (#2030) Co-authored-by: Megan Wachs Co-authored-by: Deborah Soung --- src/main/scala/chisel3/util/Decoupled.scala | 29 +++++++++++++++++++++-------- 1 file changed, 21 insertions(+), 8 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 33682020..8909ffe3 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -163,8 +163,9 @@ object DeqIO { /** An I/O Bundle for Queues * @param gen The type of data to queue * @param entries The max number of entries in the queue. + * @param hasFlush A boolean for whether the generated Queue is flushable */ -class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle +class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boolean = false) extends Bundle { // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs. /* These may look inverted, because the names (enq/deq) are from the perspective of the client, @@ -177,6 +178,9 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle val deq = Flipped(DeqIO(gen)) /** The current amount of data in the queue */ val count = Output(UInt(log2Ceil(entries + 1).W)) + /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)*/ + val flush = if (hasFlush) Some(Input(Bool())) else None + } /** A hardware module implementing a Queue @@ -187,7 +191,7 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately). * The ''valid'' signals are coupled. * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element. - * + * @param hasFlush True if generated queue requires a flush feature * @example {{{ * val q = Module(new Queue(UInt(), 16)) * q.io.enq <> producer.io.out @@ -199,7 +203,8 @@ class Queue[T <: Data](val gen: T, val entries: Int, val pipe: Boolean = false, val flow: Boolean = false, - val useSyncReadMem: Boolean = false) + val useSyncReadMem: Boolean = false, + val hasFlush: Boolean = false) (implicit compileOptions: chisel3.CompileOptions) extends Module() { require(entries > -1, "Queue must have non-negative number of entries") @@ -215,19 +220,20 @@ class Queue[T <: Data](val gen: T, } } - val io = IO(new QueueIO(genType, entries)) - + val io = IO(new QueueIO(genType, entries, hasFlush)) val ram = if (useSyncReadMem) SyncReadMem(entries, genType, SyncReadMem.WriteFirst) else Mem(entries, genType) val enq_ptr = Counter(entries) val deq_ptr = Counter(entries) val maybe_full = RegInit(false.B) - val ptr_match = enq_ptr.value === deq_ptr.value val empty = ptr_match && !maybe_full val full = ptr_match && maybe_full val do_enq = WireDefault(io.enq.fire()) val do_deq = WireDefault(io.deq.fire()) + val flush = io.flush.getOrElse(false.B) + // when flush is high, empty the queue + // Semantically, any enqueues happen before the flush. when (do_enq) { ram(enq_ptr.value) := io.enq.bits enq_ptr.inc() @@ -238,6 +244,11 @@ class Queue[T <: Data](val gen: T, when (do_enq =/= do_deq) { maybe_full := do_enq } + when(flush) { + enq_ptr.reset() + deq_ptr.reset() + maybe_full := false.B + } io.deq.valid := !empty io.enq.ready := !full @@ -265,6 +276,7 @@ class Queue[T <: Data](val gen: T, } val ptr_diff = enq_ptr.value - deq_ptr.value + if (isPow2(entries)) { io.count := Mux(maybe_full && ptr_match, entries.U, 0.U) | ptr_diff } else { @@ -296,7 +308,8 @@ object Queue entries: Int = 2, pipe: Boolean = false, flow: Boolean = false, - useSyncReadMem: Boolean = false): DecoupledIO[T] = { + useSyncReadMem: Boolean = false, + hasFlush: Boolean = false): DecoupledIO[T] = { if (entries == 0) { val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits))) deq.valid := enq.valid @@ -304,7 +317,7 @@ object Queue enq.ready := deq.ready deq } else { - val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem)) + val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, hasFlush)) q.io.enq.valid := enq.valid // not using <> so that override is allowed q.io.enq.bits := enq.bits enq.ready := q.io.enq.ready -- cgit v1.2.3 From aae627919336f0efff98dc47b2ea5694728d5350 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Thu, 12 Aug 2021 01:36:03 -0700 Subject: Pass truth table to espresso using stdin instead of temp file --- .../chisel3/util/experimental/decode/EspressoMinimizer.scala | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 0351a46a..3ecec048 100644 --- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala @@ -56,20 +56,18 @@ object EspressoMinimizer extends Minimizer with LazyLogging { .toMap } - // Since Espresso don't implements pipe, we use a temp file to do so. val input = writeTable(table) logger.trace(s"""espresso input table: |$input |""".stripMargin) - val f = os.temp(input) - val o = try { - os.proc("espresso", f).call().out.chunks.mkString + val output = try { + os.proc("espresso").call(stdin = input).out.chunks.mkString } catch { case e: java.io.IOException if e.getMessage.contains("error=2, No such file or directory") => throw EspressoNotFoundException } logger.trace(s"""espresso output table: - |$o + |$output |""".stripMargin) - TruthTable(readTable(o), table.default) + TruthTable(readTable(output), table.default) } } -- cgit v1.2.3 From 1ceb974c55c6785c21ab3934fa750ade0702e276 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Thu, 12 Aug 2021 17:04:11 -0700 Subject: Add DataView (#1955) DataView is a mechanism for "viewing" Scala objects as a subtype of `Data`. Often, this is useful for viewing one subtype of `Data`, as another. One can think about a DataView as a cross between a customizable cast and an untagged union. A DataView has a Target type `T`, and a View type `V`. DataView requires that an implementation of `DataProduct` is available for Target types. DataProduct is a type class that provides a way to iterate on `Data` children of objects of implementing types. If a DataView is provided for a type T to a type V, then the function .viewAs[V] (of type T => V) is available. The object (of type T) returned by .viewAs is called a "View" and can be used as both an rvalue and an lvalue. Unlike when using an .asTypeOf cast, connecting to a "View" will connect to the associated field or fields of the underlying Target. DataView also enables .viewAsSupertype which is available for viewing Bundles as a parent Bundle type. It is similar to .viewAs but requires a prototype object of the Target type which will be cloned in order to create the returned View. .viewAsSupertype maps between the corresponding fields of the parent and child Bundle types.--- src/main/scala/chisel3/stage/phases/Convert.scala | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/phases/Convert.scala b/src/main/scala/chisel3/stage/phases/Convert.scala index bf42b58a..b5b01b8d 100644 --- a/src/main/scala/chisel3/stage/phases/Convert.scala +++ b/src/main/scala/chisel3/stage/phases/Convert.scala @@ -29,8 +29,7 @@ class Convert extends Phase { /* Convert all Chisel Annotations to FIRRTL Annotations */ a .circuit - .annotations - .map(_.toFirrtl) ++ + .firrtlAnnotations ++ a .circuit .annotations -- cgit v1.2.3 From ed894c61474c8bc73761a6c360ef9d14505d853b Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Wed, 18 Aug 2021 03:44:17 +0800 Subject: remove DefRegInit, change DefReg API with option definition. (#1944) * remove DefRegInit, change DefReg API with option defination. * add error message * use Option[RegInitIR]. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>--- src/main/scala/chisel3/aop/Select.scala | 1 - src/main/scala/chisel3/internal/firrtl/Emitter.scala | 3 +-- 2 files changed, 1 insertion(+), 3 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 078422bb..81b4cdab 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -101,7 +101,6 @@ object Select { check(module) module._component.get.asInstanceOf[DefModule].commands.collect { case r: DefReg => r.id - case r: DefRegInit => r.id } } diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 47849d91..daa83db0 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -66,8 +66,7 @@ private class Emitter(circuit: Circuit) { val firrtlLine = e match { case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})" case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}" - case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}" - case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" + case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}${ if (e.regInit.isDefined) "with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" else ""}" case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]" case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}], ${e.readUnderWrite}" case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}" -- cgit v1.2.3 From 7c8a032e7e23902283035d93579b8dc477b32f6a Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Tue, 17 Aug 2021 18:22:16 -0700 Subject: Revert "remove DefRegInit, change DefReg API with option definition. (#1944)" (#2080) This reverts commit ed894c61474c8bc73761a6c360ef9d14505d853b.--- src/main/scala/chisel3/aop/Select.scala | 1 + src/main/scala/chisel3/internal/firrtl/Emitter.scala | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 81b4cdab..078422bb 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -101,6 +101,7 @@ object Select { check(module) module._component.get.asInstanceOf[DefModule].commands.collect { case r: DefReg => r.id + case r: DefRegInit => r.id } } diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index daa83db0..47849d91 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -66,7 +66,8 @@ private class Emitter(circuit: Circuit) { val firrtlLine = e match { case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})" case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}" - case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}${ if (e.regInit.isDefined) "with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" else ""}" + case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}" + case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]" case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}], ${e.readUnderWrite}" case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}" -- cgit v1.2.3 From a3d51e4c91059362b20296eaa00f06f96ec7a4e1 Mon Sep 17 00:00:00 2001 From: Ruige Lee Date: Sun, 22 Aug 2021 00:08:54 +0800 Subject: Update ChiselStage.scala (#2082) There might be some "@"?--- src/main/scala/chisel3/stage/ChiselStage.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala index 989b3a17..0c76f411 100644 --- a/src/main/scala/chisel3/stage/ChiselStage.scala +++ b/src/main/scala/chisel3/stage/ChiselStage.scala @@ -47,7 +47,7 @@ class ChiselStage extends Stage { /** Convert a Chisel module to a CHIRRTL string * @param gen a call-by-name Chisel module * @param args additional command line arguments to pass to Chisel - * param annotations additional annotations to pass to Chisel + * @param annotations additional annotations to pass to Chisel * @return a string containing the Verilog output */ final def emitChirrtl( @@ -70,7 +70,7 @@ class ChiselStage extends Stage { /** Convert a Chisel module to a FIRRTL string * @param gen a call-by-name Chisel module * @param args additional command line arguments to pass to Chisel - * param annotations additional annotations to pass to Chisel + * @param annotations additional annotations to pass to Chisel * @return a string containing the FIRRTL output */ final def emitFirrtl( @@ -90,7 +90,7 @@ class ChiselStage extends Stage { /** Convert a Chisel module to Verilog * @param gen a call-by-name Chisel module * @param args additional command line arguments to pass to Chisel - * param annotations additional annotations to pass to Chisel + * @param annotations additional annotations to pass to Chisel * @return a string containing the Verilog output */ final def emitVerilog( @@ -110,7 +110,7 @@ class ChiselStage extends Stage { /** Convert a Chisel module to SystemVerilog * @param gen a call-by-name Chisel module * @param args additional command line arguments to pass to Chisel - * param annotations additional annotations to pass to Chisel + * @param annotations additional annotations to pass to Chisel * @return a string containing the SystemVerilog output */ final def emitSystemVerilog( -- cgit v1.2.3 From 73bd4ee6b9b510725b692c33e075362a19512d2c Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 20 Aug 2021 11:30:27 -0700 Subject: Remove chisel3's own firrtl Emitter, use firrtl Serializer This will be slightly slower as it involves converting from Chisel modules to FIRRTL modules before turning them into Strings. This cost is somewhat mitigated by doing that conversion lazily such that we never materialize the entire firrtl Circuit in memory, only 1 module at a time. --- .../scala/chisel3/internal/firrtl/Emitter.scala | 192 +-------------------- 1 file changed, 5 insertions(+), 187 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 47849d91..53329908 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -1,194 +1,12 @@ // SPDX-License-Identifier: Apache-2.0 package chisel3.internal.firrtl -import chisel3._ -import chisel3.experimental.{Interval, _} -import chisel3.internal.BaseBlackBox +import firrtl.{ir => fir} private[chisel3] object Emitter { - def emit(circuit: Circuit): String = new Emitter(circuit).toString -} - -private class Emitter(circuit: Circuit) { - override def toString: String = res.toString - - private def emitPort(e: Port, topDir: SpecifiedDirection=SpecifiedDirection.Unspecified): String = { - val resolvedDir = SpecifiedDirection.fromParent(topDir, e.dir) - val dirString = resolvedDir match { - case SpecifiedDirection.Unspecified | SpecifiedDirection.Output => "output" - case SpecifiedDirection.Flip | SpecifiedDirection.Input => "input" - } - val clearDir = resolvedDir match { - case SpecifiedDirection.Input | SpecifiedDirection.Output => true - case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false - } - s"$dirString ${e.id.getRef.name} : ${emitType(e.id, clearDir)}" - } - - private def emitType(d: Data, clearDir: Boolean = false): String = d match { - case d: Clock => "Clock" - case _: AsyncReset => "AsyncReset" - case _: ResetType => "Reset" - case d: chisel3.experimental.EnumType => s"UInt${d.width}" - case d: UInt => s"UInt${d.width}" - case d: SInt => s"SInt${d.width}" - case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}" - case d: Interval => - val binaryPointString = d.binaryPoint match { - case UnknownBinaryPoint => "" - case KnownBinaryPoint(value) => s".$value" - } - d.toType - case d: Analog => s"Analog${d.width}" - case d: Vec[_] => s"${emitType(d.sample_element, clearDir)}[${d.length}]" - case d: Record => { - val childClearDir = clearDir || - d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output - def eltPort(elt: Data): String = (childClearDir, firrtlUserDirOf(elt)) match { - case (true, _) => - s"${elt.getRef.name} : ${emitType(elt, true)}" - case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) => - s"${elt.getRef.name} : ${emitType(elt, false)}" - case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) => - s"flip ${elt.getRef.name} : ${emitType(elt, false)}" - } - d.elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}") - } - } - - private def firrtlUserDirOf(d: Data): SpecifiedDirection = d match { - case d: Vec[_] => - SpecifiedDirection.fromParent(d.specifiedDirection, firrtlUserDirOf(d.sample_element)) - case d => d.specifiedDirection - } - - private def emit(e: Command, ctx: Component): String = { - val firrtlLine = e match { - case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})" - case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}" - case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}" - case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))" - case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]" - case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}], ${e.readUnderWrite}" - case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}" - case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}" - case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}" - case e: Attach => e.locs.map(_.fullName(ctx)).mkString("attach (", ", ", ")") - case e: Stop => s"stop(${e.clock.fullName(ctx)}, UInt<1>(1), ${e.ret})" - case e: chisel3.internal.firrtl.Printf => - val (fmt, args) = e.pable.unpack(ctx) - val printfArgs = Seq(e.clock.fullName(ctx), "UInt<1>(1)", - "\"" + printf.format(fmt) + "\"") ++ args - (printfArgs mkString ("printf(", ", ", ")")) + s": ${e.name}" - case e: Verification[_] => - s"""${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, UInt<1>(1), "${printf.format(e.message)}") : ${e.name}""" - case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid" - case e: DefInstance => s"inst ${e.name} of ${e.id.name}" - case w: WhenBegin => - // When consequences are always indented - indent() - s"when ${w.pred.fullName(ctx)} :" - case w: WhenEnd => - // If a when has no else, the indent level must be reset to the enclosing block - unindent() - if (!w.hasAlt) { for (i <- 0 until w.firrtlDepth) { unindent() } } - s"skip" - case a: AltBegin => - // Else blocks are always indented - indent() - s"else :" - case o: OtherwiseEnd => - // Chisel otherwise: ends all FIRRTL associated a Chisel when, resetting indent level - for (i <- 0 until o.firrtlDepth) { unindent() } - s"skip" - } - firrtlLine + e.sourceInfo.makeMessage(" " + _) + def emit(circuit: Circuit): String = { + val fcircuit = Converter.convertLazily(circuit) + fir.Serializer.serialize(fcircuit) } - - private def emitParam(name: String, p: Param): String = { - val str = p match { - case IntParam(value) => value.toString - case DoubleParam(value) => value.toString - case StringParam(str) => "\"" + str + "\"" - case RawParam(str) => "'" + str + "'" - } - s"parameter $name = $str" - } - - /** Generates the FIRRTL module declaration. - */ - private def moduleDecl(m: Component): String = m.id match { - case _: BaseBlackBox => newline + s"extmodule ${m.name} : " - case _: RawModule => newline + s"module ${m.name} : " - } - - /** Generates the FIRRTL module definition. - */ - private def moduleDefn(m: Component): String = { - val body = new StringBuilder - withIndent { - for (p <- m.ports) { - val portDef = m match { - case bb: DefBlackBox => emitPort(p, bb.topDir) - case mod: DefModule => emitPort(p) - } - body ++= newline + portDef - } - body ++= newline - - m match { - case bb: DefBlackBox => - // Firrtl extmodule can overrule name - body ++= newline + s"defname = ${bb.id.desiredName}" - body ++= newline + (bb.params map { case (n, p) => emitParam(n, p) } mkString newline) - case mod: DefModule => { - // Preprocess whens & elsewhens, marking those that have no alternative - val procMod = mod.copy(commands = processWhens(mod.commands)) - for (cmd <- procMod.commands) { body ++= newline + emit(cmd, procMod)} - } - } - body ++= newline - } - body.toString() - } - - /** Returns the FIRRTL declaration and body of a module, or nothing if it's a - * duplicate of something already emitted (on the basis of simple string - * matching). - */ - private def emit(m: Component): String = { - // Generate the body. - val sb = new StringBuilder - sb.append(moduleDecl(m)) - sb.append(moduleDefn(m)) - sb.result - } - - /** Preprocess the command queue, marking when/elsewhen statements - * that have no alternatives (elsewhens or otherwise). These - * alternative-free statements reset the indent level to the - * enclosing block upon emission. - */ - private def processWhens(cmds: Seq[Command]): Seq[Command] = { - if (cmds.isEmpty) { - Seq.empty - } else { - cmds.zip(cmds.tail).map{ - case (a: WhenEnd, b: AltBegin) => a.copy(hasAlt = true) - case (a, b) => a - } ++ cmds.lastOption - } - } - - private var indentLevel = 0 - private def newline = "\n" + (" " * indentLevel) - private def indent(): Unit = indentLevel += 1 - private def unindent() { require(indentLevel > 0); indentLevel -= 1 } - private def withIndent(f: => Unit) { indent(); f; unindent() } - - private val res = new StringBuilder() - res ++= s";${BuildInfo.toString}\n" - res ++= s"circuit ${circuit.name} : " - withIndent { circuit.components.foreach(c => res ++= emit(c)) } - res ++= newline } + -- cgit v1.2.3 From d9c30ea0fdd90f3e47cae161f4a7224a861a6bf0 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 20 Aug 2021 12:00:08 -0700 Subject: Emit .fir lazily, overcomes JVM 2 GiB String limit --- src/main/scala/chisel3/internal/firrtl/Emitter.scala | 15 +++++++++++++-- src/main/scala/chisel3/stage/ChiselAnnotations.scala | 7 +++++-- 2 files changed, 18 insertions(+), 4 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala index 53329908..a94558ce 100644 --- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala +++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala @@ -1,12 +1,23 @@ // SPDX-License-Identifier: Apache-2.0 package chisel3.internal.firrtl -import firrtl.{ir => fir} + +import scala.collection.immutable.LazyList // Needed for 2.12 alias +import firrtl.ir.Serializer private[chisel3] object Emitter { def emit(circuit: Circuit): String = { val fcircuit = Converter.convertLazily(circuit) - fir.Serializer.serialize(fcircuit) + Serializer.serialize(fcircuit) + } + + def emitLazily(circuit: Circuit): Iterable[String] = { + val result = LazyList(s"circuit ${circuit.name} :\n") + val modules = circuit.components.view.map(Converter.convert) + val moduleStrings = modules.flatMap { m => + Array(Serializer.serialize(m, 1), "\n\n") + } + result ++ moduleStrings } } diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index b071c60f..6cace05f 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -133,9 +133,12 @@ case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, fo protected def suffix: Option[String] = Some(format.extension) - // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures override def getBytes: Iterable[Byte] = format match { - case FirrtlFileFormat => OldEmitter.emit(circuit).getBytes + case FirrtlFileFormat => + OldEmitter.emitLazily(circuit) + .map(_.getBytes) + .flatten + // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures case ProtoBufFileFormat => val ostream = new java.io.ByteArrayOutputStream val modules = circuit.components.map(m => () => chisel3.internal.firrtl.Converter.convert(m)) -- cgit v1.2.3 From a6eb2ad8b6ff50bf245d610891808e436b19ed01 Mon Sep 17 00:00:00 2001 From: Jack Koenig Date: Fri, 20 Aug 2021 17:34:17 -0700 Subject: Use BufferedCustomFileEmission in CircuitSerializationAnnotation --- src/main/scala/chisel3/stage/ChiselAnnotations.scala | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index 6cace05f..de47ef36 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -3,7 +3,7 @@ package chisel3.stage import firrtl.annotations.{Annotation, NoTargetAnnotation} -import firrtl.options.{CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable} +import firrtl.options.{BufferedCustomFileEmission, CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable} import firrtl.options.Viewer.view import chisel3.{ChiselException, Module} import chisel3.RawModule @@ -123,7 +123,7 @@ import CircuitSerializationAnnotation._ */ case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, format: Format) extends NoTargetAnnotation - with CustomFileEmission { + with BufferedCustomFileEmission { /* Caching the hashCode for a large circuit is necessary due to repeated queries. * Not caching the hashCode will cause severe performance degredations for large [[Circuit]]s. */ @@ -133,17 +133,16 @@ case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, fo protected def suffix: Option[String] = Some(format.extension) - override def getBytes: Iterable[Byte] = format match { + override def getBytesBuffered: Iterable[Array[Byte]] = format match { case FirrtlFileFormat => OldEmitter.emitLazily(circuit) .map(_.getBytes) - .flatten // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures case ProtoBufFileFormat => val ostream = new java.io.ByteArrayOutputStream val modules = circuit.components.map(m => () => chisel3.internal.firrtl.Converter.convert(m)) firrtl.proto.ToProto.writeToStreamFast(ostream, firrtl.ir.NoInfo, modules, circuit.name) - ostream.toByteArray + List(ostream.toByteArray) } } -- cgit v1.2.3 From e74b978d5188d9cd28e3520912d858d228136e75 Mon Sep 17 00:00:00 2001 From: Jiuyang Liu Date: Fri, 27 Aug 2021 04:37:34 +0800 Subject: add new APIs to BitPat (#2076) * add Y and N to BitPat. * add ## for BitPat. * add rawString API. * use rawString in decoder * add select and slice to BitPat.--- src/main/scala/chisel3/util/BitPat.scala | 53 ++++++++++++++++++---- .../experimental/decode/EspressoMinimizer.scala | 2 +- .../util/experimental/decode/QMCMinimizer.scala | 2 +- .../util/experimental/decode/TruthTable.scala | 18 ++++---- 4 files changed, 55 insertions(+), 20 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 985d22da..0dcb2466 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -58,6 +58,22 @@ object BitPat { */ def dontCare(width: Int): BitPat = BitPat("b" + ("?" * width)) + /** Creates a [[BitPat]] of all 1 of the specified bitwidth. + * + * @example {{{ + * val myY = BitPat.Y(4) // equivalent to BitPat("b1111") + * }}} + */ + def Y(width: Int = 1): BitPat = BitPat("b" + ("1" * width)) + + /** Creates a [[BitPat]] of all 0 of the specified bitwidth. + * + * @example {{{ + * val myN = BitPat.N(4) // equivalent to BitPat("b0000") + * }}} + */ + def N(width: Int = 1): BitPat = BitPat("b" + ("0" * width)) + /** Allows BitPats to be used where a UInt is expected. * * @note the BitPat must not have don't care bits (will error out otherwise) @@ -106,9 +122,11 @@ object BitPat { */ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends SourceInfoDoc { def getWidth: Int = width + def apply(x: Int): BitPat = macro SourceInfoTransform.xArg + def apply(x: Int, y: Int): BitPat = macro SourceInfoTransform.xyArg def === (that: UInt): Bool = macro SourceInfoTransform.thatArg def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg - + def ## (that: BitPat): BitPat = macro SourceInfoTransform.thatArg override def equals(obj: Any): Boolean = { obj match { case y: BitPat => value == y.value && mask == y.mask && getWidth == y.getWidth @@ -116,6 +134,18 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou } } + /** @group SourceInfoTransformMacro */ + def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = { + do_apply(x, x) + } + + /** @group SourceInfoTransformMacro */ + def do_apply(x: Int, y: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = { + require(width > x && y >= 0, s"Invalid bit range ($x, $y), index should be bounded by (${width - 1}, 0)") + require(x >= y, s"Invalid bit range ($x, $y), x should be greater or equal to y.") + BitPat(s"b${rawString.slice(width - x - 1, width - y)}") + } + /** @group SourceInfoTransformMacro */ def do_=== (that: UInt) (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { @@ -126,12 +156,19 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = { !(this === that) } - - override def toString = { - "BitPat(" + - (0 until width).map(i => - if (((mask >> i) & 1) == 1) if (((value >> i) & 1) == 1) "1" else "0" else "?" - ).reverse.reduce(_ + _) + - ")" + /** @group SourceInfoTransformMacro */ + def do_##(that: BitPat)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = { + new BitPat((value << that.getWidth) + that.value, (mask << that.getWidth) + that.mask, this.width + that.getWidth) } + + /** Generate raw string of a BitPat. */ + def rawString: String = Seq.tabulate(width) { i => + (value.testBit(width - i - 1), mask.testBit(width - i - 1)) match { + case (true, true) => "1" + case (false, true) => "0" + case (_, false) => "?" + } + }.mkString + + override def toString = s"BitPat($rawString)" } diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 3ecec048..1d725875 100644 --- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala @@ -18,7 +18,7 @@ object EspressoMinimizer extends Minimizer with LazyLogging { .replace('1', '0') .replace('t', '1') val defaultType: Char = { - val t = table.default.toString.drop(7).dropRight(1).toCharArray.distinct + val t = table.default.rawString.toCharArray.distinct require(t.length == 1, "Internal Error: espresso only accept unified default type.") t.head } diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala index 54faa734..c1533f44 100644 --- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala @@ -286,7 +286,7 @@ object QMCMinimizer extends Minimizer { if (tb.table.exists(x => x._1 == t._1)) { tb.copy(table = tb.table.map { case (k, v) => if (k == t._1) { - def ones(bitPat: BitPat) = bitPat.toString.drop(7).dropRight(1).zipWithIndex.collect{case ('1', x) => x} + def ones(bitPat: BitPat) = bitPat.rawString.zipWithIndex.collect{case ('1', x) => x} (k, BitPat("b" + (0 until v.getWidth).map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?").mkString)) } else (k, v) }) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index f4f200ce..683de16b 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -12,9 +12,9 @@ final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) { override def toString: String = { def writeRow(map: (BitPat, BitPat)): String = - s"${TruthTable.bpStr(map._1)}->${TruthTable.bpStr(map._2)}" + s"${map._1.rawString}->${map._2.rawString}" - (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${TruthTable.bpStr(default)}")).toSeq.sorted.mkString("\n") + (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).toSeq.sorted.mkString("\n") } def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default) @@ -51,11 +51,11 @@ object TruthTable { values.head._1 -> BitPat(s"b${ Seq.tabulate(outputWidth) { i => val outputSet = values.map(_._2) - .map(bpStr) + .map(_.rawString) .map(_ (i)) .toSet .filterNot(_ == '?') - require(outputSet.size != 2, s"TruthTable conflict in :\n${values.map { case (i, o) => s"${bpStr(i)}->${bpStr(o)}" }.mkString("\n")}") + require(outputSet.size != 2, s"TruthTable conflict in :\n${values.map { case (i, o) => s"${i.rawString}->${o.rawString}" }.mkString("\n")}") outputSet.headOption.getOrElse('?') }.mkString }") @@ -74,7 +74,7 @@ object TruthTable { table: TruthTable ): Seq[(TruthTable, Seq[Int])] = { def bpFilter(bitPat: BitPat, indexes: Seq[Int]): BitPat = - BitPat(s"b${bpStr(bitPat).zipWithIndex.filter(b => indexes.contains(b._2)).map(_._1).mkString}") + BitPat(s"b${bitPat.rawString.zipWithIndex.filter(b => indexes.contains(b._2)).map(_._1).mkString}") def tableFilter(indexes: Seq[Int]): Option[(TruthTable, Seq[Int])] = { if(indexes.nonEmpty) Some((TruthTable( @@ -84,7 +84,7 @@ object TruthTable { } def index(bitPat: BitPat, bpType: Char): Seq[Int] = - bpStr(bitPat).zipWithIndex.filter(_._1 == bpType).map(_._2) + bitPat.rawString.zipWithIndex.filter(_._1 == bpType).map(_._2) Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t))) } @@ -99,7 +99,7 @@ object TruthTable { tables: Seq[(TruthTable, Seq[Int])] ): TruthTable = { def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] = - bpStr(table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).zip(indexes) + (table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).rawString.zip(indexes) def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar .sortBy(_._2) .map(_._1) @@ -111,9 +111,7 @@ object TruthTable { key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) }) } .toMap, - bitPat(tables.flatMap { case (table, indexes) => bpStr(table.default).zip(indexes) }) + bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) }) ) } - - private def bpStr(bitPat: BitPat) = bitPat.toString.drop(7).dropRight(1) } -- cgit v1.2.3 From 9fa8da227569455a77596355aeb114f9c164510a Mon Sep 17 00:00:00 2001 From: Adam Izraelevitz Date: Sun, 5 Sep 2021 12:11:32 -0700 Subject: Add Definition and Instance API (#2045) This introduces a new experimental API for module instantiation that disentagles elaborating the definition (or implementation) from instantiation of a given module. This solves Chisel's longstanding reliance on "Deduplication" for generating Verilog with multiple instances of the same module. The new API resides in package chisel3.experimental.hierarchy. Please see the hierarchy ScalaDoc, documentation, and tests for examples of use. Co-authored-by: Jack Koenig Co-authored-by: Megan Wachs Co-authored-by: Schuyler Eldridge --- src/main/scala/chisel3/aop/Select.scala | 10 +++++++--- src/main/scala/chisel3/aop/injecting/InjectingAspect.scala | 1 + 2 files changed, 8 insertions(+), 3 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala index 078422bb..2384c4d3 100644 --- a/src/main/scala/chisel3/aop/Select.scala +++ b/src/main/scala/chisel3/aop/Select.scala @@ -3,13 +3,16 @@ package chisel3.aop import chisel3._ -import chisel3.experimental.{BaseModule, FixedPoint} -import chisel3.internal.HasId +import chisel3.internal.{HasId} +import chisel3.experimental.BaseModule +import chisel3.experimental.FixedPoint import chisel3.internal.firrtl._ +import chisel3.internal.PseudoModule import chisel3.internal.BaseModule.ModuleClone import firrtl.annotations.ReferenceTarget import scala.collection.mutable +import chisel3.internal.naming.chiselName /** Use to select Chisel components in a module, after that module has been constructed * Useful for adding additional Chisel annotations or for use within an [[Aspect]] @@ -84,7 +87,8 @@ object Select { module._component.get match { case d: DefModule => d.commands.flatMap { case i: DefInstance => i.id match { - case _: ModuleClone => None + case m: ModuleClone[_] if !m._madeFromDefinition => None + case _: PseudoModule => throw new Exception("Aspect APIs are currently incompatible with Definition/Instance") case other => Some(other) } case _ => None diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index c540fc83..1a476f61 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -4,6 +4,7 @@ package chisel3.aop.injecting import chisel3.{Module, ModuleAspect, RawModule, withClockAndReset} import chisel3.aop._ +import chisel3.experimental.hierarchy.IsInstantiable import chisel3.internal.{Builder, DynamicContext} import chisel3.internal.firrtl.DefModule import chisel3.stage.DesignAnnotation -- cgit v1.2.3 From 189da9828b1990e360fe521598dc340a260d05d5 Mon Sep 17 00:00:00 2001 From: Boyang Han Date: Mon, 6 Sep 2021 21:07:47 -0700 Subject: Fix #2112, handle `?->1` case --- src/main/scala/chisel3/util/pla.scala | 39 +++++++++++++++++++++++------------ 1 file changed, 26 insertions(+), 13 deletions(-) (limited to 'src/main') diff --git a/src/main/scala/chisel3/util/pla.scala b/src/main/scala/chisel3/util/pla.scala index bb2b7996..c57ca962 100644 --- a/src/main/scala/chisel3/util/pla.scala +++ b/src/main/scala/chisel3/util/pla.scala @@ -17,6 +17,20 @@ object pla { * a `1` means this product term makes the function value to `1` * and a `0` or `?` means this product term make the function value to `0` * + * @note There is one special case which we call it `? -> 1`. In this scenario, for some of the output functions (bits), + * all input terms that make this function value to `1` is purely composed by `?`. In a real pla, this will result in + * no connection to the gates in the AND Plane (verilog `z` on gate inputs), which in turn makes the outputs of the + * AND Plane undetermined (verilog `x` on outputs). This is not desired behavior in most cases, for example the + * minimization result of following truth table: + * 0 -> 1 + * 1 -> 1 + * which is: + * ? -> 1 + * actually means something other than a verilog `x`. To ease the generation of minimized truth tables, this pla + * generation api will hard wire outputs to a `1` on this special case. + * This behavior is formally described as: if product terms that make one function value to `1` is solely consisted + * of don't-cares (`?`s), then this function is implemented as a constant `1`. + * * @param table A [[Seq]] of inputs -> outputs mapping * @param invert A [[BitPat]] specify which bit of the output should be inverted. `1` means the correspond position * of the output should be inverted in the PLA, a `0` or a `?` means direct output from the OR matrix. @@ -68,20 +82,19 @@ object pla { // the AND matrix // use `term -> AND line` map to reuse AND matrix output lines val andMatrixOutputs: Map[String, Bool] = inputTerms.map { t => - t.toString -> Cat( - Seq - .tabulate(numberOfInputs) { i => - if (t.mask.testBit(i)) { - Some( - if (t.value.testBit(i)) inputs(i) - else invInputs(i) - ) - } else { - None - } + val andMatrixInput = Seq + .tabulate(numberOfInputs) { i => + if (t.mask.testBit(i)) { + Some( + if (t.value.testBit(i)) inputs(i) + else invInputs(i) + ) + } else { + None } - .flatten - ).andR() + } + .flatten + if (andMatrixInput.nonEmpty) t.toString -> Cat(andMatrixInput).andR() else t.toString -> true.B }.toMap // the OR matrix -- cgit v1.2.3