use sv_lang::{Bit, Compilation, ConstantValue, Design, Digit, LiteralBase, OwnedSVInt, Session};
use vj_abi::Bit4;
use super::{FourStateValue, four_state_bit_at};
fn bit_to_bit4(b: Bit) -> Bit4 {
match b {
Bit::Zero => Bit4::Zero,
Bit::One => Bit4::One,
Bit::X => Bit4::X,
Bit::Z => Bit4::Z,
}
}
pub fn bits_to_four_state(bits: &[Bit]) -> FourStateValue {
if bits.len() <= 64 {
let mut val: u64 = 0;
let mut unk: u64 = 0;
for (i, &b) in bits.iter().enumerate() {
let (v, u) = bit_to_bit4(b).to_val_unk();
if v {
val |= 1u64 << i;
}
if u {
unk |= 1u64 << i;
}
}
FourStateValue::Narrow((val as u128) | ((unk as u128) << 64))
} else {
let (aval, bval) = bits_to_aval_bval(bits);
FourStateValue::Wide { aval, bval }
}
}
pub fn packed_words(bits: &[Bit]) -> Vec<u64> {
let w = bits.len();
let mut out = vec![0u64; (2 * w).div_ceil(64).max(1)];
for (i, &b) in bits.iter().enumerate() {
let (v, u) = bit_to_bit4(b).to_val_unk();
if v {
out[i / 64] |= 1u64 << (i % 64);
}
if u {
out[(w + i) / 64] |= 1u64 << ((w + i) % 64);
}
}
out
}
pub fn bits_to_aval_bval(bits: &[Bit]) -> (Vec<u64>, Vec<u64>) {
let words = bits.len().div_ceil(64).max(1);
let mut aval = vec![0u64; words];
let mut bval = vec![0u64; words];
for (i, &b) in bits.iter().enumerate() {
let (a, bv) = bit_to_bit4(b).to_aval_bval();
let word = i / 64;
let bit = i % 64;
if a {
aval[word] |= 1u64 << bit;
}
if bv {
bval[word] |= 1u64 << bit;
}
}
(aval, bval)
}
fn single_bit_known(value: bool) -> OwnedSVInt {
OwnedSVInt::from_digits(
1,
LiteralBase::Binary,
false,
false,
&[Digit::Value(value as u8)],
)
.expect("a single known binary digit at width 1 always constructs")
}
pub fn four_state_to_owned_svint(v: &FourStateValue, width: u32, signed: bool) -> OwnedSVInt {
let mut out =
OwnedSVInt::create_fill_x(width, signed).expect("caller guarantees a nonzero width");
for i in 0..width {
match four_state_bit_at(v, i) {
Bit4::X => {}
Bit4::Zero => {
let zero = single_bit_known(false);
out.set_range(i as i32, i as i32, &zero)
.expect("single-bit range within [0,width) always matches a 1-bit value");
}
Bit4::One => {
let one = single_bit_known(true);
out.set_range(i as i32, i as i32, &one)
.expect("single-bit range within [0,width) always matches a 1-bit value");
}
Bit4::Z => {
let z = OwnedSVInt::create_fill_z(1, false)
.expect("a single-bit fill-z always constructs");
out.set_range(i as i32, i as i32, &z)
.expect("single-bit range within [0,width) always matches a 1-bit value");
}
}
}
out
}
pub fn four_state_of_constant(cv: &ConstantValue) -> Option<FourStateValue> {
Some(bits_to_four_state(&svint_bits(cv.as_integer()?)?))
}
pub fn constant_expr_bits(e: &sv_lang::Expression<'_>) -> Option<Vec<Bit>> {
Some(svint_bits(e.constant_value()?.as_integer()?)?.into_owned())
}
pub fn svint_bits(v: &sv_lang::SVInt) -> Option<std::borrow::Cow<'_, [Bit]>> {
(v.bits().len() == v.bit_width() as usize).then(|| std::borrow::Cow::Borrowed(v.bits()))
}
pub(crate) fn owned_svint_to_four_state(v: &OwnedSVInt) -> Option<FourStateValue> {
Some(bits_to_four_state(&svint_bits(&v.snapshot())?))
}
pub fn compile_source(src: &str) -> Result<Design, sv_lang::Error> {
let session = Session::new();
let mut comp = Compilation::new(&session)?;
comp.add_source(src)?;
comp.compile()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn bits_to_four_state_narrow_matches_bit4_table_for_zero_one_x_z() {
let bits = [Bit::Zero, Bit::One, Bit::X, Bit::Z];
let fv = bits_to_four_state(&bits);
let expected: u128 = 0xA | (0xCu128 << 64);
match fv {
FourStateValue::Narrow(w) => assert_eq!(w, expected),
FourStateValue::Wide { .. } => panic!("expected Narrow for 4 bits"),
}
}
#[test]
fn bits_to_four_state_wide_uses_bit4_aval_bval_table() {
let bits = [Bit::X; 65];
let fv = bits_to_four_state(&bits);
match fv {
FourStateValue::Wide { aval, bval } => {
assert_eq!(aval.len(), 2);
assert_eq!(bval.len(), 2);
assert_eq!(aval[0], u64::MAX);
assert_eq!(bval[0], u64::MAX);
}
FourStateValue::Narrow(_) => panic!("expected Wide for 65 bits"),
}
}
#[test]
fn four_state_to_owned_svint_round_trips_known_and_unknown_bits() {
let all_known = FourStateValue::Narrow(0b1010u128);
let v = four_state_to_owned_svint(&all_known, 4, false);
assert_eq!(v.snapshot().as_u64(), Some(0b1010));
assert!(!v.snapshot().has_unknown());
let with_x = FourStateValue::Narrow(0b1000u128 | (0b0010u128 << 64));
let v2 = four_state_to_owned_svint(&with_x, 4, false);
assert!(v2.snapshot().has_unknown());
assert_eq!(v2.snapshot().bit(1), Some(Bit::X));
}
#[test]
fn four_state_of_constant_extracts_bits_from_a_real_compiled_constant() {
let design = compile_source("module m; localparam int p = 8'hAA; endmodule\n")
.expect("valid SV source compiles");
let body = design
.top_instances()
.next()
.expect("one top instance")
.instance_body()
.expect("instance has a body");
let p = body.find("p").expect("p is a member of m");
let cv = p
.initializer()
.expect("p has an initializer")
.constant_value()
.expect("p's initializer folds to a constant");
let fv = four_state_of_constant(&cv).expect("an integer constant yields Some");
match fv {
FourStateValue::Narrow(word) => {
let low_byte = (word & 0xFF) as u8;
assert_eq!(low_byte, 0xAA);
}
FourStateValue::Wide { .. } => panic!("expected Narrow for a 32-bit int constant"),
}
}
#[test]
fn owned_svint_to_four_state_round_trips_known_and_unknown_bits() {
let known = FourStateValue::Narrow(0xAAu128);
let v = four_state_to_owned_svint(&known, 8, false);
let back = owned_svint_to_four_state(&v).expect("a fully-known 8-bit value round-trips");
match back {
FourStateValue::Narrow(w) => {
assert_eq!(w as u64 & 0xFF, 0xAA);
assert_eq!((w >> 64) as u64 & 0xFF, 0, "no unknown bits expected");
}
FourStateValue::Wide { .. } => panic!("expected Narrow for an 8-bit value"),
}
let with_x = FourStateValue::Narrow(0b1000u128 | (0b0001u128 << 64));
let v2 = four_state_to_owned_svint(&with_x, 4, false);
let back2 =
owned_svint_to_four_state(&v2).expect("a partially-unknown 4-bit value round-trips");
match back2 {
FourStateValue::Narrow(w) => {
assert_eq!(
(w >> 64) as u64 & 0b1111,
0b0001,
"bit 0's unknown-ness must survive the round trip"
);
assert_eq!(
w as u64 & 0b1110,
0b1000,
"the known bits must survive the round trip"
);
}
FourStateValue::Wide { .. } => panic!("expected Narrow for a 4-bit value"),
}
}
#[test]
fn four_state_of_constant_reads_a_constant_wider_than_1024_bits() {
let design = compile_source(
"module m; localparam bit [1099:0] p = {1100{1'b1}};\n\
localparam bit [1099:0] q = {550{2'b10}}; endmodule\n",
)
.expect("valid SV source compiles");
let body = design
.top_instances()
.next()
.expect("one top instance")
.instance_body()
.expect("instance has a body");
let init = |name: &str| {
body.find(name)
.expect("a member of m")
.initializer()
.expect("an initializer")
};
let (p, q) = (init("p"), init("q"));
let pv = p.constant_value().expect("p folds");
assert_eq!(
format!("{:?}", four_state_of_constant(&pv)),
format!("{:?}", Some(bits_to_four_state(&[Bit::One; 1100]))),
);
let alternating: Vec<Bit> = (0..1100)
.map(|i| if i % 2 == 1 { Bit::One } else { Bit::Zero })
.collect();
assert_eq!(constant_expr_bits(&q), Some(alternating.clone()));
assert_eq!(
format!(
"{:?}",
four_state_of_constant(&q.constant_value().expect("q folds"))
),
format!("{:?}", Some(bits_to_four_state(&alternating))),
);
}
}