Skip to content

Commit

Permalink
Merge pull request #3993 from chipsalliance/optimize-truth-table-merge
Browse files Browse the repository at this point in the history
Optimize truth table merge
  • Loading branch information
jackkoenig authored Apr 12, 2024
2 parents d584dd7 + bb32df4 commit fcf5770
Show file tree
Hide file tree
Showing 5 changed files with 174 additions and 73 deletions.
12 changes: 12 additions & 0 deletions src/main/scala/chisel3/util/BitPat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -437,6 +437,18 @@ sealed class BitPat(val value: BigInt, val mask: BigInt, val width: Int)
}
}

/** Are any bits of this BitPat `?` */
private[chisel3] def hasDontCares: Boolean = width > 0 && mask != ((BigInt(1) << width) - 1)

/** Are all bits of this BitPat `0` */
private[chisel3] def allZeros: Boolean = value == 0 && !hasDontCares

/** Are all bits of this BitPat `1` */
private[chisel3] def allOnes: Boolean = !hasDontCares && value == mask

/** Are all bits of this BitPat `?` */
private[chisel3] def allDontCares: Boolean = mask == 0

override def isEmpty: Boolean = false

/** Generate raw string of a `BitPat`. */
Expand Down
29 changes: 20 additions & 9 deletions src/main/scala/chisel3/util/experimental/decode/TruthTable.scala
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,13 @@ object TruthTable {
def index(bitPat: BitPat, bpType: Char): Seq[Int] =
bitPat.rawString.zipWithIndex.filter(_._1 == bpType).map(_._2)

Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t)))
// We need to split if the default has a mix of values (no need to split if all ones, all zeros, or all ?)
val needToSplit = !(table.default.allDontCares || table.default.allZeros || table.default.allOnes)
if (needToSplit) {
Seq('1', '0', '?').flatMap(t => tableFilter(index(table.default, t)))
} else {
Seq(table -> (0 until table.default.width))
}
}

