diff options
Diffstat (limited to 'src/main/scala')
18 files changed, 237 insertions, 81 deletions
diff --git a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala index ba873c23..087bdae2 100644 --- a/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala +++ b/src/main/scala/chisel3/aop/injecting/InjectingAspect.scala @@ -58,7 +58,8 @@ abstract class InjectorAspect[T <: RawModule, M <: RawModule]( final def toAnnotation(modules: Iterable[M], circuit: String, moduleNames: Seq[String]): AnnotationSeq = { RunFirrtlTransformAnnotation(new InjectingTransform) +: modules.map { module => val chiselOptions = view[ChiselOptions](annotationsInAspect) - val dynamicContext = new DynamicContext(annotationsInAspect, chiselOptions.throwOnFirstError) + val dynamicContext = + new DynamicContext(annotationsInAspect, chiselOptions.throwOnFirstError, chiselOptions.warnReflectiveNaming) // Add existing module names into the namespace. If injection logic instantiates new modules // which would share the same name, they will get uniquified accordingly moduleNames.foreach { n => diff --git a/src/main/scala/chisel3/stage/ChiselAnnotations.scala b/src/main/scala/chisel3/stage/ChiselAnnotations.scala index e046319d..eda05a7d 100644 --- a/src/main/scala/chisel3/stage/ChiselAnnotations.scala +++ b/src/main/scala/chisel3/stage/ChiselAnnotations.scala @@ -79,6 +79,22 @@ case object ThrowOnFirstErrorAnnotation } +/** Warn when reflective naming changes names of signals */ +case object WarnReflectiveNamingAnnotation + extends NoTargetAnnotation + with ChiselOption + with HasShellOptions + with Unserializable { + + val options = Seq( + new ShellOption[Unit]( + longOption = "warn:reflective-naming", + toAnnotationSeq = _ => Seq(this), + helpText = "Warn when reflective naming changes the name of signals (3.6 migration)" + ) + ) +} + /** An [[firrtl.annotations.Annotation]] storing a function that returns a Chisel module * @param gen a generator function */ diff --git a/src/main/scala/chisel3/stage/ChiselCli.scala b/src/main/scala/chisel3/stage/ChiselCli.scala index f38bf50c..8c5eb79a 100644 --- a/src/main/scala/chisel3/stage/ChiselCli.scala +++ b/src/main/scala/chisel3/stage/ChiselCli.scala @@ -10,6 +10,7 @@ trait ChiselCli { this: Shell => NoRunFirrtlCompilerAnnotation, PrintFullStackTraceAnnotation, ThrowOnFirstErrorAnnotation, + WarnReflectiveNamingAnnotation, ChiselOutputFileAnnotation, ChiselGeneratorAnnotation ) diff --git a/src/main/scala/chisel3/stage/ChiselOptions.scala b/src/main/scala/chisel3/stage/ChiselOptions.scala index 7e4305fa..a03f3d7b 100644 --- a/src/main/scala/chisel3/stage/ChiselOptions.scala +++ b/src/main/scala/chisel3/stage/ChiselOptions.scala @@ -5,24 +5,27 @@ package chisel3.stage import chisel3.internal.firrtl.Circuit class ChiselOptions private[stage] ( - val runFirrtlCompiler: Boolean = true, - val printFullStackTrace: Boolean = false, - val throwOnFirstError: Boolean = false, - val outputFile: Option[String] = None, - val chiselCircuit: Option[Circuit] = None) { + val runFirrtlCompiler: Boolean = true, + val printFullStackTrace: Boolean = false, + val throwOnFirstError: Boolean = false, + val warnReflectiveNaming: Boolean = false, + val outputFile: Option[String] = None, + val chiselCircuit: Option[Circuit] = None) { private[stage] def copy( - runFirrtlCompiler: Boolean = runFirrtlCompiler, - printFullStackTrace: Boolean = printFullStackTrace, - throwOnFirstError: Boolean = throwOnFirstError, - outputFile: Option[String] = outputFile, - chiselCircuit: Option[Circuit] = chiselCircuit + runFirrtlCompiler: Boolean = runFirrtlCompiler, + printFullStackTrace: Boolean = printFullStackTrace, + throwOnFirstError: Boolean = throwOnFirstError, + warnReflectiveNaming: Boolean = warnReflectiveNaming, + outputFile: Option[String] = outputFile, + chiselCircuit: Option[Circuit] = chiselCircuit ): ChiselOptions = { new ChiselOptions( runFirrtlCompiler = runFirrtlCompiler, printFullStackTrace = printFullStackTrace, throwOnFirstError = throwOnFirstError, + warnReflectiveNaming = warnReflectiveNaming, outputFile = outputFile, chiselCircuit = chiselCircuit ) diff --git a/src/main/scala/chisel3/stage/package.scala b/src/main/scala/chisel3/stage/package.scala index b1064c05..10c8c524 100644 --- a/src/main/scala/chisel3/stage/package.scala +++ b/src/main/scala/chisel3/stage/package.scala @@ -15,11 +15,12 @@ package object stage { def view(options: AnnotationSeq): ChiselOptions = options.collect { case a: ChiselOption => a } .foldLeft(new ChiselOptions()) { (c, x) => x match { - case NoRunFirrtlCompilerAnnotation => c.copy(runFirrtlCompiler = false) - case PrintFullStackTraceAnnotation => c.copy(printFullStackTrace = true) - case ThrowOnFirstErrorAnnotation => c.copy(throwOnFirstError = true) - case ChiselOutputFileAnnotation(f) => c.copy(outputFile = Some(f)) - case ChiselCircuitAnnotation(a) => c.copy(chiselCircuit = Some(a)) + case NoRunFirrtlCompilerAnnotation => c.copy(runFirrtlCompiler = false) + case PrintFullStackTraceAnnotation => c.copy(printFullStackTrace = true) + case ThrowOnFirstErrorAnnotation => c.copy(throwOnFirstError = true) + case WarnReflectiveNamingAnnotation => c.copy(warnReflectiveNaming = true) + case ChiselOutputFileAnnotation(f) => c.copy(outputFile = Some(f)) + case ChiselCircuitAnnotation(a) => c.copy(chiselCircuit = Some(a)) } } diff --git a/src/main/scala/chisel3/stage/phases/Elaborate.scala b/src/main/scala/chisel3/stage/phases/Elaborate.scala index 55331cb4..ba29e5f2 100644 --- a/src/main/scala/chisel3/stage/phases/Elaborate.scala +++ b/src/main/scala/chisel3/stage/phases/Elaborate.scala @@ -29,8 +29,10 @@ class Elaborate extends Phase { case ChiselGeneratorAnnotation(gen) => val chiselOptions = view[ChiselOptions](annotations) try { + val context = + new DynamicContext(annotations, chiselOptions.throwOnFirstError, chiselOptions.warnReflectiveNaming) val (circuit, dut) = - Builder.build(Module(gen()), new DynamicContext(annotations, chiselOptions.throwOnFirstError)) + Builder.build(Module(gen()), context) Seq(ChiselCircuitAnnotation(circuit), DesignAnnotation(dut)) } catch { /* if any throwable comes back and we're in "stack trace trimming" mode, then print an error and trim the stack trace diff --git a/src/main/scala/chisel3/util/Arbiter.scala b/src/main/scala/chisel3/util/Arbiter.scala index 220a12b1..06821a25 100644 --- a/src/main/scala/chisel3/util/Arbiter.scala +++ b/src/main/scala/chisel3/util/Arbiter.scala @@ -6,7 +6,6 @@ package chisel3.util import chisel3._ -import chisel3.internal.naming.chiselName // can't use chisel3_ version because of compile order /** IO bundle definition for an Arbiter, which takes some number of ready-valid inputs and outputs * (selects) at most one. @@ -115,7 +114,6 @@ class LockingArbiter[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T * consumer.io.in <> arb.io.out * }}} */ -@chiselName class RRArbiter[T <: Data](val gen: T, val n: Int) extends LockingRRArbiter[T](gen, n, 1) /** Hardware module that is used to sequence n producers into 1 consumer. @@ -131,7 +129,6 @@ class RRArbiter[T <: Data](val gen: T, val n: Int) extends LockingRRArbiter[T](g * consumer.io.in <> arb.io.out * }}} */ -@chiselName class Arbiter[T <: Data](val gen: T, val n: Int) extends Module { val io = IO(new ArbiterIO(gen, n)) diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index d27fee14..7cb80e54 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -5,6 +5,8 @@ package chisel3.util import scala.language.experimental.macros import chisel3._ import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} +import scala.collection.mutable +import scala.util.hashing.MurmurHash3 object BitPat { @@ -253,6 +255,9 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, val width: Int) def =/=(that: UInt): Bool = macro SourceInfoTransform.thatArg def ##(that: BitPat): BitPat = macro SourceInfoTransform.thatArg + override def hashCode: Int = + MurmurHash3.seqHash(Seq(this.value, this.mask, this.width)) + /** @group SourceInfoTransformMacro */ def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = { do_apply(x, x) @@ -348,15 +353,29 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, val width: Int) override def isEmpty: Boolean = false /** Generate raw string of a [[BitPat]]. */ - def rawString: String = Seq - .tabulate(width) { i => - (value.testBit(width - i - 1), mask.testBit(width - i - 1)) match { - case (true, true) => "1" - case (false, true) => "0" - case (_, false) => "?" - } + def rawString: String = _rawString + + // This is micro-optimized and memoized because it is used for lots of BitPat operations + private lazy val _rawString: String = { + val sb = new StringBuilder(width) + var i = 0 + while (i < width) { + val bitIdx = width - i - 1 + val char = + if (mask.testBit(bitIdx)) { + if (value.testBit(bitIdx)) { + '1' + } else { + '0' + } + } else { + '?' + } + sb += char + i += 1 } - .mkString + sb.result() + } override def toString = s"BitPat($rawString)" } diff --git a/src/main/scala/chisel3/util/Bitwise.scala b/src/main/scala/chisel3/util/Bitwise.scala index 8abe3645..92ebddb4 100644 --- a/src/main/scala/chisel3/util/Bitwise.scala +++ b/src/main/scala/chisel3/util/Bitwise.scala @@ -24,13 +24,16 @@ object FillInterleaved { * * Output data-equivalent to in(size(in)-1) (n times) ## ... ## in(1) (n times) ## in(0) (n times) */ - def apply(n: Int, in: UInt): UInt = apply(n, in.asBools) + def apply(@deprecatedName('n, "Chisel 3.5") n: Int, @deprecatedName('in, "Chisel 3.5") in: UInt): UInt = + apply(n, in.asBools) /** Creates n repetitions of each bit of x in order. * * Output data-equivalent to in(size(in)-1) (n times) ## ... ## in(1) (n times) ## in(0) (n times) */ - def apply(n: Int, in: Seq[Bool]): UInt = Cat(in.map(Fill(n, _)).reverse) + def apply(@deprecatedName('n, "Chisel 3.5") n: Int, @deprecatedName('in, "Chisel 3.5") in: Seq[Bool]): UInt = Cat( + in.map(Fill(n, _)).reverse + ) } /** Returns the number of bits set (value is 1 or true) in the input signal. @@ -45,9 +48,9 @@ object FillInterleaved { * }}} */ object PopCount { - def apply(in: Iterable[Bool]): UInt = SeqUtils.count(in.toSeq) + def apply(@deprecatedName('in, "Chisel 3.5") in: Iterable[Bool]): UInt = SeqUtils.count(in.toSeq) - def apply(in: Bits): UInt = apply((0 until in.getWidth).map(in(_))) + def apply(@deprecatedName('in, "Chisel 3.5") in: Bits): UInt = apply((0 until in.getWidth).map(in(_))) } /** Create repetitions of the input using a tree fanout topology. @@ -65,7 +68,7 @@ object Fill { * Output data-equivalent to x ## x ## ... ## x (n repetitions). * @throws java.lang.IllegalArgumentException if `n` is less than zero */ - def apply(n: Int, x: UInt): UInt = { + def apply(@deprecatedName('n, "Chisel 3.5") n: Int, @deprecatedName('x, "Chisel 3.5") x: UInt): UInt = { n match { case _ if n < 0 => throw new IllegalArgumentException(s"n (=$n) must be nonnegative integer.") case 0 => UInt(0.W) @@ -111,5 +114,5 @@ object Reverse { Cat(doit(in(half - 1, 0), half), doit(in(length - 1, half), length - half)) } - def apply(in: UInt): UInt = doit(in, in.getWidth) + def apply(@deprecatedName('in, "Chisel 3.5") in: UInt): UInt = doit(in, in.getWidth) } diff --git a/src/main/scala/chisel3/util/Cat.scala b/src/main/scala/chisel3/util/Cat.scala index c5adce56..3224ec03 100644 --- a/src/main/scala/chisel3/util/Cat.scala +++ b/src/main/scala/chisel3/util/Cat.scala @@ -19,7 +19,9 @@ object Cat { /** Concatenates the argument data elements, in argument order, together. The first argument * forms the most significant bits, while the last argument forms the least significant bits. */ - def apply[T <: Bits](a: T, r: T*): UInt = apply(a :: r.toList) + def apply[T <: Bits](@deprecatedName('a, "Chisel 3.5") a: T, @deprecatedName('r, "Chisel 3.5") r: T*): UInt = apply( + a :: r.toList + ) /** Concatenates the data elements of the input sequence, in reverse sequence order, together. * The first element of the sequence forms the most significant bits, while the last element @@ -28,5 +30,5 @@ object Cat { * Equivalent to r(0) ## r(1) ## ... ## r(n-1). * @note This returns a `0.U` if applied to a zero-element `Vec`. */ - def apply[T <: Bits](r: Seq[T]): UInt = SeqUtils.asUInt(r.reverse) + def apply[T <: Bits](@deprecatedName('r, "Chisel 3.5") r: Seq[T]): UInt = SeqUtils.asUInt(r.reverse) } diff --git a/src/main/scala/chisel3/util/CircuitMath.scala b/src/main/scala/chisel3/util/CircuitMath.scala index df60f059..9e4890a9 100644 --- a/src/main/scala/chisel3/util/CircuitMath.scala +++ b/src/main/scala/chisel3/util/CircuitMath.scala @@ -6,7 +6,6 @@ package chisel3.util import chisel3._ -import chisel3.internal.naming.chiselName // can't use chisel3_ version because of compile order /** Returns the base-2 integer logarithm of an UInt. * @@ -22,7 +21,6 @@ object Log2 { /** Returns the base-2 integer logarithm of the least-significant `width` bits of an UInt. */ - @chiselName def apply(x: Bits, width: Int): UInt = { if (width < 2) { 0.U diff --git a/src/main/scala/chisel3/util/Counter.scala b/src/main/scala/chisel3/util/Counter.scala index ef1eff9f..be6e3257 100644 --- a/src/main/scala/chisel3/util/Counter.scala +++ b/src/main/scala/chisel3/util/Counter.scala @@ -3,7 +3,7 @@ package chisel3.util import chisel3._ -import chisel3.internal.naming.chiselName // can't use chisel3_ version because of compile order +import chisel3.experimental.AffectsChiselPrefix /** Used to generate an inline (logic directly in the containing Module, no internal Module is created) * hardware counter. @@ -27,8 +27,7 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because * } * }}} */ -@chiselName -class Counter private (r: Range, oldN: Option[Int] = None) { +class Counter private (r: Range, oldN: Option[Int] = None) extends AffectsChiselPrefix { require(r.length > 0, s"Counter range cannot be empty, got: $r") require(r.start >= 0 && r.end >= 0, s"Counter range must be positive, got: $r") @@ -113,7 +112,6 @@ object Counter { * @return tuple of the counter value and whether the counter will wrap (the value is at * maximum and the condition is true). */ - @chiselName def apply(cond: Bool, n: Int): (UInt, Bool) = { val c = new Counter(n) val wrap = WireInit(false.B) @@ -129,7 +127,6 @@ object Counter { * @return tuple of the counter value and whether the counter will wrap (the value is at * maximum and the condition is true). */ - @chiselName def apply(r: Range, enable: Bool = true.B, reset: Bool = false.B): (UInt, Bool) = { val c = new Counter(r) val wrap = WireInit(false.B) diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala index 5c71a4ea..b21bd04f 100644 --- a/src/main/scala/chisel3/util/Decoupled.scala +++ b/src/main/scala/chisel3/util/Decoupled.scala @@ -9,6 +9,8 @@ import chisel3._ import chisel3.experimental.{requireIsChiselType, DataMirror, Direction} import chisel3.internal.naming._ // can't use chisel3_ version because of compile order +import scala.annotation.nowarn + /** An I/O Bundle containing 'valid' and 'ready' signals that handshake * the transfer of data stored in the 'bits' subfield. * The base protocol implied by the directionality is that @@ -97,7 +99,22 @@ object ReadyValidIO { * of ready or valid. * @param gen the type of data to be wrapped in DecoupledIO */ -class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen) +class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen) { + + /** Applies the supplied functor to the bits of this interface, returning a new + * typed DecoupledIO interface. + * @param f The function to apply to this DecoupledIO's 'bits' with return type B + * @return a new DecoupledIO of type B + */ + def map[B <: Data](f: T => B): DecoupledIO[B] = { + val _map_bits = f(bits) + val _map = Wire(Decoupled(chiselTypeOf(_map_bits))) + _map.bits := _map_bits + _map.valid := valid + ready := _map.ready + _map + } +} /** This factory adds a decoupled handshaking protocol to a data bundle. */ object Decoupled { @@ -231,7 +248,6 @@ class QueueIO[T <: Data]( * consumer.io.in <> q.io.deq * }}} */ -@chiselName class Queue[T <: Data]( val gen: T, val entries: Int, @@ -342,7 +358,7 @@ object Queue { * consumer.io.in <> Queue(producer.io.out, 16) * }}} */ - @chiselName + @nowarn("cat=deprecation&msg=TransitName") def apply[T <: Data]( enq: ReadyValidIO[T], entries: Int = 2, diff --git a/src/main/scala/chisel3/util/Reg.scala b/src/main/scala/chisel3/util/Reg.scala index ddb74dd6..89381c11 100644 --- a/src/main/scala/chisel3/util/Reg.scala +++ b/src/main/scala/chisel3/util/Reg.scala @@ -12,7 +12,10 @@ object RegEnable { * val regWithEnable = RegEnable(nextVal, ena) * }}} */ - def apply[T <: Data](next: T, enable: Bool): T = { + def apply[T <: Data]( + @deprecatedName('next, "Chisel 3.5") next: T, + @deprecatedName('enable, "Chisel 3.5") enable: Bool + ): T = { val r = Reg(chiselTypeOf(next)) when(enable) { r := next } r @@ -24,7 +27,11 @@ object RegEnable { * val regWithEnableAndReset = RegEnable(nextVal, 0.U, ena) * }}} */ - def apply[T <: Data](next: T, init: T, enable: Bool): T = { + def apply[T <: Data]( + @deprecatedName('next, "Chisel 3.5") next: T, + @deprecatedName('init, "Chisel 3.5") init: T, + @deprecatedName('enable, "Chisel 3.5") enable: Bool + ): T = { val r = RegInit(init) when(enable) { r := next } r @@ -43,7 +50,11 @@ object ShiftRegister { * val regDelayTwo = ShiftRegister(nextVal, 2, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, en: Bool = true.B): T = ShiftRegisters(in, n, en).lastOption.getOrElse(in) + def apply[T <: Data]( + @deprecatedName('in, "Chisel 3.5") in: T, + @deprecatedName('n, "Chisel 3.5") n: Int, + @deprecatedName('en, "Chisel 3.5") 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. * @@ -56,7 +67,12 @@ object ShiftRegister { * val regDelayTwoReset = ShiftRegister(nextVal, 2, 0.U, ena) * }}} */ - def apply[T <: Data](in: T, n: Int, resetData: T, en: Bool): T = + def apply[T <: Data]( + @deprecatedName('in, "Chisel 3.5") in: T, + @deprecatedName('n, "Chisel 3.5") n: Int, + @deprecatedName('resetData, "Chisel 3.5") resetData: T, + @deprecatedName('en, "Chisel 3.5") en: Bool + ): T = ShiftRegisters(in, n, resetData, en).lastOption.getOrElse(in) } @@ -68,7 +84,11 @@ object ShiftRegisters { * @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] = + def apply[T <: Data]( + @deprecatedName('in, "Chisel 3.5") in: T, + @deprecatedName('n, "Chisel 3.5") n: Int, + @deprecatedName('en, "Chisel 3.5") 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. @@ -78,6 +98,11 @@ object ShiftRegisters { * @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] = + def apply[T <: Data]( + @deprecatedName('in, "Chisel 3.5") in: T, + @deprecatedName('n, "Chisel 3.5") n: Int, + @deprecatedName('resetData, "Chisel 3.5") resetData: T, + @deprecatedName('en, "Chisel 3.5") en: Bool + ): Seq[T] = Seq.iterate(in, n + 1)(util.RegEnable(_, resetData, en)).drop(1) } diff --git a/src/main/scala/chisel3/util/TransitName.scala b/src/main/scala/chisel3/util/TransitName.scala index cc8f2456..8b509db5 100644 --- a/src/main/scala/chisel3/util/TransitName.scala +++ b/src/main/scala/chisel3/util/TransitName.scala @@ -42,6 +42,12 @@ object TransitName { * @param to the thing that will receive the "good" name * @return the `from` parameter */ + @deprecated( + "Use suggestName or rely on the naming plugin instead of this function: \n" + + " val from = {to}\n" + + " val from = prefix(prefixYouWant){to}", + "Chisel 3.5.4" + ) def apply[T <: HasId](from: T, to: HasId): T = { // To transit a name, we need to hook on both the suggestName and autoSeed mechanisms from.addSuggestPostnameHook((given_name: String) => { to.suggestName(given_name) }) @@ -55,6 +61,11 @@ object TransitName { * @param to the thing that will receive the "good" name * @return the `from` parameter */ + @deprecated( + "Use suggestName or rely on the naming plugin instead of this function. Use prefix instead of suffix: \n" + + " val from = prefix(prefixYouWant){to}", + "Chisel 3.5.4" + ) def withSuffix[T <: HasId](suffix: String)(from: T, to: HasId): T = { // To transit a name, we need to hook on both the suggestName and autoSeed mechanisms from.addSuggestPostnameHook((given_name: String) => { to.suggestName(given_name + suffix) }) diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala index eeb2ab68..cb0e166a 100644 --- a/src/main/scala/chisel3/util/Valid.scala +++ b/src/main/scala/chisel3/util/Valid.scala @@ -7,6 +7,8 @@ package chisel3.util import chisel3._ +import scala.annotation.nowarn + /** A [[Bundle]] that adds a `valid` bit to some data. This indicates that the user expects a "valid" interface between * a producer and a consumer. Here, the producer asserts the `valid` bit when data on the `bits` line contains valid * data. This differs from [[DecoupledIO]] or [[IrrevocableIO]] as there is no `ready` line that the consumer can use @@ -116,6 +118,7 @@ object Pipe { * @param latency the number of pipeline stages * @return $returnType */ + @nowarn("cat=deprecation&msg=TransitName") def apply[T <: Data](enqValid: Bool, enqBits: T, latency: Int)(implicit compileOptions: CompileOptions): Valid[T] = { require(latency >= 0, "Pipe latency must be greater than or equal to zero!") if (latency == 0) { diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 86973e5b..8c85b6d1 100644 --- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala +++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala @@ -87,6 +87,6 @@ object EspressoMinimizer extends Minimizer with LazyLogging { logger.trace(s"""espresso output table: |$output |""".stripMargin) - TruthTable(readTable(output), table.default) + TruthTable.fromEspressoOutput(readTable(output), table.default) } } diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 00fa0f9c..2720e690 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -3,6 +3,7 @@ package chisel3.util.experimental.decode import chisel3.util.BitPat +import scala.collection.mutable sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default: BitPat, val sort: Boolean) { def inputWidth = table.head._1.getWidth @@ -29,40 +30,89 @@ sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default: object TruthTable { - /** Convert a table and default output into a [[TruthTable]]. */ - def apply(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): TruthTable = { + /** Pad the input signals to equalize all input widths. Pads input signals + * to the maximum width found in the table. + * + * @param table the truth table whose rows will be padded + * @return the same truth table but with inputs padded + */ + private def padInputs(table: Iterable[(BitPat, BitPat)]): Iterable[(BitPat, BitPat)] = { val inputWidth = table.map(_._1.getWidth).max - require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.") - val outputWidth = table.map(_._2.getWidth).head - val mergedTable = table.map { - // pad input signals if necessary + table.map { case (in, out) if inputWidth > in.width => (BitPat.N(inputWidth - in.width) ## in, out) case (in, out) => (in, out) } - .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}") - } - .toSeq + } + + /** For each duplicated input, collect the outputs into a single Seq. + * + * @param table the truth table + * @return a Seq of tuple of length 2, where the first element is the + * input and the second element is a Seq of OR-ed outputs + * for the input + */ + private def mergeTableOnInputs(table: Iterable[(BitPat, BitPat)]): Seq[(BitPat, Seq[BitPat])] = { + groupByIntoSeq(table)(_._1).map { + case (input, mappings) => + input -> mappings.map(_._2) + } + } + + /** Merge two BitPats by OR-ing the values and masks, and setting the + * width to the max width among the two + */ + private def merge(a: BitPat, b: BitPat): BitPat = { + new BitPat(a.value | b.value, a.mask | b.mask, a.width.max(b.width)) + } + + /** Public method for calling with the Espresso decoder format fd + * + * For Espresso, for each output, a 1 means this product term belongs to the ON-set, + * a 0 means this product term has no meaning for the value of this function. + * This is the same as the fd (or f) type in espresso. + * + * @param table the truth table + * @param default the default BitPat is made up of a single bit type, either "?", "0" or "1". + * A default of "?" sets Espresso to fr-format, while a "0" or "1" sets it to the + * fd-format. + * @param sort whether to sort the final truth table using BitPat.bitPatOrder + * @return a fully built TruthTable + */ + def fromEspressoOutput(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): TruthTable = { + apply_impl(table, default, sort, false) + } + + /** Public apply method to TruthTable. Calls apply_impl with the default value true of checkCollisions */ + def apply(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): TruthTable = { + apply_impl(table, default, sort, true) + } + + /** Convert a table and default output into a [[TruthTable]]. */ + private def apply_impl( + table: Iterable[(BitPat, BitPat)], + default: BitPat, + sort: Boolean, + checkCollisions: Boolean + ): TruthTable = { + val paddedTable = padInputs(table) + + require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.") + + val mergedTable = mergeTableOnInputs(paddedTable) + + val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map { + case (input, outputs) => + val (result, noCollisions) = outputs.tail.foldLeft((outputs.head, checkCollisions)) { + case ((acc, ok), o) => (merge(acc, o), ok && acc.overlap(o)) + } + // Throw an error if checkCollisions is true but there are bits with a non-zero overlap. + require(!checkCollisions || noCollisions, s"TruthTable conflict on merged row:\n $input -> $outputs") + (input, result) + } + import BitPat.bitPatOrder - new TruthTable(if (sort) mergedTable.sorted else mergedTable, default, sort) + new TruthTable(if (sort) finalTable.sorted else finalTable, default, sort) } /** Parse TruthTable from its string representation. */ @@ -140,4 +190,15 @@ object TruthTable { bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) }) ) } + + /** Similar to Seq.groupBy except that it preserves ordering of elements within each group */ + private def groupByIntoSeq[A, K](xs: Iterable[A])(f: A => K): Seq[(K, Seq[A])] = { + val map = mutable.LinkedHashMap.empty[K, mutable.ListBuffer[A]] + for (x <- xs) { + val key = f(x) + val l = map.getOrElseUpdate(key, mutable.ListBuffer.empty[A]) + l += x + } + map.view.map({ case (k, vs) => k -> vs.toList }).toList + } } |
