From 7c88c03acdee4217563da7c0793186ab966a3c27 Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Thu, 30 Jun 2022 18:01:55 -0700 Subject: [PATCH 01/10] Refactor TruthTable.apply and add factory method for Espresso --- src/main/scala/chisel3/util/BitPat.scala | 4 + .../decode/EspressoMinimizer.scala | 2 +- .../util/experimental/decode/TruthTable.scala | 125 ++++++++++++++---- 3 files changed, 101 insertions(+), 30 deletions(-) diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 76ebf023492..72c042afaed 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -6,6 +6,7 @@ import scala.language.experimental.macros import chisel3._ import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} import scala.collection.mutable +import scala.util.hashing.MurmurHash3.seqHash object BitPat { @@ -326,6 +327,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 = + 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) diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 86973e5b593..52912aebd73 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(readTable(output), table.default, true) } } diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 00fa0f9cf01..632d3989032 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 firrtl.Utils.groupByIntoSeq sealed class TruthTable private (val table: Seq[(BitPat, BitPat)], val default: BitPat, val sort: Boolean) { def inputWidth = table.head._1.getWidth @@ -29,40 +30,106 @@ 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 - import BitPat.bitPatOrder - new TruthTable(if (sort) mergedTable.sorted else mergedTable, default, sort) + } + + /** For each duplicated input, combine 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 combined 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 { + case (_, output) => output + } + } + } + + /** Check for BitPats that have non-zero overlap + * + * Non-zero overlap means that for two BitPats a and b, there is at least + * one bitpos where the indices at the bitpos for the two bits are + * present in [(0, 1), (1, 0)]. + * + * @param x List of BitPats to check for overlap + * @return true if the overlap is non-zero, false otherwise + */ + private def checkDups(x: BitPat*): Boolean = { + if (x.size > 1) { + val f = (a: BitPat, b: BitPat) => a.overlap(b) + val combs: Seq[Boolean] = x.combinations(2).map { a => f(a.head, a.last) }.toSeq + combs.reduce(_ | _) + } else { + false + } + } + + /** 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". The is the same as the fd (or f) type in espresso. + * + * @param table the truth table + * @param default the default BitPat. This also determines the format sent + * to Espresso + * @param sort whether to sort the final truth table or not + */ + def fromEspressoOutput(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = false): TruthTable = { + apply_impl(table, default, sort, false) + } + + 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 = true, + espressoFDFormat: Boolean = false + ): 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( + x => + ({ + if (espressoFDFormat) + (x._1, x._2.reduce(merge(_, _))) + else { + require(checkDups(x._2: _*) == false, "TruthTable conflict") + (x._1, x._2.head) + } + }) + ) + + new TruthTable(finalTable, default, sort) } /** Parse TruthTable from its string representation. */ From 304e3622c8077f56fdac0550dc98c96c5fa1648d Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Fri, 1 Jul 2022 14:02:12 -0700 Subject: [PATCH 02/10] Add sort, fix espresso and checkDups booleans --- .../util/experimental/decode/TruthTable.scala | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 632d3989032..19fe46e1075 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -55,9 +55,7 @@ object TruthTable { private def mergeTableOnInputs(table: Iterable[(BitPat, BitPat)]): Seq[(BitPat, Seq[BitPat])] = { groupByIntoSeq(table)(_._1).map { case (input, mappings) => - input -> mappings.map { - case (_, output) => output - } + input -> mappings.map(_._2) } } @@ -73,8 +71,13 @@ object TruthTable { private def checkDups(x: BitPat*): Boolean = { if (x.size > 1) { val f = (a: BitPat, b: BitPat) => a.overlap(b) - val combs: Seq[Boolean] = x.combinations(2).map { a => f(a.head, a.last) }.toSeq - combs.reduce(_ | _) + val combs: Seq[Boolean] = x + .combinations(2) + .map { a => + f(a.head, a.last) + } + .toSeq + !combs.reduce(_ | _) } else { false } @@ -97,11 +100,11 @@ object TruthTable { * @param sort whether to sort the final truth table or not */ def fromEspressoOutput(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = false): TruthTable = { - apply_impl(table, default, sort, false) + apply_impl(table, default, sort, true) } def apply(table: Iterable[(BitPat, BitPat)], default: BitPat, sort: Boolean = true): TruthTable = { - apply_impl(table, default, sort, true) + apply_impl(table, default, sort, false) } /** Convert a table and default output into a [[TruthTable]]. */ @@ -117,19 +120,17 @@ object TruthTable { val mergedTable = mergeTableOnInputs(paddedTable) - val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map( - x => - ({ - if (espressoFDFormat) - (x._1, x._2.reduce(merge(_, _))) - else { - require(checkDups(x._2: _*) == false, "TruthTable conflict") - (x._1, x._2.head) - } - }) - ) - - new TruthTable(finalTable, default, sort) + val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map(x => { + if (espressoFDFormat) + (x._1, x._2.reduce(merge(_, _))) + else { + require(checkDups(x._2: _*) == false, "TruthTable conflict") + (x._1, x._2.reduce(merge(_, _))) + } + }) + + import BitPat.bitPatOrder + new TruthTable(if (sort) finalTable.sorted else finalTable, default, sort) } /** Parse TruthTable from its string representation. */ From f795153588efccee8d310ff9637af391499e844d Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Tue, 5 Jul 2022 10:59:24 -0700 Subject: [PATCH 03/10] Code review updates Rename espressoFDFormat to checkCollisions and update its polarity. Update conflict condition to match new polarity/meaning --- src/main/scala/chisel3/util/BitPat.scala | 6 +- .../util/experimental/decode/TruthTable.scala | 70 +++++++------------ 2 files changed, 29 insertions(+), 47 deletions(-) diff --git a/src/main/scala/chisel3/util/BitPat.scala b/src/main/scala/chisel3/util/BitPat.scala index 72c042afaed..c0cfff2bd8d 100644 --- a/src/main/scala/chisel3/util/BitPat.scala +++ b/src/main/scala/chisel3/util/BitPat.scala @@ -6,7 +6,7 @@ import scala.language.experimental.macros import chisel3._ import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform} import scala.collection.mutable -import scala.util.hashing.MurmurHash3.seqHash +import scala.util.hashing.MurmurHash3 object BitPat { @@ -327,8 +327,8 @@ 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 = - seqHash(Seq(this.value, this.mask, this.width)) + 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 = { diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 19fe46e1075..115d675594e 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -45,11 +45,11 @@ object TruthTable { } } - /** For each duplicated input, combine the outputs into a single Seq. + /** 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 combined outputs + * 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])] = { @@ -59,30 +59,6 @@ object TruthTable { } } - /** Check for BitPats that have non-zero overlap - * - * Non-zero overlap means that for two BitPats a and b, there is at least - * one bitpos where the indices at the bitpos for the two bits are - * present in [(0, 1), (1, 0)]. - * - * @param x List of BitPats to check for overlap - * @return true if the overlap is non-zero, false otherwise - */ - private def checkDups(x: BitPat*): Boolean = { - if (x.size > 1) { - val f = (a: BitPat, b: BitPat) => a.overlap(b) - val combs: Seq[Boolean] = x - .combinations(2) - .map { a => - f(a.head, a.last) - } - .toSeq - !combs.reduce(_ | _) - } else { - false - } - } - /** Merge two BitPats by OR-ing the values and masks, and setting the * width to the max width among the two */ @@ -92,27 +68,32 @@ object TruthTable { /** 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". The is the same as the fd (or f) type in espresso. + * 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. This also determines the format sent - * to Espresso - * @param sort whether to sort the final truth table or not + * @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 = false): TruthTable = { - apply_impl(table, default, sort, true) + 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, false) + apply_impl(table, default, sort) } /** Convert a table and default output into a [[TruthTable]]. */ private def apply_impl( - table: Iterable[(BitPat, BitPat)], - default: BitPat, - sort: Boolean = true, - espressoFDFormat: Boolean = false + table: Iterable[(BitPat, BitPat)], + default: BitPat, + sort: Boolean = true, + checkCollisions: Boolean = true ): TruthTable = { val paddedTable = padInputs(table) @@ -120,14 +101,15 @@ object TruthTable { val mergedTable = mergeTableOnInputs(paddedTable) - val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map(x => { - if (espressoFDFormat) - (x._1, x._2.reduce(merge(_, _))) - else { - require(checkDups(x._2: _*) == false, "TruthTable conflict") - (x._1, x._2.reduce(merge(_, _))) - } - }) + val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map { + case (input, outputs) => + val (result, diffFound) = outputs.tail.foldLeft((outputs.head, checkCollisions)) { + case ((acc, err), o) => (merge(acc, o), err && acc.overlap(o)) + } + // Throw an error if checkCollisions is true but there are bits with a non-zero overlap. + require(!(checkCollisions && !diffFound), "TruthTable conflict") + (input, result) + } import BitPat.bitPatOrder new TruthTable(if (sort) finalTable.sorted else finalTable, default, sort) From 8bf637276b334630f48cdd9aa36b017cf8d574ec Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Tue, 5 Jul 2022 11:26:12 -0700 Subject: [PATCH 04/10] Fix misnomers and demorgan conflict logic --- .../scala/chisel3/util/experimental/decode/TruthTable.scala | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 115d675594e..5ca54216331 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -103,11 +103,11 @@ object TruthTable { val finalTable: Seq[(BitPat, BitPat)] = mergedTable.map { case (input, outputs) => - val (result, diffFound) = outputs.tail.foldLeft((outputs.head, checkCollisions)) { - case ((acc, err), o) => (merge(acc, o), err && acc.overlap(o)) + 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 && !diffFound), "TruthTable conflict") + require(!checkCollisions || noCollisions, "TruthTable conflict") (input, result) } From 305e4308fc55b2e717db551372a4601fe3bdbee5 Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Tue, 5 Jul 2022 11:56:49 -0700 Subject: [PATCH 05/10] Add test and update conflict error message --- .../chisel3/util/experimental/decode/TruthTable.scala | 2 +- .../chiselTests/util/experimental/TruthTableSpec.scala | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 5ca54216331..a2233bc0ea5 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -107,7 +107,7 @@ object TruthTable { 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, "TruthTable conflict") + require(!checkCollisions || noCollisions, s"TruthTable conflict on merged row: \n\t$input -> $outputs") (input, result) } diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala index fa2c6f08f9c..ea45e04cffd 100644 --- a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -104,4 +104,13 @@ class TruthTableSpec extends AnyFlatSpec { assert(t.toString contains "111->?") assert(t.toString contains " 0") } + + "Using TruthTable.fromEspressoOutput" should "merge rows on conflict" in { + val mapping = List( + (BitPat("b000?????1100011"), BitPat("b0100110")), + (BitPat("b001?????1100011"), BitPat("b0101110")), + (BitPat("b001?????1100011"), BitPat("b0100111")) + ) + assert(TruthTable.fromEspressoOutput(mapping, BitPat("b???????????")).toString contains "0101111") + } } From f4b3fdc766af89b2383fa700da7c6d4c91156820 Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Tue, 5 Jul 2022 17:04:12 -0700 Subject: [PATCH 06/10] Update test --- .../util/experimental/TruthTableSpec.scala | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala index ea45e04cffd..9b2dd60082c 100644 --- a/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala +++ b/src/test/scala/chiselTests/util/experimental/TruthTableSpec.scala @@ -107,10 +107,18 @@ class TruthTableSpec extends AnyFlatSpec { "Using TruthTable.fromEspressoOutput" should "merge rows on conflict" in { val mapping = List( - (BitPat("b000?????1100011"), BitPat("b0100110")), - (BitPat("b001?????1100011"), BitPat("b0101110")), - (BitPat("b001?????1100011"), BitPat("b0100111")) + (BitPat("b110"), BitPat("b001")), + (BitPat("b111"), BitPat("b001")), + (BitPat("b111"), BitPat("b010")), + (BitPat("b111"), BitPat("b100")) + ) + + assert( + TruthTable.fromEspressoOutput(mapping, BitPat("b?")) == + TruthTable.fromString("""110->001 + |111->111 + |? + |""".stripMargin) ) - assert(TruthTable.fromEspressoOutput(mapping, BitPat("b???????????")).toString contains "0101111") } } From 8d41a856f56355359e9616936f46465e57f5967e Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Wed, 6 Jul 2022 10:03:44 -0700 Subject: [PATCH 07/10] Fix default arguments and Espresso TruthTable call --- .../util/experimental/decode/EspressoMinimizer.scala | 2 +- .../chisel3/util/experimental/decode/TruthTable.scala | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala b/src/main/scala/chisel3/util/experimental/decode/EspressoMinimizer.scala index 52912aebd73..8c85b6d1941 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, true) + 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 a2233bc0ea5..8d3d34e4531 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -79,21 +79,21 @@ object TruthTable { * @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 = false): 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) + 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 = true, - checkCollisions: Boolean = true + sort: Boolean, + checkCollisions: Boolean ): TruthTable = { val paddedTable = padInputs(table) From 8aa627571868120f84485de716c8b25b45e18981 Mon Sep 17 00:00:00 2001 From: Aditya Naik Date: Wed, 6 Jul 2022 10:11:47 -0700 Subject: [PATCH 08/10] Add groupByIntoSeq from firrtl utils --- .../util/experimental/decode/TruthTable.scala | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 8d3d34e4531..f7517222f98 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -3,7 +3,7 @@ package chisel3.util.experimental.decode import chisel3.util.BitPat -import firrtl.Utils.groupByIntoSeq +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 @@ -190,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 + } } From 0ca71fbec4710bcc077dae4491dcacc9210d9bbb Mon Sep 17 00:00:00 2001 From: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com> Date: Wed, 6 Jul 2022 10:39:21 -0700 Subject: [PATCH 09/10] Update src/main/scala/chisel3/util/experimental/decode/TruthTable.scala Co-authored-by: Megan Wachs --- .../scala/chisel3/util/experimental/decode/TruthTable.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index f7517222f98..558724abef3 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -69,7 +69,7 @@ object TruthTable { /** 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". + * 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 From e8f82b0ca41b85dd5376ec5bee8d8171cac7229c Mon Sep 17 00:00:00 2001 From: Aditya Naik <91489422+adkian-sifive@users.noreply.github.com> Date: Wed, 6 Jul 2022 10:46:39 -0700 Subject: [PATCH 10/10] Update src/main/scala/chisel3/util/experimental/decode/TruthTable.scala Co-authored-by: Jack Koenig --- .../scala/chisel3/util/experimental/decode/TruthTable.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala index 558724abef3..2720e6906cd 100644 --- a/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala +++ b/src/main/scala/chisel3/util/experimental/decode/TruthTable.scala @@ -107,7 +107,7 @@ object TruthTable { 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\t$input -> $outputs") + require(!checkCollisions || noCollisions, s"TruthTable conflict on merged row:\n $input -> $outputs") (input, result) }