Skip to main content

rustdv_gpi/
value.rs

1//! 4-state value types: port of cocotb's `Logic`/`LogicArray`
2//! (cocotb: types/, design-doc mapping row 23). Conversion failures are
3//! `Result`s, not exceptions.
4
5use std::fmt;
6
7use crate::ValueError;
8
9/// One 4-state bit.
10#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
11pub enum Logic {
12    Zero,
13    One,
14    X,
15    Z,
16}
17
18impl Logic {
19    pub fn from_char(c: char) -> Logic {
20        match c {
21            '0' => Logic::Zero,
22            '1' => Logic::One,
23            'z' | 'Z' => Logic::Z,
24            _ => Logic::X,
25        }
26    }
27    pub fn to_char(self) -> char {
28        match self {
29            Logic::Zero => '0',
30            Logic::One => '1',
31            Logic::X => 'x',
32            Logic::Z => 'z',
33        }
34    }
35    pub fn is_resolvable(self) -> bool {
36        matches!(self, Logic::Zero | Logic::One)
37    }
38}
39
40impl From<bool> for Logic {
41    fn from(b: bool) -> Logic {
42        if b { Logic::One } else { Logic::Zero }
43    }
44}
45
46impl fmt::Display for Logic {
47    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48        write!(f, "{}", self.to_char())
49    }
50}
51
52/// A fixed-width vector of 4-state bits, MSB first (binstr order).
53#[derive(Clone, Debug, PartialEq, Eq)]
54pub struct LogicArray {
55    bits: Vec<Logic>,
56}
57
58impl LogicArray {
59    pub fn from_binstr(s: &str) -> LogicArray {
60        LogicArray { bits: s.chars().map(Logic::from_char).collect() }
61    }
62
63    pub fn from_u64(v: u64, width: usize) -> LogicArray {
64        let mut bits = Vec::with_capacity(width);
65        for i in (0..width).rev() {
66            bits.push(Logic::from((v >> i) & 1 == 1));
67        }
68        LogicArray { bits }
69    }
70
71    pub fn to_binstr(&self) -> String {
72        self.bits.iter().map(|b| b.to_char()).collect()
73    }
74
75    pub fn len(&self) -> usize {
76        self.bits.len()
77    }
78    pub fn is_empty(&self) -> bool {
79        self.bits.is_empty()
80    }
81
82    pub fn is_resolvable(&self) -> bool {
83        self.bits.iter().all(|b| b.is_resolvable())
84    }
85
86    /// MSB-first bit access.
87    pub fn bit(&self, i: usize) -> Option<Logic> {
88        self.bits.get(i).copied()
89    }
90
91    pub fn to_u64(&self) -> Result<u64, ValueError> {
92        let mut v: u64 = 0;
93        for b in &self.bits {
94            match b {
95                Logic::Zero => v <<= 1,
96                Logic::One => v = (v << 1) | 1,
97                _ => return Err(ValueError::FourState(self.to_binstr())),
98            }
99        }
100        Ok(v)
101    }
102}
103
104impl TryFrom<&LogicArray> for u64 {
105    type Error = ValueError;
106    fn try_from(v: &LogicArray) -> Result<u64, ValueError> {
107        v.to_u64()
108    }
109}
110
111impl fmt::Display for LogicArray {
112    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113        write!(f, "{}", self.to_binstr())
114    }
115}
116
117#[cfg(test)]
118mod tests {
119    use super::*;
120
121    #[test]
122    fn roundtrip_u64() {
123        let v = LogicArray::from_u64(0xA5, 8);
124        assert_eq!(v.to_binstr(), "10100101");
125        assert_eq!(v.to_u64().unwrap(), 0xA5);
126    }
127
128    #[test]
129    fn four_state_rejected() {
130        let v = LogicArray::from_binstr("1x0z");
131        assert!(v.to_u64().is_err());
132        assert!(!v.is_resolvable());
133    }
134}