use std::collections::HashSet;
use super::compute::{Score, empty_score, item_score, merge_bins, weighted_score};
use super::coverpoint::CovEvent;
use super::db::{Db, DbItem, DbType, ItemKind};
use super::decl::CovGroupDecl;
use super::instance::{CovInstance, ItemRef};
use super::options::{Level, TypeOptions};
use super::value::{CovError, Eval, Val};
pub type TypeId = usize;
const TYPE_DEFINITION_ONLY: [&str; 2] = ["strobe", "real_interval"];
#[derive(Debug)]
pub struct CovType {
pub name: String,
decl: CovGroupDecl,
pub options: TypeOptions,
cp_options: Vec<TypeOptions>,
cross_options: Vec<TypeOptions>,
instances: Vec<usize>,
}
impl CovType {
pub fn decl(&self) -> &CovGroupDecl {
&self.decl
}
pub fn find_item(&self, name: &str) -> Option<ItemRef> {
if let Some(i) = self.decl.coverpoints.iter().position(|c| c.name == name) {
return Some(ItemRef::Coverpoint(i));
}
self.decl
.crosses
.iter()
.position(|c| c.name == name)
.map(ItemRef::Cross)
}
fn check(&self, item: ItemRef) -> Result<(), CovError> {
let ok = match item {
ItemRef::Group => true,
ItemRef::Coverpoint(i) => i < self.cp_options.len(),
ItemRef::Cross(i) => i < self.cross_options.len(),
};
if ok {
Ok(())
} else {
Err(CovError::UnknownItem {
group: self.name.clone(),
item: format!("{item:?}"),
})
}
}
pub fn type_options(&self, item: ItemRef) -> Result<&TypeOptions, CovError> {
self.check(item)?;
Ok(match item {
ItemRef::Group => &self.options,
ItemRef::Coverpoint(i) => &self.cp_options[i],
ItemRef::Cross(i) => &self.cross_options[i],
})
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct LoadSummary {
pub merged: Vec<String>,
pub ignored_types: Vec<String>,
pub ignored_items: Vec<String>,
}
#[derive(Debug, Default)]
pub struct Registry {
types: Vec<CovType>,
insts: Vec<(TypeId, CovInstance)>,
baseline: Db,
db_name: Option<String>,
}
impl Registry {
pub fn new() -> Registry {
Registry::default()
}
pub fn reset(&mut self) {
self.types.clear();
self.insts.clear();
self.baseline = Db::default();
self.db_name = None;
}
pub fn set_db_name(&mut self, name: &str) {
self.db_name = Some(name.to_string());
}
pub fn db_name(&self) -> Option<&str> {
self.db_name.as_deref()
}
pub fn snapshot_db(&self) -> Result<Db, CovError> {
let mut db = Db::default();
for t in &self.types {
if t.instances.is_empty() {
continue;
}
let mut dt = DbType {
name: t.name.clone(),
items: Vec::new(),
};
for &i in &t.instances {
let inst = &self.insts[i].1;
let mut one = DbType {
name: t.name.clone(),
items: Vec::new(),
};
for (k, c) in inst.coverpoints().iter().enumerate() {
one.items.push(db_item(
inst,
ItemKind::Coverpoint,
&c.name,
ItemRef::Coverpoint(k),
)?);
}
for (k, x) in inst.crosses().iter().enumerate() {
one.items
.push(db_item(inst, ItemKind::Cross, &x.name, ItemRef::Cross(k))?);
}
dt.merge(&one);
}
db.types.push(dt);
}
db.merge(&self.baseline);
Ok(db)
}
pub fn load_db(&mut self, text: &str) -> Result<LoadSummary, CovError> {
let db = Db::parse(text)?;
let mut keep = Db::default();
let mut summary = LoadSummary::default();
for dt in db.types {
let Some(t) = self.types.iter().find(|t| t.name == dt.name) else {
summary.ignored_types.push(dt.name);
continue;
};
let mut kept = DbType {
name: dt.name.clone(),
items: Vec::new(),
};
for item in dt.items {
let declared = match item.kind {
ItemKind::Coverpoint => t.decl.coverpoints.iter().any(|c| c.name == item.name),
ItemKind::Cross => t.decl.crosses.iter().any(|c| c.name == item.name),
};
let label = format!("{}::{}", dt.name, item.name);
if declared {
summary.merged.push(label);
kept.items.push(item);
} else {
summary.ignored_items.push(label);
}
}
keep.types.push(kept);
}
self.baseline.merge(&keep);
Ok(summary)
}
fn baseline_view(
&self,
t: &CovType,
insts: &[&CovInstance],
item: ItemRef,
) -> Result<Option<DbItem>, CovError> {
let (kind, name) = match item {
ItemRef::Group => return Ok(None),
ItemRef::Coverpoint(i) => (ItemKind::Coverpoint, &t.decl.coverpoints[i].name),
ItemRef::Cross(i) => (ItemKind::Cross, &t.decl.crosses[i].name),
};
let Some(b) = self.baseline.ty(&t.name).and_then(|d| d.item(kind, name)) else {
return Ok(None);
};
let mut names = HashSet::new();
for i in insts {
for (n, _) in i.item_bins(item)? {
names.insert(n);
}
}
let mut view = b.clone();
view.bins.retain(|(n, _)| names.contains(n));
Ok(Some(view))
}
pub fn define(&mut self, decl: CovGroupDecl, ev: &mut dyn Eval) -> Result<TypeId, CovError> {
let mut seen = HashSet::new();
let names = decl
.coverpoints
.iter()
.map(|c| &c.name)
.chain(decl.crosses.iter().map(|c| &c.name));
for n in names {
if !seen.insert(n.as_str()) {
return Err(CovError::DuplicateItem {
group: decl.name.clone(),
item: n.clone(),
});
}
}
let mut options = TypeOptions::default();
for (n, e) in &decl.type_options {
options.set(Level::Covergroup, n, &e.eval(ev, &[])?)?;
}
let mut cp_options = Vec::with_capacity(decl.coverpoints.len());
for c in &decl.coverpoints {
let mut o = options.inherit();
for (n, e) in &c.type_options {
o.set(Level::Coverpoint, n, &e.eval(ev, &[])?)?;
}
cp_options.push(o);
}
let mut cross_options = Vec::with_capacity(decl.crosses.len());
for c in &decl.crosses {
let mut o = options.inherit();
for (n, e) in &c.type_options {
o.set(Level::Cross, n, &e.eval(ev, &[])?)?;
}
cross_options.push(o);
}
self.types.push(CovType {
name: decl.name.clone(),
decl,
options,
cp_options,
cross_options,
instances: Vec::new(),
});
Ok(self.types.len() - 1)
}
pub fn types(&self) -> &[CovType] {
&self.types
}
pub fn type_id(&self, name: &str) -> Option<TypeId> {
self.types.iter().position(|t| t.name == name)
}
pub fn ty(&self, ty: TypeId) -> Result<&CovType, CovError> {
self.types.get(ty).ok_or(CovError::BadHandle(ty as u64))
}
pub fn instantiate(
&mut self,
ty: TypeId,
ctor: &[Val],
ev: &mut dyn Eval,
) -> Result<(u64, Vec<String>), CovError> {
let t = self.ty(ty)?;
let auto_name = format!("{}#{}", t.name, t.instances.len());
let (inst, warnings) = CovInstance::new(&t.decl, &t.options, auto_name, ev, ctor)?;
let index = self.insts.len();
self.insts.push((ty, inst));
self.types[ty].instances.push(index);
Ok((index as u64 + 1, warnings))
}
fn index(&self, h: u64) -> Result<usize, CovError> {
let i = h.checked_sub(1).ok_or(CovError::BadHandle(h))?;
match usize::try_from(i) {
Ok(i) if i < self.insts.len() => Ok(i),
_ => Err(CovError::BadHandle(h)),
}
}
pub fn instance(&self, h: u64) -> Result<&CovInstance, CovError> {
Ok(&self.insts[self.index(h)?].1)
}
pub fn instance_mut(&mut self, h: u64) -> Result<&mut CovInstance, CovError> {
let i = self.index(h)?;
Ok(&mut self.insts[i].1)
}
pub fn type_of(&self, h: u64) -> Result<TypeId, CovError> {
Ok(self.insts[self.index(h)?].0)
}
pub fn instances_of(&self, ty: TypeId) -> Result<Vec<u64>, CovError> {
Ok(self
.ty(ty)?
.instances
.iter()
.map(|&i| i as u64 + 1)
.collect())
}
pub fn sample(
&mut self,
h: u64,
ev: &mut dyn Eval,
sargs: &[Val],
) -> Result<Vec<CovEvent>, CovError> {
self.instance_mut(h)?.sample(ev, sargs)
}
pub fn set_type_option(
&mut self,
ty: TypeId,
item: ItemRef,
name: &str,
v: &Val,
) -> Result<(), CovError> {
self.ty(ty)?.check(item)?;
let t = &mut self.types[ty];
let (opts, level) = match item {
ItemRef::Group => (&mut t.options, Level::Covergroup),
ItemRef::Coverpoint(i) => (&mut t.cp_options[i], Level::Coverpoint),
ItemRef::Cross(i) => (&mut t.cross_options[i], Level::Cross),
};
let mut next = opts.clone();
next.set(level, name, v)?;
if TYPE_DEFINITION_ONLY.contains(&name) {
return Err(CovError::DefinitionOnlyOption {
option: name.to_string(),
});
}
*opts = next;
Ok(())
}
pub fn get_type_option(&self, ty: TypeId, item: ItemRef, name: &str) -> Result<Val, CovError> {
let level = match item {
ItemRef::Group => Level::Covergroup,
ItemRef::Coverpoint(_) => Level::Coverpoint,
ItemRef::Cross(_) => Level::Cross,
};
self.ty(ty)?.type_options(item)?.get(level, name)
}
pub fn type_score(&self, ty: TypeId, item: ItemRef) -> Result<Score, CovError> {
let t = self.ty(ty)?;
t.check(item)?;
let weight = t.type_options(item)?.weight;
let insts: Vec<&CovInstance> = t.instances.iter().map(|&i| &self.insts[i].1).collect();
if !t.options.merge_instances {
let loaded = |item: ItemRef| -> Result<Option<(i64, Score)>, CovError> {
Ok(self.baseline_view(t, &insts, item)?.map(|b| {
(
b.weight,
item_score(b.weight, merge_bins(&[(b.at_least, b.bins)])),
)
}))
};
let mut parts = Vec::with_capacity(insts.len() + 1);
for i in &insts {
parts.push((i.weight(item)?, i.own_score(item)?));
}
match item {
ItemRef::Group => {
let mut items = Vec::new();
for i in 0..t.cp_options.len() {
items.extend(loaded(ItemRef::Coverpoint(i))?);
}
for i in 0..t.cross_options.len() {
items.extend(loaded(ItemRef::Cross(i))?);
}
parts.push((1, weighted_score(1, &items)));
}
_ => parts.extend(loaded(item)?),
}
return Ok(weighted_score(weight, &parts));
}
let merged = |item: ItemRef| -> Result<Score, CovError> {
let mut views = Vec::with_capacity(insts.len() + 1);
for i in &insts {
views.push((i.at_least(item)?, i.item_bins(item)?));
}
if let Some(b) = self.baseline_view(t, &insts, item)? {
views.push((b.at_least, b.bins));
}
Ok(item_score(t.type_options(item)?.weight, merge_bins(&views)))
};
match item {
ItemRef::Group => {
let mut parts = Vec::new();
for i in 0..t.cp_options.len() {
let it = ItemRef::Coverpoint(i);
parts.push((t.type_options(it)?.weight, merged(it)?));
}
for i in 0..t.cross_options.len() {
let it = ItemRef::Cross(i);
parts.push((t.type_options(it)?.weight, merged(it)?));
}
Ok(weighted_score(weight, &parts))
}
_ => merged(item),
}
}
pub fn inst_score(&self, h: u64, item: ItemRef) -> Result<Score, CovError> {
let (ty, inst) = &self.insts[self.index(h)?];
if self.types[*ty].options.merge_instances && !inst.options.get_inst_coverage {
return self.type_score(*ty, item);
}
inst.own_score(item)
}
pub fn global_coverage(&self) -> Result<f64, CovError> {
let mut parts = Vec::with_capacity(self.types.len());
for (ty, t) in self.types.iter().enumerate() {
parts.push((t.options.weight, self.type_score(ty, ItemRef::Group)?));
}
let s = weighted_score(1, &parts);
Ok(if s.contributes {
s.percent
} else {
empty_score(0).percent
})
}
}
fn db_item(
inst: &CovInstance,
kind: ItemKind,
name: &str,
item: ItemRef,
) -> Result<DbItem, CovError> {
Ok(DbItem {
kind,
name: name.to_string(),
at_least: inst.at_least(item)?,
weight: inst.weight(item)?,
bins: inst.item_bins(item)?,
})
}