Skip to main content

bitloom_sim/
coverage.rs

1//! Stable, parseable simulation coverage (FR34): at least one hit and one miss.
2
3use std::collections::{BTreeMap, BTreeSet};
4
5/// Toggle coverage over named ports/regs sampled each tick.
6#[derive(Debug, Clone, Default)]
7pub struct Coverage {
8    prev: BTreeMap<String, u64>,
9    toggled: BTreeSet<String>,
10    seen: BTreeSet<String>,
11}
12
13impl Coverage {
14    pub fn sample(&mut self, name: impl Into<String>, value: u64) {
15        let name = name.into();
16        self.seen.insert(name.clone());
17        if let Some(old) = self.prev.get(&name)
18            && *old != value
19        {
20            self.toggled.insert(name.clone());
21        }
22        self.prev.insert(name, value);
23    }
24
25    pub fn hits(&self) -> impl Iterator<Item = &str> {
26        self.toggled.iter().map(|s| s.as_str())
27    }
28
29    pub fn misses(&self) -> impl Iterator<Item = &str> {
30        self.seen.difference(&self.toggled).map(|s| s.as_str())
31    }
32
33    /// Stable line-oriented report (`# bitloom-sim coverage v1`).
34    pub fn report(&self) -> String {
35        let mut out = String::from("# bitloom-sim coverage v1\n");
36        for n in self.hits() {
37            out.push_str(&format!("hit {n}\n"));
38        }
39        for n in self.misses() {
40            out.push_str(&format!("miss {n}\n"));
41        }
42        out
43    }
44}
45
46pub fn parse_report(text: &str) -> (Vec<String>, Vec<String>) {
47    let mut hits = Vec::new();
48    let mut misses = Vec::new();
49    for line in text.lines() {
50        if let Some(n) = line.strip_prefix("hit ") {
51            hits.push(n.to_string());
52        } else if let Some(n) = line.strip_prefix("miss ") {
53            misses.push(n.to_string());
54        }
55    }
56    (hits, misses)
57}