use std::collections::HashMap;
use super::compute::{Score, empty_score, item_score, merge_bins, weighted_score};
use super::coverpoint::Coverage;
use super::decl::{
ArrayKind, BinDecl, BinKind, Bound, CovGroupDecl, CoverpointDecl, CrossDecl, RangeItem,
TransItem,
};
use super::instance::ItemRef;
use super::registry::Registry;
use super::value::{CovError, Eval, Expr, KernelId, NoKernels, TypeInfo, Val};
struct Env {
vars: HashMap<KernelId, Val>,
}
impl Env {
fn new() -> Env {
Env {
vars: HashMap::new(),
}
}
fn set(&mut self, id: KernelId, v: i128) {
self.vars.insert(id, Val::Int(v));
}
}
impl Eval for Env {
fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError> {
if id >= 100 {
args.get((id - 100) as usize)
.cloned()
.ok_or(CovError::NoKernel(id))
} else {
self.vars.get(&id).cloned().ok_or(CovError::NoKernel(id))
}
}
}
fn near(a: f64, b: f64) {
assert!((a - b).abs() < 1e-9, "{a} != {b}");
}
fn pair(s: Score) -> (u64, u64) {
(s.covered, s.total)
}
fn r(lo: i128, hi: i128) -> RangeItem {
RangeItem::Range(Bound::Expr(Expr::int(lo)), Bound::Expr(Expr::int(hi)))
}
fn s(v: i128) -> RangeItem {
RangeItem::Single(Expr::int(v))
}
fn bin(name: &str, items: Vec<RangeItem>) -> BinDecl {
BinDecl::ranges(name, items)
}
fn cpd(name: &str, kernel: KernelId, bins: Vec<BinDecl>) -> CoverpointDecl {
let mut d = CoverpointDecl::new(name, TypeInfo::unsigned(8), Expr::Kernel(kernel));
d.bins = bins;
d
}
fn group(decl: CovGroupDecl) -> (Registry, usize) {
let mut reg = Registry::new();
let ty = reg.define(decl, &mut NoKernels).expect("defines");
(reg, ty)
}
fn new_inst(reg: &mut Registry, ty: usize, ev: &mut Env, ctor: &[Val]) -> u64 {
reg.instantiate(ty, ctor, ev).expect("instantiates").0
}
const A: ItemRef = ItemRef::Coverpoint(0);
const B: ItemRef = ItemRef::Coverpoint(1);
#[test]
fn zero_denominator_returns_zero_or_hundred_by_weight() {
let none = Coverage::default();
let s = item_score(1, none);
assert_eq!((s.percent, pair(s), s.contributes), (0.0, (0, 0), false));
let s = item_score(0, none);
assert_eq!((s.percent, pair(s), s.contributes), (100.0, (0, 0), false));
assert_eq!(empty_score(7).percent, 0.0);
assert_eq!(empty_score(0).percent, 100.0);
}
#[test]
fn weighted_average_skips_items_that_do_not_contribute() {
let full = item_score(
1,
Coverage {
covered: 2,
total: 2,
},
);
let half = item_score(
1,
Coverage {
covered: 1,
total: 2,
},
);
let none = item_score(5, Coverage::default());
let g = weighted_score(1, &[(3, full), (1, half), (5, none)]);
near(g.percent, (3.0 * 100.0 + 50.0) / 4.0);
assert_eq!(pair(g), (3, 4));
assert!(g.contributes);
}
#[test]
fn all_zero_weights_make_the_denominator_zero() {
let half = item_score(
0,
Coverage {
covered: 1,
total: 2,
},
);
let g = weighted_score(1, &[(0, half), (0, half)]);
assert_eq!((g.percent, pair(g), g.contributes), (0.0, (0, 0), false));
let g = weighted_score(0, &[(0, half)]);
assert_eq!(g.percent, 100.0);
}
#[test]
fn merge_unions_by_name_adds_hits_and_uses_the_largest_at_least() {
let one = |n: &str, h: u64| (n.to_string(), h);
let c = merge_bins(&[
(1, vec![one("b[0]", 1), one("b[1]", 1)]),
(3, vec![one("b[1]", 1), one("b[2]", 0)]),
]);
assert_eq!((c.covered, c.total), (0, 3));
let c = merge_bins(&[
(1, vec![one("b[1]", 2)]),
(3, vec![one("b[1]", 1), one("b[2]", 3)]),
]);
assert_eq!((c.covered, c.total), (2, 2));
assert_eq!(
merge_bins(&[]),
Coverage {
covered: 0,
total: 0
}
);
}
fn gt_decl() -> CovGroupDecl {
let range = RangeItem::Range(
Bound::Expr(Expr::Kernel(100)),
Bound::Expr(Expr::Kernel(101)),
);
let mut a = CoverpointDecl::new("a", TypeInfo::signed(32), Expr::Kernel(0));
a.bins.push(bin("b", vec![range]).array(ArrayKind::Open));
CovGroupDecl::new("gt").coverpoint(a)
}
fn gt_run(decl: CovGroupDecl) -> (Registry, usize, u64, u64) {
let (mut reg, ty) = group(decl);
let mut env = Env::new();
let gv1 = new_inst(&mut reg, ty, &mut env, &[Val::Int(0), Val::Int(1)]);
let gv2 = new_inst(&mut reg, ty, &mut env, &[Val::Int(1), Val::Int(2)]);
env.set(0, 0);
reg.sample(gv1, &mut env, &[]).unwrap();
env.set(0, 1);
reg.sample(gv1, &mut env, &[]).unwrap();
reg.sample(gv2, &mut env, &[]).unwrap();
(reg, ty, gv1, gv2)
}
#[test]
fn lrm_19_11_3_gt_merged_type_coverage_is_two_of_three_bins() {
let (reg, ty, gv1, gv2) = gt_run(
gt_decl()
.type_option("merge_instances", Expr::int(1))
.option("get_inst_coverage", Expr::int(1)),
);
let t = reg.type_score(ty, ItemRef::Group).unwrap();
near(t.percent, 200.0 / 3.0); assert_eq!(pair(t), (2, 3));
let a = reg.type_score(ty, A).unwrap();
near(a.percent, 200.0 / 3.0);
let b1 = |h| {
reg.instance(h)
.unwrap()
.item_bins(A)
.unwrap()
.into_iter()
.find(|(n, _)| n == "b[1]")
.map(|(_, h)| h)
};
assert_eq!((b1(gv1), b1(gv2)), (Some(1), Some(1)));
near(reg.inst_score(gv1, ItemRef::Group).unwrap().percent, 100.0);
near(reg.inst_score(gv2, ItemRef::Group).unwrap().percent, 50.0);
}
#[test]
fn lrm_19_7_get_inst_coverage_option_off_returns_the_type_coverage_when_merging() {
let (reg, ty, gv1, gv2) = gt_run(gt_decl().type_option("merge_instances", Expr::int(1)));
let t = reg.type_score(ty, ItemRef::Group).unwrap();
assert_eq!(reg.inst_score(gv1, ItemRef::Group).unwrap(), t);
assert_eq!(reg.inst_score(gv2, ItemRef::Group).unwrap(), t);
assert_eq!(
reg.inst_score(gv1, A).unwrap(),
reg.type_score(ty, A).unwrap()
);
}
#[test]
fn lrm_19_11_3_gt_without_merging_is_the_weighted_average_of_instances() {
let (reg, ty, gv1, gv2) = gt_run(gt_decl());
let t = reg.type_score(ty, ItemRef::Group).unwrap();
near(t.percent, 75.0);
assert_eq!(pair(t), (3, 4));
near(reg.inst_score(gv1, ItemRef::Group).unwrap().percent, 100.0);
near(reg.inst_score(gv2, ItemRef::Group).unwrap().percent, 50.0);
}
#[test]
fn lrm_19_11_3_instance_weights_scale_the_unmerged_type_coverage() {
let (mut reg, ty, gv1, _gv2) = gt_run(gt_decl());
reg.instance_mut(gv1)
.unwrap()
.set_option(ItemRef::Group, "weight", &Val::Int(3))
.unwrap();
near(
reg.type_score(ty, ItemRef::Group).unwrap().percent,
(3.0 * 100.0 + 50.0) / 4.0,
);
}
#[test]
fn lrm_19_11_3_ga_auto_bin_names_do_not_overlap_so_the_type_has_96_bins() {
let mut a = CoverpointDecl::new("a", TypeInfo::unsigned(8), Expr::Kernel(0));
a.bins.push(bin("i", vec![s(3)]).kind(BinKind::Ignore));
let decl = CovGroupDecl::new("ga")
.option("auto_bin_max", Expr::Kernel(100))
.type_option("merge_instances", Expr::int(1))
.coverpoint(a);
let (mut reg, ty) = group(decl);
let mut env = Env::new();
let gv1 = new_inst(&mut reg, ty, &mut env, &[Val::Int(64)]);
let gv2 = new_inst(&mut reg, ty, &mut env, &[Val::Int(32)]);
let names = |h| -> Vec<String> {
reg.instance(h)
.unwrap()
.item_bins(A)
.unwrap()
.into_iter()
.map(|(n, _)| n)
.collect()
};
let n1 = names(gv1);
let n2 = names(gv2);
assert_eq!((n1.len(), n2.len()), (64, 32));
assert_eq!(
(n1[0].as_str(), n1[63].as_str()),
("auto[0:3]", "auto[252:255]")
);
assert_eq!(
(n2[0].as_str(), n2[31].as_str()),
("auto[0:7]", "auto[248:255]")
);
assert!(n1.contains(&"auto[0:3]".to_string()));
assert_eq!(reg.type_score(ty, A).unwrap().total, 96);
assert_eq!(reg.type_score(ty, ItemRef::Group).unwrap().total, 96);
}
#[test]
fn lrm_19_11_1_ct_ignored_values_empty_an_auto_bin_which_then_does_not_count() {
let mut b = CoverpointDecl::new("b", TypeInfo::unsigned(3), Expr::Kernel(0));
b.options.push(("auto_bin_max".into(), Expr::int(4)));
b.bins
.push(bin("ig", vec![r(0, 1), r(5, 6)]).kind(BinKind::Ignore));
let (mut reg, ty) = group(CovGroupDecl::new("ct").coverpoint(b));
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
let s = reg.inst_score(h, A).unwrap();
assert_eq!(pair(s), (0, 3));
env.set(0, 3);
reg.sample(h, &mut env, &[]).unwrap();
env.set(0, 0); reg.sample(h, &mut env, &[]).unwrap();
let s = reg.inst_score(h, A).unwrap();
assert_eq!(pair(s), (1, 3));
near(s.percent, 100.0 / 3.0);
}
#[test]
fn lrm_19_8_cv1_cv2_covered_and_total_arguments() {
let upto = |k: KernelId| {
vec![RangeItem::Range(
Bound::Expr(Expr::int(0)),
Bound::Expr(Expr::Kernel(k)),
)]
};
let mut x = CoverpointDecl::new("x", TypeInfo::signed(32), Expr::Kernel(0));
x.bins.push(bin("xbins", upto(100)).array(ArrayKind::Open));
let mut y = CoverpointDecl::new("y", TypeInfo::signed(32), Expr::Kernel(1));
y.bins.push(bin("ybins", upto(101)).array(ArrayKind::Open));
let (mut reg, ty) = group(CovGroupDecl::new("cg").coverpoint(x).coverpoint(y));
let mut env = Env::new();
let cv1 = new_inst(&mut reg, ty, &mut env, &[Val::Int(1), Val::Int(2)]);
let _cv2 = new_inst(&mut reg, ty, &mut env, &[Val::Int(3), Val::Int(6)]);
assert_eq!(reg.inst_score(cv1, A).unwrap().total, 2); assert_eq!(reg.inst_score(cv1, ItemRef::Group).unwrap().total, 5); assert_eq!(reg.type_score(ty, A).unwrap().total, 6); assert_eq!(reg.type_score(ty, ItemRef::Group).unwrap().total, 16); }
fn two_value_cp(name: &str, kernel: KernelId) -> CoverpointDecl {
cpd(name, kernel, vec![bin("v", vec![s(1)])])
}
#[test]
fn group_without_items_returns_zero_or_hundred_by_its_weight() {
let (mut reg, ty) = group(CovGroupDecl::new("empty"));
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
let s = reg.inst_score(h, ItemRef::Group).unwrap();
assert_eq!((s.percent, pair(s)), (0.0, (0, 0)));
let s = reg.type_score(ty, ItemRef::Group).unwrap();
assert_eq!((s.percent, pair(s)), (0.0, (0, 0)));
reg.instance_mut(h)
.unwrap()
.set_option(ItemRef::Group, "weight", &Val::Int(0))
.unwrap();
assert_eq!(reg.inst_score(h, ItemRef::Group).unwrap().percent, 100.0);
assert_eq!(reg.type_score(ty, ItemRef::Group).unwrap().percent, 0.0);
reg.set_type_option(ty, ItemRef::Group, "weight", &Val::Int(0))
.unwrap();
assert_eq!(reg.type_score(ty, ItemRef::Group).unwrap().percent, 100.0);
}
#[test]
fn type_without_instances_has_no_coverage_to_report() {
let (reg, ty) = group(CovGroupDecl::new("g").coverpoint(two_value_cp("a", 0)));
let s = reg.type_score(ty, ItemRef::Group).unwrap();
assert_eq!((s.percent, pair(s), s.contributes), (0.0, (0, 0), false));
let s = reg.type_score(ty, A).unwrap();
assert_eq!((s.percent, pair(s)), (0.0, (0, 0)));
}
#[test]
fn coverpoint_with_every_bin_empty_does_not_contribute() {
let mut dead = two_value_cp("dead", 1);
dead.bins.push(bin("ig", vec![s(1)]).kind(BinKind::Ignore));
let live = two_value_cp("live", 0);
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(live).coverpoint(dead));
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 1);
reg.sample(h, &mut env, &[]).unwrap();
let dead = reg.inst_score(h, B).unwrap();
assert_eq!((dead.percent, pair(dead)), (0.0, (0, 0)));
let g = reg.inst_score(h, ItemRef::Group).unwrap();
assert_eq!((g.percent, pair(g)), (100.0, (1, 1)));
reg.instance_mut(h)
.unwrap()
.set_option(B, "weight", &Val::Int(0))
.unwrap();
assert_eq!(reg.inst_score(h, B).unwrap().percent, 100.0);
}
#[test]
fn all_item_weights_zero_makes_the_group_denominator_zero() {
let mut a = two_value_cp("a", 0);
a.options.push(("weight".into(), Expr::int(0)));
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(a));
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
reg.sample(h, &mut env, &[]).unwrap();
let g = reg.inst_score(h, ItemRef::Group).unwrap();
assert_eq!((g.percent, pair(g), g.contributes), (0.0, (0, 0), false));
let c = reg.inst_score(h, A).unwrap();
assert_eq!((c.percent, pair(c)), (100.0, (1, 1)));
}
#[test]
fn dollar_get_coverage_is_100_with_no_instances() {
let mut reg = Registry::new();
assert_eq!(reg.global_coverage().unwrap(), 100.0);
reg.define(
CovGroupDecl::new("g").coverpoint(two_value_cp("a", 0)),
&mut NoKernels,
)
.unwrap();
assert_eq!(reg.global_coverage().unwrap(), 100.0);
}
fn single_cp_group(name: &str, weight: Option<i64>) -> CovGroupDecl {
let mut d = CovGroupDecl::new(name).coverpoint(two_value_cp("a", 0));
if let Some(w) = weight {
d = d.type_option("weight", Expr::int(w as i128));
}
d
}
#[test]
fn dollar_get_coverage_weights_types_by_type_option_weight() {
let mut reg = Registry::new();
let mut env = Env::new();
let t1 = reg.define(single_cp_group("g1", None), &mut env).unwrap();
let t2 = reg
.define(single_cp_group("g2", Some(3)), &mut env)
.unwrap();
let h1 = new_inst(&mut reg, t1, &mut env, &[]);
new_inst(&mut reg, t2, &mut env, &[]);
env.set(0, 1);
reg.sample(h1, &mut env, &[]).unwrap();
near(reg.global_coverage().unwrap(), 25.0);
}
#[test]
fn dollar_get_coverage_is_100_when_every_type_weight_is_zero() {
let mut reg = Registry::new();
let mut env = Env::new();
let t = reg.define(single_cp_group("g", Some(0)), &mut env).unwrap();
new_inst(&mut reg, t, &mut env, &[]);
assert_eq!(reg.global_coverage().unwrap(), 100.0);
}
#[test]
fn dollar_get_coverage_leaves_out_types_that_do_not_contribute() {
let mut reg = Registry::new();
let mut env = Env::new();
let t1 = reg.define(single_cp_group("g1", None), &mut env).unwrap();
reg.define(single_cp_group("unused", None), &mut env)
.unwrap();
let h = new_inst(&mut reg, t1, &mut env, &[]);
env.set(0, 1);
reg.sample(h, &mut env, &[]).unwrap();
near(reg.global_coverage().unwrap(), 100.0);
}
#[test]
fn coverpoint_weights_scale_the_instance_coverage() {
let mut a = two_value_cp("a", 0);
a.options.push(("weight".into(), Expr::Kernel(100)));
let (mut reg, ty) = group(
CovGroupDecl::new("g")
.coverpoint(a)
.coverpoint(two_value_cp("b", 1)),
);
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[Val::Int(3)]);
env.set(0, 1);
env.set(1, 0);
reg.sample(h, &mut env, &[]).unwrap();
near(reg.inst_score(h, ItemRef::Group).unwrap().percent, 75.0);
reg.instance_mut(h)
.unwrap()
.set_option(A, "weight", &Val::Int(1))
.unwrap();
near(reg.inst_score(h, ItemRef::Group).unwrap().percent, 50.0);
}
#[test]
fn covergroup_weight_and_goal_are_not_defaults_for_the_items() {
let g = CovGroupDecl::new("g")
.option("weight", Expr::int(5))
.option("goal", Expr::int(70))
.option("comment", Expr::Const(Val::Str("c".into())))
.coverpoint(two_value_cp("a", 0));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
let i = reg.instance(h).unwrap();
assert_eq!(i.get_option(ItemRef::Group, "weight"), Ok(Val::Int(5)));
assert_eq!(i.get_option(ItemRef::Group, "goal"), Ok(Val::Int(70)));
assert_eq!(i.get_option(A, "weight"), Ok(Val::Int(1)));
assert_eq!(i.get_option(A, "goal"), Ok(Val::Int(100)));
assert_eq!(i.get_option(A, "comment"), Ok(Val::Str(String::new())));
}
#[test]
fn at_least_is_a_default_for_items_and_decides_covered() {
let g = CovGroupDecl::new("g")
.option("at_least", Expr::int(3))
.coverpoint(two_value_cp("a", 0))
.coverpoint({
let mut b = two_value_cp("b", 0);
b.options.push(("at_least".into(), Expr::int(1)));
b
});
let (mut reg, ty) = group(g);
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
reg.sample(h, &mut env, &[]).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(pair(reg.inst_score(h, A).unwrap()), (0, 1)); assert_eq!(pair(reg.inst_score(h, B).unwrap()), (1, 1)); reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(pair(reg.inst_score(h, A).unwrap()), (1, 1));
}
#[test]
fn merged_type_coverage_uses_the_largest_at_least_of_all_instances() {
let mut a = two_value_cp("a", 0);
a.options.push(("at_least".into(), Expr::Kernel(100)));
let (mut reg, ty) = group(
CovGroupDecl::new("g")
.type_option("merge_instances", Expr::int(1))
.coverpoint(a),
);
let mut env = Env::new();
let h1 = new_inst(&mut reg, ty, &mut env, &[Val::Int(1)]);
let h2 = new_inst(&mut reg, ty, &mut env, &[Val::Int(3)]);
env.set(0, 1);
reg.sample(h1, &mut env, &[]).unwrap();
reg.sample(h2, &mut env, &[]).unwrap();
assert_eq!(pair(reg.type_score(ty, A).unwrap()), (0, 1));
reg.sample(h1, &mut env, &[]).unwrap();
assert_eq!(pair(reg.type_score(ty, A).unwrap()), (1, 1));
}
#[test]
fn merged_type_coverage_weights_items_by_type_option_weight() {
let mut a = two_value_cp("a", 0);
a.type_options.push(("weight".into(), Expr::int(3)));
a.options.push(("weight".into(), Expr::int(2)));
let mut b = two_value_cp("b", 1);
b.type_options.push(("weight".into(), Expr::int(5)));
let (mut reg, ty) = group(
CovGroupDecl::new("g1")
.type_option("merge_instances", Expr::int(1))
.coverpoint(a)
.coverpoint(b),
);
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 0);
reg.sample(h, &mut env, &[]).unwrap();
near(
reg.type_score(ty, ItemRef::Group).unwrap().percent,
300.0 / 8.0,
);
near(
reg.instance(h)
.unwrap()
.own_score(ItemRef::Group)
.unwrap()
.percent,
200.0 / 3.0,
);
reg.set_type_option(ty, A, "weight", &Val::Int(1)).unwrap();
near(
reg.type_score(ty, ItemRef::Group).unwrap().percent,
100.0 / 6.0,
);
}
#[test]
fn unmerged_type_coverage_of_an_item_weights_instances_by_item_weight() {
let mut a = two_value_cp("a", 0);
a.options.push(("weight".into(), Expr::Kernel(100)));
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(a));
let mut env = Env::new();
let h1 = new_inst(&mut reg, ty, &mut env, &[Val::Int(3)]);
let _h2 = new_inst(&mut reg, ty, &mut env, &[Val::Int(1)]);
env.set(0, 1);
reg.sample(h1, &mut env, &[]).unwrap(); near(reg.type_score(ty, A).unwrap().percent, 75.0);
}
#[test]
fn merging_instances_can_be_switched_on_procedurally() {
let (mut reg, ty, _gv1, _gv2) = gt_run(gt_decl());
near(reg.type_score(ty, ItemRef::Group).unwrap().percent, 75.0);
reg.set_type_option(ty, ItemRef::Group, "merge_instances", &Val::Int(1))
.unwrap();
near(
reg.type_score(ty, ItemRef::Group).unwrap().percent,
200.0 / 3.0,
);
}
#[test]
fn options_only_the_definition_may_set_are_refused_procedurally() {
let g = CovGroupDecl::new("g")
.coverpoint(two_value_cp("a", 0))
.coverpoint(two_value_cp("b", 1))
.cross(CrossDecl::new("x", &["a", "b"]));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
let x = ItemRef::Cross(0);
let inst = reg.instance_mut(h).unwrap();
let one = Val::Int(1);
for (item, name) in [
(ItemRef::Group, "per_instance"),
(ItemRef::Group, "get_inst_coverage"),
(ItemRef::Group, "auto_bin_max"),
(ItemRef::Group, "detect_overlap"),
(ItemRef::Group, "cross_retain_auto_bins"),
(A, "auto_bin_max"),
(A, "detect_overlap"),
(x, "cross_retain_auto_bins"),
] {
assert_eq!(
inst.set_option(item, name, &one),
Err(CovError::DefinitionOnlyOption {
option: name.to_string()
}),
"{name}"
);
}
assert_eq!(
inst.get_option(ItemRef::Group, "per_instance"),
Ok(Val::Int(0))
);
assert!(matches!(
inst.set_option(A, "per_instance", &one),
Err(CovError::OptionLevel { .. })
));
for (item, name, v) in [
(ItemRef::Group, "weight", Val::Int(2)),
(ItemRef::Group, "goal", Val::Int(80)),
(ItemRef::Group, "comment", Val::Str("hi".into())),
(ItemRef::Group, "at_least", Val::Int(4)),
(ItemRef::Group, "cross_num_print_missing", Val::Int(2)),
(ItemRef::Group, "name", Val::Str("n".into())),
(A, "weight", Val::Int(2)),
(x, "at_least", Val::Int(2)),
] {
inst.set_option(item, name, &v).unwrap();
assert_eq!(inst.get_option(item, name), Ok(v));
}
}
#[test]
fn type_options_strobe_and_real_interval_are_definition_only() {
let g = CovGroupDecl::new("g")
.type_option("strobe", Expr::int(1))
.coverpoint(two_value_cp("a", 0));
let (mut reg, ty) = group(g);
for (item, name, v) in [
(ItemRef::Group, "strobe", Val::Int(0)),
(ItemRef::Group, "real_interval", Val::Real(0.5)),
(A, "real_interval", Val::Real(0.5)),
] {
assert_eq!(
reg.set_type_option(ty, item, name, &v),
Err(CovError::DefinitionOnlyOption {
option: name.to_string()
})
);
}
assert_eq!(
reg.get_type_option(ty, ItemRef::Group, "strobe"),
Ok(Val::Int(1))
);
for (name, v) in [
("weight", Val::Int(2)),
("goal", Val::Int(50)),
("comment", Val::Str("c".into())),
("merge_instances", Val::Int(1)),
("distribute_first", Val::Int(1)),
] {
reg.set_type_option(ty, ItemRef::Group, name, &v).unwrap();
assert_eq!(reg.get_type_option(ty, ItemRef::Group, name), Ok(v));
}
assert!(matches!(
reg.set_type_option(ty, A, "merge_instances", &Val::Int(1)),
Err(CovError::OptionLevel { .. })
));
}
#[test]
fn type_options_assigned_in_the_definition_reach_the_type() {
let mut a = two_value_cp("a", 0);
a.type_options.push(("weight".into(), Expr::int(3)));
let g = CovGroupDecl::new("g1")
.type_option("comment", Expr::Const(Val::Str("model".into())))
.type_option("strobe", Expr::int(1))
.type_option("merge_instances", Expr::int(1))
.coverpoint(a);
let (reg, ty) = group(g);
let t = reg.ty(ty).unwrap();
assert_eq!(t.options.comment, "model");
assert!(t.options.strobe && t.options.merge_instances);
assert_eq!(reg.get_type_option(ty, A, "weight"), Ok(Val::Int(3)));
assert_eq!(reg.get_type_option(ty, A, "goal"), Ok(Val::Int(100)));
}
#[test]
fn instance_names_are_unique_unless_set() {
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(two_value_cp("a", 0)));
let mut env = Env::new();
let h1 = new_inst(&mut reg, ty, &mut env, &[]);
let h2 = new_inst(&mut reg, ty, &mut env, &[]);
assert_ne!(
reg.instance(h1).unwrap().name(),
reg.instance(h2).unwrap().name()
);
assert!(!reg.instance(h1).unwrap().name().is_empty());
reg.instance_mut(h1).unwrap().set_inst_name("top.dut.cg");
assert_eq!(reg.instance(h1).unwrap().name(), "top.dut.cg");
assert_eq!(
reg.instance(h1).unwrap().get_option(ItemRef::Group, "name"),
Ok(Val::Str("top.dut.cg".into()))
);
}
#[test]
fn option_name_in_the_definition_may_use_constructor_arguments() {
let g = CovGroupDecl::new("g")
.option("name", Expr::Kernel(100))
.coverpoint(two_value_cp("a", 0));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[Val::Str("mine".into())]);
assert_eq!(reg.instance(h).unwrap().name(), "mine");
}
#[test]
fn per_instance_and_get_inst_coverage_are_stored_options() {
let g = CovGroupDecl::new("g")
.option("per_instance", Expr::int(1))
.option("get_inst_coverage", Expr::int(1))
.coverpoint(two_value_cp("a", 0));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
let i = reg.instance(h).unwrap();
assert_eq!(
i.get_option(ItemRef::Group, "per_instance"),
Ok(Val::Int(1))
);
assert_eq!(
i.get_option(ItemRef::Group, "get_inst_coverage"),
Ok(Val::Int(1))
);
}
#[test]
fn covergroup_level_defaults_reach_coverpoints_and_crosses_at_instantiation() {
let a = cpd("a", 0, vec![]);
let b = cpd("b", 1, vec![]);
let g = CovGroupDecl::new("g")
.option("auto_bin_max", Expr::int(2))
.option("at_least", Expr::int(4))
.option("cross_num_print_missing", Expr::int(3))
.coverpoint(a)
.coverpoint(b)
.cross(CrossDecl::new("x", &["a", "b"]));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
let i = reg.instance(h).unwrap();
assert_eq!(i.get_option(A, "auto_bin_max"), Ok(Val::Int(2)));
assert_eq!(i.get_option(A, "at_least"), Ok(Val::Int(4)));
assert_eq!(
i.get_option(ItemRef::Cross(0), "cross_num_print_missing"),
Ok(Val::Int(3))
);
assert_eq!(i.coverpoints()[0].bins.len(), 2);
assert_eq!(pair(reg.inst_score(h, ItemRef::Cross(0)).unwrap()), (0, 4));
assert_eq!(i.cross("x").unwrap().missing_bins().len(), 3);
}
#[test]
fn cross_num_print_missing_defaults_to_listing_nothing() {
let g = CovGroupDecl::new("g")
.coverpoint(two_value_cp("a", 0))
.coverpoint(two_value_cp("b", 1))
.cross(CrossDecl::new("x", &["a", "b"]));
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
assert!(
reg.instance(h)
.unwrap()
.cross("x")
.unwrap()
.missing_bins()
.is_empty()
);
}
#[test]
fn cross_num_print_missing_lists_only_the_uncovered_bins() {
let mut x = CrossDecl::new("x", &["a", "b"]);
x.options
.push(("cross_num_print_missing".into(), Expr::int(10)));
let g = CovGroupDecl::new("g")
.coverpoint(two_value_cp("a", 0))
.coverpoint(two_value_cp("b", 1))
.cross(x);
let (mut reg, ty) = group(g);
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().missing_bins(),
["<v,v>"]
);
env.set(0, 1);
env.set(1, 1);
reg.sample(h, &mut env, &[]).unwrap();
assert!(
reg.instance(h)
.unwrap()
.cross("x")
.unwrap()
.missing_bins()
.is_empty()
);
}
#[test]
fn cross_retain_auto_bins_at_the_covergroup_is_the_crosss_default() {
let mut x = CrossDecl::new("x", &["a", "b"]);
x.bins.push(super::decl::CrossBinDecl::new(
"ub",
BinKind::Normal,
super::decl::SelectExpr::Binsof {
cp: "a".into(),
bin: Some("lo".into()),
intersect: None,
},
));
let a = cpd("a", 0, vec![bin("lo", vec![s(0)]), bin("hi", vec![s(1)])]);
let b = cpd("b", 1, vec![bin("p", vec![s(0)]), bin("q", vec![s(1)])]);
let decl = |retain: i128| {
CovGroupDecl::new("g")
.option("cross_retain_auto_bins", Expr::int(retain))
.coverpoint(a.clone())
.coverpoint(b.clone())
.cross(x.clone())
};
let total = |retain| {
let (mut reg, ty) = group(decl(retain));
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
reg.inst_score(h, ItemRef::Cross(0)).unwrap().total
};
assert_eq!(total(1), 1 + 2); assert_eq!(total(0), 1);
}
#[test]
fn detect_overlap_at_the_covergroup_warns_for_every_coverpoint() {
let overlapping = || {
cpd(
"a",
0,
vec![bin("x", vec![r(0, 3)]), bin("y", vec![r(2, 5)])],
)
};
let mut reg = Registry::new();
let mut env = Env::new();
let on = reg
.define(
CovGroupDecl::new("on")
.option("detect_overlap", Expr::int(1))
.coverpoint(overlapping()),
&mut env,
)
.unwrap();
let off = reg
.define(CovGroupDecl::new("off").coverpoint(overlapping()), &mut env)
.unwrap();
let (_, w_on) = reg.instantiate(on, &[], &mut env).unwrap();
let (_, w_off) = reg.instantiate(off, &[], &mut env).unwrap();
assert_eq!(w_on.len(), 1, "{w_on:?}");
assert!(w_off.is_empty());
}
#[test]
fn real_interval_at_the_covergroup_sets_the_real_bin_width() {
let range = vec![RangeItem::Range(
Bound::Expr(Expr::real(0.0)),
Bound::Expr(Expr::real(2.0)),
)];
let real_cp = |name: &str| {
let mut d = CoverpointDecl::new(name, TypeInfo::real(), Expr::Kernel(0));
d.bins.push(bin("b", range.clone()).array(ArrayKind::Open));
d
};
let mut over = real_cp("over");
over.type_options
.push(("real_interval".into(), Expr::real(1.0)));
let g = CovGroupDecl::new("g")
.type_option("real_interval", Expr::real(0.5))
.coverpoint(real_cp("inherit"))
.coverpoint(over);
let (mut reg, ty) = group(g);
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
assert_eq!(reg.inst_score(h, A).unwrap().total, 4);
assert_eq!(reg.inst_score(h, B).unwrap().total, 2);
assert_eq!(
reg.get_type_option(ty, ItemRef::Group, "real_interval"),
Ok(Val::Real(0.5))
);
}
fn value_cross_group() -> CovGroupDecl {
let a = cpd("a", 0, vec![bin("a0", vec![s(0)]), bin("a1", vec![s(1)])]);
let b = cpd("b", 1, vec![bin("b0", vec![s(0)]), bin("b1", vec![s(1)])]);
CovGroupDecl::new("g")
.coverpoint(a)
.coverpoint(b)
.cross(CrossDecl::new("x", &["a", "b"]))
}
fn hits(reg: &Registry, h: u64, cp: &str, bin: &str) -> u64 {
reg.instance(h)
.unwrap()
.coverpoint(cp)
.unwrap()
.hits(bin)
.unwrap()
}
#[test]
fn stop_and_start_gate_the_whole_covergroup() {
let (mut reg, ty) = group(value_cross_group());
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 0);
env.set(1, 0);
reg.sample(h, &mut env, &[]).unwrap();
reg.instance_mut(h).unwrap().stop(ItemRef::Group).unwrap();
assert_eq!(
reg.instance(h).unwrap().is_started(ItemRef::Group),
Ok(false)
);
reg.sample(h, &mut NoKernels, &[]).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a0"), 1);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().hits("<a0,b0>"),
Some(1)
);
reg.instance_mut(h).unwrap().start(ItemRef::Group).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a0"), 2);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().hits("<a0,b0>"),
Some(2)
);
}
#[test]
fn stopping_one_coverpoint_leaves_the_others_and_starves_its_cross() {
let (mut reg, ty) = group(value_cross_group());
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 1);
reg.instance_mut(h).unwrap().stop(A).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a1"), 0);
assert_eq!(hits(®, h, "b", "b1"), 1);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().hits("<a1,b1>"),
Some(0)
);
reg.instance_mut(h).unwrap().start(A).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a1"), 1);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().hits("<a1,b1>"),
Some(1)
);
}
#[test]
fn stopping_a_cross_leaves_its_coverpoints() {
let (mut reg, ty) = group(value_cross_group());
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 1);
let x = reg.instance(h).unwrap().find_item("x").unwrap();
assert_eq!(x, ItemRef::Cross(0));
reg.instance_mut(h).unwrap().stop(x).unwrap();
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a1"), 1);
assert_eq!(
reg.instance(h).unwrap().cross("x").unwrap().hits("<a1,b1>"),
Some(0)
);
}
#[test]
fn starting_the_group_does_not_restart_a_separately_stopped_coverpoint() {
let (mut reg, ty) = group(value_cross_group());
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 1);
let inst = reg.instance_mut(h).unwrap();
inst.stop(A).unwrap();
inst.stop(ItemRef::Group).unwrap();
inst.start(ItemRef::Group).unwrap();
assert_eq!(inst.is_started(A), Ok(false));
reg.sample(h, &mut env, &[]).unwrap();
assert_eq!(hits(®, h, "a", "a1"), 0);
assert_eq!(hits(®, h, "b", "b1"), 1);
}
#[test]
fn stop_drops_a_transition_in_flight() {
let trans = || {
vec![BinDecl::trans(
"t",
vec![vec![TransItem::new(vec![s(1)]), TransItem::new(vec![s(2)])]],
)]
};
let run = |stop: bool| {
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(cpd("a", 0, trans())));
let mut env = Env::new();
let h = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
reg.sample(h, &mut env, &[]).unwrap();
if stop {
let inst = reg.instance_mut(h).unwrap();
inst.stop(A).unwrap();
inst.start(A).unwrap();
}
env.set(0, 2);
reg.sample(h, &mut env, &[]).unwrap();
hits(®, h, "a", "t")
};
assert_eq!(run(false), 1);
assert_eq!(run(true), 0);
}
#[test]
fn bad_handles_and_items_are_named_errors() {
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(two_value_cp("a", 0)));
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
assert_eq!(
reg.sample(0, &mut NoKernels, &[]),
Err(CovError::BadHandle(0))
);
assert_eq!(
reg.inst_score(h + 1, ItemRef::Group),
Err(CovError::BadHandle(h + 1))
);
assert!(matches!(
reg.instance_mut(h).unwrap().stop(ItemRef::Coverpoint(9)),
Err(CovError::UnknownItem { .. })
));
assert!(matches!(
reg.type_score(ty + 1, ItemRef::Group),
Err(CovError::BadHandle(_))
));
assert_eq!(reg.instance(h).unwrap().find_item("nope"), None);
assert_eq!(reg.ty(ty).unwrap().find_item("a"), Some(A));
}
#[test]
fn duplicate_item_names_and_unknown_cross_items_are_rejected() {
let mut reg = Registry::new();
let dup = CovGroupDecl::new("g")
.coverpoint(two_value_cp("a", 0))
.coverpoint(two_value_cp("a", 1));
assert!(matches!(
reg.define(dup, &mut NoKernels),
Err(CovError::DuplicateItem { .. })
));
let bad = CovGroupDecl::new("h")
.coverpoint(two_value_cp("a", 0))
.cross(CrossDecl::new("x", &["a", "zzz"]));
let ty = reg.define(bad, &mut NoKernels).unwrap();
assert!(matches!(
reg.instantiate(ty, &[], &mut NoKernels),
Err(CovError::CrossSelect { .. })
));
}
#[test]
fn merged_cross_bins_overlap_by_their_product_name() {
let (mut reg, ty) = group(value_cross_group().type_option("merge_instances", Expr::int(1)));
let mut env = Env::new();
let h1 = new_inst(&mut reg, ty, &mut env, &[]);
let h2 = new_inst(&mut reg, ty, &mut env, &[]);
env.set(0, 1);
env.set(1, 0);
reg.sample(h1, &mut env, &[]).unwrap();
reg.sample(h2, &mut env, &[]).unwrap();
let x = ItemRef::Cross(0);
assert_eq!(pair(reg.type_score(ty, x).unwrap()), (1, 4));
assert_eq!(
pair(reg.instance(h1).unwrap().own_score(x).unwrap()),
(1, 4)
);
}
#[test]
fn reset_forgets_every_type_and_instance() {
let (mut reg, ty) = group(CovGroupDecl::new("g").coverpoint(two_value_cp("a", 0)));
let h = new_inst(&mut reg, ty, &mut Env::new(), &[]);
reg.reset();
assert!(reg.types().is_empty());
assert_eq!(reg.instance(h).err(), Some(CovError::BadHandle(h)));
assert_eq!(reg.global_coverage().unwrap(), 100.0);
}