summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util
diff options
context:
space:
mode:
authorJack2021-12-18 08:27:38 +0000
committerJack2021-12-18 08:27:38 +0000
commitdd9ad534771247ac16eaa47eb9794102736b5102 (patch)
treed4566d317cb8526b79017de1e438aea8217dd1d4 /src/main/scala/chisel3/util
parent440edc4436fb3a8a4175ae425a0d31c4997ee60f (diff)
parentf50f74f583fba7b98e550c440df091e559ce32b8 (diff)
Merge branch 'master' into 3.5-release
Diffstat (limited to 'src/main/scala/chisel3/util')
-rw-r--r--src/main/scala/chisel3/util/Arbiter.scala17
-rw-r--r--src/main/scala/chisel3/util/BitPat.scala218
-rw-r--r--src/main/scala/chisel3/util/Decoupled.scala110
-rw-r--r--src/main/scala/chisel3/util/Enum.scala1
-rw-r--r--src/main/scala/chisel3/util/Valid.scala23
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala15
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala19
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/TruthTable.scala47
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/decoder.scala35
-rw-r--r--src/main/scala/chisel3/util/random/GaloisLFSR.scala4
-rw-r--r--src/main/scala/chisel3/util/random/PRNG.scala15
11 files changed, 406 insertions, 98 deletions
diff --git a/src/main/scala/chisel3/util/Arbiter.scala b/src/main/scala/chisel3/util/Arbiter.scala
index 059bdd14..b68acae1 100644
--- a/src/main/scala/chisel3/util/Arbiter.scala
+++ b/src/main/scala/chisel3/util/Arbiter.scala
@@ -10,6 +10,7 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because
/** IO bundle definition for an Arbiter, which takes some number of ready-valid inputs and outputs
* (selects) at most one.
+ * @groupdesc Signals The actual hardware fields of the Bundle
*
* @param gen data type
* @param n number of inputs
@@ -17,8 +18,20 @@ import chisel3.internal.naming.chiselName // can't use chisel3_ version because
class ArbiterIO[T <: Data](private val gen: T, val n: Int) extends Bundle {
// See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
+/** Input data, one per potential sender
+ *
+ * @group Signals
+ */
val in = Flipped(Vec(n, Decoupled(gen)))
+/** Output data after arbitration
+ *
+ * @group Signals
+ */
val out = Decoupled(gen)
+/** One-Hot vector indicating which output was chosen
+ *
+ * @group Signals
+ */
val chosen = Output(UInt(log2Ceil(n).W))
}
@@ -47,7 +60,7 @@ abstract class LockingArbiterLike[T <: Data](gen: T, n: Int, count: Int, needsLo
val locked = lockCount.value =/= 0.U
val wantsLock = needsLock.map(_(io.out.bits)).getOrElse(true.B)
- when (io.out.fire() && wantsLock) {
+ when (io.out.fire && wantsLock) {
lockIdx := io.chosen
lockCount.inc()
}
@@ -63,7 +76,7 @@ abstract class LockingArbiterLike[T <: Data](gen: T, n: Int, count: Int, needsLo
class LockingRRArbiter[T <: Data](gen: T, n: Int, count: Int, needsLock: Option[T => Bool] = None)
extends LockingArbiterLike[T](gen, n, count, needsLock) {
- lazy val lastGrant = RegEnable(io.chosen, io.out.fire())
+ lazy val lastGrant = RegEnable(io.chosen, io.out.fire)
lazy val grantMask = (0 until n).map(_.asUInt > lastGrant)
lazy val validMask = io.in zip grantMask map { case (in, g) => in.valid && g }
diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala
index 0dcb2466..808245de 100644
--- a/src/main/scala/chisel3/util/BitPat.scala
+++ b/src/main/scala/chisel3/util/BitPat.scala
@@ -4,11 +4,16 @@ package chisel3.util
import scala.language.experimental.macros
import chisel3._
-import chisel3.internal.chiselRuntimeDeprecated
import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform}
object BitPat {
+
+ private[chisel3] implicit val bitPatOrder = new Ordering[BitPat] {
+ import scala.math.Ordered.orderingToOrdered
+ def compare(x: BitPat, y: BitPat): Int = (x.getWidth, x.value, x.mask) compare (y.getWidth, y.value, y.mask)
+ }
+
/** Parses a bit pattern string into (bits, mask, width).
*
* @return bits the literal value, with don't cares being 0
@@ -90,6 +95,7 @@ object BitPat {
* @note the UInt must be a literal
*/
def apply(x: UInt): BitPat = {
+ require(x.isLit, s"$x is not a literal, BitPat.apply(x: UInt) only accepts literals")
val len = if (x.isWidthKnown) x.getWidth else 0
apply("b" + x.litValue.toString(2).reverse.padTo(len, "0").reverse.mkString)
}
@@ -111,6 +117,119 @@ object BitPat {
}
}
+package experimental {
+ object BitSet {
+
+ /** Construct a [[BitSet]] from a sequence of [[BitPat]].
+ * All [[BitPat]] must have the same width.
+ */
+ def apply(bitpats: BitPat*): BitSet = {
+ val bs = new BitSet { def terms = bitpats.flatMap(_.terms).toSet }
+ // check width
+ bs.getWidth
+ bs
+ }
+
+ /** Empty [[BitSet]]. */
+ val empty: BitSet = new BitSet {
+ def terms = Set()
+ }
+
+ /** Construct a [[BitSet]] from String.
+ * each line should be a valid [[BitPat]] string with the same width.
+ */
+ def fromString(str: String): BitSet = {
+ val bs = new BitSet { def terms = str.split('\n').map(str => BitPat(str)).toSet }
+ // check width
+ bs.getWidth
+ bs
+ }
+ }
+
+ /** A Set of [[BitPat]] represents a set of bit vector with mask. */
+ sealed trait BitSet { outer =>
+ /** all [[BitPat]] elements in [[terms]] make up this [[BitSet]].
+ * all [[terms]] should be have the same width.
+ */
+ def terms: Set[BitPat]
+
+ /** Get specified width of said BitSet */
+ def getWidth: Int = {
+ require(terms.map(_.width).size <= 1, s"All BitPats must be the same size! Got $this")
+ // set width = 0 if terms is empty.
+ terms.headOption.map(_.width).getOrElse(0)
+ }
+
+ import BitPat.bitPatOrder
+ override def toString: String = terms.toSeq.sorted.mkString("\n")
+
+ /** whether this [[BitSet]] is empty (i.e. no value matches) */
+ def isEmpty: Boolean = terms.forall(_.isEmpty)
+
+ /** Check whether this [[BitSet]] overlap with that [[BitSet]], i.e. !(intersect.isEmpty)
+ *
+ * @param that [[BitSet]] to be checked.
+ * @return true if this and that [[BitSet]] have overlap.
+ */
+ def overlap(that: BitSet): Boolean =
+ !terms.flatMap(a => that.terms.map(b => (a, b))).forall { case (a, b) => !a.overlap(b) }
+
+ /** Check whether this [[BitSet]] covers that (i.e. forall b matches that, b also matches this)
+ *
+ * @param that [[BitSet]] to be covered
+ * @return true if this [[BitSet]] can cover that [[BitSet]]
+ */
+ def cover(that: BitSet): Boolean =
+ that.subtract(this).isEmpty
+
+ /** Intersect `this` and `that` [[BitSet]].
+ *
+ * @param that [[BitSet]] to be intersected.
+ * @return a [[BitSet]] containing all elements of `this` that also belong to `that`.
+ */
+ def intersect(that: BitSet): BitSet =
+ terms
+ .flatMap(a => that.terms.map(b => a.intersect(b)))
+ .filterNot(_.isEmpty)
+ .fold(BitSet.empty)(_.union(_))
+
+ /** Subtract that from this [[BitSet]].
+ *
+ * @param that subtrahend [[BitSet]].
+ * @return a [[BitSet]] containing elements of `this` which are not the elements of `that`.
+ */
+ def subtract(that: BitSet): BitSet =
+ terms.map { a =>
+ that.terms.map(b => a.subtract(b)).fold(a)(_.intersect(_))
+ }.filterNot(_.isEmpty).fold(BitSet.empty)(_.union(_))
+
+ /** Union this and that [[BitSet]]
+ *
+ * @param that [[BitSet]] to union.
+ * @return a [[BitSet]] containing all elements of `this` and `that`.
+ */
+ def union(that: BitSet): BitSet = new BitSet {
+ def terms = outer.terms ++ that.terms
+ }
+
+ /** Test whether two [[BitSet]] matches the same set of value
+ *
+ * @note
+ * This method can be very expensive compared to ordinary == operator between two Objects
+ *
+ * @return true if two [[BitSet]] is same.
+ */
+ override def equals(obj: Any): Boolean = {
+ obj match {
+ case that: BitSet => this.getWidth == that.getWidth && this.cover(that) && that.cover(this)
+ case _ => false
+ }
+ }
+ }
+
+}
+
+
/** Bit patterns are literals with masks, used to represent values with don't
* care bits. Equality comparisons will ignore don't care bits.
*
@@ -120,19 +239,19 @@ object BitPat {
* "b10001".U === BitPat("b101??") // evaluates to false.B
* }}}
*/
-sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends SourceInfoDoc {
- def getWidth: Int = width
+sealed class BitPat(val value: BigInt, val mask: BigInt, val width: Int) extends util.experimental.BitSet with SourceInfoDoc {
+ import chisel3.util.experimental.BitSet
+ def terms = Set(this)
+
+ /**
+ * Get specified width of said BitPat
+ */
+ override def getWidth: Int = width
def apply(x: Int): BitPat = macro SourceInfoTransform.xArg
def apply(x: Int, y: Int): BitPat = macro SourceInfoTransform.xyArg
def === (that: UInt): Bool = macro SourceInfoTransform.thatArg
def =/= (that: UInt): Bool = macro SourceInfoTransform.thatArg
def ## (that: BitPat): BitPat = macro SourceInfoTransform.thatArg
- override def equals(obj: Any): Boolean = {
- obj match {
- case y: BitPat => value == y.value && mask == y.mask && getWidth == y.getWidth
- case _ => false
- }
- }
/** @group SourceInfoTransformMacro */
def do_apply(x: Int)(implicit sourceInfo: SourceInfo, compileOptions: CompileOptions): BitPat = {
@@ -161,14 +280,83 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, width: Int) extends Sou
new BitPat((value << that.getWidth) + that.value, (mask << that.getWidth) + that.mask, this.width + that.getWidth)
}
- /** Generate raw string of a BitPat. */
- def rawString: String = Seq.tabulate(width) { i =>
+ /** Check whether this [[BitPat]] overlap with that [[BitPat]], i.e. !(intersect.isEmpty)
+ *
+ * @param that [[BitPat]] to be checked.
+ * @return true if this and that [[BitPat]] have overlap.
+ */
+ def overlap(that: BitPat): Boolean = ((mask & that.mask) & (value ^ that.value)) == 0
+
+ /** Check whether this [[BitSet]] covers that (i.e. forall b matches that, b also matches this)
+ *
+ * @param that [[BitPat]] to be covered
+ * @return true if this [[BitSet]] can cover that [[BitSet]]
+ */
+ def cover(that: BitPat): Boolean = (mask & (~that.mask | (value ^ that.value))) == 0
+
+ /** Intersect `this` and `that` [[BitPat]].
+ *
+ * @param that [[BitPat]] to be intersected.
+ * @return a [[BitSet]] containing all elements of `this` that also belong to `that`.
+ */
+ def intersect(that: BitPat): BitSet = {
+ if (!overlap(that)) {
+ BitSet.empty
+ } else {
+ new BitPat(this.value | that.value, this.mask | that.mask, this.width.max(that.width))
+ }
+ }
+
+ /** Subtract a [[BitPat]] from this.
+ *
+ * @param that subtrahend [[BitPat]].
+ * @return a [[BitSet]] containing elements of `this` which are not the elements of `that`.
+ */
+ def subtract(that: BitPat): BitSet = {
+ require(width == that.width)
+ def enumerateBits(mask: BigInt): Seq[BigInt] = {
+ if (mask == 0) {
+ Nil
+ } else {
+ // bits comes after the first '1' in a number are inverted in its two's complement.
+ // therefore bit is always the first '1' in x (counting from least significant bit).
+ val bit = mask & (-mask)
+ bit +: enumerateBits(mask & ~bit)
+ }
+ }
+
+ val intersection = intersect(that)
+ val omask = this.mask
+ if (intersection.isEmpty) {
+ this
+ } else {
+ new BitSet {
+ val terms =
+ intersection.terms.flatMap { remove =>
+ enumerateBits(~omask & remove.mask).map { bit =>
+ // Only care about higher than current bit in remove
+ val nmask = (omask | ~(bit - 1)) & remove.mask
+ val nvalue = (remove.value ^ bit) & nmask
+ val nwidth = remove.width
+ new BitPat(nvalue, nmask, nwidth)
+ }
+ }
+ }
+ }
+ }
+
+ override def isEmpty: Boolean = false
+
+ /** Generate raw string of a [[BitPat]]. */
+ def rawString: String = Seq
+ .tabulate(width) { i =>
(value.testBit(width - i - 1), mask.testBit(width - i - 1)) match {
- case (true, true) => "1"
- case (false, true) => "0"
- case (_, false) => "?"
+ case (true, true) => "1"
+ case (false, true) => "0"
+ case (_, false) => "?"
+ }
}
- }.mkString
+ .mkString
override def toString = s"BitPat($rawString)"
}
diff --git a/src/main/scala/chisel3/util/Decoupled.scala b/src/main/scala/chisel3/util/Decoupled.scala
index 8909ffe3..4b8b3eeb 100644
--- a/src/main/scala/chisel3/util/Decoupled.scala
+++ b/src/main/scala/chisel3/util/Decoupled.scala
@@ -16,6 +16,7 @@ import chisel3.internal.naming._ // can't use chisel3_ version because of compi
* while the consumer uses the flipped interface (inputs bits).
* The actual semantics of ready/valid are enforced via the use of concrete subclasses.
* @param gen the type of data to be wrapped in Ready/Valid
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
{
@@ -26,8 +27,19 @@ abstract class ReadyValidIO[+T <: Data](gen: T) extends Bundle
case _ => gen
}
+/** Indicates that the consumer is ready to accept the data this cycle
+ * @group Signals
+ */
val ready = Input(Bool())
+
+/** Indicates that the producer has put valid data in 'bits'
+ * @group Signals
+ */
val valid = Output(Bool())
+
+/** The data to be transferred when ready and valid are asserted at the same cycle
+ * @group Signals
+ */
val bits = Output(genType)
}
@@ -37,7 +49,10 @@ object ReadyValidIO {
/** Indicates if IO is both ready and valid
*/
- def fire(): Bool = target.ready && target.valid
+ def fire: Bool = target.ready && target.valid
+
+ @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5")
+ def fire(dummy: Int = 0): Bool = fire
/** Push dat onto the output bits of this interface to let the consumer know it has happened.
* @param dat the values to assign to bits.
@@ -82,9 +97,6 @@ object ReadyValidIO {
* @param gen the type of data to be wrapped in DecoupledIO
*/
class DecoupledIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
-{
- override def cloneType: this.type = new DecoupledIO(gen).asInstanceOf[this.type]
-}
/** This factory adds a decoupled handshaking protocol to a data bundle. */
object Decoupled
@@ -121,11 +133,9 @@ object Decoupled
* Additionally, once 'valid' is raised it will never be lowered until after
* 'ready' has also been raised.
* @param gen the type of data to be wrapped in IrrevocableIO
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class IrrevocableIO[+T <: Data](gen: T) extends ReadyValidIO[T](gen)
-{
- override def cloneType: this.type = new IrrevocableIO(gen).asInstanceOf[this.type]
-}
/** Factory adds an irrevocable handshaking protocol to a data bundle. */
object Irrevocable
@@ -164,6 +174,7 @@ object DeqIO {
* @param gen The type of data to queue
* @param entries The max number of entries in the queue.
* @param hasFlush A boolean for whether the generated Queue is flushable
+ * @groupdesc Signals The hardware fields of the Bundle
*/
class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boolean = false) extends Bundle
{ // See github.com/freechipsproject/chisel3/issues/765 for why gen is a private val and proposed replacement APIs.
@@ -172,13 +183,21 @@ class QueueIO[T <: Data](private val gen: T, val entries: Int, val hasFlush: Boo
* but internally, the queue implementation itself sits on the other side
* of the interface so uses the flipped instance.
*/
- /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped. */
+ /** I/O to enqueue data (client is producer, and Queue object is consumer), is [[Chisel.DecoupledIO]] flipped.
+ * @group Signals
+ */
val enq = Flipped(EnqIO(gen))
- /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]*/
+ /** I/O to dequeue data (client is consumer and Queue object is producer), is [[Chisel.DecoupledIO]]
+ * @group Signals
+ */
val deq = Flipped(DeqIO(gen))
- /** The current amount of data in the queue */
+ /** The current amount of data in the queue
+ * @group Signals
+ */
val count = Output(UInt(log2Ceil(entries + 1).W))
- /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)*/
+ /** When asserted, reset the enqueue and dequeue pointers, effectively flushing the queue (Optional IO for a flushable Queue)
+ * @group Signals
+ */
val flush = if (hasFlush) Some(Input(Bool())) else None
}
@@ -228,9 +247,9 @@ class Queue[T <: Data](val gen: T,
val ptr_match = enq_ptr.value === deq_ptr.value
val empty = ptr_match && !maybe_full
val full = ptr_match && maybe_full
- val do_enq = WireDefault(io.enq.fire())
- val do_deq = WireDefault(io.deq.fire())
- val flush = io.flush.getOrElse(false.B)
+ val do_enq = WireDefault(io.enq.fire)
+ val do_deq = WireDefault(io.deq.fire)
+ val flush = io.flush.getOrElse(false.B)
// when flush is high, empty the queue
// Semantically, any enqueues happen before the flush.
@@ -288,20 +307,26 @@ class Queue[T <: Data](val gen: T,
}
}
-/** Factory for a generic hardware queue.
- *
- * @param enq input (enqueue) interface to the queue, also determines width of queue elements
- * @param entries depth (number of elements) of the queue
- *
- * @return output (dequeue) interface from the queue
- *
- * @example {{{
- * consumer.io.in <> Queue(producer.io.out, 16)
- * }}}
- */
+/** Factory for a generic hardware queue. */
object Queue
{
- /** Create a queue and supply a DecoupledIO containing the product. */
+ /** Create a [[Queue]] and supply a [[DecoupledIO]] containing the product.
+ *
+ * @param enq input (enqueue) interface to the queue, also determines type of queue elements.
+ * @param entries depth (number of elements) of the queue
+ * @param pipe True if a single entry queue can run at full throughput (like a pipeline). The `ready` signals are
+ * combinationally coupled.
+ * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately).
+ * The `valid` signals are coupled.
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element.
+ * @param flush Optional [[Bool]] signal, if defined, the [[Queue.hasFlush]] will be true, and connect correspond
+ * signal to [[Queue]] instance.
+ * @return output (dequeue) interface from the queue.
+ *
+ * @example {{{
+ * consumer.io.in <> Queue(producer.io.out, 16)
+ * }}}
+ */
@chiselName
def apply[T <: Data](
enq: ReadyValidIO[T],
@@ -309,7 +334,7 @@ object Queue
pipe: Boolean = false,
flow: Boolean = false,
useSyncReadMem: Boolean = false,
- hasFlush: Boolean = false): DecoupledIO[T] = {
+ flush: Option[Bool] = None): DecoupledIO[T] = {
if (entries == 0) {
val deq = Wire(new DecoupledIO(chiselTypeOf(enq.bits)))
deq.valid := enq.valid
@@ -317,7 +342,8 @@ object Queue
enq.ready := deq.ready
deq
} else {
- val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, hasFlush))
+ val q = Module(new Queue(chiselTypeOf(enq.bits), entries, pipe, flow, useSyncReadMem, flush.isDefined))
+ q.io.flush.zip(flush).foreach(f => f._1 := f._2)
q.io.enq.valid := enq.valid // not using <> so that override is allowed
q.io.enq.bits := enq.bits
enq.ready := q.io.enq.ready
@@ -325,10 +351,25 @@ object Queue
}
}
- /** Create a queue and supply a IrrevocableIO containing the product.
- * Casting from Decoupled is safe here because we know the Queue has
- * Irrevocable semantics; we didn't want to change the return type of
- * apply() for backwards compatibility reasons.
+ /** Create a queue and supply a [[IrrevocableIO]] containing the product.
+ * Casting from [[DecoupledIO]] is safe here because we know the [[Queue]] has
+ * Irrevocable semantics.
+ * we didn't want to change the return type of apply() for backwards compatibility reasons.
+ *
+ * @param enq [[DecoupledIO]] signal to enqueue.
+ * @param entries The max number of entries in the queue
+ * @param pipe True if a single entry queue can run at full throughput (like a pipeline). The ''ready'' signals are
+ * combinationally coupled.
+ * @param flow True if the inputs can be consumed on the same cycle (the inputs "flow" through the queue immediately).
+ * The ''valid'' signals are coupled.
+ * @param useSyncReadMem True uses SyncReadMem instead of Mem as an internal memory element.
+ * @param flush Optional [[Bool]] signal, if defined, the [[Queue.hasFlush]] will be true, and connect correspond
+ * signal to [[Queue]] instance.
+ * @return a [[DecoupledIO]] signal which should connect to the dequeue signal.
+ *
+ * @example {{{
+ * consumer.io.in <> Queue(producer.io.out, 16)
+ * }}}
*/
@chiselName
def irrevocable[T <: Data](
@@ -336,8 +377,9 @@ object Queue
entries: Int = 2,
pipe: Boolean = false,
flow: Boolean = false,
- useSyncReadMem: Boolean = false): IrrevocableIO[T] = {
- val deq = apply(enq, entries, pipe, flow, useSyncReadMem)
+ useSyncReadMem: Boolean = false,
+ flush: Option[Bool] = None): IrrevocableIO[T] = {
+ val deq = apply(enq, entries, pipe, flow, useSyncReadMem, flush)
require(entries > 0, "Zero-entry queues don't guarantee Irrevocability")
val irr = Wire(new IrrevocableIO(chiselTypeOf(deq.bits)))
irr.bits := deq.bits
diff --git a/src/main/scala/chisel3/util/Enum.scala b/src/main/scala/chisel3/util/Enum.scala
index bf150464..4501a2de 100644
--- a/src/main/scala/chisel3/util/Enum.scala
+++ b/src/main/scala/chisel3/util/Enum.scala
@@ -6,7 +6,6 @@
package chisel3.util
import chisel3._
-import chisel3.internal.chiselRuntimeDeprecated
/** Defines a set of unique UInt constants
*
diff --git a/src/main/scala/chisel3/util/Valid.scala b/src/main/scala/chisel3/util/Valid.scala
index 6c6d685e..5d80502a 100644
--- a/src/main/scala/chisel3/util/Valid.scala
+++ b/src/main/scala/chisel3/util/Valid.scala
@@ -17,20 +17,26 @@ import chisel3._
* @tparam T the type of the data
* @param gen some data
* @see [[Valid$ Valid factory]] for concrete examples
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class Valid[+T <: Data](gen: T) extends Bundle {
- /** A bit that will be asserted when `bits` is valid */
+ /** A bit that will be asserted when `bits` is valid
+ * @group Signals
+ */
val valid = Output(Bool())
- /** Some data */
+ /** The data to be transferred, qualified by `valid`
+ * @group Signals
+ */
val bits = Output(gen)
/** True when `valid` is asserted
* @return a Chisel [[Bool]] true if `valid` is asserted
*/
- def fire(dummy: Int = 0): Bool = valid
+ def fire: Bool = valid
- override def cloneType: this.type = Valid(gen).asInstanceOf[this.type]
+ @deprecated("Calling this function with an empty argument list is invalid in Scala 3. Use the form without parentheses instead", "Chisel 3.5")
+ def fire(dummy: Int = 0): Bool = valid
}
/** Factory for generating "valid" interfaces. A "valid" interface is a data-communicating interface between a producer
@@ -172,13 +178,18 @@ class Pipe[T <: Data](val gen: T, val latency: Int = 1)(implicit compileOptions:
/** Interface for [[Pipe]]s composed of a [[Valid]] input and [[Valid]] output
* @define notAQueue
+ * @groupdesc Signals Hardware fields of the Bundle
*/
class PipeIO extends Bundle {
- /** [[Valid]] input */
+ /** [[Valid]] input
+ * @group Signals
+ */
val enq = Input(Valid(gen))
- /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`. */
+ /** [[Valid]] output. Data will appear here `latency` cycles after being valid at `enq`.
+ * @group Signals
+ */
val deq = Output(Valid(gen))
}
diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
index 1d725875..4dcea99e 100644
--- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
@@ -7,11 +7,21 @@ import logger.LazyLogging
case object EspressoNotFoundException extends Exception
+/** A [[Minimizer]] implementation to use espresso to minimize the [[TruthTable]].
+ *
+ * espresso uses heuristic algorithm providing a sub-optimized) result.
+ * For implementation details, please refer to:
+ * [[https://www.springerprofessional.de/en/logic-minimization-algorithms-for-vlsi-synthesis/13780088]]
+ *
+ * a espresso executable should be downloaded from [[https://github.com/chipsalliance/espresso]]
+ *
+ * If user want to user the this [[Minimizer]], a espresso executable should be added to system PATH environment.
+ */
object EspressoMinimizer extends Minimizer with LazyLogging {
def minimize(table: TruthTable): TruthTable =
TruthTable.merge(TruthTable.split(table).map{case (table, indexes) => (espresso(table), indexes)})
- def espresso(table: TruthTable): TruthTable = {
+ private def espresso(table: TruthTable): TruthTable = {
def writeTable(table: TruthTable): String = {
def invert(string: String) = string
.replace('0', 't')
@@ -45,7 +55,7 @@ object EspressoMinimizer extends Minimizer with LazyLogging {
|""".stripMargin ++ (if (defaultType == '1') invertRawTable else rawTable)
}
- def readTable(espressoTable: String): Map[BitPat, BitPat] = {
+ def readTable(espressoTable: String) = {
def bitPat(espresso: String): BitPat = BitPat("b" + espresso.replace('-', '?'))
espressoTable
@@ -53,7 +63,6 @@ object EspressoMinimizer extends Minimizer with LazyLogging {
.filterNot(_.startsWith("."))
.map(_.split(' '))
.map(row => bitPat(row(0)) -> bitPat(row(1)))
- .toMap
}
val input = writeTable(table)
diff --git a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
index c1533f44..59120221 100644
--- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
@@ -8,6 +8,15 @@ import scala.annotation.tailrec
import scala.math.Ordered.orderingToOrdered
import scala.language.implicitConversions
+/** A [[Minimizer]] implementation to use Quine-Mccluskey algorithm to minimize the [[TruthTable]].
+ *
+ * This algorithm can always find the best solution, but is a NP-Complete algorithm,
+ * which means, for large-scale [[TruthTable]] minimization task, it will be really slow,
+ * and might run out of memory of JVM stack.
+ *
+ * In this situation, users should consider switch to [[EspressoMinimizer]],
+ * which uses heuristic algorithm providing a sub-optimized result.
+ */
object QMCMinimizer extends Minimizer {
private implicit def toImplicant(x: BitPat): Implicant = new Implicant(x)
@@ -225,11 +234,11 @@ object QMCMinimizer extends Minimizer {
val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i)
// Minterms, implicants that makes the output to be 1
- val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.map(_._1).map(toImplicant)
// Maxterms, implicants that makes the output to be 0
- val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.keys.map(toImplicant).toSeq
+ val maxt: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && !t.value.testBit(i) }.map(_._1).map(toImplicant)
// Don't cares, implicants that can produce either 0 or 1 as output
- val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.keys.map(toImplicant).toSeq
+ val dc: Seq[Implicant] = table.table.filter { case (_, t) => !t.mask.testBit(i) }.map(_._1).map(toImplicant)
val (implicants, defaultToDc) = table.default match {
case x if x.mask.testBit(i) && !x.value.testBit(i) => // default to 0
@@ -282,7 +291,7 @@ object QMCMinimizer extends Minimizer {
(essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp))
})
- minimized.tail.foldLeft(table.copy(table = Map(minimized.head))) { case (tb, t) =>
+ minimized.tail.foldLeft(table.copy(table = Seq(minimized.head))) { case (tb, t) =>
if (tb.table.exists(x => x._1 == t._1)) {
tb.copy(table = tb.table.map { case (k, v) =>
if (k == t._1) {
@@ -291,7 +300,7 @@ object QMCMinimizer extends Minimizer {
} else (k, v)
})
} else {
- tb.copy(table = tb.table + t)
+ tb.copy(table = tb.table :+ t)
}
}
}
diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
index 683de16b..322466f9 100644
--- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
@@ -4,8 +4,7 @@ package chisel3.util.experimental.decode
import chisel3.util.BitPat
-final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) {
-
+sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default: BitPat, val sort: Boolean) {
def inputWidth = table.head._1.getWidth
def outputWidth = table.head._2.getWidth
@@ -14,10 +13,10 @@ final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) {
def writeRow(map: (BitPat, BitPat)): String =
s"${map._1.rawString}->${map._2.rawString}"
- (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).toSeq.sorted.mkString("\n")
+ (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).mkString("\n")
}
- def copy(table: Map[BitPat, BitPat] = this.table, default: BitPat = this.default) = new TruthTable(table, default)
+ def copy(table: Seq[(BitPat, BitPat)] = this.table, default: BitPat = this.default, sort: Boolean = this.sort) = TruthTable(table, default, sort)
override def equals(y: Any): Boolean = {
y match {
@@ -28,25 +27,12 @@ final class TruthTable(val table: Map[BitPat, BitPat], val default: BitPat) {
}
object TruthTable {
- /** Parse TruthTable from its string representation. */
- def apply(tableString: String): TruthTable = {
- TruthTable(
- tableString
- .split("\n")
- .filter(_.contains("->"))
- .map(_.split("->").map(str => BitPat(s"b$str")))
- .map(bps => bps(0) -> bps(1))
- .toSeq,
- BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}")
- )
- }
-
/** Convert a table and default output into a [[TruthTable]]. */
- def apply(table: Iterable[(BitPat, BitPat)], default: BitPat): TruthTable = {
+ def apply(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): TruthTable = {
require(table.map(_._1.getWidth).toSet.size == 1, "input width not equal.")
require(table.map(_._2.getWidth).toSet.size == 1, "output width not equal.")
val outputWidth = table.map(_._2.getWidth).head
- new TruthTable(table.toSeq.groupBy(_._1.toString).map { case (key, values) =>
+ val mergedTable = table.groupBy(_._1.toString).map { case (key, values) =>
// merge same input inputs.
values.head._1 -> BitPat(s"b${
Seq.tabulate(outputWidth) { i =>
@@ -59,9 +45,23 @@ object TruthTable {
outputSet.headOption.getOrElse('?')
}.mkString
}")
- }, default)
+ }.toSeq
+ import BitPat.bitPatOrder
+ new TruthTable(if(sort) mergedTable.sorted else mergedTable, default, sort)
}
+ /** Parse TruthTable from its string representation. */
+ def fromString(tableString: String): TruthTable = {
+ TruthTable(
+ tableString
+ .split("\n")
+ .filter(_.contains("->"))
+ .map(_.split("->").map(str => BitPat(s"b$str")))
+ .map(bps => bps(0) -> bps(1))
+ .toSeq,
+ BitPat(s"b${tableString.split("\n").filterNot(_.contains("->")).head.replace(" ", "")}")
+ )
+ }
/** consume 1 table, split it into up to 3 tables with the same default bits.
*
@@ -99,18 +99,17 @@ object TruthTable {
tables: Seq[(TruthTable, Seq[Int])]
): TruthTable = {
def reIndex(bitPat: BitPat, table: TruthTable, indexes: Seq[Int]): Seq[(Char, Int)] =
- (table.table.map(a => a._1.toString -> a._2).getOrElse(bitPat.toString, BitPat.dontCare(indexes.size))).rawString.zip(indexes)
+ table.table.map(a => a._1.toString -> a._2).collectFirst{ case (k, v) if k == bitPat.toString => v}.getOrElse(BitPat.dontCare(indexes.size)).rawString.zip(indexes)
def bitPat(indexedChar: Seq[(Char, Int)]) = BitPat(s"b${indexedChar
.sortBy(_._2)
.map(_._1)
.mkString}")
TruthTable(
tables
- .flatMap(_._1.table.keys)
+ .flatMap(_._1.table.map(_._1))
.map { key =>
key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) })
- }
- .toMap,
+ },
bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) })
)
}
diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
index 42e374d1..e0bf83b2 100644
--- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
@@ -5,7 +5,7 @@ package chisel3.util.experimental.decode
import chisel3._
import chisel3.experimental.{ChiselAnnotation, annotate}
import chisel3.util.{BitPat, pla}
-import chisel3.util.experimental.getAnnotations
+import chisel3.util.experimental.{BitSet, getAnnotations}
import firrtl.annotations.Annotation
import logger.LazyLogging
@@ -19,7 +19,7 @@ object decoder extends LazyLogging {
*/
def apply(minimizer: Minimizer, input: UInt, truthTable: TruthTable): UInt = {
val minimizedTable = getAnnotations().collect {
- case DecodeTableAnnotation(_, in, out) => TruthTable(in) -> TruthTable(out)
+ case DecodeTableAnnotation(_, in, out) => TruthTable.fromString(in) -> TruthTable.fromString(out)
}.toMap.getOrElse(truthTable, minimizer.minimize(truthTable))
if (minimizedTable.table.isEmpty) {
val outputs = Wire(UInt(minimizedTable.default.getWidth.W))
@@ -80,4 +80,35 @@ object decoder extends LazyLogging {
qmcFallBack(input, truthTable)
}
}
+
+
+ /** Generate a decoder circuit that matches the input to each bitSet.
+ *
+ * The resulting circuit functions like the following but is optimized with a logic minifier.
+ * {{{
+ * when(input === bitSets(0)) { output := b000001 }
+ * .elsewhen (input === bitSets(1)) { output := b000010 }
+ * ....
+ * .otherwise { if (errorBit) output := b100000 else output := DontCare }
+ * }}}
+ *
+ * @param input input to the decoder circuit, width should be equal to bitSets.width
+ * @param bitSets set of ports to be matched, all width should be the equal
+ * @param errorBit whether generate an additional decode error bit at MSB of output.
+ * @return decoded wire
+ */
+ def bitset(input: chisel3.UInt, bitSets: Seq[BitSet], errorBit: Boolean = false): chisel3.UInt =
+ chisel3.util.experimental.decode.decoder(
+ input,
+ chisel3.util.experimental.decode.TruthTable.fromString(
+ {
+ bitSets.zipWithIndex.flatMap {
+ case (bs, i) =>
+ bs.terms.map(bp =>
+ s"${bp.rawString}->${if (errorBit) "0"}${"0" * (bitSets.size - i - 1)}1${"0" * i}"
+ )
+ } ++ Seq(s"${if (errorBit) "1"}${"?" * bitSets.size}")
+ }.mkString("\n")
+ )
+ )
}
diff --git a/src/main/scala/chisel3/util/random/GaloisLFSR.scala b/src/main/scala/chisel3/util/random/GaloisLFSR.scala
index 0d407c87..68346e82 100644
--- a/src/main/scala/chisel3/util/random/GaloisLFSR.scala
+++ b/src/main/scala/chisel3/util/random/GaloisLFSR.scala
@@ -13,7 +13,7 @@ import chisel3._
*
* $seedExplanation
*
- * In the example below, a 4-bit LFSR Fibonacci LFSR is constructed. The tap points are defined as four and three
+ * In the example below, a 4-bit LFSR Galois LFSR is constructed. The tap points are defined as four and three
* (using LFSR convention of indexing from one). This results in the hardware configuration shown in the diagram.
*
* {{{
@@ -85,7 +85,7 @@ class MaxPeriodGaloisLFSR(width: Int, seed: Option[BigInt] = Some(1), reduction:
*/
object GaloisLFSR {
- /** Return a pseudorandom [[UInt]] generated from a [[FibonacciLFSR]].
+ /** Return a pseudorandom [[UInt]] generated from a [[GaloisLFSR]].
* $paramWidth
* $paramTaps
* $paramIncrement
diff --git a/src/main/scala/chisel3/util/random/PRNG.scala b/src/main/scala/chisel3/util/random/PRNG.scala
index d94b78e8..3a44385a 100644
--- a/src/main/scala/chisel3/util/random/PRNG.scala
+++ b/src/main/scala/chisel3/util/random/PRNG.scala
@@ -7,16 +7,23 @@ import chisel3.util.Valid
/** Pseudo Random Number Generators (PRNG) interface
* @param n the width of the LFSR
+ * @groupdesc Signals The actual hardware fields of the Bundle
*/
class PRNGIO(val n: Int) extends Bundle {
- /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state) */
+ /** A [[chisel3.util.Valid Valid]] interface that can be used to set the seed (internal PRNG state)
+ * @group Signals
+ */
val seed: Valid[Vec[Bool]] = Input(Valid(Vec(n, Bool())))
- /** When asserted, the PRNG will increment by one */
+ /** When asserted, the PRNG will increment by one
+ * @group Signals
+ */
val increment: Bool = Input(Bool())
- /** The current state of the PRNG */
+ /** The current state of the PRNG
+ * @group Signals
+ */
val out: Vec[Bool] = Output(Vec(n, Bool()))
}
@@ -62,7 +69,7 @@ abstract class PRNG(val width: Int, val seed: Option[BigInt], step: Int = 1, upd
state := nextState(state)
}
- when (io.seed.fire()) {
+ when (io.seed.fire) {
state := (if (updateSeed) { nextState(io.seed.bits) } else { io.seed.bits })
}