1use std::fmt;
16
17#[derive(Clone, Debug, PartialEq)]
19pub enum Val {
20 Int(i128),
22 Real(f64),
23 Str(String),
24 List(Vec<Val>),
26 Unknown,
28 Pat {
31 bits: u128,
32 care: u128,
33 },
34}
35
36pub type KernelId = u32;
37
38#[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
62pub trait Eval {
64 fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError>;
65}
66
67pub 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#[derive(Clone, Copy, Debug, PartialEq, Eq)]
78pub struct TypeInfo {
79 pub width: u32,
80 pub signed: bool,
81 pub real: bool,
82}
83
84pub 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 pub fn dmin(&self) -> i128 {
114 if self.signed {
115 -(1i128 << (self.width - 1))
116 } else {
117 0
118 }
119 }
120
121 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 pub fn bits_of(&self, v: i128) -> u128 {
132 (v as u128) & self.mask()
133 }
134
135 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#[derive(Clone, Debug, PartialEq)]
152pub enum CovError {
153 NoKernel(KernelId),
155 BadValue {
157 ctx: String,
158 got: String,
159 },
160 TooManyValues {
162 ctx: String,
163 limit: u128,
164 },
165 NonPositiveBins {
167 bin: String,
168 n: i128,
169 },
170 RealUnsupported {
172 bin: String,
173 what: &'static str,
174 },
175 RealNeedsBins {
177 coverpoint: String,
178 },
179 RealDefaultArray {
181 bin: String,
182 },
183 WidthTooLarge {
185 coverpoint: String,
186 width: u32,
187 },
188 TransUnbounded {
191 bin: String,
192 what: &'static str,
193 },
194 TransZeroLength {
196 bin: String,
197 },
198 TransRepeat {
200 bin: String,
201 detail: String,
202 },
203 CrossSelect {
206 cross: String,
207 detail: String,
208 },
209 BadHandle(u64),
211 UnknownItem {
213 group: String,
214 item: String,
215 },
216 DuplicateItem {
218 group: String,
219 item: String,
220 },
221 DefinitionOnlyOption {
224 option: String,
225 },
226 BadDb {
228 line: usize,
229 detail: String,
230 },
231 UnknownOption(String),
232 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 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}