use serde::{Deserialize, Serialize};
use vj_abi::bit4::Bit4;
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct PathDelaySet {
t: [u64; 12],
}
pub const TRANSITIONS: [(Bit4, Bit4); 12] = [
(Bit4::Zero, Bit4::One),
(Bit4::One, Bit4::Zero),
(Bit4::Zero, Bit4::Z),
(Bit4::Z, Bit4::One),
(Bit4::One, Bit4::Z),
(Bit4::Z, Bit4::Zero),
(Bit4::Zero, Bit4::X),
(Bit4::X, Bit4::One),
(Bit4::One, Bit4::X),
(Bit4::X, Bit4::Zero),
(Bit4::X, Bit4::Z),
(Bit4::Z, Bit4::X),
];
fn transition_index(from: Bit4, to: Bit4) -> usize {
use Bit4::{One, X, Z, Zero};
match (from, to) {
(Zero, One) => 0,
(One, Zero) => 1,
(Zero, Z) => 2,
(Z, One) => 3,
(One, Z) => 4,
(Z, Zero) => 5,
(Zero, X) => 6,
(X, One) => 7,
(One, X) => 8,
(X, Zero) => 9,
(X, Z) => 10,
(Z, X) => 11,
(Zero, Zero) | (One, One) | (X, X) | (Z, Z) => {
unreachable!(
"transition_index: from == to ({from:?}) is not a transition -- every caller \
must guard against this before calling"
)
}
}
}
impl PathDelaySet {
pub fn from_list(d: &[u64]) -> Result<PathDelaySet, String> {
if let [t01, t10, t0z, tz1, t1z, tz0, t0x, tx1, t1x, tx0, txz, tzx] = d {
return Ok(PathDelaySet {
t: [
*t01, *t10, *t0z, *tz1, *t1z, *tz0, *t0x, *tx1, *t1x, *tx0, *txz, *tzx,
],
});
}
let (r01, r10, r0z, rz1, r1z, rz0) = match d {
[] => return Err("a path delay with no values (unexpected AST shape)".to_string()),
[t] => (*t, *t, *t, *t, *t, *t),
[r, f] => (*r, *f, *r, *r, *f, *f),
[r, f, z] => (*r, *f, *z, *r, *z, *f),
[t01, t10, t0z, tz1, t1z, tz0] => (*t01, *t10, *t0z, *tz1, *t1z, *tz0),
_ => {
return Err(format!(
"a {}-value path delay list (LRM Table 30-2 defines only the 1-, 2-, 3-, \
6- and 12-value `path_delay_value` forms)",
d.len()
));
}
};
let mut t = [0u64; 12];
t[transition_index(Bit4::Zero, Bit4::One)] = r01;
t[transition_index(Bit4::One, Bit4::Zero)] = r10;
t[transition_index(Bit4::Zero, Bit4::Z)] = r0z;
t[transition_index(Bit4::Z, Bit4::One)] = rz1;
t[transition_index(Bit4::One, Bit4::Z)] = r1z;
t[transition_index(Bit4::Z, Bit4::Zero)] = rz0;
t[transition_index(Bit4::Zero, Bit4::X)] = r0z.min(r01);
t[transition_index(Bit4::One, Bit4::X)] = r1z.min(r10);
t[transition_index(Bit4::Z, Bit4::X)] = rz1.min(rz0);
t[transition_index(Bit4::X, Bit4::Zero)] = rz0.max(r10);
t[transition_index(Bit4::X, Bit4::One)] = rz1.max(r01);
t[transition_index(Bit4::X, Bit4::Z)] = r1z.max(r0z);
Ok(PathDelaySet { t })
}
pub fn from_transitions(t: [u64; 12]) -> PathDelaySet {
PathDelaySet { t }
}
pub fn transitions(&self) -> [u64; 12] {
self.t
}
pub fn uniform(v: u64) -> PathDelaySet {
PathDelaySet { t: [v; 12] }
}
pub fn of(&self, from: Bit4, to: Bit4) -> u64 {
self.t[transition_index(from, to)]
}
pub fn at_least(self, floor: u64) -> PathDelaySet {
PathDelaySet {
t: self.t.map(|d| d.max(floor)),
}
}
pub fn min_all(&self) -> u64 {
self.t.iter().copied().min().unwrap_or(0)
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
pub enum PulseStyle {
#[default]
OnEvent,
OnDetect,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CondUnaryOp {
BitwiseNot,
ReductionAnd,
ReductionOr,
ReductionXor,
ReductionNand,
ReductionNor,
ReductionXnor,
LogicalNot,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub enum CondBinaryOp {
BitwiseAnd,
BitwiseOr,
BitwiseXor,
BitwiseXnor,
Equality,
Inequality,
LogicalAnd,
LogicalOr,
CaseEquality,
CaseInequality,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EdgeTransition {
ZeroOne,
OneZero,
ZeroX,
XZero,
OneX,
XOne,
}
impl EdgeTransition {
pub const ALL: [EdgeTransition; 6] = [
EdgeTransition::ZeroOne,
EdgeTransition::ZeroX,
EdgeTransition::OneZero,
EdgeTransition::OneX,
EdgeTransition::XZero,
EdgeTransition::XOne,
];
fn mask(self) -> u8 {
1 << (self as u8)
}
pub fn reversed(self) -> EdgeTransition {
match self {
EdgeTransition::ZeroOne => EdgeTransition::OneZero,
EdgeTransition::OneZero => EdgeTransition::ZeroOne,
EdgeTransition::ZeroX => EdgeTransition::XZero,
EdgeTransition::XZero => EdgeTransition::ZeroX,
EdgeTransition::OneX => EdgeTransition::XOne,
EdgeTransition::XOne => EdgeTransition::OneX,
}
}
pub fn of_descriptor(d: &str) -> Option<EdgeTransition> {
let &[a, b] = d.as_bytes() else { return None };
let unknown = |c: u8| matches!(c, b'x' | b'X' | b'z' | b'Z');
match (a, b) {
(b'0', b'1') => Some(EdgeTransition::ZeroOne),
(b'1', b'0') => Some(EdgeTransition::OneZero),
(b'0', c) if unknown(c) => Some(EdgeTransition::ZeroX),
(b'1', c) if unknown(c) => Some(EdgeTransition::OneX),
(c, b'0') if unknown(c) => Some(EdgeTransition::XZero),
(c, b'1') if unknown(c) => Some(EdgeTransition::XOne),
_ => None,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct EdgeSet(u8);
impl EdgeSet {
pub const EMPTY: EdgeSet = EdgeSet(0);
pub const ALL: EdgeSet = EdgeSet(0b11_1111);
pub const POSEDGE: EdgeSet = EdgeSet(
(1 << EdgeTransition::ZeroOne as u8)
| (1 << EdgeTransition::ZeroX as u8)
| (1 << EdgeTransition::XOne as u8),
);
pub const NEGEDGE: EdgeSet = EdgeSet(
(1 << EdgeTransition::OneZero as u8)
| (1 << EdgeTransition::XZero as u8)
| (1 << EdgeTransition::OneX as u8),
);
pub fn with(self, t: EdgeTransition) -> EdgeSet {
EdgeSet(self.0 | t.mask())
}
pub fn contains(self, t: EdgeTransition) -> bool {
self.0 & t.mask() != 0
}
pub fn reversed(self) -> EdgeSet {
EdgeTransition::ALL
.into_iter()
.filter(|&t| self.contains(t))
.fold(EdgeSet::EMPTY, |set, t| set.with(t.reversed()))
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum EdgeControl {
AnyChange,
Edges(EdgeSet),
}