use vj_sdf::ast::CheckKind;
use vj_sim::netlist::specify::CheckArgs;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(crate) enum SvCheck {
Setup,
Hold,
SetupHold,
Recovery,
Removal,
RecRem,
Skew,
TimeSkew,
FullSkew,
Width,
Period,
NoChange,
}
impl SvCheck {
pub(crate) fn of(args: &CheckArgs) -> SvCheck {
match args {
CheckArgs::Setup(_) => SvCheck::Setup,
CheckArgs::Hold(_) => SvCheck::Hold,
CheckArgs::SetupHold(_) => SvCheck::SetupHold,
CheckArgs::Recovery(_) => SvCheck::Recovery,
CheckArgs::Removal(_) => SvCheck::Removal,
CheckArgs::RecRem(_) => SvCheck::RecRem,
CheckArgs::Skew(_) => SvCheck::Skew,
CheckArgs::TimeSkew(_) => SvCheck::TimeSkew,
CheckArgs::FullSkew(_) => SvCheck::FullSkew,
CheckArgs::Width(_) => SvCheck::Width,
CheckArgs::Period(_) => SvCheck::Period,
CheckArgs::NoChange(_) => SvCheck::NoChange,
}
}
pub(crate) fn name(self) -> &'static str {
match self {
SvCheck::Setup => "$setup",
SvCheck::Hold => "$hold",
SvCheck::SetupHold => "$setuphold",
SvCheck::Recovery => "$recovery",
SvCheck::Removal => "$removal",
SvCheck::RecRem => "$recrem",
SvCheck::Skew => "$skew",
SvCheck::TimeSkew => "$timeskew",
SvCheck::FullSkew => "$fullskew",
SvCheck::Width => "$width",
SvCheck::Period => "$period",
SvCheck::NoChange => "$nochange",
}
}
}
pub(crate) struct Target {
pub(crate) check: SvCheck,
pub(crate) slots: &'static [(usize, usize)],
}
fn t(check: SvCheck, slots: &'static [(usize, usize)]) -> Target {
Target { check, slots }
}
pub(crate) fn fan_out(kind: CheckKind) -> Vec<Target> {
use SvCheck as S;
match kind {
CheckKind::Setup => vec![t(S::Setup, &[(0, 0)]), t(S::SetupHold, &[(0, 0)])],
CheckKind::Hold => vec![t(S::Hold, &[(0, 0)]), t(S::SetupHold, &[(0, 1)])],
CheckKind::SetupHold => vec![
t(S::Setup, &[(0, 0)]),
t(S::Hold, &[(1, 0)]),
t(S::SetupHold, &[(0, 0), (1, 1)]),
],
CheckKind::Recovery => vec![t(S::Recovery, &[(0, 0)]), t(S::RecRem, &[(0, 0)])],
CheckKind::Removal => vec![t(S::Removal, &[(0, 0)]), t(S::RecRem, &[(0, 1)])],
CheckKind::Recrem => vec![
t(S::Recovery, &[(0, 0)]),
t(S::Removal, &[(1, 0)]),
t(S::RecRem, &[(0, 0), (1, 1)]),
],
CheckKind::Skew => vec![t(S::Skew, &[(0, 0)]), t(S::TimeSkew, &[(0, 0)])],
CheckKind::BidirectSkew => vec![t(S::FullSkew, &[(0, 0), (1, 1)])],
CheckKind::Width => vec![t(S::Width, &[(0, 0)])],
CheckKind::Period => vec![t(S::Period, &[(0, 0)])],
CheckKind::NoChange => vec![t(S::NoChange, &[(0, 0), (1, 1)])],
}
}
pub(crate) fn value_count(kind: CheckKind) -> usize {
match kind {
CheckKind::SetupHold
| CheckKind::Recrem
| CheckKind::BidirectSkew
| CheckKind::NoChange => 2,
CheckKind::Setup
| CheckKind::Hold
| CheckKind::Recovery
| CheckKind::Removal
| CheckKind::Skew
| CheckKind::Width
| CheckKind::Period => 1,
}
}
pub(crate) fn sdf_name(kind: CheckKind) -> &'static str {
match kind {
CheckKind::Setup => "SETUP",
CheckKind::Hold => "HOLD",
CheckKind::SetupHold => "SETUPHOLD",
CheckKind::Recovery => "RECOVERY",
CheckKind::Removal => "REMOVAL",
CheckKind::Recrem => "RECREM",
CheckKind::Skew => "SKEW",
CheckKind::BidirectSkew => "BIDIRECTSKEW",
CheckKind::Width => "WIDTH",
CheckKind::Period => "PERIOD",
CheckKind::NoChange => "NOCHANGE",
}
}
pub(crate) fn first_port_is_data(kind: CheckKind) -> bool {
!matches!(
kind,
CheckKind::Skew | CheckKind::BidirectSkew | CheckKind::Width | CheckKind::Period
)
}
#[cfg(test)]
mod tests {
use super::*;
fn row(kind: CheckKind) -> Vec<(&'static str, Vec<(usize, usize)>)> {
fan_out(kind)
.iter()
.map(|t| (t.check.name(), t.slots.to_vec()))
.collect()
}
#[test]
fn table_32_2_rows() {
assert_eq!(
row(CheckKind::Setup),
[("$setup", vec![(0, 0)]), ("$setuphold", vec![(0, 0)])]
);
assert_eq!(
row(CheckKind::Hold),
[("$hold", vec![(0, 0)]), ("$setuphold", vec![(0, 1)])]
);
assert_eq!(
row(CheckKind::SetupHold),
[
("$setup", vec![(0, 0)]),
("$hold", vec![(1, 0)]),
("$setuphold", vec![(0, 0), (1, 1)])
]
);
assert_eq!(
row(CheckKind::Recovery),
[("$recovery", vec![(0, 0)]), ("$recrem", vec![(0, 0)])]
);
assert_eq!(
row(CheckKind::Removal),
[("$removal", vec![(0, 0)]), ("$recrem", vec![(0, 1)])]
);
assert_eq!(
row(CheckKind::Recrem),
[
("$recovery", vec![(0, 0)]),
("$removal", vec![(1, 0)]),
("$recrem", vec![(0, 0), (1, 1)])
]
);
assert_eq!(
row(CheckKind::Skew),
[("$skew", vec![(0, 0)]), ("$timeskew", vec![(0, 0)])]
);
assert_eq!(
row(CheckKind::BidirectSkew),
[("$fullskew", vec![(0, 0), (1, 1)])]
);
assert_eq!(row(CheckKind::Width), [("$width", vec![(0, 0)])]);
assert_eq!(row(CheckKind::Period), [("$period", vec![(0, 0)])]);
assert_eq!(
row(CheckKind::NoChange),
[("$nochange", vec![(0, 0), (1, 1)])]
);
}
#[test]
fn table_32_2_value_indices_are_in_range() {
for kind in [
CheckKind::Setup,
CheckKind::Hold,
CheckKind::SetupHold,
CheckKind::Recovery,
CheckKind::Removal,
CheckKind::Recrem,
CheckKind::Skew,
CheckKind::BidirectSkew,
CheckKind::Width,
CheckKind::Period,
CheckKind::NoChange,
] {
for target in fan_out(kind) {
for &(v, _) in target.slots {
assert!(v < value_count(kind), "{kind:?} -> {}", target.check.name());
}
}
}
}
}