use super::codec::{ImageError, Reader, Writer};
use crate::covergroup::decl::{
ArrayKind, BinBody, BinDecl, BinKind, Bound, CovGroupDecl, CoverpointDecl, CrossBinDecl,
CrossDecl, Matches, RangeItem, RepeatRange, SelectExpr, TransItem, TransRep,
};
use crate::covergroup::value::{Expr, TypeInfo, Val};
fn bad(what: &'static str, tag: u8) -> ImageError {
ImageError::BadTag { what, tag }
}
fn put_val(w: &mut Writer, v: &Val) {
match v {
Val::Int(i) => {
w.u8(0);
w.u128(*i as u128);
}
Val::Real(f) => {
w.u8(1);
w.u64(f.to_bits());
}
Val::Str(s) => {
w.u8(2);
w.str(s);
}
Val::List(items) => {
w.u8(3);
w.len(items.len());
for i in items {
put_val(w, i);
}
}
Val::Unknown => w.u8(4),
Val::Pat { bits, care } => {
w.u8(5);
w.u128(*bits);
w.u128(*care);
}
}
}
fn get_val(r: &mut Reader<'_>) -> Result<Val, ImageError> {
Ok(match r.u8()? {
0 => Val::Int(r.u128()? as i128),
1 => Val::Real(f64::from_bits(r.u64()?)),
2 => Val::Str(r.str()?),
3 => {
let n = r.len()?;
Val::List((0..n).map(|_| get_val(r)).collect::<Result<_, _>>()?)
}
4 => Val::Unknown,
5 => Val::Pat {
bits: r.u128()?,
care: r.u128()?,
},
tag => return Err(bad("covergroup value", tag)),
})
}
fn put_expr(w: &mut Writer, e: &Expr) {
match e {
Expr::Const(v) => {
w.u8(0);
put_val(w, v);
}
Expr::Kernel(k) => {
w.u8(1);
w.u32(*k);
}
}
}
fn get_expr(r: &mut Reader<'_>) -> Result<Expr, ImageError> {
Ok(match r.u8()? {
0 => Expr::Const(get_val(r)?),
1 => Expr::Kernel(r.u32()?),
tag => return Err(bad("covergroup expression", tag)),
})
}
fn put_opt_expr(w: &mut Writer, e: &Option<Expr>) {
match e {
None => w.u8(0),
Some(e) => {
w.u8(1);
put_expr(w, e);
}
}
}
fn get_opt_expr(r: &mut Reader<'_>) -> Result<Option<Expr>, ImageError> {
Ok(match r.u8()? {
0 => None,
1 => Some(get_expr(r)?),
tag => return Err(bad("covergroup optional expression", tag)),
})
}
fn put_named_exprs(w: &mut Writer, v: &[(String, Expr)]) {
w.len(v.len());
for (name, e) in v {
w.str(name);
put_expr(w, e);
}
}
fn get_named_exprs(r: &mut Reader<'_>) -> Result<Vec<(String, Expr)>, ImageError> {
let n = r.len()?;
(0..n).map(|_| Ok((r.str()?, get_expr(r)?))).collect()
}
fn put_bound(w: &mut Writer, b: &Bound) {
match b {
Bound::Expr(e) => {
w.u8(0);
put_expr(w, e);
}
Bound::Dollar => w.u8(1),
}
}
fn get_bound(r: &mut Reader<'_>) -> Result<Bound, ImageError> {
Ok(match r.u8()? {
0 => Bound::Expr(get_expr(r)?),
1 => Bound::Dollar,
tag => return Err(bad("covergroup range bound", tag)),
})
}
fn put_range_item(w: &mut Writer, i: &RangeItem) {
match i {
RangeItem::Single(e) => {
w.u8(0);
put_expr(w, e);
}
RangeItem::Range(lo, hi) => {
w.u8(1);
put_bound(w, lo);
put_bound(w, hi);
}
RangeItem::Tolerance {
center,
tol,
relative,
} => {
w.u8(2);
put_expr(w, center);
put_expr(w, tol);
w.bool(*relative);
}
}
}
fn get_range_item(r: &mut Reader<'_>) -> Result<RangeItem, ImageError> {
Ok(match r.u8()? {
0 => RangeItem::Single(get_expr(r)?),
1 => RangeItem::Range(get_bound(r)?, get_bound(r)?),
2 => RangeItem::Tolerance {
center: get_expr(r)?,
tol: get_expr(r)?,
relative: r.bool()?,
},
tag => return Err(bad("covergroup range item", tag)),
})
}
fn put_range_items(w: &mut Writer, v: &[RangeItem]) {
w.len(v.len());
for i in v {
put_range_item(w, i);
}
}
fn get_range_items(r: &mut Reader<'_>) -> Result<Vec<RangeItem>, ImageError> {
let n = r.len()?;
(0..n).map(|_| get_range_item(r)).collect()
}
fn put_repeat(w: &mut Writer, rep: &RepeatRange) {
put_expr(w, &rep.lo);
put_opt_expr(w, &rep.hi);
}
fn get_repeat(r: &mut Reader<'_>) -> Result<RepeatRange, ImageError> {
Ok(RepeatRange {
lo: get_expr(r)?,
hi: get_opt_expr(r)?,
})
}
fn put_trans_item(w: &mut Writer, t: &TransItem) {
put_range_items(w, &t.values);
match &t.rep {
TransRep::Once => w.u8(0),
TransRep::Consecutive(rep) => {
w.u8(1);
put_repeat(w, rep);
}
TransRep::Goto(rep) => {
w.u8(2);
put_repeat(w, rep);
}
TransRep::NonConsecutive(rep) => {
w.u8(3);
put_repeat(w, rep);
}
}
}
fn get_trans_item(r: &mut Reader<'_>) -> Result<TransItem, ImageError> {
let values = get_range_items(r)?;
let rep = match r.u8()? {
0 => TransRep::Once,
1 => TransRep::Consecutive(get_repeat(r)?),
2 => TransRep::Goto(get_repeat(r)?),
3 => TransRep::NonConsecutive(get_repeat(r)?),
tag => return Err(bad("covergroup transition repetition", tag)),
};
Ok(TransItem { values, rep })
}
fn put_bin_kind(w: &mut Writer, k: BinKind) {
w.u8(match k {
BinKind::Normal => 0,
BinKind::Ignore => 1,
BinKind::Illegal => 2,
});
}
fn get_bin_kind(r: &mut Reader<'_>) -> Result<BinKind, ImageError> {
Ok(match r.u8()? {
0 => BinKind::Normal,
1 => BinKind::Ignore,
2 => BinKind::Illegal,
tag => return Err(bad("covergroup bin kind", tag)),
})
}
fn put_bin(w: &mut Writer, b: &BinDecl) {
w.str(&b.name);
put_bin_kind(w, b.kind);
match &b.array {
ArrayKind::None => w.u8(0),
ArrayKind::Open => w.u8(1),
ArrayKind::Fixed(e) => {
w.u8(2);
put_expr(w, e);
}
}
w.bool(b.wildcard);
put_opt_expr(w, &b.iff);
match &b.body {
BinBody::Trans(sets) => {
w.u8(0);
w.len(sets.len());
for set in sets {
w.len(set.len());
for item in set {
put_trans_item(w, item);
}
}
}
BinBody::DefaultSeq => w.u8(1),
BinBody::Ranges { items, with } => {
w.u8(2);
put_range_items(w, items);
put_opt_expr(w, with);
}
BinBody::Set(e) => {
w.u8(3);
put_expr(w, e);
}
BinBody::Default => w.u8(4),
}
}
fn get_bin(r: &mut Reader<'_>) -> Result<BinDecl, ImageError> {
let name = r.str()?;
let kind = get_bin_kind(r)?;
let array = match r.u8()? {
0 => ArrayKind::None,
1 => ArrayKind::Open,
2 => ArrayKind::Fixed(get_expr(r)?),
tag => return Err(bad("covergroup bin array", tag)),
};
let wildcard = r.bool()?;
let iff = get_opt_expr(r)?;
let body = match r.u8()? {
0 => {
let n = r.len()?;
let mut sets = Vec::with_capacity(n);
for _ in 0..n {
let m = r.len()?;
sets.push(
(0..m)
.map(|_| get_trans_item(r))
.collect::<Result<_, _>>()?,
);
}
BinBody::Trans(sets)
}
1 => BinBody::DefaultSeq,
2 => BinBody::Ranges {
items: get_range_items(r)?,
with: get_opt_expr(r)?,
},
3 => BinBody::Set(get_expr(r)?),
4 => BinBody::Default,
tag => return Err(bad("covergroup bin body", tag)),
};
Ok(BinDecl {
name,
kind,
array,
wildcard,
iff,
body,
})
}
fn put_matches(w: &mut Writer, m: &Matches) {
match m {
Matches::Count(e) => {
w.u8(0);
put_expr(w, e);
}
Matches::All => w.u8(1),
}
}
fn get_matches(r: &mut Reader<'_>) -> Result<Matches, ImageError> {
Ok(match r.u8()? {
0 => Matches::Count(get_expr(r)?),
1 => Matches::All,
tag => return Err(bad("covergroup matches clause", tag)),
})
}
fn put_select(w: &mut Writer, s: &SelectExpr) {
match s {
SelectExpr::Binsof { cp, bin, intersect } => {
w.u8(0);
w.str(cp);
match bin {
None => w.u8(0),
Some(b) => {
w.u8(1);
w.str(b);
}
}
match intersect {
None => w.u8(0),
Some(items) => {
w.u8(1);
put_range_items(w, items);
}
}
}
SelectExpr::Not(a) => {
w.u8(1);
put_select(w, a);
}
SelectExpr::And(a, b) => {
w.u8(2);
put_select(w, a);
put_select(w, b);
}
SelectExpr::Or(a, b) => {
w.u8(3);
put_select(w, a);
put_select(w, b);
}
SelectExpr::With { sel, expr, matches } => {
w.u8(4);
put_select(w, sel);
put_expr(w, expr);
put_matches(w, matches);
}
SelectExpr::All => w.u8(5),
SelectExpr::Set { expr, matches } => {
w.u8(6);
put_expr(w, expr);
put_matches(w, matches);
}
}
}
fn get_select(r: &mut Reader<'_>) -> Result<SelectExpr, ImageError> {
Ok(match r.u8()? {
0 => {
let cp = r.str()?;
let bin = match r.u8()? {
0 => None,
1 => Some(r.str()?),
tag => return Err(bad("covergroup binsof bin", tag)),
};
let intersect = match r.u8()? {
0 => None,
1 => Some(get_range_items(r)?),
tag => return Err(bad("covergroup binsof intersect", tag)),
};
SelectExpr::Binsof { cp, bin, intersect }
}
1 => SelectExpr::Not(Box::new(get_select(r)?)),
2 => SelectExpr::And(Box::new(get_select(r)?), Box::new(get_select(r)?)),
3 => SelectExpr::Or(Box::new(get_select(r)?), Box::new(get_select(r)?)),
4 => SelectExpr::With {
sel: Box::new(get_select(r)?),
expr: get_expr(r)?,
matches: get_matches(r)?,
},
5 => SelectExpr::All,
6 => SelectExpr::Set {
expr: get_expr(r)?,
matches: get_matches(r)?,
},
tag => return Err(bad("covergroup select expression", tag)),
})
}
fn put_cross(w: &mut Writer, x: &CrossDecl) {
w.str(&x.name);
w.len(x.items.len());
for i in &x.items {
w.str(i);
}
put_opt_expr(w, &x.iff);
w.len(x.bins.len());
for b in &x.bins {
w.str(&b.name);
put_bin_kind(w, b.kind);
put_select(w, &b.select);
put_opt_expr(w, &b.iff);
}
put_named_exprs(w, &x.options);
put_named_exprs(w, &x.type_options);
}
fn get_cross(r: &mut Reader<'_>) -> Result<CrossDecl, ImageError> {
let name = r.str()?;
let n = r.len()?;
let items = (0..n).map(|_| r.str()).collect::<Result<_, _>>()?;
let iff = get_opt_expr(r)?;
let n = r.len()?;
let bins = (0..n)
.map(|_| {
Ok(CrossBinDecl {
name: r.str()?,
kind: get_bin_kind(r)?,
select: get_select(r)?,
iff: get_opt_expr(r)?,
})
})
.collect::<Result<_, ImageError>>()?;
Ok(CrossDecl {
name,
items,
iff,
bins,
options: get_named_exprs(r)?,
type_options: get_named_exprs(r)?,
})
}
fn put_coverpoint(w: &mut Writer, c: &CoverpointDecl) {
w.str(&c.name);
w.u32(c.ty.width);
w.bool(c.ty.signed);
w.bool(c.ty.real);
put_expr(w, &c.expr);
put_opt_expr(w, &c.iff);
w.len(c.bins.len());
for b in &c.bins {
put_bin(w, b);
}
match &c.enum_values {
None => w.u8(0),
Some(values) => {
w.u8(1);
w.len(values.len());
for (name, v) in values {
w.str(name);
w.u128(*v as u128);
}
}
}
put_named_exprs(w, &c.options);
put_named_exprs(w, &c.type_options);
}
fn get_coverpoint(r: &mut Reader<'_>) -> Result<CoverpointDecl, ImageError> {
let name = r.str()?;
let ty = TypeInfo {
width: r.u32()?,
signed: r.bool()?,
real: r.bool()?,
};
let expr = get_expr(r)?;
let iff = get_opt_expr(r)?;
let n = r.len()?;
let bins = (0..n).map(|_| get_bin(r)).collect::<Result<_, _>>()?;
let enum_values = match r.u8()? {
0 => None,
1 => {
let n = r.len()?;
Some(
(0..n)
.map(|_| Ok((r.str()?, r.u128()? as i128)))
.collect::<Result<_, ImageError>>()?,
)
}
tag => return Err(bad("covergroup enum values", tag)),
};
Ok(CoverpointDecl {
name,
ty,
expr,
iff,
bins,
enum_values,
options: get_named_exprs(r)?,
type_options: get_named_exprs(r)?,
})
}
pub fn put_covgroup(w: &mut Writer, d: &CovGroupDecl) {
w.str(&d.name);
put_named_exprs(w, &d.options);
put_named_exprs(w, &d.type_options);
w.len(d.coverpoints.len());
for c in &d.coverpoints {
put_coverpoint(w, c);
}
w.len(d.crosses.len());
for x in &d.crosses {
put_cross(w, x);
}
}
pub fn get_covgroup(r: &mut Reader<'_>) -> Result<CovGroupDecl, ImageError> {
let name = r.str()?;
let options = get_named_exprs(r)?;
let type_options = get_named_exprs(r)?;
let n = r.len()?;
let coverpoints = (0..n)
.map(|_| get_coverpoint(r))
.collect::<Result<_, _>>()?;
let n = r.len()?;
let crosses = (0..n).map(|_| get_cross(r)).collect::<Result<_, _>>()?;
Ok(CovGroupDecl {
name,
options,
type_options,
coverpoints,
crosses,
})
}
#[cfg(test)]
mod tests {
use super::*;
fn rich() -> CovGroupDecl {
let ranges = vec![
RangeItem::Single(Expr::int(-3)),
RangeItem::Range(Bound::Expr(Expr::int(0)), Bound::Dollar),
RangeItem::Range(Bound::Dollar, Bound::Expr(Expr::Kernel(4))),
RangeItem::Tolerance {
center: Expr::real(2.5),
tol: Expr::int(1),
relative: true,
},
];
let trans = vec![
vec![
TransItem::new(vec![RangeItem::Single(Expr::int(1))])
.rep(TransRep::Consecutive(RepeatRange::between(2, 4))),
TransItem::new(vec![RangeItem::Single(Expr::int(2))])
.rep(TransRep::Goto(RepeatRange::exactly(3))),
],
vec![
TransItem::new(ranges.clone())
.rep(TransRep::NonConsecutive(RepeatRange::exactly(1))),
],
];
let pattern = Expr::Const(Val::Pat {
bits: 0b1010,
care: 0b1110,
});
let cp = CoverpointDecl {
name: "a".to_owned(),
ty: TypeInfo::signed(12),
expr: Expr::Kernel(0),
iff: Some(Expr::Kernel(1)),
bins: vec![
BinDecl::ranges("lo", ranges.clone()).array(ArrayKind::Fixed(Expr::int(4))),
BinDecl::ranges("w", vec![RangeItem::Single(pattern)])
.wildcard()
.iff(Expr::Kernel(2))
.with(Expr::Kernel(3)),
BinDecl::trans("t", trans).kind(BinKind::Illegal),
BinDecl::default_seq("ds").kind(BinKind::Ignore),
BinDecl::default_bin("d").array(ArrayKind::Open),
BinDecl {
name: "s".to_owned(),
kind: BinKind::Normal,
array: ArrayKind::None,
wildcard: false,
iff: None,
body: BinBody::Set(Expr::Const(Val::List(vec![
Val::Int(i128::MIN),
Val::Str("x".to_owned()),
Val::Unknown,
Val::Real(-0.5),
]))),
},
],
enum_values: Some(vec![("A".to_owned(), 0), ("B".to_owned(), -7)]),
options: vec![("weight".to_owned(), Expr::int(2))],
type_options: vec![("goal".to_owned(), Expr::int(90))],
};
let select = SelectExpr::Or(
Box::new(SelectExpr::And(
Box::new(SelectExpr::Binsof {
cp: "a".to_owned(),
bin: Some("lo".to_owned()),
intersect: Some(ranges),
}),
Box::new(SelectExpr::Not(Box::new(SelectExpr::All))),
)),
Box::new(SelectExpr::With {
sel: Box::new(SelectExpr::Set {
expr: Expr::Kernel(5),
matches: Matches::All,
}),
expr: Expr::Kernel(6),
matches: Matches::one(),
}),
);
let mut x = CrossDecl::new("x", &["a", "b"])
.bin(CrossBinDecl::new("cb", BinKind::Ignore, select).iff(Expr::Kernel(7)));
x.iff = Some(Expr::Kernel(8));
x.options.push(("at_least".to_owned(), Expr::int(3)));
x.type_options.push(("weight".to_owned(), Expr::int(0)));
CovGroupDecl::new("cg").coverpoint(cp).cross(x)
}
#[test]
fn a_declaration_round_trips_and_every_truncation_is_refused() {
let d = rich();
let mut w = Writer::default();
put_covgroup(&mut w, &d);
let mut r = Reader::new(&w.buf);
assert_eq!(get_covgroup(&mut r).unwrap(), d);
for n in 0..w.buf.len() {
let mut r = Reader::new(&w.buf[..n]);
assert!(get_covgroup(&mut r).is_err(), "prefix {n} decoded");
}
}
#[test]
fn a_corrupted_byte_never_panics() {
let mut w = Writer::default();
put_covgroup(&mut w, &rich());
for i in 0..w.buf.len() {
let mut b = w.buf.clone();
b[i] ^= 0xff;
let _ = get_covgroup(&mut Reader::new(&b));
}
}
}