vj-sim 0.3.0

Netlist, scheduler and process model of the vj SystemVerilog simulator.
Documentation
//! One covergroup instance (IEEE 1800-2023 19.3): its coverpoints and crosses,
//! instance options, sampling, `start` / `stop` and instance coverage.

use super::compute::{Score, item_score, weighted_score};
use super::coverpoint::{CovEvent, Coverpoint};
use super::cross::Cross;
use super::decl::CovGroupDecl;
use super::options::{Level, Options, TypeOptions};
use super::value::{CovError, Eval, Val};

/// A covergroup or one of its coverage items.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum ItemRef {
    Group,
    /// Index into the covergroup's coverpoints.
    Coverpoint(usize),
    /// Index into the covergroup's crosses.
    Cross(usize),
}

/// Instance options that 19.7 lets only the covergroup definition set.
const DEFINITION_ONLY: [&str; 5] = [
    "per_instance",
    "get_inst_coverage",
    "auto_bin_max",
    "detect_overlap",
    "cross_retain_auto_bins",
];

#[derive(Debug)]
pub struct CovInstance {
    /// The covergroup type's name.
    pub type_name: String,
    /// `option.*` of the covergroup (including `name`, `set_inst_name`).
    pub options: Options,
    coverpoints: Vec<Coverpoint>,
    crosses: Vec<Cross>,
    /// For each cross, the indices of its coverpoints in item order.
    cross_items: Vec<Vec<usize>>,
    group_on: bool,
    cp_on: Vec<bool>,
    cross_on: Vec<bool>,
    /// Sample events this instance took part in (not stopped).
    pub samples: u64,
}

impl CovInstance {
    /// Instantiates `decl` with constructor arguments `ctor`; `topts` are the
    /// covergroup's type options in effect, `auto_name` the name used when
    /// the covergroup does not assign `option.name` (19.7: "unique name").
    /// Returns the instance and the warnings the bins raised.
    pub fn new(
        decl: &CovGroupDecl,
        topts: &TypeOptions,
        auto_name: String,
        ev: &mut dyn Eval,
        ctor: &[Val],
    ) -> Result<(CovInstance, Vec<String>), CovError> {
        let mut options = Options::default();
        for (n, e) in &decl.options {
            let v = e.eval(ev, ctor)?;
            options.set(Level::Covergroup, n, &v)?;
        }
        if options.name.is_empty() {
            options.name = auto_name;
        }
        let mut warnings = Vec::new();
        let mut coverpoints: Vec<Coverpoint> = Vec::with_capacity(decl.coverpoints.len());
        for d in &decl.coverpoints {
            let (cp, w) = Coverpoint::new(d, &options, topts, ev, ctor)?;
            warnings.extend(w);
            coverpoints.push(cp);
        }
        let mut crosses = Vec::with_capacity(decl.crosses.len());
        let mut cross_items = Vec::with_capacity(decl.crosses.len());
        for d in &decl.crosses {
            let mut idx = Vec::with_capacity(d.items.len());
            for item in &d.items {
                let i = coverpoints
                    .iter()
                    .position(|c| &c.name == item)
                    .ok_or_else(|| CovError::CrossSelect {
                        cross: d.name.clone(),
                        detail: format!(
                            "item {item} is not a coverpoint of covergroup {}",
                            decl.name
                        ),
                    })?;
                idx.push(i);
            }
            let refs: Vec<&Coverpoint> = idx.iter().map(|&i| &coverpoints[i]).collect();
            let (x, w) = Cross::new(d, &refs, &options, topts, ev, ctor)?;
            warnings.extend(w);
            crosses.push(x);
            cross_items.push(idx);
        }
        Ok((
            CovInstance {
                type_name: decl.name.clone(),
                options,
                cp_on: vec![true; coverpoints.len()],
                cross_on: vec![true; crosses.len()],
                coverpoints,
                crosses,
                cross_items,
                group_on: true,
                samples: 0,
            },
            warnings,
        ))
    }

    pub fn name(&self) -> &str {
        &self.options.name
    }

    /// `set_inst_name(name)` (19.8).
    pub fn set_inst_name(&mut self, name: &str) {
        self.options.name = name.to_string();
    }

    pub fn coverpoints(&self) -> &[Coverpoint] {
        &self.coverpoints
    }

    pub fn crosses(&self) -> &[Cross] {
        &self.crosses
    }

    pub fn coverpoint(&self, name: &str) -> Option<&Coverpoint> {
        self.coverpoints.iter().find(|c| c.name == name)
    }

    pub fn cross(&self, name: &str) -> Option<&Cross> {
        self.crosses.iter().find(|c| c.name == name)
    }

