use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum Val {
Int(i128),
Real(f64),
Str(String),
List(Vec<Val>),
Unknown,
Pat {
bits: u128,
care: u128,
},
}
pub type KernelId = u32;
#[derive(Clone, Debug, PartialEq)]
pub enum Expr {
Const(Val),
Kernel(KernelId),
}
impl Expr {
pub fn int(v: i128) -> Expr {
Expr::Const(Val::Int(v))
}
pub fn real(v: f64) -> Expr {
Expr::Const(Val::Real(v))
}
pub fn eval(&self, ev: &mut dyn Eval, args: &[Val]) -> Result<Val, CovError> {
match self {
Expr::Const(v) => Ok(v.clone()),
Expr::Kernel(id) => ev.eval(*id, args),
}
}
}
pub trait Eval {
fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError>;
}
pub struct NoKernels;
impl Eval for NoKernels {
fn eval(&mut self, id: KernelId, _args: &[Val]) -> Result<Val, CovError> {
Err(CovError::NoKernel(id))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct TypeInfo {
pub width: u32,
pub signed: bool,
pub real: bool,
}
pub const MAX_WIDTH: u32 = 64;
impl TypeInfo {
pub fn unsigned(width: u32) -> TypeInfo {
TypeInfo {
width,
signed: false,
real: false,
}
}
pub fn signed(width: u32) -> TypeInfo {
TypeInfo {
width,
signed: true,
real: false,
}
}
pub fn real() -> TypeInfo {
TypeInfo {
width: 64,
signed: true,
real: true,
}
}
pub fn dmin(&self) -> i128 {
if self.signed {
-(1i128 << (self.width - 1))
} else {
0
}
}
pub fn dmax(&self) -> i128 {
if self.signed {
(1i128 << (self.width - 1)) - 1
} else {
(1i128 << self.width) - 1
}
}
pub fn bits_of(&self, v: i128) -> u128 {
(v as u128) & self.mask()
}
pub fn value_of(&self, bits: u128) -> i128 {
let bits = bits & self.mask();
if self.signed && ((bits >> (self.width - 1)) & 1) == 1 {
(bits as i128) - (1i128 << self.width)
} else {
bits as i128
}
}
pub fn mask(&self) -> u128 {
(1u128 << self.width) - 1
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum CovError {
NoKernel(KernelId),
BadValue {
ctx: String,
got: String,
},
TooManyValues {
ctx: String,
limit: u128,
},
NonPositiveBins {
bin: String,
n: i128,
},
RealUnsupported {
bin: String,
what: &'static str,
},
RealNeedsBins {
coverpoint: String,
},
RealDefaultArray {
bin: String,
},
WidthTooLarge {
coverpoint: String,
width: u32,
},
TransUnbounded {
bin: String,
what: &'static str,
},
TransZeroLength {
bin: String,
},
TransRepeat {
bin: String,
detail: String,
},
CrossSelect {
cross: String,
detail: String,
},
BadHandle(u64),
UnknownItem {
group: String,
item: String,
},
DuplicateItem {
group: String,
item: String,
},
DefinitionOnlyOption {
option: String,
},
BadDb {
line: usize,
detail: String,
},
UnknownOption(String),
OptionLevel {
option: String,
level: &'static str,
},
OptionType {
option: String,
},
OptionValue {
option: String,
detail: String,
},
Unsupported(String),
}
impl fmt::Display for CovError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CovError::NoKernel(id) => write!(f, "coverage kernel {id} has no evaluator"),
CovError::BadValue { ctx, got } => write!(f, "{ctx}: unexpected value {got}"),
CovError::TooManyValues { ctx, limit } => {
write!(f, "{ctx}: more than {limit} individual values to expand")
}
CovError::NonPositiveBins { bin, n } => {
write!(f, "bin {bin}: number of bins must be positive, got {n}")
}
CovError::RealUnsupported { bin, what } => {
write!(f, "bin {bin}: {what} is not allowed for real coverpoints")
}
CovError::RealNeedsBins { coverpoint } => write!(
f,
"coverpoint {coverpoint}: real coverpoints need at least one explicit bins construct"
),
CovError::RealDefaultArray { bin } => write!(
f,
"bin {bin}: a default bin of a real coverpoint cannot be an array"
),
CovError::WidthTooLarge { coverpoint, width } => write!(
f,
"coverpoint {coverpoint}: {width}-bit coverpoints are not supported (max {MAX_WIDTH})"
),
CovError::TransUnbounded { bin, what } => write!(
f,
"bin {bin}: a transition of unbounded or varying length (goto or nonconsecutive repetition) cannot be used for {what}"
),
CovError::TransZeroLength { bin } => write!(
f,
"bin {bin}: a transition bin of length 0 is illegal (19.5.2)"
),
CovError::TransRepeat { bin, detail } => {
write!(f, "bin {bin}: bad repeat range: {detail}")
}
CovError::CrossSelect { cross, detail } => write!(f, "cross {cross}: {detail}"),
CovError::BadHandle(h) => write!(f, "{h:#x} is not a covergroup handle"),
CovError::UnknownItem { group, item } => {
write!(f, "covergroup {group} has no coverpoint or cross '{item}'")
}
CovError::DuplicateItem { group, item } => write!(
f,
"covergroup {group} declares the coverpoint or cross '{item}' twice"
),
CovError::DefinitionOnlyOption { option } => write!(
f,
"coverage option '{option}' can only be set in the covergroup definition (19.7)"
),
CovError::BadDb { line, detail } => {
write!(f, "coverage database line {line}: {detail}")
}
CovError::UnknownOption(n) => write!(f, "unknown coverage option '{n}'"),
CovError::OptionLevel { option, level } => {
write!(
f,
"coverage option '{option}' is not allowed at {level} level"
)
}
CovError::OptionType { option } => {
write!(
f,
"coverage option '{option}' assigned a value of the wrong type"
)
}
CovError::OptionValue { option, detail } => {
write!(f, "coverage option '{option}': {detail}")
}
CovError::Unsupported(what) => write!(f, "unsupported covergroup construct: {what}"),
}
}
}
impl std::error::Error for CovError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn domain_of_signed_and_unsigned() {
let u = TypeInfo::unsigned(3);
assert_eq!((u.dmin(), u.dmax()), (0, 7));
let s = TypeInfo::signed(3);
assert_eq!((s.dmin(), s.dmax()), (-4, 3));
assert_eq!(TypeInfo::unsigned(64).dmax(), u64::MAX as i128);
assert_eq!(TypeInfo::signed(64).dmin(), i64::MIN as i128);
}
#[test]
fn bits_roundtrip_for_negative_values() {
let s = TypeInfo::signed(4);
assert_eq!(s.bits_of(-1), 0b1111);
assert_eq!(s.value_of(0b1000), -8);
assert_eq!(s.value_of(s.bits_of(-5)), -5);
let u = TypeInfo::unsigned(4);
assert_eq!(u.value_of(0b1000), 8);
}
#[test]
fn expr_const_and_kernel() {
struct Doubler;
impl Eval for Doubler {
fn eval(&mut self, id: KernelId, args: &[Val]) -> Result<Val, CovError> {
assert_eq!(id, 7);
match args {
[Val::Int(v)] => Ok(Val::Int(v * 2)),
_ => Err(CovError::NoKernel(id)),
}
}
}
assert_eq!(Expr::int(5).eval(&mut NoKernels, &[]), Ok(Val::Int(5)));
assert_eq!(
Expr::Kernel(7).eval(&mut Doubler, &[Val::Int(21)]),
Ok(Val::Int(42))
);
assert_eq!(
Expr::Kernel(3).eval(&mut NoKernels, &[]),
Err(CovError::NoKernel(3))
);
}
}