summaryrefslogtreecommitdiff
path: root/src/main/scala/chisel3/util/experimental/decode
diff options
context:
space:
mode:
authorJack Koenig2022-01-10 10:39:52 -0800
committerJack Koenig2022-01-10 15:53:55 -0800
commit3131c0daad41dea78bede4517669e376c41a325a (patch)
tree55baed78a6a01f80ff3952a08233ca553a19964f /src/main/scala/chisel3/util/experimental/decode
parentdd36f97a82746cec0b25b94651581fe799e24579 (diff)
Apply scalafmt
Command: sbt scalafmtAll
Diffstat (limited to 'src/main/scala/chisel3/util/experimental/decode')
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala19
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/Minimizer.scala3
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala78
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/TruthTable.scala66
-rw-r--r--src/main/scala/chisel3/util/experimental/decode/decoder.scala15
5 files changed, 112 insertions, 69 deletions
diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
index 4dcea99e..de2f207b 100644
--- a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala
@@ -19,7 +19,7 @@ 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)})
+ TruthTable.merge(TruthTable.split(table).map { case (table, indexes) => (espresso(table), indexes) })
private def espresso(table: TruthTable): TruthTable = {
def writeTable(table: TruthTable): String = {
@@ -34,10 +34,9 @@ object EspressoMinimizer extends Minimizer with LazyLogging {
}
val tableType: String = defaultType match {
case '?' => "fr"
- case _ => "fd"
+ case _ => "fd"
}
- val rawTable = table
- .toString
+ val rawTable = table.toString
.split("\n")
.filter(_.contains("->"))
.mkString("\n")
@@ -69,11 +68,13 @@ object EspressoMinimizer extends Minimizer with LazyLogging {
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
- }
+ 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)
diff --git a/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
index 86847710..c4065ac9 100644
--- a/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/Minimizer.scala
@@ -3,6 +3,7 @@
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.
*
@@ -26,4 +27,4 @@ abstract class Minimizer {
* }}}
*/
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
index 59120221..a3481869 100644
--- a/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/QMCMinimizer.scala
@@ -125,12 +125,15 @@ object QMCMinimizer extends Minimizer {
* 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]) = {
+ 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)) {
+ 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
@@ -165,6 +168,7 @@ object QMCMinimizer extends Minimizer {
* @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
@@ -193,7 +197,8 @@ object QMCMinimizer extends Minimizer {
* @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))
+ 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(_ < _))
}
@@ -216,8 +221,14 @@ object QMCMinimizer extends Minimizer {
// 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")
+ 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)
@@ -234,9 +245,11 @@ object QMCMinimizer extends Minimizer {
val outputBp = BitPat("b" + "?" * (m - i - 1) + "1" + "?" * i)
// Minterms, implicants that makes the output to be 1
- val mint: Seq[Implicant] = table.table.filter { case (_, t) => t.mask.testBit(i) && t.value.testBit(i) }.map(_._1).map(toImplicant)
+ 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) }.map(_._1).map(toImplicant)
+ 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) }.map(_._1).map(toImplicant)
@@ -251,16 +264,14 @@ object QMCMinimizer extends Minimizer {
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):_*)
- )
- )
+ 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))
+ 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) {
@@ -268,14 +279,14 @@ object QMCMinimizer extends Minimizer {
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
+ 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
+ if (!maxt.exists(_.intersects(t))) mergeTable(i + 1)(j) += a.merge(t)
}
}
}
@@ -288,20 +299,29 @@ object QMCMinimizer extends Minimizer {
val (essentialPrimeImplicants, nonessentialPrimeImplicants, uncoveredImplicants) =
getEssentialPrimeImplicants(primeImplicants, implicants)
- (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a => (a.bp, outputBp))
+ (essentialPrimeImplicants ++ getCover(nonessentialPrimeImplicants, uncoveredImplicants)).map(a =>
+ (a.bp, outputBp)
+ )
})
- 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) {
- 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)
- }
+ 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) {
+ 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
index 322466f9..e742fd66 100644
--- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
@@ -13,10 +13,11 @@ sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default:
def writeRow(map: (BitPat, BitPat)): String =
s"${map._1.rawString}->${map._2.rawString}"
- (table.map(writeRow) ++ Seq(s"${" "*(inputWidth + 2)}${default.rawString}")).mkString("\n")
+ (table.map(writeRow) ++ Seq(s"${" " * (inputWidth + 2)}${default.rawString}")).mkString("\n")
}
- def copy(table: Seq[(BitPat, BitPat)] = this.table, default: BitPat = this.default, sort: Boolean = this.sort) = TruthTable(table, default, sort)
+ 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 {
@@ -27,27 +28,36 @@ 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 = {
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
- val mergedTable = table.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
+ val mergedTable = table
+ .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
import BitPat.bitPatOrder
- new TruthTable(if(sort) mergedTable.sorted else mergedTable, default, sort)
+ new TruthTable(if (sort) mergedTable.sorted else mergedTable, default, sort)
}
/** Parse TruthTable from its string representation. */
@@ -77,10 +87,17 @@ object TruthTable {
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
+ 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] =
@@ -99,7 +116,12 @@ 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).collectFirst{ case (k, v) if k == bitPat.toString => v}.getOrElse(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)
diff --git a/src/main/scala/chisel3/util/experimental/decode/decoder.scala b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
index e0bf83b2..4feda672 100644
--- a/src/main/scala/chisel3/util/experimental/decode/decoder.scala
+++ b/src/main/scala/chisel3/util/experimental/decode/decoder.scala
@@ -3,13 +3,14 @@
package chisel3.util.experimental.decode
import chisel3._
-import chisel3.experimental.{ChiselAnnotation, annotate}
-import chisel3.util.{BitPat, pla}
-import chisel3.util.experimental.{BitSet, getAnnotations}
+import chisel3.experimental.{annotate, ChiselAnnotation}
+import chisel3.util.{pla, BitPat}
+import chisel3.util.experimental.{getAnnotations, BitSet}
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]].
@@ -71,7 +72,8 @@ object decoder extends LazyLogging {
qmc(input, truthTable)
}
- try espresso(input, truthTable) catch {
+ 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)
@@ -81,7 +83,6 @@ object decoder extends LazyLogging {
}
}
-
/** 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.
@@ -104,9 +105,7 @@ object decoder extends LazyLogging {
{
bitSets.zipWithIndex.flatMap {
case (bs, i) =>
- bs.terms.map(bp =>
- s"${bp.rawString}->${if (errorBit) "0"}${"0" * (bitSets.size - i - 1)}1${"0" * 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")
)