Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BitSetRange API #2449

Merged
merged 12 commits into from
May 3, 2022
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package chiselTests.util.experimental

import chisel3._
import chisel3.util.experimental.BitSetRange
import chiseltest._
import chiseltest.formal._
import scala.util.Random
import org.scalatest.flatspec.AnyFlatSpec

class BitSetRangeTestModule(start: BigInt, length: BigInt, width: Int) extends Module {
val input = IO(Input(UInt(width.W)))

val range = BitSetRange(start, length, width, true)
val testee = range.matches(input)
val ref = input >= start.U && input < (start + length).U

assert(testee === ref)
}

class BitSetRangeTest extends AnyFlatSpec with ChiselScalatestTester with Formal {
val rng = new Random(0x19260817)
val cases = 128
"BitSetRange" should "be identical as comparesions" in {
for(i <- 1 to cases) {
val a = rng.nextLong(Long.MaxValue)
val b = rng.nextLong(Long.MaxValue)
val start = a.min(b)
val len = a.max(b) - start + 1
verify(new BitSetRangeTestModule(start, len, 64), Seq(BoundedCheck(1)))
}
}
}
48 changes: 48 additions & 0 deletions src/main/scala/chisel3/util/BitPat.scala
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package chisel3.util
import scala.language.experimental.macros
import chisel3._
import chisel3.internal.sourceinfo.{SourceInfo, SourceInfoTransform}
import scala.collection.mutable

object BitPat {

Expand Down Expand Up @@ -165,6 +166,8 @@ package experimental {
/** whether this [[BitSet]] is empty (i.e. no value matches) */
def isEmpty: Boolean = terms.forall(_.isEmpty)

def matches(input: UInt) = VecInit(terms.map(_ === input).toSeq).asUInt.orR

/** Check whether this [[BitSet]] overlap with that [[BitSet]], i.e. !(intersect.isEmpty)
*
* @param that [[BitSet]] to be checked.
Expand Down Expand Up @@ -226,6 +229,51 @@ package experimental {
}
}

sealed class BitSetRange(val start: BigInt, val length: BigInt, val width: Int) extends BitSet { outer =>
override def terms = {
val collected = mutable.Set[BitPat]();
var ptr = start;
var left = length;
while (left > 0) {
var cur_pow = left.bitLength - 1
if (ptr != 0) {
val max_pow = ptr.lowestSetBit
if (max_pow < cur_pow) cur_pow = max_pow
}

val inc = BigInt(1) << cur_pow
assert((ptr & inc - 1) == 0)
val mask = (BigInt(1) << width) - inc
collected.add(new BitPat(ptr, mask, width))
ptr += inc
left -= inc
}

collected.toSet
}

// TODO: width!
override def toString: String = s"BitSetRange(0x${start.toString(16)} - 0x${(start + length).toString(16)})"
}

object BitSetRange {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs some ScalaDoc to explain the BitSetRange

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I'll add them after the API goes through discussion.

def apply(
start: BigInt,
length: BigInt,
width: Int = -1,
allowUnaligned: Boolean = false
): BitSetRange = {
require(length > 0, "Cannot construct a empty BitSetRange")
val max_known_length = (start + length - 1).bitLength
val w = if (width >= 0) width else max_known_length
require(w >= max_known_length, "Cannot construct a BitSetRange with width smaller than its range end")
val ret = new BitSetRange(start, length, w)

if (!allowUnaligned) require(ret.terms.size == 1, "Unaligned BitSetRange")

ret
}
}
}

/** Bit patterns are literals with masks, used to represent values with don't
Expand Down