diff options
| author | Jack Koenig | 2022-01-10 10:39:52 -0800 |
|---|---|---|
| committer | Jack Koenig | 2022-01-10 15:53:55 -0800 |
| commit | 3131c0daad41dea78bede4517669e376c41a325a (patch) | |
| tree | 55baed78a6a01f80ff3952a08233ca553a19964f /src/main/scala/chisel3/util/experimental/decode | |
| parent | dd36f97a82746cec0b25b94651581fe799e24579 (diff) | |
Apply scalafmt
Command:
sbt scalafmtAll
Diffstat (limited to 'src/main/scala/chisel3/util/experimental/decode')
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") ) |
