Skip to main content

sv_sim_sim/covergroup/
value.rs

1//! Values, expressions and errors of the functional-coverage model.
2//!
3//! The model is pure: it never touches the simulator. Everything that depends
4//! on the running design (a coverpoint expression, a bin `iff`, a constructor
5//! argument used in a bin range, a `with` clause) is an [`Expr`], either a
6//! constant or an opaque [`KernelId`] that an [`Eval`] implementation runs.
7//!
8//! Kernel argument convention (fixed here, honoured by the codegen side):
9//! * construction-time kernels (bin ranges, `with`, set expressions,
10//!   `number_of_bins`, option values) receive the constructor arguments; a
11//!   `with` kernel receives them followed by one extra value, `item`;
12//! * sample-time kernels (coverpoint expression, coverpoint/bin `iff`) receive
13//!   the constructor arguments followed by the `sample()` arguments.
14
15use std::fmt;
16
17/// A value crossing the model/design boundary.
18#[derive(Clone, Debug, PartialEq)]
19pub enum Val {
20    /// A two-state integral value (sign-extended to `i128`).
21    Int(i128),
22    Real(f64),
23    Str(String),
24    /// An array-valued expression (a `set_covergroup_expression`).
25    List(Vec<Val>),
26    /// An integral value with an x or z bit. Never matches a non-wildcard bin.
27    Unknown,
28    /// A wildcard bin value: `care` has a 1 in every bit position that is a
29    /// known 0/1 in `bits`; the other positions were x, z or `?`.
30    Pat {
31        bits: u128,
32        care: u128,
33    },
34}
35
36pub type KernelId = u32;
37
38/// A model input: a constant, or a kernel to be run by an [`Eval`].
39#[derive(Clone, Debug, PartialEq)]
40pub enum Expr {
41    Const(Val),
42    Kernel(KernelId),
43}
44
45impl Expr {
46    pub fn int(v: i128) -> Expr {
47        Expr::Const(Val::Int(v))
48    }
49
50    pub fn real(v: f64) -> Expr {
51        Expr::Const(Val::Real(v))
52    }
53
54    pub fn eval(&self, ev: &mut dyn Eval, args: &[Val]) -> Result<Val, CovError> {
55        match self {
56            Expr::Const(v) => Ok(v.clone()),
57            Expr::Kernel(id) => ev.eval(*id, args),
58        }
59    }
60}
61
62/// Runs kernels. `args` follows the convention in the module docs.
63pub trait Eval {
64    fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError>;
65}
66
67/// An [`Eval`] for models built only from constants: any kernel is an error.
68pub struct NoKernels;
69
70impl Eval for NoKernels {
71    fn eval(&mut self, id: KernelId, _args: &[Val]) -> Result<Val, CovError> {
72        Err(CovError::NoKernel(id))
73    }
74}
75
76/// The type of a coverpoint expression.
77#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct TypeInfo {
79    pub width: u32,
80    pub signed: bool,
81    pub real: bool,
82}
83
84/// The widest integral coverpoint the model represents (values fit `i128`).
85pub const MAX_WIDTH: u32 = 64;
86
87impl TypeInfo {
88    pub fn unsigned(width: u32) -> TypeInfo {
89        TypeInfo {
90            width,
91            signed: false,
92            real: false,
93        }
94    }
95
96    pub fn signed(width: u32) -> TypeInfo {
97        TypeInfo {
98            width,
99            signed: true,
100            real: false,
101        }
102    }
103
104    pub fn real() -> TypeInfo {
105        TypeInfo {
106            width: 64,
107            signed: true,
108            real: true,
109        }
110    }
111
112    /// Smallest value expressible by an integral type.
113    pub fn dmin(&self) -> i128 {
114        if self.signed {
115            -(1i128 << (self.width - 1))
116        } else {
117            0
118        }
119    }
120
121    /// Largest value expressible by an integral type.
122    pub fn dmax(&self) -> i128 {
123        if self.signed {
124            (1i128 << (self.width - 1)) - 1
125        } else {
126            (1i128 << self.width) - 1
127        }
128    }
129
130    /// The two's-complement bit pattern of `v` in this type's width.
131    pub fn bits_of(&self, v: i128) -> u128 {
132        (v as u128) & self.mask()
133    }
134
135    /// The value whose bit pattern is `bits`.
136    pub fn value_of(&self, bits: u128) -> i128 {
137        let bits = bits & self.mask();
138        if self.signed && ((bits >> (self.width - 1)) & 1) == 1 {
139            (bits as i128) - (1i128 << self.width)
140        } else {
141            bits as i128
142        }
143    }
144
145    pub fn mask(&self) -> u128 {
146        (1u128 << self.width) - 1
147    }
148}
149
150/// Everything the model rejects, loudly.
151#[derive(Clone, Debug, PartialEq)]
152pub enum CovError {
153    /// A kernel was needed but the evaluator has none.
154    NoKernel(KernelId),
155    /// An expression evaluated to a value of the wrong kind.
156    BadValue {
157        ctx: String,
158        got: String,
159    },
160    /// A bin would need more individual values than the model expands.
161    TooManyValues {
162        ctx: String,
163        limit: u128,
164    },
165    /// `bins b[N]` with `N` not positive.
166    NonPositiveBins {
167        bin: String,
168        n: i128,
169    },
170    /// `with`/wildcard/transition bins on a real coverpoint (19.5.1, 19.5.2).
171    RealUnsupported {
172        bin: String,
173        what: &'static str,
174    },
175    /// A real coverpoint without a single explicit bin (19.5).
176    RealNeedsBins {
177        coverpoint: String,
178    },
179    /// A default bin of a real coverpoint with `[]` or `[N]` (19.5.1).
180    RealDefaultArray {
181        bin: String,
182    },
183    /// A coverpoint wider than [`MAX_WIDTH`].
184    WidthTooLarge {
185        coverpoint: String,
186        width: u32,
187    },
188    /// A transition bin that needs a bounded sequence but has a goto or
189    /// nonconsecutive repetition (19.5.2, 19.5.5, 19.5.6).
190    TransUnbounded {
191        bin: String,
192        what: &'static str,
193    },
194    /// A transition bin of length 0 (19.5.2).
195    TransZeroLength {
196        bin: String,
197    },
198    /// A bad repeat range in a transition bin (negative, reversed, zero goto).
199    TransRepeat {
200        bin: String,
201        detail: String,
202    },
203    /// A malformed cross or select expression (an operand that is not crossed,
204    /// a bin that does not exist, a non-positive `matches`), 19.6.
205    CrossSelect {
206        cross: String,
207        detail: String,
208    },
209    /// A handle that names no covergroup instance or type.
210    BadHandle(u64),
211    /// A coverpoint/cross name the covergroup does not declare.
212    UnknownItem {
213        group: String,
214        item: String,
215    },
216    /// Two coverpoints/crosses of one covergroup with the same name.
217    DuplicateItem {
218        group: String,
219        item: String,
220    },
221    /// An option that 19.7/19.7.1 allow only in the covergroup definition,
222    /// assigned procedurally.
223    DefinitionOnlyOption {
224        option: String,
225    },
226    /// A coverage database file that does not parse (19.9), by 1-based line.
227    BadDb {
228        line: usize,
229        detail: String,
230    },
231    UnknownOption(String),
232    /// An option used at a level where Table 19-2/19-3 does not allow it.
233    OptionLevel {
234        option: String,
235        level: &'static str,
236    },
237    OptionType {
238        option: String,
239    },
240    OptionValue {
241        option: String,
242        detail: String,
243    },
244    /// Valid SystemVerilog the model does not implement (named, never silent).
245    Unsupported(String),
246}
247
248impl fmt::Display for CovError {
249    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
250        match self {
251            CovError::NoKernel(id) => write!(f, "coverage kernel {id} has no evaluator"),
252            CovError::BadValue { ctx, got } => write!(f, "{ctx}: unexpected value {got}"),
253            CovError::TooManyValues { ctx, limit } => {
254                write!(f, "{ctx}: more than {limit} individual values to expand")
255            }
256            CovError::NonPositiveBins { bin, n } => {
257                write!(f, "bin {bin}: number of bins must be positive, got {n}")
258            }
259            CovError::RealUnsupported { bin, what } => {
260                write!(f, "bin {bin}: {what} is not allowed for real coverpoints")
261            }
262            CovError::RealNeedsBins { coverpoint } => write!(
263                f,
264                "coverpoint {coverpoint}: real coverpoints need at least one explicit bins construct"
265            ),
266            CovError::RealDefaultArray { bin } => write!(
267                f,
268                "bin {bin}: a default bin of a real coverpoint cannot be an array"
269            ),
270            CovError::WidthTooLarge { coverpoint, width } => write!(
271                f,
272                "coverpoint {coverpoint}: {width}-bit coverpoints are not supported (max {MAX_WIDTH})"
273            ),
274            CovError::TransUnbounded { bin, what } => write!(
275                f,
276                "bin {bin}: a transition of unbounded or varying length (goto or nonconsecutive repetition) cannot be used for {what}"
277            ),
278            CovError::TransZeroLength { bin } => write!(
279                f,
280                "bin {bin}: a transition bin of length 0 is illegal (19.5.2)"
281            ),
282            CovError::TransRepeat { bin, detail } => {
283                write!(f, "bin {bin}: bad repeat range: {detail}")
284            }
285            CovError::CrossSelect { cross, detail } => write!(f, "cross {cross}: {detail}"),
286            CovError::BadHandle(h) => write!(f, "{h:#x} is not a covergroup handle"),
287            CovError::UnknownItem { group, item } => {
288                write!(f, "covergroup {group} has no coverpoint or cross '{item}'")
289            }
290            CovError::DuplicateItem { group, item } => write!(
291                f,
292                "covergroup {group} declares the coverpoint or cross '{item}' twice"
293            ),
294            CovError::DefinitionOnlyOption { option } => write!(
295                f,
296                "coverage option '{option}' can only be set in the covergroup definition (19.7)"
297            ),
298            CovError::BadDb { line, detail } => {
299                write!(f, "coverage database line {line}: {detail}")
300            }
301            CovError::UnknownOption(n) => write!(f, "unknown coverage option '{n}'"),
302            CovError::OptionLevel { option, level } => {
303                write!(
304                    f,
305                    "coverage option '{option}' is not allowed at {level} level"
306                )
307            }
308            CovError::OptionType { option } => {
309                write!(
310                    f,
311                    "coverage option '{option}' assigned a value of the wrong type"
312                )
313            }
314            CovError::OptionValue { option, detail } => {
315                write!(f, "coverage option '{option}': {detail}")
316            }
317            CovError::Unsupported(what) => write!(f, "unsupported covergroup construct: {what}"),
318        }
319    }
320}
321
322impl std::error::Error for CovError {}
323
324#[cfg(test)]
325mod tests {
326    use super::*;
327
328    #[test]
329    fn domain_of_signed_and_unsigned() {
330        let u = TypeInfo::unsigned(3);
331        assert_eq!((u.dmin(), u.dmax()), (0, 7));
332        let s = TypeInfo::signed(3);
333        assert_eq!((s.dmin(), s.dmax()), (-4, 3));
334        assert_eq!(TypeInfo::unsigned(64).dmax(), u64::MAX as i128);
335        assert_eq!(TypeInfo::signed(64).dmin(), i64::MIN as i128);
336    }
337
338    #[test]
339    fn bits_roundtrip_for_negative_values() {
340        let s = TypeInfo::signed(4);
341        assert_eq!(s.bits_of(-1), 0b1111);
342        assert_eq!(s.value_of(0b1000), -8);
343        assert_eq!(s.value_of(s.bits_of(-5)), -5);
344        let u = TypeInfo::unsigned(4);
345        assert_eq!(u.value_of(0b1000), 8);
346    }
347
348    #[test]
349    fn expr_const_and_kernel() {
350        struct Doubler;
351        impl Eval for Doubler {
352            fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError> {
353                assert_eq!(id, 7);
354                match args {
355                    [Val::Int(v)] => Ok(Val::Int(v * 2)),
356                    _ => Err(CovError::NoKernel(id)),
357                }
358            }
359        }
360        assert_eq!(Expr::int(5).eval(&mut NoKernels, &[]), Ok(Val::Int(5)));
361        assert_eq!(
362            Expr::Kernel(7).eval(&mut Doubler, &[Val::Int(21)]),
363            Ok(Val::Int(42))
364        );
365        assert_eq!(
366            Expr::Kernel(3).eval(&mut NoKernels, &[]),
367            Err(CovError::NoKernel(3))
368        );
369    }
370}