/** consume tables, merge it into single table with different default bits.
Expand All @@ -185,13 +191,18 @@ object TruthTable {
.sortBy(_._2)
.map(_._1)
.mkString}")
TruthTable(
tables
.flatMap(_._1.table.map(_._1))
.map { key =>
key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) })
},
bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) })
)
val needToMerge = tables.size > 1
if (needToMerge) {
TruthTable(
tables
.flatMap(_._1.table.map(_._1))
.map { key =>
key -> bitPat(tables.flatMap { case (table, indexes) => reIndex(key, table, indexes) })
},
bitPat(tables.flatMap { case (table, indexes) => table.default.rawString.zip(indexes) })
)
} else {
tables.head._1
}
}
}
121 changes: 121 additions & 0 deletions src/test/scala/chisel3/util/BitPatSpec.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// SPDX-License-Identifier: Apache-2.0

package chisel3.util

import chisel3._
import chisel3.util.BitPat
import _root_.circt.stage.ChiselStage
import org.scalatest.flatspec.AnyFlatSpec
import org.scalatest.matchers.should.Matchers

class BitPatSpec extends AnyFlatSpec with Matchers {
behavior.of(classOf[BitPat].toString)

it should "convert a BitPat to readable form" in {
val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
BitPat("b" + testPattern).toString should be(s"BitPat($testPattern)")
}

it should "convert a BitPat to raw form" in {
val testPattern = "0" * 32 + "1" * 32 + "?" * 32 + "?01" * 32
BitPat("b" + testPattern).rawString should be(testPattern)
}

it should "not fail if BitPat width is 0" in {
intercept[IllegalArgumentException] { BitPat("b") }
}

it should "concat BitPat via ##" in {
(BitPat.Y(4) ## BitPat.dontCare(3) ## BitPat.N(2)).toString should be(s"BitPat(1111???00)")
}

it should "throw when BitPat apply to a Hardware" in {
intercept[java.lang.IllegalArgumentException] {
ChiselStage.emitCHIRRTL(new chisel3.Module {
BitPat(chisel3.Reg(chisel3.Bool()))
})
}
}

it should "index and return new BitPat" in {
val b = BitPat("b1001???")
b(0) should be(BitPat.dontCare(1))
b(6) should be(BitPat.Y())
b(5) should be(BitPat.N())
}

it should "slice and return new BitPat" in {
val b = BitPat("b1001???")
b(2, 0) should be(BitPat("b???"))
b(4, 3) should be(BitPat("b01"))
b(6, 6) should be(BitPat("b1"))
}

it should "parse UInt literals correctly" in {
BitPat(0.U) should be(new BitPat(0, 1, 1))
// Note that this parses as 1-bit width, there are other APIs that don't support zero-width UInts correctly
BitPat(0.U(0.W)) should be(new BitPat(0, 1, 1))
BitPat(1.U) should be(new BitPat(1, 1, 1))
BitPat(2.U) should be(new BitPat(2, 3, 2))
BitPat(0xdeadbeefL.U) should be(new BitPat(BigInt("deadbeef", 16), BigInt("ffffffff", 16), 32))
}

it should "support .hasDontCares" in {
BitPat("b?").hasDontCares should be(true)
BitPat("b??").hasDontCares should be(true)
BitPat("b0?").hasDontCares should be(true)
BitPat("b?1").hasDontCares should be(true)
BitPat("b0").hasDontCares should be(false)
BitPat("b10").hasDontCares should be(false)
BitPat("b01").hasDontCares should be(false)
BitPat(0xdeadbeefL.U).hasDontCares should be(false)
// Zero-width not supported yet
intercept[IllegalArgumentException] { BitPat("b").hasDontCares should be(false) }
}

it should "support .allZeros" in {
BitPat("b?").allZeros should be(false)
BitPat("b??").allZeros should be(false)
BitPat("b0?").allZeros should be(false)
BitPat("b?1").allZeros should be(false)
BitPat("b0").allZeros should be(true)
BitPat("b10").allZeros should be(false)
BitPat("b01").allZeros should be(false)
BitPat(0.U(128.W)).allZeros should be(true)
BitPat.N(23).allZeros should be(true)
BitPat(0xdeadbeefL.U).allZeros should be(false)
// Zero-width not supported yet
intercept[IllegalArgumentException] { BitPat("b").allZeros should be(true) }
}

it should "support .allOnes" in {
BitPat("b?").allOnes should be(false)
BitPat("b??").allOnes should be(false)
BitPat("b0?").allOnes should be(false)
BitPat("b?1").allOnes should be(false)
BitPat("b0").allOnes should be(false)
BitPat("b10").allOnes should be(false)
BitPat("b01").allOnes should be(false)
BitPat("b1").allOnes should be(true)
BitPat("b" + ("1" * 128)).allOnes should be(true)
BitPat.Y(23).allOnes should be(true)
BitPat(0xdeadbeefL.U).allOnes should be(false)
// Zero-width not supported yet
intercept[IllegalArgumentException] { BitPat("b").allOnes should be(true) }
}

it should "support .allDontCares" in {
BitPat("b?").allDontCares should be(true)
BitPat("b??").allDontCares should be(true)
BitPat("b0?").allDontCares should be(false)
BitPat("b?1").allDontCares should be(false)
BitPat("b0").allDontCares should be(false)
BitPat("b10").allDontCares should be(false)
BitPat("b1").allDontCares should be(false)
BitPat("b" + ("1" * 128)).allDontCares should be(false)
BitPat.dontCare(23).allDontCares should be(true)
BitPat(0xdeadbeefL.U).allDontCares should be(false)
// Zero-width not supported yet
intercept[IllegalArgumentException] { BitPat("b").allDontCares should be(true) }
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: Apache-2.0

package chiselTests.util.experimental
package chisel3.util.experimental.decode

import chisel3._
import chisel3.util.BitPat
Expand All @@ -9,7 +9,7 @@ import org.scalatest.flatspec.AnyFlatSpec

class TruthTableSpec extends AnyFlatSpec {
val table = TruthTable(
Map(
Seq(
// BitPat("b000") -> BitPat("b0"),
BitPat("b001") -> BitPat("b?"),
BitPat("b010") -> BitPat("b?"),
Expand Down Expand Up @@ -123,4 +123,23 @@ class TruthTableSpec extends AnyFlatSpec {
|""".stripMargin)
)
}

behavior.of("TruthTable.split")

it should "not change the TruthTable when the default only uses a single value" in {
val result = TruthTable.split(table)
assert(result == Seq(table -> Seq(0)))
// As an optimization, it should return the same literal object
assert(result.head._1 eq table)
}

behavior.of("TruthTable.merge")

it should "not change the TruthTable when merging a single one" in {
val tables = Seq(table -> Seq(0))
val result = TruthTable.merge(tables)
assert(result == table)
// As an optimization, it should return the same literal object
assert(result eq table)
}
}
62 changes: 0 additions & 62 deletions src/test/scala/chiselTests/util/BitPatSpec.scala

This file was deleted.

0 comments on commit fcf5770

Please sign in to comment.