summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util/experimental/decode
diff options
context:
space:
mode:
authorJack2022-07-30 22:41:15 +0000
committerJack2022-07-30 22:41:15 +0000
commit4cd44fa4dab370fcc5c20bcacc1fa0ee02327252 (patch)
tree05730be260feca0d2a870c4bb88325d36631a8fc /src/main/scala/chisel3/util/experimental/decode
parentfe9635ef21bad233945617a24ab16cfa4055f2d1 (diff)
parentbced77045c8fc5db37e40b159c49220929e15d46 (diff)
Merge branch '3.5.x' into 3.5-release
Diffstat (limited to 'src/main/scala/chisel3/util/experimental/decode')
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala2
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/TruthTable.scala117
2 files changed, 90 insertions, 29 deletions
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
+ }
}