summaryrefslogtreecommitdiff
path: root/src/main
diff options
context:
space:
mode:
authorJack Koenig2021-09-17 21:01:26 -0700
committerJack Koenig2021-09-17 21:01:26 -0700
commit5c8c19345e6711279594cf1f9ddab33623c8eba7 (patch)
treed9d6ced3934aa4a8be3dec19ddcefe50a7a93d5a /src/main
parente63b9667d89768e0ec6dc8a9153335cb48a213a7 (diff)
parent958904cb2f2f65d02b2ab3ec6d9ec2e06d04e482 (diff)
Merge branch 'master' into 3.5-release
Diffstat (limited to 'src/main')
-rw-r--r--src/main/resources/chisel3/top.cpp12
-rw-r--r--src/main/scala/chisel3/Driver.scala200
-rw-r--r--src/main/scala/chisel3/aop/Select.scala29
-rw-r--r--src/main/scala/chisel3/aop/injecting/InjectingAspect.scala20
-rw-r--r--src/main/scala/chisel3/compatibility.scala5
-rw-r--r--src/main/scala/chisel3/internal/firrtl/Emitter.scala197
-rw-r--r--src/main/scala/chisel3/stage/ChiselAnnotations.scala24
-rw-r--r--src/main/scala/chisel3/stage/ChiselStage.scala50
-rw-r--r--src/main/scala/chisel3/stage/phases/AspectPhase.scala2
-rw-r--r--src/main/scala/chisel3/stage/phases/Convert.scala3
-rw-r--r--src/main/scala/chisel3/stage/phases/DriverCompatibility.scala152
-rw-r--r--src/main/scala/chisel3/stage/phases/Elaborate.scala26
-rw-r--r--src/main/scala/chisel3/util/BitPat.scala65
-rw-r--r--src/main/scala/chisel3/util/BlackBoxUtils.scala36
-rw-r--r--src/main/scala/chisel3/util/Conditional.scala9
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala53
-rw-r--r--src/main/scala/chisel3/util/ExtModuleUtils.scala7
-rw-r--r--src/main/scala/chisel3/util/MixedVec.scala4
-rw-r--r--src/main/scala/chisel3/util/Reg.scala43
-rw-r--r--src/main/scala/chisel3/util/experimental/BoringUtils.scala20
-rw-r--r--src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala82
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala22
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala73
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/Minimizer.scala29
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala298
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/TruthTable.scala117
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/decoder.scala83
-rw-r--r--src/main/scala/chisel3/util/experimental/getAnnotations.scala9
-rw-r--r--src/main/scala/chisel3/util/pla.scala132
-rw-r--r--src/main/scala/chisel3/verilog.scala15
30 files changed, 1139 insertions, 678 deletions
diff --git a/src/main/resources/chisel3/top.cpp b/src/main/resources/chisel3/top.cpp
index 4e9c1433..a3475e2f 100644
--- a/src/main/resources/chisel3/top.cpp
+++ b/src/main/resources/chisel3/top.cpp
@@ -47,8 +47,15 @@ int main(int argc, char** argv) {
cout << "Starting simulation!\n";
while (!Verilated::gotFinish() && main_time < timeout) {
- if (main_time > 10) {
- top->reset = 0; // Deassert reset
+ // Deassert reset on timestep 10. This needs to occur before the clock
+ // asserts on timestep 11 because there is a single call to top->eval() in
+ // this loop. Verilator evaluates sequential logic (always blocks) before
+ // combinational logic during top->eval(). Staggering the reset update is
+ // necessary to produce the same simulation behavior independent of whether
+ // or not the generated Verilog puts synchronous reset logic inside or
+ // outside its associated always block.
+ if (main_time == 10) {
+ top->reset = 0;
}
if ((main_time % 10) == 1) {
top->clock = 1; // Toggle clock
@@ -92,4 +99,3 @@ int main(int argc, char** argv) {
if (tfp) tfp->close();
#endif
}
-
diff --git a/src/main/scala/chisel3/Driver.scala b/src/main/scala/chisel3/Driver.scala
index 998f5ca0..fb564446 100644
--- a/src/main/scala/chisel3/Driver.scala
+++ b/src/main/scala/chisel3/Driver.scala
@@ -84,203 +84,3 @@ case class ChiselExecutionSuccess(
*/
@deprecated("This will be removed in Chisel 3.5", "Chisel 3.4")
case class ChiselExecutionFailure(message: String) extends ChiselExecutionResult
-
-@deprecated("Please switch to chisel3.stage.ChiselStage", "3.2.4")
-object Driver extends BackendCompilationUtilities {
-
- /**
- * Elaborate the Module specified in the gen function into a Chisel IR Circuit.
- *
- * @param gen A function that creates a Module hierarchy.
- * @return The resulting Chisel IR in the form of a Circuit. (TODO: Should be FIRRTL IR)
- */
- @deprecated("Use ChiselStage.elaborate or use a ChiselStage class. This will be removed in 3.4.", "3.2.4")
- def elaborate[T <: RawModule](gen: () => T): Circuit = internal.Builder.build(Module(gen()))._1
-
- /**
- * Convert the given Chisel IR Circuit to a FIRRTL Circuit.
- *
- * @param ir Chisel IR Circuit, generated e.g. by elaborate().
- */
- @deprecated("Use ChiselStage.convert or use a ChiselStage class. This will be removed in 3.4.", "3.2.4")
- def toFirrtl(ir: Circuit): firrtl.ir.Circuit = Converter.convert(ir)
-
- /**
- * Emit the Module specified in the gen function directly as a FIRRTL string without
- * invoking FIRRTL.
- *
- * @param gen A function that creates a Module hierarchy.
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl. This will be removed in 3.4.", "3.2.2")
- def emit[T <: RawModule](gen: () => T): String = Driver.emit(elaborate(gen))
-
- /**
- * Emit the given Chisel IR Circuit as a FIRRTL string, without invoking FIRRTL.
- *
- * @param ir Chisel IR Circuit, generated e.g. by elaborate().
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitChirrtl", "3.2.2")
- def emit[T <: RawModule](ir: Circuit): String = Emitter.emit(ir)
-
- /**
- * Elaborate the Module specified in the gen function into Verilog.
- *
- * @param gen A function that creates a Module hierarchy.
- * @return A String containing the design in Verilog.
- */
- @deprecated("Use (new chisel3.stage.ChiselStage).emitVerilog. This will be removed in 3.4.", "3.2.2")
- def emitVerilog[T <: RawModule](gen: => T): String = {
- execute(Array[String](), { () => gen }) match {
- case ChiselExecutionSuccess(_, _, Some(firrtl.FirrtlExecutionSuccess(_, verilog))) => verilog
- case _ => sys.error("Cannot get Verilog!")
- }
- }
-
- /**
- * Dump the elaborated Chisel IR Circuit as a FIRRTL String, without invoking FIRRTL.
- *
- * If no File is given as input, it will dump to a default filename based on the name of the
- * top Module.
- *
- * @param c Elaborated Chisel Circuit.
- * @param optName File to dump to. If unspecified, defaults to "<topmodule>.fir".
- * @return The File the circuit was dumped to.
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpFirrtl(ir: Circuit, optName: Option[File]): File = {
- val f = optName.getOrElse(new File(ir.name + ".fir"))
- val w = new FileWriter(f)
- w.write(Driver.emit(ir))
- w.close()
- f
- }
-
- /**
- * Emit the annotations of a circuit
- *
- * @param ir The circuit containing annotations to be emitted
- * @param optName An optional filename (will use s"\${ir.name}.json" otherwise)
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpAnnotations(ir: Circuit, optName: Option[File]): File = {
- val f = optName.getOrElse(new File(ir.name + ".anno.json"))
- val w = new FileWriter(f)
- w.write(JsonProtocol.serialize(ir.annotations.map(_.toFirrtl)))
- w.close()
- f
- }
-
- /**
- * Dump the elaborated Circuit to ProtoBuf.
- *
- * If no File is given as input, it will dump to a default filename based on the name of the
- * top Module.
- *
- * @param c Elaborated Chisel Circuit.
- * @param optFile Optional File to dump to. If unspecified, defaults to "<topmodule>.pb".
- * @return The File the circuit was dumped to.
- */
- @deprecated("Migrate to chisel3.stage.ChiselStage. This will be removed in 3.4.", "3.2.4")
- def dumpProto(c: Circuit, optFile: Option[File]): File = {
- val f = optFile.getOrElse(new File(c.name + ".pb"))
- val ostream = new java.io.FileOutputStream(f)
- // Lazily convert modules to make intermediate objects garbage collectable
- val modules = c.components.map(m => () => Converter.convert(m))
- firrtl.proto.ToProto.writeToStreamFast(ostream, ir.NoInfo, modules, c.name)
- f
- }
-
- private var target_dir: Option[String] = None
- @deprecated("Use chisel3.stage.ChiselStage with '--target-directory'. This will be removed in 3.4.", "3.2.2")
- def parseArgs(args: Array[String]): Unit = {
- for (i <- 0 until args.size) {
- if (args(i) == "--targetDir") {
- target_dir = Some(args(i + 1))
- }
- }
- }
-
- @deprecated("This has no effect on Chisel3 Driver! This will be removed in 3.4.", "3.2.2")
- def targetDir(): String = { target_dir getOrElse new File(".").getCanonicalPath }
-
- /**
- * Run the chisel3 compiler and possibly the firrtl compiler with options specified
- *
- * @param optionsManager The options specified
- * @param dut The device under test
- * @return An execution result with useful stuff, or failure with message
- */
- @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2")
- def execute(
- optionsManager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions,
- dut: () => RawModule): ChiselExecutionResult = {
-
- val annos: AnnotationSeq =
- Seq(DriverCompatibility.OptionsManagerAnnotation(optionsManager), ChiselGeneratorAnnotation(dut)) ++
- optionsManager.chiselOptions.toAnnotations ++
- optionsManager.firrtlOptions.toAnnotations ++
- optionsManager.commonOptions.toAnnotations
-
- val targets =
- Seq( Dependency[DriverCompatibility.AddImplicitOutputFile],
- Dependency[DriverCompatibility.AddImplicitOutputAnnotationFile],
- Dependency[DriverCompatibility.DisableFirrtlStage],
- Dependency[ChiselStage],
- Dependency[DriverCompatibility.MutateOptionsManager],
- Dependency[DriverCompatibility.ReEnableFirrtlStage],
- Dependency[DriverCompatibility.FirrtlPreprocessing],
- Dependency[chisel3.stage.phases.MaybeFirrtlStage] )
- val currentState =
- Seq( Dependency[firrtl.stage.phases.DriverCompatibility.AddImplicitFirrtlFile],
- Dependency[chisel3.stage.phases.Convert] )
-
- val phases: Seq[Phase] = new PhaseManager(targets, currentState) {
- override val wrappers = Seq( DeletedWrapper(_: Phase) )
- }.transformOrder
-
- val annosx = try {
- phases.foldLeft(annos)( (a, p) => p.transform(a) )
- } catch {
- /* ChiselStage and FirrtlStage can throw StageError. Since Driver is not a StageMain, it cannot catch these. While
- * Driver is deprecated and removed in 3.2.1+, the Driver catches all errors.
- */
- case e: StageError => annos
- }
-
- view[ChiselExecutionResult](annosx)
- }
-
- /**
- * Run the chisel3 compiler and possibly the firrtl compiler with options specified via an array of Strings
- *
- * @param args The options specified, command line style
- * @param dut The device under test
- * @return An execution result with useful stuff, or failure with message
- */
- @deprecated("Use chisel3.stage.ChiselStage.execute. This will be removed in 3.4.", "3.2.2")
- def execute(args: Array[String], dut: () => RawModule): ChiselExecutionResult = {
- val optionsManager = new ExecutionOptionsManager("chisel3") with HasChiselExecutionOptions with HasFirrtlOptions
-
- optionsManager.parse(args) match {
- case true =>
- execute(optionsManager, dut)
- case _ =>
- ChiselExecutionFailure("could not parse results")
- }
- }
-
- /**
- * This is just here as command line way to see what the options are
- * It will not successfully run
- * TODO: Look into dynamic class loading as way to make this main useful
- *
- * @param args unused args
- */
- @deprecated("Use chisel3.stage.ChiselMain. This will be removed in 3.4.", "3.2.2")
- def main(args: Array[String]) {
- execute(Array("--help"), null)
- }
-
- val version = BuildInfo.version
- val chiselVersionString = BuildInfo.toString
-}
diff --git a/src/main/scala/chisel3/aop/Select.scala b/src/main/scala/chisel3/aop/Select.scala
index e2689f39..2384c4d3 100644
--- a/src/main/scala/chisel3/aop/Select.scala
+++ b/src/main/scala/chisel3/aop/Select.scala
@@ -3,12 +3,16 @@
package chisel3.aop
import chisel3._
-import chisel3.experimental.{BaseModule, FixedPoint}
-import chisel3.internal.HasId
+import chisel3.internal.{HasId}
+import chisel3.experimental.BaseModule
+import chisel3.experimental.FixedPoint
import chisel3.internal.firrtl._
+import chisel3.internal.PseudoModule
+import chisel3.internal.BaseModule.ModuleClone
import firrtl.annotations.ReferenceTarget
import scala.collection.mutable
+import chisel3.internal.naming.chiselName
/** Use to select Chisel components in a module, after that module has been constructed
* Useful for adding additional Chisel annotations or for use within an [[Aspect]]
@@ -81,8 +85,13 @@ object Select {
def instances(module: BaseModule): Seq[BaseModule] = {
check(module)
module._component.get match {
- case d: DefModule => d.commands.collect {
- case i: DefInstance => i.id
+ case d: DefModule => d.commands.flatMap {
+ case i: DefInstance => i.id match {
+ case m: ModuleClone[_] if !m._madeFromDefinition => None
+ case _: PseudoModule => throw new Exception("Aspect APIs are currently incompatible with Definition/Instance")
+ case other => Some(other)
+ }
+ case _ => None
}
case other => Nil
}
@@ -248,7 +257,7 @@ object Select {
case other =>
}
})
- predicatedConnects
+ predicatedConnects.toSeq
}
/** Selects all stop statements, and includes the predicates surrounding the stop statement
@@ -264,7 +273,7 @@ object Select {
case other =>
}
})
- stops
+ stops.toSeq
}
/** Selects all printf statements, and includes the predicates surrounding the printf statement
@@ -276,11 +285,11 @@ object Select {
val printfs = mutable.ArrayBuffer[Printf]()
searchWhens(module, (cmd: Command, preds: Seq[Predicate]) => {
cmd match {
- case chisel3.internal.firrtl.Printf(_, clock, pable) => printfs += Printf(preds, pable, getId(clock).asInstanceOf[Clock])
+ case chisel3.internal.firrtl.Printf(id, _, clock, pable) => printfs += Printf(id, preds, pable, getId(clock).asInstanceOf[Clock])
case other =>
}
})
- printfs
+ printfs.toSeq
}
// Checks that a module has finished its construction
@@ -321,7 +330,7 @@ object Select {
}
} catch {
case e: ChiselException => i.getOptionRef.get match {
- case l: LitArg => l.num.intValue().toString
+ case l: LitArg => l.num.intValue.toString
}
}
@@ -413,7 +422,7 @@ object Select {
* @param pable
* @param clock
*/
- case class Printf(preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable {
+ case class Printf(id: printf.Printf, preds: Seq[Predicate], pable: Printable, clock: Clock) extends Serializeable {
def serialize: String = {
s"printf when(${preds.map(_.serialize).mkString(" & ")}) on ${getName(clock)}: $pable"
}
diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
index 39590b93..1a476f61 100644
--- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
+++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala
@@ -4,6 +4,7 @@ package chisel3.aop.injecting
import chisel3.{Module, ModuleAspect, RawModule, withClockAndReset}
import chisel3.aop._
+import chisel3.experimental.hierarchy.IsInstantiable
import chisel3.internal.{Builder, DynamicContext}
import chisel3.internal.firrtl.DefModule
import chisel3.stage.DesignAnnotation
@@ -54,13 +55,14 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
* @return
*/
final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = {
- val dynamicContext = new DynamicContext()
- // Add existing module names into the namespace. If injection logic instantiates new modules
- // which would share the same name, they will get uniquified accordingly
- moduleNames.foreach { n =>
- dynamicContext.globalNamespace.name(n)
- }
RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module =>
+ val dynamicContext = new DynamicContext(annotationsInAspect)
+ // Add existing module names into the namespace. If injection logic instantiates new modules
+ // which would share the same name, they will get uniquified accordingly
+ moduleNames.foreach { n =>
+ dynamicContext.globalNamespace.name(n)
+ }
+
val (chiselIR, _) = Builder.build(Module(new ModuleAspect(module) {
module match {
case x: Module => withClockAndReset(x.clock, x.reset) { injection(module) }
@@ -75,16 +77,20 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule](
val annotations = chiselIR.annotations.map(_.toFirrtl).filterNot{ a => a.isInstanceOf[DesignAnnotation[_]] }
+ /** Statements to be injected via aspect. */
val stmts = mutable.ArrayBuffer[ir.Statement]()
+ /** Modules to be injected via aspect. */
val modules = Aspect.getFirrtl(chiselIR.copy(components = comps)).modules.flatMap {
+ // for "container" modules, inject their statements
case m: firrtl.ir.Module if m.name == module.name =>
stmts += m.body
Nil
+ // for modules to be injected
case other: firrtl.ir.DefModule =>
Seq(other)
}
- InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts), modules, annotations)
+ InjectStatement(ModuleTarget(circuit, module.name), ir.Block(stmts.toSeq), modules, annotations)
}.toSeq
}
}
diff --git a/src/main/scala/chisel3/compatibility.scala b/src/main/scala/chisel3/compatibility.scala
index e62cba7d..dde2321d 100644
--- a/src/main/scala/chisel3/compatibility.scala
+++ b/src/main/scala/chisel3/compatibility.scala
@@ -363,8 +363,6 @@ package object Chisel {
implicit class fromIntToWidth(x: Int) extends chisel3.fromIntToWidth(x)
type BackendCompilationUtilities = firrtl.util.BackendCompilationUtilities
- @deprecated("Please switch to chisel3.stage.ChiselStage", "3.4")
- val Driver = chisel3.Driver
val ImplicitConversions = chisel3.util.ImplicitConversions
// Deprecated as of Chisel3
@@ -400,8 +398,8 @@ package object Chisel {
}
// Deprecated as of Chsiel3
- @throws(classOf[Exception])
object throwException {
+ @throws(classOf[Exception])
def apply(s: String, t: Throwable = null): Nothing = {
val xcpt = new Exception(s, t)
throw xcpt
@@ -455,7 +453,6 @@ package object Chisel {
val Log2 = chisel3.util.Log2
- val unless = chisel3.util.unless
type SwitchContext[T <: Bits] = chisel3.util.SwitchContext[T]
val is = chisel3.util.is
val switch = chisel3.util.switch
diff --git a/src/main/scala/chisel3/internal/firrtl/Emitter.scala b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
index 354be0c0..a94558ce 100644
--- a/src/main/scala/chisel3/internal/firrtl/Emitter.scala
+++ b/src/main/scala/chisel3/internal/firrtl/Emitter.scala
@@ -1,194 +1,23 @@
// SPDX-License-Identifier: Apache-2.0
package chisel3.internal.firrtl
-import chisel3._
-import chisel3.experimental.{Interval, _}
-import chisel3.internal.BaseBlackBox
-private[chisel3] object Emitter {
- def emit(circuit: Circuit): String = new Emitter(circuit).toString
-}
-
-private class Emitter(circuit: Circuit) {
- override def toString: String = res.toString
-
- private def emitPort(e: Port, topDir: SpecifiedDirection=SpecifiedDirection.Unspecified): String = {
- val resolvedDir = SpecifiedDirection.fromParent(topDir, e.dir)
- val dirString = resolvedDir match {
- case SpecifiedDirection.Unspecified | SpecifiedDirection.Output => "output"
- case SpecifiedDirection.Flip | SpecifiedDirection.Input => "input"
- }
- val clearDir = resolvedDir match {
- case SpecifiedDirection.Input | SpecifiedDirection.Output => true
- case SpecifiedDirection.Unspecified | SpecifiedDirection.Flip => false
- }
- s"$dirString ${e.id.getRef.name} : ${emitType(e.id, clearDir)}"
- }
-
- private def emitType(d: Data, clearDir: Boolean = false): String = d match {
- case d: Clock => "Clock"
- case _: AsyncReset => "AsyncReset"
- case _: ResetType => "Reset"
- case d: chisel3.core.EnumType => s"UInt${d.width}"
- case d: UInt => s"UInt${d.width}"
- case d: SInt => s"SInt${d.width}"
- case d: FixedPoint => s"Fixed${d.width}${d.binaryPoint}"
- case d: Interval =>
- val binaryPointString = d.binaryPoint match {
- case UnknownBinaryPoint => ""
- case KnownBinaryPoint(value) => s".$value"
- }
- d.toType
- case d: Analog => s"Analog${d.width}"
- case d: Vec[_] => s"${emitType(d.sample_element, clearDir)}[${d.length}]"
- case d: Record => {
- val childClearDir = clearDir ||
- d.specifiedDirection == SpecifiedDirection.Input || d.specifiedDirection == SpecifiedDirection.Output
- def eltPort(elt: Data): String = (childClearDir, firrtlUserDirOf(elt)) match {
- case (true, _) =>
- s"${elt.getRef.name} : ${emitType(elt, true)}"
- case (false, SpecifiedDirection.Unspecified | SpecifiedDirection.Output) =>
- s"${elt.getRef.name} : ${emitType(elt, false)}"
- case (false, SpecifiedDirection.Flip | SpecifiedDirection.Input) =>
- s"flip ${elt.getRef.name} : ${emitType(elt, false)}"
- }
- d.elements.toIndexedSeq.reverse.map(e => eltPort(e._2)).mkString("{", ", ", "}")
- }
- }
-
- private def firrtlUserDirOf(d: Data): SpecifiedDirection = d match {
- case d: Vec[_] =>
- SpecifiedDirection.fromParent(d.specifiedDirection, firrtlUserDirOf(d.sample_element))
- case d => d.specifiedDirection
- }
-
- private def emit(e: Command, ctx: Component): String = {
- val firrtlLine = e match {
- case e: DefPrim[_] => s"node ${e.name} = ${e.op.name}(${e.args.map(_.fullName(ctx)).mkString(", ")})"
- case e: DefWire => s"wire ${e.name} : ${emitType(e.id)}"
- case e: DefReg => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)}"
- case e: DefRegInit => s"reg ${e.name} : ${emitType(e.id)}, ${e.clock.fullName(ctx)} with : (reset => (${e.reset.fullName(ctx)}, ${e.init.fullName(ctx)}))"
- case e: DefMemory => s"cmem ${e.name} : ${emitType(e.t)}[${e.size}]"
- case e: DefSeqMemory => s"smem ${e.name} : ${emitType(e.t)}[${e.size}], ${e.readUnderWrite}"
- case e: DefMemPort[_] => s"${e.dir} mport ${e.name} = ${e.source.fullName(ctx)}[${e.index.fullName(ctx)}], ${e.clock.fullName(ctx)}"
- case e: Connect => s"${e.loc.fullName(ctx)} <= ${e.exp.fullName(ctx)}"
- case e: BulkConnect => s"${e.loc1.fullName(ctx)} <- ${e.loc2.fullName(ctx)}"
- case e: Attach => e.locs.map(_.fullName(ctx)).mkString("attach (", ", ", ")")
- case e: Stop => s"stop(${e.clock.fullName(ctx)}, UInt<1>(1), ${e.ret})"
- case e: Printf =>
- val (fmt, args) = e.pable.unpack(ctx)
- val printfArgs = Seq(e.clock.fullName(ctx), "UInt<1>(1)",
- "\"" + printf.format(fmt) + "\"") ++ args
- printfArgs mkString ("printf(", ", ", ")")
- case e: Verification => s"${e.op}(${e.clock.fullName(ctx)}, ${e.predicate.fullName(ctx)}, " +
- s"UInt<1>(1), " + "\"" + s"${printf.format(e.message)}" + "\")"
- case e: DefInvalid => s"${e.arg.fullName(ctx)} is invalid"
- case e: DefInstance => s"inst ${e.name} of ${e.id.name}"
- case w: WhenBegin =>
- // When consequences are always indented
- indent()
- s"when ${w.pred.fullName(ctx)} :"
- case w: WhenEnd =>
- // If a when has no else, the indent level must be reset to the enclosing block
- unindent()
- if (!w.hasAlt) { for (i <- 0 until w.firrtlDepth) { unindent() } }
- s"skip"
- case a: AltBegin =>
- // Else blocks are always indented
- indent()
- s"else :"
- case o: OtherwiseEnd =>
- // Chisel otherwise: ends all FIRRTL associated a Chisel when, resetting indent level
- for (i <- 0 until o.firrtlDepth) { unindent() }
- s"skip"
- }
- firrtlLine + e.sourceInfo.makeMessage(" " + _)
- }
-
- private def emitParam(name: String, p: Param): String = {
- val str = p match {
- case IntParam(value) => value.toString
- case DoubleParam(value) => value.toString
- case StringParam(str) => "\"" + str + "\""
- case RawParam(str) => "'" + str + "'"
- }
- s"parameter $name = $str"
- }
-
- /** Generates the FIRRTL module declaration.
- */
- private def moduleDecl(m: Component): String = m.id match {
- case _: BaseBlackBox => newline + s"extmodule ${m.name} : "
- case _: RawModule => newline + s"module ${m.name} : "
- }
-
- /** Generates the FIRRTL module definition.
- */
- private def moduleDefn(m: Component): String = {
- val body = new StringBuilder
- withIndent {
- for (p <- m.ports) {
- val portDef = m match {
- case bb: DefBlackBox => emitPort(p, bb.topDir)
- case mod: DefModule => emitPort(p)
- }
- body ++= newline + portDef
- }
- body ++= newline
+import scala.collection.immutable.LazyList // Needed for 2.12 alias
+import firrtl.ir.Serializer
- m match {
- case bb: DefBlackBox =>
- // Firrtl extmodule can overrule name
- body ++= newline + s"defname = ${bb.id.desiredName}"
- body ++= newline + (bb.params map { case (n, p) => emitParam(n, p) } mkString newline)
- case mod: DefModule => {
- // Preprocess whens & elsewhens, marking those that have no alternative
- val procMod = mod.copy(commands = processWhens(mod.commands))
- for (cmd <- procMod.commands) { body ++= newline + emit(cmd, procMod)}
- }
- }
- body ++= newline
- }
- body.toString()
- }
-
- /** Returns the FIRRTL declaration and body of a module, or nothing if it's a
- * duplicate of something already emitted (on the basis of simple string
- * matching).
- */
- private def emit(m: Component): String = {
- // Generate the body.
- val sb = new StringBuilder
- sb.append(moduleDecl(m))
- sb.append(moduleDefn(m))
- sb.result
+private[chisel3] object Emitter {
+ def emit(circuit: Circuit): String = {
+ val fcircuit = Converter.convertLazily(circuit)
+ Serializer.serialize(fcircuit)
}
- /** Preprocess the command queue, marking when/elsewhen statements
- * that have no alternatives (elsewhens or otherwise). These
- * alternative-free statements reset the indent level to the
- * enclosing block upon emission.
- */
- private def processWhens(cmds: Seq[Command]): Seq[Command] = {
- if (cmds.isEmpty) {
- Seq.empty
- } else {
- cmds.zip(cmds.tail).map{
- case (a: WhenEnd, b: AltBegin) => a.copy(hasAlt = true)
- case (a, b) => a
- } ++ cmds.lastOption
+ def emitLazily(circuit: Circuit): Iterable[String] = {
+ val result = LazyList(s"circuit ${circuit.name} :\n")
+ val modules = circuit.components.view.map(Converter.convert)
+ val moduleStrings = modules.flatMap { m =>
+ Array(Serializer.serialize(m, 1), "\n\n")
}
+ result ++ moduleStrings
}
-
- private var indentLevel = 0
- private def newline = "\n" + (" " * indentLevel)
- private def indent(): Unit = indentLevel += 1
- private def unindent() { require(indentLevel > 0); indentLevel -= 1 }
- private def withIndent(f: => Unit) { indent(); f; unindent() }
-
- private val res = new StringBuilder()
- res ++= s";${BuildInfo.toString}\n"
- res ++= s"circuit ${circuit.name} : "
- withIndent { circuit.components.foreach(c => res ++= emit(c)) }
- res ++= newline
}
+
diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
index bbe86ab4..de47ef36 100644
--- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala
+++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala
@@ -3,7 +3,7 @@
package chisel3.stage
import firrtl.annotations.{Annotation, NoTargetAnnotation}
-import firrtl.options.{CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable}
+import firrtl.options.{BufferedCustomFileEmission, CustomFileEmission, HasShellOptions, OptionsException, ShellOption, StageOptions, Unserializable}
import firrtl.options.Viewer.view
import chisel3.{ChiselException, Module}
import chisel3.RawModule
@@ -56,15 +56,7 @@ case class ChiselGeneratorAnnotation(gen: () => RawModule) extends NoTargetAnnot
/** Run elaboration on the Chisel module generator function stored by this [[firrtl.annotations.Annotation]]
*/
- def elaborate: AnnotationSeq = try {
- val (circuit, dut) = Builder.build(Module(gen()))
- Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
- } catch {
- case e @ (_: OptionsException | _: ChiselException) => throw e
- case e: Throwable =>
- throw new ChiselException(s"Exception thrown when elaborating ChiselGeneratorAnnotation", e)
- }
-
+ def elaborate: AnnotationSeq = (new chisel3.stage.phases.Elaborate).transform(Seq(this))
}
object ChiselGeneratorAnnotation extends HasShellOptions {
@@ -131,7 +123,7 @@ import CircuitSerializationAnnotation._
*/
case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, format: Format)
extends NoTargetAnnotation
- with CustomFileEmission {
+ with BufferedCustomFileEmission {
/* Caching the hashCode for a large circuit is necessary due to repeated queries.
* Not caching the hashCode will cause severe performance degredations for large [[Circuit]]s.
*/
@@ -141,14 +133,16 @@ case class CircuitSerializationAnnotation(circuit: Circuit, filename: String, fo
protected def suffix: Option[String] = Some(format.extension)
- // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures
- override def getBytes: Iterable[Byte] = format match {
- case FirrtlFileFormat => OldEmitter.emit(circuit).getBytes
+ override def getBytesBuffered: Iterable[Array[Byte]] = format match {
+ case FirrtlFileFormat =>
+ OldEmitter.emitLazily(circuit)
+ .map(_.getBytes)
+ // TODO Use lazy Iterables so that we don't have to materialize full intermediate data structures
case ProtoBufFileFormat =>
val ostream = new java.io.ByteArrayOutputStream
val modules = circuit.components.map(m => () => chisel3.internal.firrtl.Converter.convert(m))
firrtl.proto.ToProto.writeToStreamFast(ostream, firrtl.ir.NoInfo, modules, circuit.name)
- ostream.toByteArray
+ List(ostream.toByteArray)
}
}
diff --git a/src/main/scala/chisel3/stage/ChiselStage.scala b/src/main/scala/chisel3/stage/ChiselStage.scala
index aae7ad8d..0c76f411 100644
--- a/src/main/scala/chisel3/stage/ChiselStage.scala
+++ b/src/main/scala/chisel3/stage/ChiselStage.scala
@@ -13,7 +13,7 @@ import firrtl.{
VerilogEmitter,
SystemVerilogEmitter
}
-import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageError, StageMain}
+import firrtl.options.{Dependency, Phase, PhaseManager, Shell, Stage, StageMain}
import firrtl.options.phases.DeletedWrapper
import firrtl.stage.{FirrtlCircuitAnnotation, FirrtlCli, RunFirrtlTransformAnnotation}
import firrtl.options.Viewer.view
@@ -28,7 +28,7 @@ class ChiselStage extends Stage {
override def prerequisites = Seq.empty
override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq.empty
+ override def optionalPrerequisiteOf = Seq(Dependency[firrtl.stage.FirrtlStage])
override def invalidates(a: Phase) = false
val shell: Shell = new Shell("chisel") with ChiselCli with FirrtlCli
@@ -42,28 +42,12 @@ class ChiselStage extends Stage {
}
}
- def run(annotations: AnnotationSeq): AnnotationSeq = try {
- phaseManager.transform(annotations)
- } catch {
- case ce: ChiselException =>
- val stackTrace = if (!view[ChiselOptions](annotations).printFullStackTrace) {
- ce.chiselStackTrace
- } else {
- val sw = new StringWriter
- ce.printStackTrace(new PrintWriter(sw))
- sw.toString
- }
- Predef
- .augmentString(stackTrace)
- .lines
- .foreach(line => println(s"${ErrorLog.errTag} $line"))
- throw new StageError(cause=ce)
- }
+ def run(annotations: AnnotationSeq): AnnotationSeq = phaseManager.transform(annotations)
/** Convert a Chisel module to a CHIRRTL string
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the Verilog output
*/
final def emitChirrtl(
@@ -86,7 +70,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to a FIRRTL string
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the FIRRTL output
*/
final def emitFirrtl(
@@ -106,7 +90,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to Verilog
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the Verilog output
*/
final def emitVerilog(
@@ -126,7 +110,7 @@ class ChiselStage extends Stage {
/** Convert a Chisel module to SystemVerilog
* @param gen a call-by-name Chisel module
* @param args additional command line arguments to pass to Chisel
- * param annotations additional annotations to pass to Chisel
+ * @param annotations additional annotations to pass to Chisel
* @return a string containing the SystemVerilog output
*/
final def emitSystemVerilog(
@@ -188,6 +172,26 @@ object ChiselStage {
.get
}
+ /** Return a [[firrtl.ir.Circuit]] for a [[chisel3.internal.firrtl.Circuit]](aka chirrtl)
+ * @param chirrtl [[chisel3.internal.firrtl.Circuit]] which need to be converted to [[firrtl.ir.Circuit]]
+ */
+ def convert(chirrtl: cir.Circuit): fir.Circuit = {
+ val phase = new ChiselPhase {
+ override val targets = Seq(
+ Dependency[chisel3.stage.phases.AddImplicitOutputFile],
+ Dependency[chisel3.stage.phases.AddImplicitOutputAnnotationFile],
+ Dependency[chisel3.stage.phases.MaybeAspectPhase],
+ Dependency[chisel3.stage.phases.Convert] )
+ }
+
+ phase
+ .transform(Seq(ChiselCircuitAnnotation(chirrtl)))
+ .collectFirst {
+ case FirrtlCircuitAnnotation(a) => a
+ }
+ .get
+ }
+
/** Return a CHIRRTL string for a Chisel module
* @param gen a call-by-name Chisel module
*/
diff --git a/src/main/scala/chisel3/stage/phases/AspectPhase.scala b/src/main/scala/chisel3/stage/phases/AspectPhase.scala
index 1a27b8fa..72965861 100644
--- a/src/main/scala/chisel3/stage/phases/AspectPhase.scala
+++ b/src/main/scala/chisel3/stage/phases/AspectPhase.scala
@@ -29,7 +29,7 @@ class AspectPhase extends Phase {
case other => Seq(other)
}
if(dut.isDefined) {
- val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get) }
+ val newAnnotations = aspects.flatMap { _.resolveAspect(dut.get, remainingAnnotations) }
remainingAnnotations ++ newAnnotations
} else annotations
}
diff --git a/src/main/scala/chisel3/stage/phases/Convert.scala b/src/main/scala/chisel3/stage/phases/Convert.scala
index bf42b58a..b5b01b8d 100644
--- a/src/main/scala/chisel3/stage/phases/Convert.scala
+++ b/src/main/scala/chisel3/stage/phases/Convert.scala
@@ -29,8 +29,7 @@ class Convert extends Phase {
/* Convert all Chisel Annotations to FIRRTL Annotations */
a
.circuit
- .annotations
- .map(_.toFirrtl) ++
+ .firrtlAnnotations ++
a
.circuit
.annotations
diff --git a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
index 659914ae..9305c5c9 100644
--- a/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
+++ b/src/main/scala/chisel3/stage/phases/DriverCompatibility.scala
@@ -16,153 +16,5 @@ import chisel3.stage.{ChiselStage, NoRunFirrtlCompilerAnnotation, ChiselOutputFi
* Primarily, this object includes [[firrtl.options.Phase Phase]]s that generate [[firrtl.annotations.Annotation]]s
* derived from the deprecated [[firrtl.stage.phases.DriverCompatibility.TopNameAnnotation]].
*/
-object DriverCompatibility {
-
- /** Adds a [[ChiselOutputFileAnnotation]] derived from a [[TopNameAnnotation]] if no [[ChiselOutputFileAnnotation]]
- * already exists. If no [[TopNameAnnotation]] exists, then no [[firrtl.stage.OutputFileAnnotation]] is added. ''This is not a
- * replacement for [[chisel3.stage.phases.AddImplicitOutputFile AddImplicitOutputFile]] as this only adds an output
- * file based on a discovered top name and not on a discovered elaborated circuit.'' Consequently, this will provide
- * the correct behavior before a circuit has been elaborated.
- * @note the output suffix is unspecified and will be set by the underlying [[firrtl.EmittedComponent]]
- */
- private [chisel3] class AddImplicitOutputFile extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = {
- val hasOutputFile = annotations
- .collectFirst{ case a: ChiselOutputFileAnnotation => a }
- .isDefined
- lazy val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
-
- if (!hasOutputFile && top.isDefined) {
- ChiselOutputFileAnnotation(top.get) +: annotations
- } else {
- annotations
- }
- }
- }
-
- /** If a [[firrtl.options.OutputAnnotationFileAnnotation]] does not exist, this adds one derived from a
- * [[TopNameAnnotation]]. ''This is not a replacement for [[chisel3.stage.phases.AddImplicitOutputAnnotationFile]] as
- * this only adds an output annotation file based on a discovered top name.'' Consequently, this will provide the
- * correct behavior before a circuit has been elaborated.
- * @note the output suffix is unspecified and will be set by [[firrtl.options.phases.WriteOutputAnnotations]]
- */
- private[chisel3] class AddImplicitOutputAnnotationFile extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[chisel3.stage.ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq =
- annotations
- .collectFirst{ case _: OutputAnnotationFileAnnotation => annotations }
- .getOrElse{
- val top = annotations.collectFirst{ case TopNameAnnotation(a) => a }
- if (top.isDefined) {
- OutputAnnotationFileAnnotation(top.get) +: annotations
- } else {
- annotations
- }
- }
- }
-
- private[chisel3] case object RunFirrtlCompilerAnnotation extends NoTargetAnnotation
-
- /** Disables the execution of [[firrtl.stage.FirrtlStage]]. This can be used to call [[chisel3.stage.ChiselStage]] and
- * guarantee that the FIRRTL compiler will not run. This is necessary for certain [[chisel3.Driver]] compatibility
- * situations where you need to do something between Chisel compilation and FIRRTL compilations, e.g., update a
- * mutable data structure.
- */
- private[chisel3] class DisableFirrtlStage extends Phase {
-
- override def prerequisites = Seq.empty
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[ChiselStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
- .collectFirst { case NoRunFirrtlCompilerAnnotation => annotations }
- .getOrElse { Seq(RunFirrtlCompilerAnnotation, NoRunFirrtlCompilerAnnotation) ++ annotations }
- }
-
- private[chisel3] class ReEnableFirrtlStage extends Phase {
-
- override def prerequisites = Seq(Dependency[DisableFirrtlStage], Dependency[ChiselStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq.empty
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = annotations
- .collectFirst { case RunFirrtlCompilerAnnotation =>
- val a: AnnotationSeq = annotations.filter {
- case NoRunFirrtlCompilerAnnotation | RunFirrtlCompilerAnnotation => false
- case _ => true
- }
- a
- }
- .getOrElse{ annotations }
-
- }
-
- private[chisel3] case class OptionsManagerAnnotation(
- manager: ExecutionOptionsManager with HasChiselExecutionOptions with HasFirrtlOptions)
- extends NoTargetAnnotation with Unserializable
-
- /** Mutate an input [[firrtl.ExecutionOptionsManager]] based on information encoded in an [[firrtl.AnnotationSeq]].
- * This is intended to be run between [[chisel3.stage.ChiselStage ChiselStage]] and [[firrtl.stage.FirrtlStage]] if
- * you want to have backwards compatibility with an [[firrtl.ExecutionOptionsManager]].
- */
- private[chisel3] class MutateOptionsManager extends Phase {
-
- override def prerequisites = Seq(Dependency[chisel3.stage.ChiselStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[ReEnableFirrtlStage])
- override def invalidates(a: Phase) = false
-
- def transform(annotations: AnnotationSeq): AnnotationSeq = {
-
- val optionsManager = annotations
- .collectFirst{ case OptionsManagerAnnotation(a) => a }
- .getOrElse{ throw new OptionsException(
- "An OptionsManagerException must exist for Chisel Driver compatibility mode") }
-
- val firrtlCircuit = annotations.collectFirst{ case FirrtlCircuitAnnotation(a) => a }
- optionsManager.firrtlOptions = optionsManager.firrtlOptions.copy(
- firrtlCircuit = firrtlCircuit,
- annotations = optionsManager.firrtlOptions.annotations ++ annotations,
- customTransforms = optionsManager.firrtlOptions.customTransforms ++
- annotations.collect{ case RunFirrtlTransformAnnotation(a) => a } )
-
- annotations
-
- }
-
- }
-
- /** A [[Phase]] that lets us run
- * @todo a better solution than the current state hack below may be needed
- */
- private [chisel3] class FirrtlPreprocessing extends Phase {
-
- override def prerequisites = Seq(Dependency[ChiselStage], Dependency[MutateOptionsManager], Dependency[ReEnableFirrtlStage])
- override def optionalPrerequisites = Seq.empty
- override def optionalPrerequisiteOf = Seq(Dependency[MaybeFirrtlStage])
- override def invalidates(a: Phase) = false
-
- private val phases =
- Seq( new firrtl.stage.phases.DriverCompatibility.AddImplicitOutputFile,
- new firrtl.stage.phases.DriverCompatibility.AddImplicitEmitter )
-
- override def transform(annotations: AnnotationSeq): AnnotationSeq =
- phases
- .foldLeft(annotations)( (a, p) => p.transform(a) )
-
- }
-
-}
+@deprecated("This object contains no public members. This will be removed in Chisel 3.6.", "Chisel 3.5")
+object DriverCompatibility
diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala
index 04cfc33e..e8f2623e 100644
--- a/src/main/scala/chisel3/stage/phases/Elaborate.scala
+++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala
@@ -2,14 +2,13 @@
package chisel3.stage.phases
-import java.io.{PrintWriter, StringWriter}
-
-import chisel3.ChiselException
-import chisel3.internal.ErrorLog
-import chisel3.stage.{ChiselGeneratorAnnotation, ChiselOptions}
+import chisel3.Module
+import chisel3.internal.ExceptionHelpers.ThrowableHelpers
+import chisel3.internal.{Builder, DynamicContext}
+import chisel3.stage.{ChiselCircuitAnnotation, ChiselGeneratorAnnotation, ChiselOptions, DesignAnnotation}
import firrtl.AnnotationSeq
+import firrtl.options.Phase
import firrtl.options.Viewer.view
-import firrtl.options.{OptionsException, Phase}
/** Elaborate all [[chisel3.stage.ChiselGeneratorAnnotation]]s into [[chisel3.stage.ChiselCircuitAnnotation]]s.
*/
@@ -21,8 +20,19 @@ class Elaborate extends Phase {
override def invalidates(a: Phase) = false
def transform(annotations: AnnotationSeq): AnnotationSeq = annotations.flatMap {
- case a: ChiselGeneratorAnnotation => a.elaborate
- case a => Some(a)
+ case ChiselGeneratorAnnotation(gen) => try {
+ val (circuit, dut) = Builder.build(Module(gen()), new DynamicContext(annotations))
+ Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut))
+ } catch {
+ /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace
+ */
+ case scala.util.control.NonFatal(a) =>
+ if (!view[ChiselOptions](annotations).printFullStackTrace) {
+ a.trimStackTraceToUserCode()
+ }
+ throw(a)
+ }
+ case a => Some(a)
}
}
diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala
index 40563e23..0dcb2466 100644
--- a/src/main/scala/chisel3/util/BitPat.scala
+++ b/src/main/scala/chisel3/util/BitPat.scala
@@ -23,6 +23,7 @@ object BitPat {
// If ? parsing is to be exposed, the return API needs further scrutiny
// (especially with things like mask polarity).
require(x.head == 'b', "BitPats must be in binary and be prefixed with 'b'")
+ require(x.length > 1, "BitPat width cannot be 0.")
var bits = BigInt(0)
var mask = BigInt(0)
var count = 0
@@ -57,6 +58,22 @@ object BitPat {
*/
def dontCare(width: Int): BitPat = BitPat("b" + ("?" * width))
+ /** Creates a [[BitPat]] of all 1 of the specified bitwidth.
+ *
+ * @example {{{
+ * val myY = BitPat.Y(4) // equivalent to BitPat("b1111")
+ * }}}
+ */
+ def Y(width: Int = 1): BitPat = BitPat("b" + ("1" * width))
+
+ /** Creates a [[BitPat]] of all 0 of the specified bitwidth.
+ *
+ * @example {{{
+ * val myN = BitPat.N(4) // equivalent to BitPat("b0000")
+ * }}}
+ */
+ def N(width: Int = 1): BitPat = BitPat("b" + ("0" * width))
+
/** Allows BitPats to be used where a UInt is expected.
*
* @note the BitPat must not have don't care bits (will error out otherwise)
@@ -91,12 +108,6 @@ object BitPat {
/** @group SourceInfoTransformMacro */
def do_=/= (that: BitPat)
(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that =/= x
-
- final def != (that: BitPat): Bool = macro SourceInfoTransform.thatArg
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- def do_!= (that: BitPat)
- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = that != x
}
}
@@ -111,8 +122,29 @@ object BitPat {
*/
sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends SourceInfoDoc {
def getWidth: Int = width
+ def apply(x: Int): BitPat = macro SourceInfoTransform.xArg
+ def apply(x: Int, y: Int): BitPat = macro SourceInfoTransform.xyArg
def === (that: UInt): Bool = macro SourceInfoTransform.thatArg
def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg
+ def ## (that: BitPat): BitPat = macro SourceInfoTransform.thatArg
+ override def equals(obj: Any): Boolean = {
+ obj match {
+ case y: BitPat => value == y.value && mask == y.mask && getWidth == y.getWidth
+ case _ => false
+ }
+ }
+
+ /** @group SourceInfoTransformMacro */
+ def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ do_apply(x, x)
+ }
+
+ /** @group SourceInfoTransformMacro */
+ def do_apply(x: Int, y: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ require(width > x && y >= 0, s"Invalid bit range ($x, $y), index should be bounded by (${width - 1}, 0)")
+ require(x >= y, s"Invalid bit range ($x, $y), x should be greater or equal to y.")
+ BitPat(s"b${rawString.slice(width - x - 1, width - y)}")
+ }
/** @group SourceInfoTransformMacro */
def do_=== (that: UInt)
@@ -124,12 +156,19 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou
(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
!(this === that)
}
-
- def != (that: UInt): Bool = macro SourceInfoTransform.thatArg
- @chiselRuntimeDeprecated
- @deprecated("Use '=/=', which avoids potential precedence problems", "3.0")
- def do_!= (that: UInt)
- (implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): Bool = {
- this =/= that
+ /** @group SourceInfoTransformMacro */
+ def do_##(that: BitPat)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
+ new BitPat((value << that.getWidth) + that.value, (mask << that.getWidth) + that.mask, this.width + that.getWidth)
}
+
+ /** Generate raw string of a BitPat. */
+ def rawString: String = Seq.tabulate(width) { i =>
+ (value.testBit(width - i - 1), mask.testBit(width - i - 1)) match {
+ case (true, true) => "1"
+ case (false, true) => "0"
+ case (_, false) => "?"
+ }
+ }.mkString
+
+ override def toString = s"BitPat($rawString)"
}
diff --git a/src/main/scala/chisel3/util/BlackBoxUtils.scala b/src/main/scala/chisel3/util/BlackBoxUtils.scala
index 21bd4dfa..443d7f3e 100644
--- a/src/main/scala/chisel3/util/BlackBoxUtils.scala
+++ b/src/main/scala/chisel3/util/BlackBoxUtils.scala
@@ -4,15 +4,39 @@ package chisel3.util
import chisel3._
import chisel3.experimental.{ChiselAnnotation, RunFirrtlTransform}
-import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper}
+import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper,
+ BlackBoxNotFoundException}
+import firrtl.annotations.ModuleName
+import logger.LazyLogging
+
+private[util] object BlackBoxHelpers {
+
+ implicit class BlackBoxInlineAnnoHelpers(anno: BlackBoxInlineAnno.type) extends LazyLogging {
+ /** Generate a BlackBoxInlineAnno from a Java Resource and a module name. */
+ def fromResource(resourceName: String, moduleName: ModuleName) = try {
+ val blackBoxFile = os.resource / os.RelPath(resourceName.dropWhile(_ == '/'))
+ val contents = os.read(blackBoxFile)
+ if (contents.size > BigInt(2).pow(20)) {
+ val message =
+ s"Black box resource $resourceName, which will be converted to an inline annotation, is greater than 1 MiB." +
+ "This may affect compiler performance. Consider including this resource via a black box path."
+ logger.warn(message)
+ }
+ BlackBoxInlineAnno(moduleName, blackBoxFile.last, contents)
+ } catch {
+ case e: os.ResourceNotFoundException =>
+ throw new BlackBoxNotFoundException(resourceName, e.getMessage)
+ }
+ }
+}
+
+import BlackBoxHelpers._
trait HasBlackBoxResource extends BlackBox {
self: BlackBox =>
- @deprecated("Use addResource instead", "3.2")
- def setResource(blackBoxResource: String): Unit = addResource(blackBoxResource)
-
- /** Copies a resource file to the target directory
+ /** Copies a Java resource containing some text into the output directory. This is typically used to copy a Verilog file
+ * to the final output directory, but may be used to copy any Java resource (e.g., a C++ testbench).
*
* Resource files are located in project_root/src/main/resources/.
* Example of adding the resource file project_root/src/main/resources/blackbox.v:
@@ -22,7 +46,7 @@ trait HasBlackBoxResource extends BlackBox {
*/
def addResource(blackBoxResource: String): Unit = {
val anno = new ChiselAnnotation with RunFirrtlTransform {
- def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource)
+ def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed)
def transformClass = classOf[BlackBoxSourceHelper]
}
chisel3.experimental.annotate(anno)
diff --git a/src/main/scala/chisel3/util/Conditional.scala b/src/main/scala/chisel3/util/Conditional.scala
index b934f27f..1ac94bfe 100644
--- a/src/main/scala/chisel3/util/Conditional.scala
+++ b/src/main/scala/chisel3/util/Conditional.scala
@@ -12,15 +12,6 @@ import scala.reflect.macros.blackbox._
import chisel3._
import chisel3.internal.sourceinfo.SourceInfo
-@deprecated("The unless conditional is deprecated, use when(!condition){...} instead", "3.2")
-object unless {
- /** Does the same thing as [[when$ when]], but with the condition inverted.
- */
- def apply(c: Bool)(block: => Any) {
- when (!c) { block }
- }
-}
-
/** Implementation details for [[switch]]. See [[switch]] and [[chisel3.util.is is]] for the
* user-facing API.
* @note DO NOT USE. This API is subject to change without warning.
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 032d731d..8909ffe3 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -108,7 +108,7 @@ object Decoupled
@chiselName
def apply[T <: Data](irr: IrrevocableIO[T]): DecoupledIO[T] = {
require(DataMirror.directionOf(irr.bits) == Direction.Output, "Only safe to cast produced Irrevocable bits to Decoupled.")
- val d = Wire(new DecoupledIO(irr.bits))
+ val d = Wire(new DecoupledIO(chiselTypeOf(irr.bits)))
d.bits := irr.bits
d.valid := irr.valid
irr.ready := d.ready
@@ -139,7 +139,7 @@ object Irrevocable
*/
def apply[T <: Data](dec: DecoupledIO[T]): IrrevocableIO[T] = {
require(DataMirror.directionOf(dec.bits) == Direction.Input, "Only safe to cast consumed Decoupled bits to Irrevocable.")
- val i = Wire(new IrrevocableIO(dec.bits))
+ val i = Wire(new IrrevocableIO(chiselTypeOf(dec.bits)))
dec.bits := i.bits
dec.valid := i.valid
i.ready := dec.ready
@@ -163,8 +163,9 @@ object DeqIO {
/** An I/O Bundle for Queues
* @param gen The type of data to queue
* @param entries The max number of entries in the queue.
+ * @param hasFlush A boolean for whether the generated Queue is flushable
*/
-class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
+class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boolean = false) extends Bundle
{ // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
/* These may look inverted, because the names (enq/deq) are from the perspective of the client,
@@ -177,6 +178,9 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
val deq = Flipped(DeqIO(gen))
/** The current amount of data in the queue */
val count = Output(UInt(log2Ceil(entries + 1).W))
+ /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)*/
+ val flush = if (hasFlush) Some(Input(Bool())) else None
+
}
/** A hardware module implementing a Queue
@@ -186,7 +190,8 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
* combinationally coupled.
* @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately).
* The ''valid'' signals are coupled.
- *
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element.
+ * @param hasFlush True if generated queue requires a flush feature
* @example {{{
* val q = Module(new Queue(UInt(), 16))
* q.io.enq <> producer.io.out
@@ -197,7 +202,9 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int) extends Bundle
class Queue[T <: Data](val gen: T,
val entries: Int,
val pipe: Boolean = false,
- val flow: Boolean = false)
+ val flow: Boolean = false,
+ val useSyncReadMem: Boolean = false,
+ val hasFlush: Boolean = false)
(implicit compileOptions: chisel3.CompileOptions)
extends Module() {
require(entries > -1, "Queue must have non-negative number of entries")
@@ -213,19 +220,20 @@ class Queue[T <: Data](val gen: T,
}
}
- val io = IO(new QueueIO(genType, entries))
-
- val ram = Mem(entries, genType)
+ val io = IO(new QueueIO(genType, entries, hasFlush))
+ val ram = if (useSyncReadMem) SyncReadMem(entries, genType, SyncReadMem.WriteFirst) else Mem(entries, genType)
val enq_ptr = Counter(entries)
val deq_ptr = Counter(entries)
val maybe_full = RegInit(false.B)
-
val ptr_match = enq_ptr.value === deq_ptr.value
val empty = ptr_match && !maybe_full
val full = ptr_match && maybe_full
val do_enq = WireDefault(io.enq.fire())
val do_deq = WireDefault(io.deq.fire())
+ val flush = io.flush.getOrElse(false.B)
+ // when flush is high, empty the queue
+ // Semantically, any enqueues happen before the flush.
when (do_enq) {
ram(enq_ptr.value) := io.enq.bits
enq_ptr.inc()
@@ -236,10 +244,23 @@ class Queue[T <: Data](val gen: T,
when (do_enq =/= do_deq) {
maybe_full := do_enq
}
+ when(flush) {
+ enq_ptr.reset()
+ deq_ptr.reset()
+ maybe_full := false.B
+ }
io.deq.valid := !empty
io.enq.ready := !full
- io.deq.bits := ram(deq_ptr.value)
+
+ if (useSyncReadMem) {
+ val deq_ptr_next = Mux(deq_ptr.value === (entries.U - 1.U), 0.U, deq_ptr.value + 1.U)
+ val r_addr = WireDefault(Mux(do_deq, deq_ptr_next, deq_ptr.value))
+ io.deq.bits := ram.read(r_addr)
+ }
+ else {
+ io.deq.bits := ram(deq_ptr.value)
+ }
if (flow) {
when (io.enq.valid) { io.deq.valid := true.B }
@@ -255,6 +276,7 @@ class Queue[T <: Data](val gen: T,
}
val ptr_diff = enq_ptr.value - deq_ptr.value
+
if (isPow2(entries)) {
io.count := Mux(maybe_full && ptr_match, entries.U, 0.U) | ptr_diff
} else {
@@ -285,7 +307,9 @@ object Queue
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
- flow: Boolean = false): DecoupledIO[T] = {
+ flow: Boolean = false,
+ useSyncReadMem: Boolean = false,
+ hasFlush: Boolean = false): DecoupledIO[T] = {
if (entries == 0) {
val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits)))
deq.valid := enq.valid
@@ -293,7 +317,7 @@ object Queue
enq.ready := deq.ready
deq
} else {
- val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow))
+ val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, hasFlush))
q.io.enq.valid := enq.valid // not using <> so that override is allowed
q.io.enq.bits := enq.bits
enq.ready := q.io.enq.ready
@@ -311,8 +335,9 @@ object Queue
enq: ReadyValidIO[T],
entries: Int = 2,
pipe: Boolean = false,
- flow: Boolean = false): IrrevocableIO[T] = {
- val deq = apply(enq, entries, pipe, flow)
+ flow: Boolean = false,
+ useSyncReadMem: Boolean = false): IrrevocableIO[T] = {
+ val deq = apply(enq, entries, pipe, flow, useSyncReadMem)
require(entries > 0, "Zero-entry queues don't guarantee Irrevocability")
val irr = Wire(new IrrevocableIO(chiselTypeOf(deq.bits)))
irr.bits := deq.bits
diff --git a/src/main/scala/chisel3/util/ExtModuleUtils.scala b/src/main/scala/chisel3/util/ExtModuleUtils.scala
index 831639be..62f384bc 100644
--- a/src/main/scala/chisel3/util/ExtModuleUtils.scala
+++ b/src/main/scala/chisel3/util/ExtModuleUtils.scala
@@ -3,7 +3,10 @@
package chisel3.util
import chisel3.experimental.{ChiselAnnotation, ExtModule, RunFirrtlTransform}
-import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper}
+import firrtl.transforms.{BlackBoxPathAnno, BlackBoxResourceAnno, BlackBoxInlineAnno, BlackBoxSourceHelper,
+ BlackBoxNotFoundException}
+
+import BlackBoxHelpers._
trait HasExtModuleResource extends ExtModule {
self: ExtModule =>
@@ -18,7 +21,7 @@ trait HasExtModuleResource extends ExtModule {
*/
def addResource(blackBoxResource: String): Unit = {
val anno = new ChiselAnnotation with RunFirrtlTransform {
- def toFirrtl = BlackBoxResourceAnno(self.toNamed, blackBoxResource)
+ def toFirrtl = BlackBoxInlineAnno.fromResource(blackBoxResource, self.toNamed)
def transformClass = classOf[BlackBoxSourceHelper]
}
chisel3.experimental.annotate(anno)
diff --git a/src/main/scala/chisel3/util/MixedVec.scala b/src/main/scala/chisel3/util/MixedVec.scala
index a632ec3a..14d6be38 100644
--- a/src/main/scala/chisel3/util/MixedVec.scala
+++ b/src/main/scala/chisel3/util/MixedVec.scala
@@ -91,6 +91,10 @@ final class MixedVec[T <: Data](private val eltsIn: Seq[T]) extends Record with
eltsIn.foreach(e => requireIsChiselType(e))
}
+ // In Scala 2.13, this is protected in IndexedSeq, must override as public because it's public in
+ // Record
+ override def className: String = "MixedVec"
+
// Clone the inputs so that we have our own references.
private val elts: IndexedSeq[T] = eltsIn.map(_.cloneTypeFull).toIndexedSeq
diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala
index 982d80d0..e2b5d172 100644
--- a/src/main/scala/chisel3/util/Reg.scala
+++ b/src/main/scala/chisel3/util/Reg.scala
@@ -42,14 +42,7 @@ object ShiftRegister
* val regDelayTwo = ShiftRegister(nextVal, 2, ena)
* }}}
*/
- def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = {
- // The order of tests reflects the expected use cases.
- if (n != 0) {
- RegEnable(apply(in, n-1, en), en)
- } else {
- in
- }
- }
+ def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).lastOption.getOrElse(in)
/** Returns the n-cycle delayed version of the input signal with reset initialization.
*
@@ -62,12 +55,30 @@ object ShiftRegister
* val regDelayTwoReset = ShiftRegister(nextVal, 2, 0.U, ena)
* }}}
*/
- def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = {
- // The order of tests reflects the expected use cases.
- if (n != 0) {
- RegEnable(apply(in, n-1, resetData, en), resetData, en)
- } else {
- in
- }
- }
+ def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = ShiftRegisters(in, n, resetData, en).lastOption.getOrElse(in)
+}
+
+
+object ShiftRegisters
+{
+ /** Returns a sequence of delayed input signal registers from 1 to n.
+ *
+ * @param in input to delay
+ * @param n number of cycles to delay
+ * @param en enable the shift
+ *
+ */
+ def apply[T <: Data](in: T, n: Int, en: Bool = true.B): Seq[T] =
+ Seq.iterate(in, n + 1)(util.RegEnable(_, en)).drop(1)
+
+ /** Returns delayed input signal registers with reset initialization from 1 to n.
+ *
+ * @param in input to delay
+ * @param n number of cycles to delay
+ * @param resetData reset value for each register in the shift
+ * @param en enable the shift
+ *
+ */
+ def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): Seq[T] =
+ Seq.iterate(in, n + 1)(util.RegEnable(_, resetData, en)).drop(1)
}
diff --git a/src/main/scala/chisel3/util/experimental/BoringUtils.scala b/src/main/scala/chisel3/util/experimental/BoringUtils.scala
index 18551da8..f2a3e757 100644
--- a/src/main/scala/chisel3/util/experimental/BoringUtils.scala
+++ b/src/main/scala/chisel3/util/experimental/BoringUtils.scala
@@ -19,7 +19,7 @@ class BoringUtilsException(message: String) extends Exception(message)
/** Utilities for generating synthesizable cross module references that "bore" through the hierarchy. The underlying
* cross module connects are handled by FIRRTL's Wiring Transform.
*
- * Consider the following exmple where you want to connect a component in one module to a component in another. Module
+ * Consider the following example where you want to connect a component in one module to a component in another. Module
* `Constant` has a wire tied to `42` and `Expect` will assert unless connected to `42`:
* {{{
* class Constant extends Module {
@@ -45,8 +45,8 @@ class BoringUtilsException(message: String) extends Exception(message)
*
* ===Hierarchical Boring===
*
- * Hierarchcical boring involves connecting one sink instance to another source instance in a parent module. Below,
- * module `Top` contains an instance of `Cosntant` and `Expect`. Using [[BoringUtils.bore]], we can connect
+ * Hierarchical boring involves connecting one sink instance to another source instance in a parent module. Below,
+ * module `Top` contains an instance of `Constant` and `Expect`. Using [[BoringUtils.bore]], we can connect
* `constant.x` to `expect.y`.
*
* {{{
@@ -89,7 +89,7 @@ class BoringUtilsException(message: String) extends Exception(message)
* ==Comments==
*
* Both hierarchical and non-hierarchical boring emit FIRRTL annotations that describe sources and sinks. These are
- * matched by a `name` key that indicates they should be wired together. Hierarhical boring safely generates this name
+ * matched by a `name` key that indicates they should be wired together. Hierarchical boring safely generates this name
* automatically. Non-hierarchical boring unsafely relies on user input to generate this name. Use of non-hierarchical
* naming may result in naming conflicts that the user must handle.
*
@@ -115,7 +115,7 @@ object BoringUtils {
/** Add a named source cross module reference
* @param component source circuit component
* @param name unique identifier for this source
- * @param disableDedup disable dedupblication of this source component (this should be true if you are trying to wire
+ * @param disableDedup disable deduplication of this source component (this should be true if you are trying to wire
* from specific identical sources differently)
* @param uniqueName if true, this will use a non-conflicting name from the global namespace
* @return the name used
@@ -137,7 +137,7 @@ object BoringUtils {
def transformClass = classOf[WiringTransform] },
new ChiselAnnotation { def toFirrtl = DontTouchAnnotation(component.toNamed) } ) ++ maybeDedup
- annotations.map(annotate(_))
+ annotations.foreach(annotate(_))
id
}
@@ -146,8 +146,8 @@ object BoringUtils {
* @param name unique identifier for this sink that must resolve to
* @param disableDedup disable deduplication of this sink component (this should be true if you are trying to wire
* specific, identical sinks differently)
- * @param forceExists if true, require that the provided `name` paramater already exists in the global namespace
- * @throws BoringUtilsException if name is expected to exist and itdoesn't
+ * @param forceExists if true, require that the provided `name` parameter already exists in the global namespace
+ * @throws BoringUtilsException if name is expected to exist and it doesn't
*/
def addSink(
component: InstanceId,
@@ -169,7 +169,7 @@ object BoringUtils {
Seq(new ChiselAnnotation with RunFirrtlTransform {
def toFirrtl = SinkAnnotation(component.toNamed, name)
def transformClass = classOf[WiringTransform] }) ++ maybeDedup
- annotations.map(annotate(_))
+ annotations.foreach(annotate(_))
}
/** Connect a source to one or more sinks
@@ -187,7 +187,7 @@ object BoringUtils {
case _: Exception => "bore"
}
val genName = addSource(source, boringName, true, true)
- sinks.map(addSink(_, genName, true, true))
+ sinks.foreach(addSink(_, genName, true, true))
genName
}
diff --git a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
index d91d97b7..93981485 100644
--- a/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
+++ b/src/main/scala/chisel3/util/experimental/LoadMemoryTransform.scala
@@ -40,7 +40,7 @@ case class ChiselLoadMemoryAnnotation[T <: Data](
}
-/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file. This relies on
+/** [[loadMemoryFromFile]] is an annotation generator that helps with loading a memory from a text file as a bind module. This relies on
* Verilator and Verilog's `\$readmemh` or `\$readmemb`. The [[https://github.com/freechipsproject/treadle Treadle
* backend]] can also recognize this annotation and load memory at run-time.
*
@@ -116,6 +116,86 @@ object loadMemoryFromFile {
}
}
+
+/** [[loadMemoryFromFileInline]] is an annotation generator that helps with loading a memory from a text file inlined in
+ * the Verilog module. This relies on Verilator and Verilog's `\$readmemh` or `\$readmemb`.
+ * The [[https://github.com/freechipsproject/treadle Treadlebackend]] can also recognize this annotation and load memory at run-time.
+ *
+ * This annotation, when the FIRRTL compiler runs, triggers the [[MemoryFileInlineAnnotation]] that will add Verilog
+ * directives inlined to the module enabling the specified memories to be initialized from files.
+ * The module supports both `hex` and `bin` files by passing the appropriate [[MemoryLoadFileType.FileType]] argument with
+ * [[MemoryLoadFileType.Hex]] or [[MemoryLoadFileType.Binary]]. Hex is the default.
+ *
+ * ==Example module==
+ *
+ * Consider a simple Module containing a memory:
+ * {{{
+ * import chisel3._
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * }
+ * }}}
+ *
+ * ==Above module with annotation==
+ *
+ * To load this memory from the file `/workspace/workdir/mem1.hex.txt` just add an import and annotate the memory:
+ * {{{
+ * import chisel3._
+ * import chisel3.util.experimental.loadMemoryFromFileInline // <<-- new import here
+ * class UsesMem(memoryDepth: Int, memoryType: Data) extends Module {
+ * val io = IO(new Bundle {
+ * val address = Input(UInt(memoryType.getWidth.W))
+ * val value = Output(memoryType)
+ * })
+ * val memory = Mem(memoryDepth, memoryType)
+ * io.value := memory(io.address)
+ * loadMemoryFromFileInline(memory, "/workspace/workdir/mem1.hex.txt") // <<-- Note the annotation here
+ * }
+ * }}}
+ *
+ * ==Example file format==
+ *
+ * A memory file should consist of ASCII text in either hex or binary format. The following example shows such a
+ * file formatted to use hex:
+ * {{{
+ * 0
+ * 7
+ * d
+ * 15
+ * }}}
+ *
+ * A binary file can be similarly constructed.
+ * Chisel does not validate the file format or existence. It is supposed to be in a path accessible by the synthesis
+ * tool together with the generated Verilog.
+ *
+ * @see Chisel3 Wiki entry on
+ * [[https://github.com/freechipsproject/chisel3/wiki/Chisel-Memories#loading-memories-in-simulation "Loading Memories
+ * in Simulation"]]
+ */
+object loadMemoryFromFileInline {
+
+
+ /** Annotate a memory such that it can be initialized inline using a file
+ * @param memory the memory
+ * @param fileName the file used for initialization
+ * @param hexOrBinary whether the file uses a hex or binary number representation
+ */
+ def apply[T <: Data](
+ memory: MemBase[T],
+ fileName: String,
+ hexOrBinary: MemoryLoadFileType.FileType = MemoryLoadFileType.Hex
+ ): Unit = {
+ annotate(new ChiselAnnotation {
+ override def toFirrtl = MemoryFileInlineAnnotation(memory.toTarget, fileName, hexOrBinary)
+ })
+ }
+}
+
/** This transform only is activated if Verilog is being generated (determined by presence of the proper emit
* annotation) when activated it creates additional Verilog files that contain modules bound to the modules that
* contain an initializable memory
diff --git a/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala
new file mode 100644
index 00000000..2b50ef90
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/DecodeTableAnnotation.scala
@@ -0,0 +1,22 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import firrtl.annotations.{Annotation, ReferenceTarget, SingleTargetAnnotation}
+
+/** DecodeTableAnnotation used to store a decode result for a specific [[TruthTable]].
+ * This is useful for saving large [[TruthTable]] during a elaboration time.
+ *
+ * @note user should manage the correctness of [[minimizedTable]].
+ *
+ * @param target output wire of a decoder.
+ * @param truthTable input [[TruthTable]] encoded in a serialized [[TruthTable]].
+ * @param minimizedTable minimized [[truthTable]] encoded in a serialized [[TruthTable]].
+ */
+case class DecodeTableAnnotation(
+ target: ReferenceTarget,
+ truthTable: String,
+ minimizedTable: String)
+ extends SingleTargetAnnotation[ReferenceTarget] {
+ override def duplicate(n: ReferenceTarget): Annotation = this.copy(target = n)
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
new file mode 100644
index 00000000..1d725875
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
@@ -0,0 +1,73 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+import logger.LazyLogging
+
+case object EspressoNotFoundException extends Exception
+
+object EspressoMinimizer extends Minimizer with LazyLogging {
+ def minimize(table: TruthTable): TruthTable =
+ TruthTable.merge(TruthTable.split(table).map{case (table, indexes) => (espresso(table), indexes)})
+
+ def espresso(table: TruthTable): TruthTable = {
+ def writeTable(table: TruthTable): String = {
+ def invert(string: String) = string
+ .replace('0', 't')
+ .replace('1', '0')
+ .replace('t', '1')
+ val defaultType: Char = {
+ val t = table.default.rawString.toCharArray.distinct
+ require(t.length == 1, "Internal Error: espresso only accept unified default type.")
+ t.head
+ }
+ val tableType: String = defaultType match {
+ case '?' => "fr"
+ case _ => "fd"
+ }
+ val rawTable = table
+ .toString
+ .split("\n")
+ .filter(_.contains("->"))
+ .mkString("\n")
+ .replace("->", " ")
+ .replace('?', '-')
+ // invert all output, since espresso cannot handle default is on.
+ val invertRawTable = rawTable
+ .split("\n")
+ .map(_.split(" "))
+ .map(row => s"${row(0)} ${invert(row(1))}")
+ .mkString("\n")
+ s""".i ${table.inputWidth}
+ |.o ${table.outputWidth}
+ |.type ${tableType}
+ |""".stripMargin ++ (if (defaultType == '1') invertRawTable else rawTable)
+ }
+
+ def readTable(espressoTable: String): Map[BitPat, BitPat] = {
+ def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?'))
+
+ espressoTable
+ .split("\n")
+ .filterNot(_.startsWith("."))
+ .map(_.split(' '))
+ .map(row => bitPat(row(0)) -> bitPat(row(1)))
+ .toMap
+ }
+
+ val input = writeTable(table)
+ logger.trace(s"""espresso input table:
+ |$input
+ |""".stripMargin)
+ val output = try {
+ os.proc("espresso").call(stdin = input).out.chunks.mkString
+ } catch {
+ case e: java.io.IOException if e.getMessage.contains("error=2, No such file or directory") => throw EspressoNotFoundException
+ }
+ logger.trace(s"""espresso output table:
+ |$output
+ |""".stripMargin)
+ TruthTable(readTable(output), table.default)
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
new file mode 100644
index 00000000..86847710
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
@@ -0,0 +1,29 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+abstract class Minimizer {
+ /** Minimize a multi-input multi-output logic function given by the truth table `table`, with function output values
+ * on unspecified inputs treated as `default`, and return a minimized PLA-like representation of the function.
+ *
+ * Each bit of `table[]._1` encodes one 1-bit input variable of the logic function, and each bit of `default` and
+ * `table[]._2` represents one 1-bit output value of the function.
+ *
+ * @param table Truth table, can have don't cares in both inputs and outputs, specified as [(inputs, outputs), ...]
+ * @return Minimized truth table, [(inputs, outputs), ...]
+ *
+ * @example {{{
+ * minimize(BitPat("b?"), Seq(
+ * (BitPat("b000"), BitPat("b0")),
+ * // (BitPat("b001"), BitPat("b?")), // same as default, can be omitted
+ * // (BitPat("b010"), BitPat("b?")), // same as default, can be omitted
+ * (BitPat("b011"), BitPat("b0")),
+ * (BitPat("b100"), BitPat("b1")),
+ * (BitPat("b101"), BitPat("b1")),
+ * (BitPat("b110"), BitPat("b0")),
+ * (BitPat("b111"), BitPat("b1")),
+ * ))
+ * }}}
+ */
+ def minimize(table: TruthTable): TruthTable
+} \ No newline at end of file
diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
new file mode 100644
index 00000000..c1533f44
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
@@ -0,0 +1,298 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+
+import scala.annotation.tailrec
+import scala.math.Ordered.orderingToOrdered
+import scala.language.implicitConversions
+
+object QMCMinimizer extends Minimizer {
+ private implicit def toImplicant(x: BitPat): Implicant = new Implicant(x)
+
+ private class Implicant(val bp: BitPat) {
+ var isPrime: Boolean = true
+
+ def width = bp.getWidth
+
+ override def equals(that: Any): Boolean = that match {
+ case x: Implicant => bp.value == x.bp.value && bp.mask == x.bp.mask
+ case _ => false
+ }
+
+ override def hashCode = bp.value.toInt
+
+ /** Check whether two implicants have the same value on all of the cared bits (intersection).
+ *
+ * {{{
+ * value ^^ x.value // bits that are different
+ * (bits that are different) & x.mask // bits that are different and `this` care
+ * (bits that are different and `this` care) & y.mask // bits that are different and `both` care
+ * (bits that are different and both care) == 0 // no (bits that are different and we both care) exists
+ * no (bits that are different and we both care) exists // all cared bits are the same, two terms intersect
+ * }}}
+ *
+ * @param y Implicant to be checked with
+ * @return Whether two implicants intersect
+ */
+ def intersects(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask & y.bp.mask) == 0
+
+ /** Check whether two implicants are similar.
+ * Two implicants are "similar" when they satisfy all the following rules:
+ * 1. have the same mask ('?'s are at the same positions)
+ * 1. values only differ by one bit
+ * 1. the bit at the differed position of this term is '1' (that of the other term is '0')
+ *
+ * @example this = 11?0, x = 10?0 -> similar
+ * @example this = 11??, x = 10?0 -> not similar, violated rule 1
+ * @example this = 11?1, x = 10?0 -> not similar, violated rule 2
+ * @example this = 10?0, x = 11?0 -> not similar, violated rule 3
+ * @param y Implicant to be checked with
+ * @return Whether this term is similar to the other
+ */
+ def similar(y: Implicant): Boolean = {
+ val diff = bp.value - y.bp.value
+ bp.mask == y.bp.mask && bp.value > y.bp.value && (diff & diff - 1) == 0
+ }
+
+ /** Merge two similar implicants
+ * Rule of merging: '0' and '1' merge to '?'
+ *
+ * @param y Term to be merged with
+ * @return A new term representing the merge result
+ */
+ def merge(y: Implicant): Implicant = {
+ require(similar(y), s"merge is only reasonable when $this is similar to $y")
+
+ // if two term can be merged, then they both are not prime implicants.
+ isPrime = false
+ y.isPrime = false
+ val bit = bp.value - y.bp.value
+ new BitPat(bp.value &~ bit, bp.mask &~ bit, width)
+ }
+
+ /** Check all bits in `x` cover the correspond position in `y`.
+ *
+ * Rule to define coverage relationship among `0`, `1` and `?`:
+ * 1. '?' covers '0' and '1', '0' covers '0', '1' covers '1'
+ * 1. '1' doesn't cover '?', '1' doesn't cover '0'
+ * 1. '0' doesn't cover '?', '0' doesn't cover '1'
+ *
+ * For all bits that `x` don't care, `y` can be `0`, `1`, `?`
+ * For all bits that `x` care, `y` must be the same value and not masked.
+ * {{{
+ * (~x.mask & -1) | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1
+ * -> ~x.mask | ((x.mask) & ((x.value xnor y.value) & y.mask)) = -1
+ * -> ~x.mask | ((x.value xnor y.value) & y.mask) = -1
+ * -> x.mask & ~((x.value xnor y.value) & y.mask) = 0
+ * -> x.mask & (~(x.value xnor y.value) | ~y.mask) = 0
+ * -> x.mask & ((x.value ^ y.value) | ~y.mask) = 0
+ * -> ((x.value ^ y.value) & x.mask | ~y.mask & x.mask) = 0
+ * }}}
+ *
+ * @param y to check is covered by `x` or not.
+ * @return Whether `x` covers `y`
+ */
+ def covers(y: Implicant): Boolean = ((bp.value ^ y.bp.value) & bp.mask | ~y.bp.mask & bp.mask) == 0
+
+ override def toString = (if (!isPrime) "Non" else "") + "Prime" + bp.toString.replace("BitPat", "Implicant")
+ }
+
+ /**
+ * If two terms have different value, then their order is determined by the value, or by the mask.
+ */
+ private implicit def ordering: Ordering[Implicant] = new Ordering[Implicant] {
+ override def compare(x: Implicant, y: Implicant): Int =
+ if (x.bp.value < y.bp.value || x.bp.value == y.bp.value && x.bp.mask > y.bp.mask) -1 else 1
+ }
+
+ /** Calculate essential prime implicants based on previously calculated prime implicants and all implicants.
+ *
+ * @param primes Prime implicants
+ * @param minterms All implicants
+ * @return (a, b, c)
+ * a: essential prime implicants
+ * b: nonessential prime implicants
+ * c: implicants that are not cover by any of the essential prime implicants
+ */
+ private def getEssentialPrimeImplicants(primes: Seq[Implicant], minterms: Seq[Implicant]): (Seq[Implicant], Seq[Implicant], Seq[Implicant]) = {
+ // primeCovers(i): implicants that `prime(i)` covers
+ val primeCovers = primes.map(p => minterms.filter(p.covers))
+ // eliminate prime implicants that can be covered by other prime implicants
+ for (((icover, pi), i) <- (primeCovers zip primes).zipWithIndex) {
+ for (((jcover, pj), _) <- (primeCovers zip primes).zipWithIndex.drop(i + 1)) {
+ // we prefer prime implicants with wider implicants coverage
+ if (icover.size > jcover.size && jcover.forall(pi.covers)) {
+ // calculate essential prime implicants with `pj` eliminated from prime implicants table
+ return getEssentialPrimeImplicants(primes.filter(_ != pj), minterms)
+ }
+ }
+ }
+
+ // implicants that only one prime implicant covers
+ val essentiallyCovered = minterms.filter(t => primes.count(_.covers(t)) == 1)
+ // essential prime implicants, prime implicants that covers only one implicant
+ val essential = primes.filter(p => essentiallyCovered.exists(p.covers))
+ // {nonessential} = {prime implicants} - {essential prime implicants}
+ val nonessential = primes.filterNot(essential contains _)
+ // implicants that no essential prime implicants covers
+ val uncovered = minterms.filterNot(t => essential.exists(_.covers(t)))
+ if (essential.isEmpty || uncovered.isEmpty)
+ (essential, nonessential, uncovered)
+ else {
+ // now there are implicants (`uncovered`) that are covered by multiple nonessential prime implicants (`nonessential`)
+ // need to reduce prime implicants
+ val (a, b, c) = getEssentialPrimeImplicants(nonessential, uncovered)
+ (essential ++ a, b, c)
+ }
+ }
+
+ /** Use [[https://en.wikipedia.org/wiki/Petrick%27s_method]] to select a [[Seq]] of nonessential prime implicants
+ * that covers all implicants that are not covered by essential prime implicants.
+ *
+ * @param implicants Nonessential prime implicants
+ * @param minterms Implicants that are not covered by essential prime implicants
+ * @return Selected nonessential prime implicants
+ */
+ private def getCover(implicants: Seq[Implicant], minterms: Seq[Implicant]): Seq[Implicant] = {
+ /** Calculate the implementation cost (using comparators) of a list of implicants, more don't cares is cheaper
+ *
+ * @param cover Implicant list
+ * @return How many comparators need to implement this list of implicants
+ */
+ def getCost(cover: Seq[Implicant]): Int = cover.map(_.bp.mask.bitCount).sum
+
+ /** Determine if one combination of prime implicants is cheaper when implementing as comparators.
+ * Shorter term list is cheaper, term list with more don't cares is cheaper (less comparators)
+ *
+ * @param a Operand a
+ * @param b Operand b
+ * @return `a` < `b`
+ */
+ def cheaper(a: Seq[Implicant], b: Seq[Implicant]): Boolean = {
+ val ca = getCost(a)
+ val cb = getCost(b)
+
+ /** If `a` < `b`
+ *
+ * Like comparing the dictionary order of two strings.
+ * Define `a` < `b` if both `a` and `b` are empty.
+ *
+ * @param a Operand a
+ * @param b Operand b
+ * @return `a` < `b`
+ */
+ @tailrec
+ def listLess(a: Seq[Implicant], b: Seq[Implicant]): Boolean = b.nonEmpty && (a.isEmpty || a.head < b.head || a.head == b.head && listLess(a.tail, b.tail))
+
+ ca < cb || ca == cb && listLess(a.sortWith(_ < _), b.sortWith(_ < _))
+ }
+
+ // if there are no implicant that is not covered by essential prime implicants, which means all implicants are
+ // covered by essential prime implicants, there is no need to apply Petrick's method
+ if (minterms.nonEmpty) {
+ // cover(i): nonessential prime implicants that covers `minterms(i)`
+ val cover = minterms.map(m => implicants.filter(_.covers(m)))
+ // all subsets of `cover`, NP algorithm, O(2 ^ len(cover))
+ val all = cover.tail.foldLeft(cover.head.map(Set(_)))((c0, c1) => c0.flatMap(a => c1.map(a + _)))
+ all.map(_.toList).reduceLeft((a, b) => if (cheaper(a, b)) a else b)
+ } else
+ Seq[Implicant]()
+ }
+
+ def minimize(table: TruthTable): TruthTable = {
+ require(table.table.nonEmpty, "Truth table must not be empty")
+
+ // extract decode table to inputs and outputs
+ val (inputs, outputs) = table.table.unzip
+
+ require(outputs.map(_.getWidth == table.default.getWidth).reduce(_ && _), "All output BitPats and default BitPat must have the same length")
+ require(if (inputs.toSeq.length > 1) inputs.tail.map(_.width == inputs.head.width).reduce(_ && _) else true, "All input BitPats must have the same length")
+
+ // make sure no two inputs specified in the truth table intersect
+ for (t <- inputs.tails; if t.nonEmpty)
+ for (u <- t.tail)
+ require(!t.head.intersects(u), "truth table entries " + t.head + " and " + u + " overlap")
+
+ // number of inputs
+ val n = inputs.head.width
+ // number of outputs
+ val m = outputs.head.getWidth
+
+ // for all outputs
+ val minimized = (0 until m).flatMap(i => {
+ val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i)
+
+ // Minterms, implicants that makes the output to be 1
+ val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ // Maxterms, implicants that makes the output to be 0
+ val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ // Don't cares, implicants that can produce either 0 or 1 as output
+ val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.keys.map(toImplicant).toSeq
+
+ val (implicants, defaultToDc) = table.default match {
+ case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0
+ (mint ++ dc, false)
+ case x if x.mask.testBit(i) && x.value.testBit(i) => // default to 1
+ (maxt ++ dc, false)
+ case x if !x.mask.testBit(i) => // default to ?
+ (mint, true)
+ }
+
+ implicants.foreach(_.isPrime = true)
+ val cols = (0 to n).reverse.map(b => implicants.filter(b == _.bp.mask.bitCount))
+ val mergeTable = cols.map(
+ c => (0 to n).map(
+ b => collection.mutable.Set(c.filter(b == _.bp.value.bitCount):_*)
+ )
+ )
+
+ // O(n ^ 3)
+ for (i <- 0 to n) {
+ for (j <- 0 until n - i) {
+ mergeTable(i)(j).foreach(a => mergeTable(i + 1)(j) ++= mergeTable(i)(j + 1).filter(_ similar a).map(_ merge a))
+ }
+ if (defaultToDc) {
+ for (j <- 0 until n - i) {
+ for (a <- mergeTable(i)(j).filter(_.isPrime)) {
+ if (a.bp.mask.testBit(i) && !a.bp.value.testBit(i)) {
+ // this bit is `0`
+ val t = new BitPat(a.bp.value.setBit(i), a.bp.mask, a.width)
+ if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += t merge a
+ }
+ }
+ for (a <- mergeTable(i)(j + 1).filter(_.isPrime)) {
+ if (a.bp.mask.testBit(i) && a.bp.value.testBit(i)) {
+ // this bit is `1`
+ val t = new BitPat(a.bp.value.clearBit(i), a.bp.mask, a.width)
+ if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += a merge t
+ }
+ }
+ }
+ }
+ }
+
+ val primeImplicants = mergeTable.flatten.flatten.filter(_.isPrime).sortWith(_ < _)
+
+ // O(len(primeImplicants) ^ 4)
+ val (essentialPrimeImplicants, nonessentialPrimeImplicants, uncoveredImplicants) =
+ getEssentialPrimeImplicants(primeImplicants, implicants)
+
+ (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp))
+ })
+
+ minimized.tail.foldLeft(table.copy(table = Map(minimized.head))) { case (tb, t) =>
+ if (tb.table.exists(x => x._1 == t._1)) {
+ tb.copy(table = tb.table.map { case (k, v) =>
+ if (k == t._1) {
+ def ones(bitPat: BitPat) = bitPat.rawString.zipWithIndex.collect{case ('1', x) => x}
+ (k, BitPat("b" + (0 until v.getWidth).map(i => if ((ones(v) ++ ones(t._2)).contains(i)) "1" else "?").mkString))
+ } else (k, v)
+ })
+ } else {
+ tb.copy(table = tb.table + t)
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
new file mode 100644
index 00000000..683de16b
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3.util.BitPat
+
+final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) {
+
+ def inputWidth = table.head._1.getWidth
+
+ def outputWidth = table.head._2.getWidth
+
+ override def toString: String = {
+ def writeRow(map: (BitPat, BitPat)): String =
+ s"${map._1.rawString}->${map._2.rawString}"
+
+ (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).toSeq.sorted.mkString("\n")
+ }
+
+ def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default)
+
+ override def equals(y: Any): Boolean = {
+ y match {
+ case y: TruthTable => toString == y.toString
+ case _ => false
+ }
+ }
+}
+
+object TruthTable {
+ /** Parse TruthTable from its string representation. */
+ def apply(tableString: String): TruthTable = {
+ TruthTable(
+ tableString
+ .split("\n")
+ .filter(_.contains("->"))
+ .map(_.split("->").map(str => BitPat(s"b$str")))
+ .map(bps => bps(0) -> bps(1))
+ .toSeq,
+ BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}")
+ )
+ }
+
+ /** Convert a table and default output into a [[TruthTable]]. */
+ def apply(table: Iterable[(BitPat, BitPat)], default: BitPat): TruthTable = {
+ require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.")
+ require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.")
+ val outputWidth = table.map(_._2.getWidth).head
+ new TruthTable(table.toSeq.groupBy(_._1.toString).map { case (key, values) =>
+ // merge same input inputs.
+ values.head._1 -> BitPat(s"b${
+ Seq.tabulate(outputWidth) { i =>
+ val outputSet = values.map(_._2)
+ .map(_.rawString)
+ .map(_ (i))
+ .toSet
+ .filterNot(_ == '?')
+ require(outputSet.size != 2, s"TruthTable conflict in :\n${values.map { case (i, o) => s"${i.rawString}->${o.rawString}" }.mkString("\n")}")
+ outputSet.headOption.getOrElse('?')
+ }.mkString
+ }")
+ }, default)
+ }
+
+
+ /** consume 1 table, split it into up to 3 tables with the same default bits.
+ *
+ * @return table and its indexes from original bits.
+ * @note
+ * Since most of minimizer(like espresso) cannot handle a multiple default table.
+ * It is useful to split a table into 3 tables based on the default type.
+ */
+ private[decode] def split(
+ table: TruthTable
+ ): Seq[(TruthTable, Seq[Int])] = {
+ def bpFilter(bitPat: BitPat, indexes: Seq[Int]): BitPat =
+ BitPat(s"b${bitPat.rawString.zipWithIndex.filter(b => indexes.contains(b._2)).map(_._1).mkString}")
+
+ def tableFilter(indexes: Seq[Int]): Option[(TruthTable, Seq[Int])] = {
+ if(indexes.nonEmpty) Some((TruthTable(
+ table.table.map { case (in, out) => in -> bpFilter(out, indexes) },
+ bpFilter(table.default, indexes)
+ ), indexes)) else None
+ }
+
+ def index(bitPat: BitPat, bpType: Char): Seq[Int] =
+ bitPat.rawString.zipWithIndex.filter(_._1 == bpType).map(_._2)
+
+ Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t)))
+ }
+
+ /** consume tables, merge it into single table with different default bits.
+ *
+ * @note
+ * Since most of minimizer(like espresso) cannot handle a multiple default table.
+ * It is useful to split a table into 3 tables based on the default type.
+ */
+ private[decode] def merge(
+ tables: Seq[(TruthTable, Seq[Int])]
+ ): TruthTable = {
+ def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] =
+ (table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).rawString.zip(indexes)
+ def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar
+ .sortBy(_._2)
+ .map(_._1)
+ .mkString}")
+ TruthTable(
+ tables
+ .flatMap(_._1.table.keys)
+ .map { key =>
+ key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) })
+ }
+ .toMap,
+ bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) })
+ )
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
new file mode 100644
index 00000000..42e374d1
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util.experimental.decode
+
+import chisel3._
+import chisel3.experimental.{ChiselAnnotation, annotate}
+import chisel3.util.{BitPat, pla}
+import chisel3.util.experimental.getAnnotations
+import firrtl.annotations.Annotation
+import logger.LazyLogging
+
+object decoder extends LazyLogging {
+ /** Use a specific [[Minimizer]] to generated decoded signals.
+ *
+ * @param minimizer specific [[Minimizer]], can be [[QMCMinimizer]] or [[EspressoMinimizer]].
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = {
+ val minimizedTable = getAnnotations().collect {
+ case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out)
+ }.toMap.getOrElse(truthTable, minimizer.minimize(truthTable))
+ if (minimizedTable.table.isEmpty) {
+ val outputs = Wire(UInt(minimizedTable.default.getWidth.W))
+ outputs := minimizedTable.default.value.U(minimizedTable.default.getWidth.W)
+ outputs
+ } else {
+ val (plaInput, plaOutput) =
+ pla(minimizedTable.table.toSeq, BitPat(minimizedTable.default.value.U(minimizedTable.default.getWidth.W)))
+
+ annotate(new ChiselAnnotation {
+ override def toFirrtl: Annotation =
+ DecodeTableAnnotation(plaOutput.toTarget, truthTable.toString, minimizedTable.toString)
+ })
+
+ plaInput := input
+ plaOutput
+ }
+ }
+
+ /** Use [[EspressoMinimizer]] to generated decoded signals.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def espresso(input: UInt, truthTable: TruthTable): UInt = apply(EspressoMinimizer, input, truthTable)
+
+ /** Use [[QMCMinimizer]] to generated decoded signals.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def qmc(input: UInt, truthTable: TruthTable): UInt = apply(QMCMinimizer, input, truthTable)
+
+ /** try to use [[EspressoMinimizer]] to decode `input` by `truthTable`
+ * if `espresso` not exist in your PATH environment it will fall back to [[QMCMinimizer]], and print a warning.
+ *
+ * @param input input signal that contains decode table input
+ * @param truthTable [[TruthTable]] to decode user input.
+ * @return decode table output.
+ */
+ def apply(input: UInt, truthTable: TruthTable): UInt = {
+ def qmcFallBack(input: UInt, truthTable: TruthTable) = {
+ """fall back to QMC.
+ |Quine-McCluskey is a NP complete algorithm, may run forever or run out of memory in large decode tables.
+ |To get rid of this warning, please use `decoder.qmc` directly, or add espresso to your PATH.
+ |""".stripMargin
+ qmc(input, truthTable)
+ }
+
+ try espresso(input, truthTable) catch {
+ case EspressoNotFoundException =>
+ logger.error(s"espresso is not found in your PATH:\n${sys.env("PATH").split(":").mkString("\n")}".stripMargin)
+ qmcFallBack(input, truthTable)
+ case e: java.io.IOException =>
+ logger.error(s"espresso failed to run with ${e.getMessage}")
+ qmcFallBack(input, truthTable)
+ }
+ }
+}
diff --git a/src/main/scala/chisel3/util/experimental/getAnnotations.scala b/src/main/scala/chisel3/util/experimental/getAnnotations.scala
new file mode 100644
index 00000000..dc9b75ee
--- /dev/null
+++ b/src/main/scala/chisel3/util/experimental/getAnnotations.scala
@@ -0,0 +1,9 @@
+package chisel3.util.experimental
+
+import chisel3.internal.Builder
+import firrtl.AnnotationSeq
+
+object getAnnotations {
+ /** Returns the global Annotations */
+ def apply(): AnnotationSeq = Builder.annotationSeq
+}
diff --git a/src/main/scala/chisel3/util/pla.scala b/src/main/scala/chisel3/util/pla.scala
new file mode 100644
index 00000000..c57ca962
--- /dev/null
+++ b/src/main/scala/chisel3/util/pla.scala
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: Apache-2.0
+
+package chisel3.util
+
+import chisel3._
+
+object pla {
+
+ /** Construct a [[https://en.wikipedia.org/wiki/Programmable_logic_array]] from specified table.
+ *
+ * Each position in the input matrix corresponds to an input variable where
+ * `0` implies the corresponding input literal appears complemented in the product term.
+ * `1` implies the input literal appears uncomplemented in the product term
+ * `?` implies the input literal does not appear in the product term.
+ *
+ * For each output
+ * a `1` means this product term makes the function value to `1`
+ * and a `0` or `?` means this product term make the function value to `0`
+ *
+ * @note There is one special case which we call it `? -> 1`. In this scenario, for some of the output functions (bits),
+ * all input terms that make this function value to `1` is purely composed by `?`. In a real pla, this will result in
+ * no connection to the gates in the AND Plane (verilog `z` on gate inputs), which in turn makes the outputs of the
+ * AND Plane undetermined (verilog `x` on outputs). This is not desired behavior in most cases, for example the
+ * minimization result of following truth table:
+ * 0 -> 1
+ * 1 -> 1
+ * which is:
+ * ? -> 1
+ * actually means something other than a verilog `x`. To ease the generation of minimized truth tables, this pla
+ * generation api will hard wire outputs to a `1` on this special case.
+ * This behavior is formally described as: if product terms that make one function value to `1` is solely consisted
+ * of don't-cares (`?`s), then this function is implemented as a constant `1`.
+ *
+ * @param table A [[Seq]] of inputs -> outputs mapping
+ * @param invert A [[BitPat]] specify which bit of the output should be inverted. `1` means the correspond position
+ * of the output should be inverted in the PLA, a `0` or a `?` means direct output from the OR matrix.
+ * @return the (input, output) [[Wire]] of [[UInt]] of the constructed pla.
+ * {{{
+ * // A 1-of-8 decoder (like the 74xx138) can be constructed as follow
+ * val (inputs, outputs) = pla(Seq(
+ * (BitPat("b000"), BitPat("b00000001")),
+ * (BitPat("b001"), BitPat("b00000010")),
+ * (BitPat("b010"), BitPat("b00000100")),
+ * (BitPat("b011"), BitPat("b00001000")),
+ * (BitPat("b100"), BitPat("b00010000")),
+ * (BitPat("b101"), BitPat("b00100000")),
+ * (BitPat("b110"), BitPat("b01000000")),
+ * (BitPat("b111"), BitPat("b10000000")),
+ * ))
+ * }}}
+ */
+ def apply(table: Seq[(BitPat, BitPat)], invert: BitPat = BitPat("b0")): (UInt, UInt) = {
+ require(table.nonEmpty, "pla table must not be empty")
+
+ val (inputTerms, outputTerms) = table.unzip
+ require(
+ inputTerms.map(_.getWidth).distinct.size == 1,
+ "all `BitPat`s in the input part of specified PLA table must have the same width"
+ )
+ require(
+ outputTerms.map(_.getWidth).distinct.size == 1,
+ "all `BitPat`s in the output part of specified PLA table must have the same width"
+ )
+
+ // now all inputs / outputs have the same width
+ val numberOfInputs = inputTerms.head.getWidth
+ val numberOfOutputs = outputTerms.head.getWidth
+
+ val inverterMask = invert.value & invert.mask
+ if (inverterMask.bitCount != 0)
+ require(invert.getWidth == numberOfOutputs,
+ "non-zero inverter mask must have the same width as the output part of specified PLA table"
+ )
+
+ // input wires of the generated PLA
+ val inputs = Wire(UInt(numberOfInputs.W))
+ val invInputs = ~inputs
+
+ // output wires of the generated PLA
+ val outputs = Wire(UInt(numberOfOutputs.W))
+
+ // the AND matrix
+ // use `term -> AND line` map to reuse AND matrix output lines
+ val andMatrixOutputs: Map[String, Bool] = inputTerms.map { t =>
+ val andMatrixInput = Seq
+ .tabulate(numberOfInputs) { i =>
+ if (t.mask.testBit(i)) {
+ Some(
+ if (t.value.testBit(i)) inputs(i)
+ else invInputs(i)
+ )
+ } else {
+ None
+ }
+ }
+ .flatten
+ if (andMatrixInput.nonEmpty) t.toString -> Cat(andMatrixInput).andR() else t.toString -> true.B
+ }.toMap
+
+ // the OR matrix
+ val orMatrixOutputs: UInt = Cat(
+ Seq
+ .tabulate(numberOfOutputs) { i =>
+ val andMatrixLines = table
+ // OR matrix composed by input terms which makes this output bit a `1`
+ .filter {
+ case (_, or) => or.mask.testBit(i) && or.value.testBit(i)
+ }.map {
+ case (inputTerm, _) =>
+ andMatrixOutputs(inputTerm.toString)
+ }
+ if (andMatrixLines.isEmpty) false.B
+ else Cat(andMatrixLines).orR()
+ }
+ .reverse
+ )
+
+ // the INV matrix, useful for decoders
+ val invMatrixOutputs: UInt = Cat(
+ Seq
+ .tabulate(numberOfOutputs) { i =>
+ if (inverterMask.testBit(i)) ~orMatrixOutputs(i)
+ else orMatrixOutputs(i)
+ }
+ .reverse
+ )
+
+ outputs := invMatrixOutputs
+
+ (inputs, outputs)
+ }
+}
diff --git a/src/main/scala/chisel3/verilog.scala b/src/main/scala/chisel3/verilog.scala
new file mode 100644
index 00000000..a91444de
--- /dev/null
+++ b/src/main/scala/chisel3/verilog.scala
@@ -0,0 +1,15 @@
+package chisel3
+
+import chisel3.stage.ChiselStage
+import firrtl.AnnotationSeq
+
+object getVerilogString {
+ def apply(gen: => RawModule): String = ChiselStage.emitVerilog(gen)
+}
+
+object emitVerilog {
+ def apply(gen: => RawModule, args: Array[String] = Array.empty,
+ annotations: AnnotationSeq = Seq.empty): Unit = {
+ (new ChiselStage).emitVerilog(gen, args, annotations)
+ }
+}