1use std::fmt;
6
7use num_bigint::BigUint;
8
9use crate::ValueError;
10
11#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
13pub enum Logic {
14 Zero,
15 One,
16 X,
17 Z,
18}
19
20impl Logic {
21 pub fn from_char(c: char) -> Logic {
22 match c {
23 '0' => Logic::Zero,
24 '1' => Logic::One,
25 'z' | 'Z' => Logic::Z,
26 _ => Logic::X,
27 }
28 }
29 pub fn to_char(self) -> char {
30 match self {
31 Logic::Zero => '0',
32 Logic::One => '1',
33 Logic::X => 'x',
34 Logic::Z => 'z',
35 }
36 }
37 pub fn is_resolvable(self) -> bool {
38 matches!(self, Logic::Zero | Logic::One)
39 }
40}
41
42impl From<bool> for Logic {
43 fn from(b: bool) -> Logic {
44 if b { Logic::One } else { Logic::Zero }
45 }
46}
47
48impl fmt::Display for Logic {
49 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50 write!(f, "{}", self.to_char())
51 }
52}
53
54#[derive(Clone, Debug, PartialEq, Eq)]
57pub struct LogicArray {
58 a_val: BigUint,
59 b_val: BigUint,
60 width: usize,
61}
62
63impl LogicArray {
64 pub fn from_vpi_words(words: &[(u32, u32)], width: usize) -> LogicArray {
68 let word_count = width.div_ceil(32);
69 assert_eq!(
70 words.len(),
71 word_count,
72 "{width}-bit LogicArray needs {word_count} VPI word(s), got {}",
73 words.len()
74 );
75
76 let mut a_words: Vec<u32> = words.iter().map(|(aval, _)| *aval).collect();
77 let mut b_words: Vec<u32> = words.iter().map(|(_, bval)| *bval).collect();
78 if !width.is_multiple_of(32) {
79 let mask = (1u32 << (width % 32)) - 1;
80 *a_words.last_mut().expect("partial width has a VPI word") &= mask;
81 *b_words.last_mut().expect("partial width has a VPI word") &= mask;
82 }
83
84 LogicArray {
85 a_val: BigUint::from_slice(&a_words),
86 b_val: BigUint::from_slice(&b_words),
87 width,
88 }
89 }
90
91 pub fn from_binstr(s: &str) -> LogicArray {
92 let mut a_val = BigUint::ZERO;
93 let mut b_val = BigUint::ZERO;
94 let mut width = 0;
95 for (bit, c) in s.chars().rev().enumerate() {
96 let bit = bit as u64;
97 match Logic::from_char(c) {
98 Logic::Zero => {}
99 Logic::One => a_val.set_bit(bit, true),
100 Logic::X => {
101 a_val.set_bit(bit, true);
102 b_val.set_bit(bit, true);
103 }
104 Logic::Z => b_val.set_bit(bit, true),
105 }
106 width += 1;
107 }
108 LogicArray {
109 a_val,
110 b_val,
111 width,
112 }
113 }
114
115 pub fn from_bool(v: bool, width: usize) -> LogicArray {
116 LogicArray::from_u128(v as u128, width)
117 }
118
119 pub fn from_u8(v: u8, width: usize) -> LogicArray {
120 LogicArray::from_u128(v as u128, width)
121 }
122
123 pub fn from_u16(v: u16, width: usize) -> LogicArray {
124 LogicArray::from_u128(v as u128, width)
125 }
126
127 pub fn from_u32(v: u32, width: usize) -> LogicArray {
128 LogicArray::from_u128(v as u128, width)
129 }
130
131 pub fn from_u64(v: u64, width: usize) -> LogicArray {
132 LogicArray::from_u128(v as u128, width)
133 }
134
135 pub fn from_u128(v: u128, width: usize) -> LogicArray {
136 let mut a_val = BigUint::ZERO;
137 for bit in 0..width.min(u128::BITS as usize) {
138 if v & (1u128 << bit) != 0 {
139 a_val.set_bit(bit as u64, true);
140 }
141 }
142 LogicArray {
143 a_val,
144 b_val: BigUint::ZERO,
145 width,
146 }
147 }
148
149 pub fn from_bigint(v: &BigUint, width: usize) -> LogicArray {
150 let mask = (BigUint::from(1u8) << width) - BigUint::from(1u8);
151 LogicArray {
152 a_val: v & mask,
153 b_val: BigUint::ZERO,
154 width,
155 }
156 }
157
158 pub fn to_vpi_words(&self) -> Vec<(u32, u32)> {
161 let word_count = self.width.div_ceil(32);
162 let mut a_words = self.a_val.iter_u32_digits();
163 let mut b_words = self.b_val.iter_u32_digits();
164 (0..word_count)
165 .map(|_| (a_words.next().unwrap_or(0), b_words.next().unwrap_or(0)))
166 .collect()
167 }
168
169 pub fn to_binstr(&self) -> String {
170 (0..self.width)
171 .rev()
172 .map(|index| {
173 self[index]
174 .expect("index generated from LogicArray width")
175 .to_char()
176 })
177 .collect()
178 }
179
180 pub fn len(&self) -> usize {
181 self.width
182 }
183 pub fn is_empty(&self) -> bool {
184 self.width == 0
185 }
186
187 pub fn is_resolvable(&self) -> bool {
189 self.b_val == BigUint::ZERO
190 }
191}
192
193impl TryFrom<&LogicArray> for bool {
194 type Error = ValueError;
195 fn try_from(v: &LogicArray) -> Result<bool, ValueError> {
196 if !v.is_resolvable() {
197 return Err(ValueError::FourState(v.to_binstr()));
198 }
199 Ok(v.a_val != BigUint::ZERO)
200 }
201}
202
203impl TryFrom<&LogicArray> for u8 {
204 type Error = ValueError;
205 fn try_from(v: &LogicArray) -> Result<u8, ValueError> {
206 if !v.is_resolvable() {
207 return Err(ValueError::FourState(v.to_binstr()));
208 }
209 Ok(v.a_val.iter_u32_digits().next().unwrap_or(0) as u8)
210 }
211}
212
213impl TryFrom<&LogicArray> for u16 {
214 type Error = ValueError;
215 fn try_from(v: &LogicArray) -> Result<u16, ValueError> {
216 if !v.is_resolvable() {
217 return Err(ValueError::FourState(v.to_binstr()));
218 }
219 Ok(v.a_val.iter_u32_digits().next().unwrap_or(0) as u16)
220 }
221}
222
223impl TryFrom<&LogicArray> for u32 {
224 type Error = ValueError;
225 fn try_from(v: &LogicArray) -> Result<u32, ValueError> {
226 if !v.is_resolvable() {
227 return Err(ValueError::FourState(v.to_binstr()));
228 }
229 Ok(v.a_val.iter_u32_digits().next().unwrap_or(0))
230 }
231}
232
233impl TryFrom<&LogicArray> for u64 {
234 type Error = ValueError;
235 fn try_from(v: &LogicArray) -> Result<u64, ValueError> {
236 if !v.is_resolvable() {
237 return Err(ValueError::FourState(v.to_binstr()));
238 }
239 Ok(v.a_val.iter_u64_digits().next().unwrap_or(0))
240 }
241}
242
243impl TryFrom<&LogicArray> for u128 {
244 type Error = ValueError;
245 fn try_from(v: &LogicArray) -> Result<u128, ValueError> {
246 if !v.is_resolvable() {
247 return Err(ValueError::FourState(v.to_binstr()));
248 }
249 let mut digits = v.a_val.iter_u64_digits();
250 let low = digits.next().unwrap_or(0) as u128;
251 let high = digits.next().unwrap_or(0) as u128;
252 Ok(low | (high << 64))
253 }
254}
255
256impl TryFrom<&LogicArray> for BigUint {
257 type Error = ValueError;
258 fn try_from(v: &LogicArray) -> Result<BigUint, ValueError> {
259 if !v.is_resolvable() {
260 return Err(ValueError::FourState(v.to_binstr()));
261 }
262 Ok(v.a_val.clone())
263 }
264}
265
266impl std::ops::Index<usize> for LogicArray {
268 type Output = Option<Logic>;
269
270 fn index(&self, index: usize) -> &Self::Output {
271 static ZERO: Option<Logic> = Some(Logic::Zero);
273 static ONE: Option<Logic> = Some(Logic::One);
274 static X: Option<Logic> = Some(Logic::X);
275 static Z: Option<Logic> = Some(Logic::Z);
276 if index >= self.width {
278 return &None;
279 }
280 match (self.a_val.bit(index as u64), self.b_val.bit(index as u64)) {
283 (false, false) => &ZERO,
284 (true, false) => &ONE,
285 (true, true) => &X,
286 (false, true) => &Z,
287 }
288 }
289}
290
291impl fmt::Display for LogicArray {
292 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
293 write!(f, "{}", self.to_binstr())
294 }
295}
296
297#[cfg(test)]
298mod tests {
299 use super::*;
300
301 #[test]
302 fn roundtrip_u64() {
303 let v = LogicArray::from_u64(0xA5, 8);
304 assert_eq!(v.to_binstr(), "10100101");
305 assert_eq!(u64::try_from(&v).unwrap(), 0xA5);
306 }
307
308 #[test]
309 fn constructs_from_two_state_primitive_types() {
310 let v = LogicArray::from_bool(true, 1);
311 assert_eq!(v.to_binstr(), "1");
312
313 let v = LogicArray::from_u8(0xa5, 8);
314 assert_eq!(u8::try_from(&v), Ok(0xa5));
315
316 let v = LogicArray::from_u16(0xa5b6, 16);
317 assert_eq!(u16::try_from(&v), Ok(0xa5b6));
318
319 let v = LogicArray::from_u32(0xa5b6_c7d8, 32);
320 assert_eq!(u32::try_from(&v), Ok(0xa5b6_c7d8));
321
322 let v = LogicArray::from_u64(0x0123_4567_89ab_cdef, 64);
323 assert_eq!(u64::try_from(&v), Ok(0x0123_4567_89ab_cdef));
324
325 let value = 0x0123_4567_89ab_cdef_fedc_ba98_7654_3210;
326 let v = LogicArray::from_u128(value, 128);
327 assert_eq!(u128::try_from(&v), Ok(value));
328
329 let truncated = LogicArray::from_u8(0xa5, 4);
330 assert_eq!(truncated.to_binstr(), "0101");
331 }
332
333 #[test]
334 fn bigint_conversion_preserves_arbitrary_width_values() {
335 let value = BigUint::from_slice(&[
336 0x7654_3210,
337 0xfedc_ba98,
338 0x89ab_cdef,
339 0x0123_4567,
340 0xa5a5_5a5a,
341 ]);
342 let v = LogicArray::from_bigint(&value, 160);
343
344 assert_eq!(v.len(), 160);
345 assert_eq!(BigUint::try_from(&v), Ok(value));
346
347 let truncated = LogicArray::from_bigint(&BigUint::from(0xffu8), 4);
348 assert_eq!(BigUint::try_from(&truncated), Ok(BigUint::from(0x0fu8)));
349 }
350
351 #[test]
352 fn converts_to_two_state_primitive_types() {
353 let zero = LogicArray::from_binstr("0");
354 assert_eq!(bool::try_from(&zero), Ok(false));
355
356 let v = LogicArray::from_vpi_words(
357 &[
358 (0x7654_3210, 0),
359 (0xfedc_ba98, 0),
360 (0x89ab_cdef, 0),
361 (0x0123_4567, 0),
362 ],
363 128,
364 );
365 assert_eq!(bool::try_from(&v), Ok(true));
366 assert_eq!(u8::try_from(&v), Ok(0x10));
367 assert_eq!(u16::try_from(&v), Ok(0x3210));
368 assert_eq!(u32::try_from(&v), Ok(0x7654_3210));
369 assert_eq!(u64::try_from(&v), Ok(0xfedc_ba98_7654_3210));
370 assert_eq!(
371 u128::try_from(&v),
372 Ok(0x0123_4567_89ab_cdef_fedc_ba98_7654_3210)
373 );
374 }
375
376 #[test]
377 fn four_state_rejected() {
378 let v = LogicArray::from_binstr("1x0z");
379 assert!(bool::try_from(&v).is_err());
380 assert!(u8::try_from(&v).is_err());
381 assert!(u16::try_from(&v).is_err());
382 assert!(u32::try_from(&v).is_err());
383 assert!(u64::try_from(&v).is_err());
384 assert!(u128::try_from(&v).is_err());
385 assert!(BigUint::try_from(&v).is_err());
386 assert!(!v.is_resolvable());
387 }
388
389 #[test]
390 fn vpi_encoding_and_lsb_first_bit_access() {
391 let v = LogicArray::from_binstr("01xz");
392
393 assert_eq!(v.a_val, BigUint::from(0b0110u8));
394 assert_eq!(v.b_val, BigUint::from(0b0011u8));
395 assert_eq!(v[0], Some(Logic::Z));
396 assert_eq!(v[1], Some(Logic::X));
397 assert_eq!(v[2], Some(Logic::One));
398 assert_eq!(v[3], Some(Logic::Zero));
399 assert_eq!(v[4], None);
400 assert_eq!(v.to_binstr(), "01xz");
401 }
402
403 #[test]
404 fn constructs_from_vpi_words_and_masks_unused_bits() {
405 let states = LogicArray::from_vpi_words(&[(0b0110, 0b0011)], 4);
406 assert_eq!(states.to_binstr(), "01xz");
407 assert_eq!(states.to_vpi_words(), vec![(0b0110, 0b0011)]);
408
409 let partial = LogicArray::from_vpi_words(&[(0xffff_fff5, 0xffff_fff8)], 3);
410 assert_eq!(partial.to_binstr(), "101");
411 assert!(partial.is_resolvable());
412 assert_eq!(partial.to_vpi_words(), vec![(0b101, 0)]);
413 }
414
415 #[test]
416 fn width_is_preserved_independently_of_biguint_magnitude() {
417 let zeros = LogicArray::from_binstr("00000");
418 assert_eq!(zeros.len(), 5);
419 assert_eq!(zeros.to_binstr(), "00000");
420
421 let wide = LogicArray::from_u64(1, 65);
422 assert_eq!(wide.len(), 65);
423 assert_eq!(wide[0], Some(Logic::One));
424 assert_eq!(wide[64], Some(Logic::Zero));
425 assert_eq!(u64::try_from(&wide), Ok(1));
426
427 let empty = LogicArray::from_binstr("");
428 assert!(empty.is_empty());
429 assert_eq!(empty.to_binstr(), "");
430 }
431}