    /// The reference to the coverpoint or cross called `name`.
    pub fn find_item(&self, name: &str) -> Option<ItemRef> {
        if let Some(i) = self.coverpoints.iter().position(|c| c.name == name) {
            return Some(ItemRef::Coverpoint(i));
        }
        self.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.coverpoints.len(),
            ItemRef::Cross(i) => i < self.crosses.len(),
        };
        if ok {
            Ok(())
        } else {
            Err(CovError::UnknownItem {
                group: self.type_name.clone(),
                item: format!("{item:?}"),
            })
        }
    }

    /// Whether the item is collecting (the covergroup must be started too).
    pub fn is_started(&self, item: ItemRef) -> Result<bool, CovError> {
        self.check(item)?;
        Ok(match item {
            ItemRef::Group => self.group_on,
            ItemRef::Coverpoint(i) => self.cp_on[i],
            ItemRef::Cross(i) => self.cross_on[i],
        })
    }

    /// `start()` (19.8): collection resumes. The covergroup's own switch is
    /// independent of its items': starting the group does not start a coverpoint
    /// that was stopped on its own.
    pub fn start(&mut self, item: ItemRef) -> Result<(), CovError> {
        self.check(item)?;
        match item {
            ItemRef::Group => self.group_on = true,
            ItemRef::Coverpoint(i) => self.cp_on[i] = true,
            ItemRef::Cross(i) => self.cross_on[i] = true,
        }
        Ok(())
    }

    /// `stop()` (19.8): later samples do not count. Sequences in flight are
    /// dropped (the gap breaks consecutiveness, see [`Coverpoint::skip`]).
    pub fn stop(&mut self, item: ItemRef) -> Result<(), CovError> {
        self.check(item)?;
        match item {
            ItemRef::Group => {
                self.group_on = false;
                self.coverpoints.iter_mut().for_each(Coverpoint::skip);
            }
            ItemRef::Coverpoint(i) => {
                self.cp_on[i] = false;
                self.coverpoints[i].skip();
            }
            ItemRef::Cross(i) => self.cross_on[i] = false,
        }
        Ok(())
    }

    /// One sampling event with the `sample()` arguments `sargs` (19.3, 19.8).
    pub fn sample(&mut self, ev: &mut dyn Eval, sargs: &[Val]) -> Result<Vec<CovEvent>, CovError> {
        if !self.group_on {
            return Ok(Vec::new());
        }
        self.samples += 1;
        let mut events = Vec::new();
        for (cp, on) in self.coverpoints.iter_mut().zip(&self.cp_on) {
            if *on {
                events.extend(cp.sample(ev, sargs)?);
            } else {
                cp.skip();
            }
        }
        for ((x, on), idx) in self
            .crosses
            .iter_mut()
            .zip(&self.cross_on)
            .zip(&self.cross_items)
        {
            if *on {
                let cps: Vec<&Coverpoint> = idx.iter().map(|&i| &self.coverpoints[i]).collect();
                events.extend(x.sample(&cps, ev, sargs)?);
            }
        }
        Ok(events)
    }

    /// `option.<name> = v` executed procedurally (19.7). The options only the
    /// definition may set are refused.
    pub fn set_option(&mut self, item: ItemRef, name: &str, v: &Val) -> Result<(), CovError> {
        self.check(item)?;
        let (opts, level) = match item {
            ItemRef::Group => (&mut self.options, Level::Covergroup),
            ItemRef::Coverpoint(i) => (&mut self.coverpoints[i].options, Level::Coverpoint),
            ItemRef::Cross(i) => (&mut self.crosses[i].options, Level::Cross),
        };
        let mut next = opts.clone();
        next.set(level, name, v)?;
        if DEFINITION_ONLY.contains(&name) {
            return Err(CovError::DefinitionOnlyOption {
                option: name.to_string(),
            });
        }
        *opts = next;
        Ok(())
    }

    /// `option.<name>` read back.
    pub fn get_option(&self, item: ItemRef, name: &str) -> Result<Val, CovError> {
        self.check(item)?;
        match item {
            ItemRef::Group => self.options.get(Level::Covergroup, name),
            ItemRef::Coverpoint(i) => self.coverpoints[i].options.get(Level::Coverpoint, name),
            ItemRef::Cross(i) => self.crosses[i].options.get(Level::Cross, name),
        }
    }

    /// The `option.weight` of an item of this instance.
    pub fn weight(&self, item: ItemRef) -> Result<i64, CovError> {
        self.check(item)?;
        Ok(match item {
            ItemRef::Group => self.options.weight,
            ItemRef::Coverpoint(i) => self.coverpoints[i].options.weight,
            ItemRef::Cross(i) => self.crosses[i].options.weight,
        })
    }

    /// The `option.at_least` of an item (the covergroup's own is only the
    /// default its items started with).
    pub fn at_least(&self, item: ItemRef) -> Result<u64, CovError> {
        self.check(item)?;
        Ok(match item {
            ItemRef::Group => self.options.at_least,
            ItemRef::Coverpoint(i) => self.coverpoints[i].options.at_least,
            ItemRef::Cross(i) => self.crosses[i].options.at_least,
        })
    }

    /// The bins an item's coverage is measured over, with hit counts.
    pub fn item_bins(&self, item: ItemRef) -> Result<Vec<(String, u64)>, CovError> {
        self.check(item)?;
        match item {
            ItemRef::Group => Err(CovError::Unsupported(
                "a covergroup has coverpoints and crosses, not bins".to_string(),
            )),
            ItemRef::Coverpoint(i) => Ok(self.coverpoints[i].coverage_bins()),
            ItemRef::Cross(i) => Ok(self.crosses[i].coverage_bins()),
        }
    }

    /// `get_inst_coverage()` of this instance's own bins (19.11.1-19.11.2,
    /// the weighted average of 19.11 for the group). 19.11.3's exception for
    /// `merge_instances` is applied by the caller that knows the type.
    pub fn own_score(&self, item: ItemRef) -> Result<Score, CovError> {
        self.check(item)?;
        Ok(match item {
            ItemRef::Group => {
                let mut parts = Vec::new();
                for (i, c) in self.coverpoints.iter().enumerate() {
                    parts.push((c.options.weight, self.own_score(ItemRef::Coverpoint(i))?));
                }
                for (i, x) in self.crosses.iter().enumerate() {
                    parts.push((x.options.weight, self.own_score(ItemRef::Cross(i))?));
                }
                weighted_score(self.options.weight, &parts)
            }
            ItemRef::Coverpoint(i) => {
                let c = &self.coverpoints[i];
                item_score(c.options.weight, c.coverage())
            }
            ItemRef::Cross(i) => {
                let x = &self.crosses[i];
                item_score(x.options.weight, x.coverage())
            }
        })
    }
}