From 6b689b23cbc7416284406a4651c8d3fe6afb2dd2 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 30 Nov 2018 15:33:22 +0100 Subject: [PATCH 1/3] reworked logicvector to work with u128 instead of vec name bench ns/iter bench2 ns/iter diff ns/iter diff % speedup create_from_int 6,198,733 76,972 -6,121,761 -98.76% x 80.53 create_from_str 1,074,832 1,036,309 -38,523 -3.58% x 1.04 create_from_vec 801,988 2,876,653 2,074,665 258.69% x 0.28 create_width 917,481 65,436 -852,045 -92.87% x 14.02 to_u128 10,294,197 303,107 -9,991,090 -97.06% x 33.96 As one can see, the main construction, namely creating a logicvector from an int has been increased by factor 80. The rest is also faster, expect the construction from a vec itself, which is logical because beforehand we just passed the vec to the internal storage, now we have to do some logic --- examples/vcd_reg_dump.rs | 6 +- src/dump/mod.rs | 2 +- src/logicbit/logicvector.rs | 379 ------------------ src/logicbit/logicvector/masks.rs | 159 ++++++++ src/logicbit/logicvector/mod.rs | 617 ++++++++++++++++++++++++++++++ 5 files changed, 778 insertions(+), 385 deletions(-) delete mode 100644 src/logicbit/logicvector.rs create mode 100644 src/logicbit/logicvector/masks.rs create mode 100644 src/logicbit/logicvector/mod.rs diff --git a/examples/vcd_reg_dump.rs b/examples/vcd_reg_dump.rs index 284670a..bd7f66e 100644 --- a/examples/vcd_reg_dump.rs +++ b/examples/vcd_reg_dump.rs @@ -2,10 +2,6 @@ use logical::dump::Vcd; use logical::{Ieee1164, LogicVector}; fn main() { - let v = LogicVector::from_int_value(5, 8).unwrap(); - assert_eq!(v.width(), 8); - assert_eq!(v, 5); - let mut dumper = Vcd::new("VCD Example"); let mut foo: LogicVector; @@ -35,9 +31,9 @@ fn main() { let one = LogicVector::from_int_value(1, 16).unwrap(); for _ in 0..90 { + foo = foo + &one; dumper.serialize_logivector("foo", &foo); dumper.tick(); - foo = (foo + &one).unwrap(); } dumper.dump("/home/marcel/b.vcd").unwrap(); diff --git a/src/dump/mod.rs b/src/dump/mod.rs index 354328b..3be0d43 100644 --- a/src/dump/mod.rs +++ b/src/dump/mod.rs @@ -49,7 +49,7 @@ impl fmt::Display for Type { #[derive(Debug, Clone, PartialEq, Eq)] struct Ident { ty: Type, - width: usize, + width: u8, ident: char, name: String, } diff --git a/src/logicbit/logicvector.rs b/src/logicbit/logicvector.rs deleted file mode 100644 index 3b17d71..0000000 --- a/src/logicbit/logicvector.rs +++ /dev/null @@ -1,379 +0,0 @@ -use std::cmp::Ordering; -use std::convert::TryFrom; -use std::fmt; -use std::ops::{Add, BitAnd, BitOr, BitXor, Index, IndexMut}; -use std::str::FromStr; - -use crate::{Ieee1164, Ieee1164Value, Resolve}; - -macro_rules! expand_op_logicvector { - ($func_name:ident, $trait_name:ident, $fn_name:ident) => { - expand_op!( - $func_name, - $trait_name, - $fn_name, - LogicVector, - LogicVector, - Option - ); - }; -} - -#[derive(Debug, Clone)] -pub struct LogicVector { - inner: Vec, //TODO: maybe use masks instead of actual bits... may be faster or so -} - -impl LogicVector { - pub fn from_ieee_value(value: Ieee1164, width: usize) -> Self { - assert_ne!(width, 0); - assert!(width <= 128); - Self { - inner: vec![value; width], - } - } - - pub fn from_int_value(value: u128, width: usize) -> Option { - let zeros = value.leading_zeros() as usize; - if width < (128 - zeros) || width == 0 { - return None; - } - let mut v: LogicVector = Self::from_str(&format!("{:b}", value)).unwrap(); - v.resize(width, Ieee1164::_0); - Some(v) - } - - pub fn with_width(width: usize) -> Self { - Self::from_ieee_value(Ieee1164::default(), width) - } -} - -impl LogicVector { - pub fn width(&self) -> usize { - self.inner.len() - } - - pub fn set_width(&mut self, new_width: usize) { - self.resize(new_width, Ieee1164::_U); - } - - pub fn resize(&mut self, new_width: usize, value: Ieee1164) { - let old_width = self.width(); - self.inner = match old_width.cmp(&new_width) { - Ordering::Equal => return, - Ordering::Less => [vec![value; new_width - old_width].as_slice(), &self.inner].concat(), - Ordering::Greater => self.inner.as_slice()[(old_width - new_width)..].to_vec(), - }; - } - - pub fn set_all_to(&mut self, value: Ieee1164) { - self.inner.iter_mut().for_each(|v| *v = value); - } - - pub fn set_int_value(&mut self, value: u128) -> Result<(), ()> { - std::mem::replace(self, Self::from_int_value(value, self.width()).ok_or(())?); - Ok(()) - } - - pub fn as_u128(&self) -> Option { - // TODO: maybe not pub? - if self.has_UXZ() { - return None; - } - Some(self.inner.iter().fold(0, |s, e| { - (s << 1) - | if e.is_1H() { - 1 - } else if e.is_0L() { - 0 - } else { - unreachable!("Logic error?!") - } - })) - } -} - -impl Index for LogicVector { - type Output = Ieee1164; - - fn index(&self, index: usize) -> &>::Output { - &self.inner[index] - } -} - -impl IndexMut for LogicVector { - fn index_mut(&mut self, index: usize) -> &mut >::Output { - &mut self.inner[index] - } -} - -fn and(lhs: &LogicVector, rhs: &LogicVector) -> Option { - if lhs.width() != rhs.width() { - return None; - } - Some( - lhs.inner - .iter() - .zip(rhs.inner.iter()) - .map(|(l, r)| l & r) - .collect::>() - .into(), - ) -} -expand_op_logicvector!(and, BitAnd, bitand); - -fn or(lhs: &LogicVector, rhs: &LogicVector) -> Option { - if lhs.width() != rhs.width() { - return None; - } - Some( - lhs.inner - .iter() - .zip(rhs.inner.iter()) - .map(|(l, r)| l | r) - .collect::>() - .into(), - ) -} -expand_op_logicvector!(or, BitOr, bitor); - -fn xor(lhs: &LogicVector, rhs: &LogicVector) -> Option { - if lhs.width() != rhs.width() { - return None; - } - Some( - lhs.inner - .iter() - .zip(rhs.inner.iter()) - .map(|(l, r)| l ^ r) - .collect::>() - .into(), - ) -} -expand_op_logicvector!(xor, BitXor, bitxor); - -fn add(lhs: &LogicVector, rhs: &LogicVector) -> Option { - if lhs.width() != rhs.width() { - return None; - } - if let (Some(a), Some(b)) = (lhs.as_u128(), rhs.as_u128()) { - LogicVector::from_int_value((a + b) & ((1 << lhs.width()) - 1), lhs.width()) - } else { - Some(LogicVector::with_width(lhs.width())) - } -} -expand_op_logicvector!(add, Add, add); - -fn resolve(lhs: &LogicVector, rhs: &LogicVector) -> LogicVector { - if lhs.width() != rhs.width() { - panic!("Cannot resolve two different sized logicvectors.") - } - - if lhs.is_ZZZ() && rhs.is_ZZZ() { - LogicVector::from_ieee_value(Ieee1164::_Z, lhs.width()) - } else if lhs.is_ZZZ() || rhs.is_ZZZ() { - if lhs.is_ZZZ() { - rhs.clone() - } else { - lhs.clone() - } - } else { - lhs.inner.iter().zip(rhs.inner.iter()).map(|(a, b)| a.resolve(b)).collect::>().into() - } -} -expand_op!(resolve, Resolve, resolve, LogicVector, LogicVector, LogicVector); - -impl PartialEq for LogicVector { - fn eq(&self, other: &LogicVector) -> bool { - if let (Some(a), Some(b)) = (self.as_u128(), other.as_u128()) { - a == b - } else { - false - } - } -} - -impl Eq for LogicVector {} - -impl PartialEq for LogicVector { - fn eq(&self, other: &u128) -> bool { - if let Some(this) = self.as_u128() { - this == *other - } else { - false - } - } -} - -#[allow(non_snake_case)] -impl LogicVector { - fn contains(&self, value: Ieee1164) -> bool { - self.inner.contains(&value) - } - pub fn has_U(&self) -> bool { - self.contains(Ieee1164::Uninitialized) - } - - pub fn has_X(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::Unknown)) - } - - pub fn has_0(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::Zero)) - } - - pub fn has_1(&self) -> bool { - self.contains(Ieee1164::Strong(Ieee1164Value::One)) - } - - pub fn has_Z(&self) -> bool { - self.contains(Ieee1164::HighImpedance) - } - - pub fn has_W(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::Unknown)) - } - - pub fn has_D(&self) -> bool { - self.contains(Ieee1164::DontCare) - } - - pub fn has_L(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::Zero)) - } - - pub fn has_H(&self) -> bool { - self.contains(Ieee1164::Weak(Ieee1164Value::One)) - } - - pub fn has_UXZ(&self) -> bool { - self.inner.iter().any(|x| x.is_UXZ()) - } - - pub fn is_000(&self) -> bool { - self.inner.iter().all(|x| x.is_0()) - } - - pub fn is_111(&self) -> bool { - self.inner.iter().all(|x| x.is_1()) - } - - pub fn is_ZZZ(&self) -> bool { - self.inner.iter().all(|x| x.is_Z()) - } -} - -#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] -pub enum LogicVectorConversionError { - InalidChar(char), -} - -impl From> for LogicVector { - fn from(v: Vec) -> LogicVector { - LogicVector { inner: v } - } -} - -impl FromStr for LogicVector { - type Err = LogicVectorConversionError; - - fn from_str(s: &str) -> Result::Err> { - s.chars() - .try_fold(vec![], |mut v, c| { - v.push(Ieee1164::try_from(c).map_err(|_| LogicVectorConversionError::InalidChar(c))?); - Ok(v) - }) - .map(|v| v.into()) - } -} - -impl fmt::Display for LogicVector { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for v in self.inner.iter() { - write!(f, "{}", v)?; - } - Ok(()) - } -} - -impl PartialOrd for LogicVector { - fn partial_cmp(&self, other: &LogicVector) -> Option { - if self.width() != other.width() { - return None; - } - if self.has_UXZ() || other.has_UXZ() { - return None; - } - - self.as_u128().partial_cmp(&other.as_u128()) - } -} - -#[cfg(test)] -mod tests { - use super::*; - use proptest::{prop_assert, prop_assert_eq, proptest, proptest_helper}; - - proptest! { - #[test] - fn atm_ctor_value(value in 0u64..) { - let v = LogicVector::from_int_value(value as u128, 128); - prop_assert!(v.is_some()); - prop_assert_eq!(v.unwrap(), value as u128); - } - } - - #[test] - fn ctor_width() { - for width in 1..=128 { - let v = LogicVector::with_width(width); - assert_eq!(width, v.width()); - assert!(v.has_U(), "{:?}", v); - assert!(!v.has_X(), "{:?}", v); - assert!(!v.has_0(), "{:?}", v); - assert!(!v.has_1(), "{:?}", v); - assert!(!v.has_Z(), "{:?}", v); - assert!(!v.has_W(), "{:?}", v); - assert!(!v.has_D(), "{:?}", v); - assert!(!v.has_L(), "{:?}", v); - assert!(!v.has_H(), "{:?}", v); - } - } - - #[test] - fn ctor_value() { - let v = LogicVector::from_int_value(5, 3); - let v = v.unwrap(); - assert_eq!(v.width(), 3); - assert_eq!(v, 5); - let v = LogicVector::from_int_value(0, 128); - let v = v.unwrap(); - assert_eq!(v.width(), 128); - assert_eq!(v, 0); - } - - #[test] - fn test_resize() { - let mut v = LogicVector::with_width(5); - assert_eq!(v.width(), 5); - v.set_width(10); - assert_eq!(v.width(), 10); - v.set_width(10); - assert_eq!(v.width(), 10); - v.set_width(3); - assert_eq!(v.width(), 3); - } - - #[test] - fn test_resize_value() { - let mut v = LogicVector::from_int_value(5, 8).unwrap(); - assert_eq!(v.width(), 8); - assert_eq!(v, 5); - v.set_width(10); - assert_eq!(v.width(), 10); - v.set_width(10); - assert_eq!(v.width(), 10); - v.set_width(3); - assert_eq!(v.width(), 3); - } -} diff --git a/src/logicbit/logicvector/masks.rs b/src/logicbit/logicvector/masks.rs new file mode 100644 index 0000000..2e783f7 --- /dev/null +++ b/src/logicbit/logicvector/masks.rs @@ -0,0 +1,159 @@ +use crate::{Ieee1164, Ieee1164Value}; +use std::ops::{Index, IndexMut}; + +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub enum SanityChecked { + MoreThanOne(u8), + NoOne(u8), + OneAboveWidth(u8), +} + +#[allow(non_snake_case)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] +pub struct Masks { + _U: u128, + _X: u128, + _1: u128, + _0: u128, + _W: u128, + _H: u128, + _L: u128, + _Z: u128, + _D: u128, +} + +impl Masks { + pub fn sanity_check(&self, width: u8) -> Result<(), SanityChecked> { + for d in 0..128 { + let mut has_one = false; + for mask in self { + if (mask.1 >> d) & 1 == 1 { + if has_one { + return Err(SanityChecked::MoreThanOne(d)); + } + if d > width { + return Err(SanityChecked::OneAboveWidth(d)); + } + has_one = true; + } + } + if d < width && !has_one { + return Err(SanityChecked::NoOne(d)); + } + } + + Ok(()) + } +} + +impl Index for Masks { + type Output = u128; + + fn index(&self, index: Ieee1164) -> &u128 { + match index { + Ieee1164::Uninitialized => &self._U, + Ieee1164::Strong(Ieee1164Value::Unknown) => &self._X, + Ieee1164::Strong(Ieee1164Value::One) => &self._1, + Ieee1164::Strong(Ieee1164Value::Zero) => &self._0, + Ieee1164::Weak(Ieee1164Value::Unknown) => &self._W, + Ieee1164::Weak(Ieee1164Value::One) => &self._H, + Ieee1164::Weak(Ieee1164Value::Zero) => &self._L, + Ieee1164::HighImpedance => &self._Z, + Ieee1164::DontCare => &self._D, + } + } +} + +impl IndexMut for Masks { + fn index_mut(&mut self, index: Ieee1164) -> &mut u128 { + match index { + Ieee1164::Uninitialized => &mut self._U, + Ieee1164::Strong(Ieee1164Value::Unknown) => &mut self._X, + Ieee1164::Strong(Ieee1164Value::One) => &mut self._1, + Ieee1164::Strong(Ieee1164Value::Zero) => &mut self._0, + Ieee1164::Weak(Ieee1164Value::Unknown) => &mut self._W, + Ieee1164::Weak(Ieee1164Value::One) => &mut self._H, + Ieee1164::Weak(Ieee1164Value::Zero) => &mut self._L, + Ieee1164::HighImpedance => &mut self._Z, + Ieee1164::DontCare => &mut self._D, + } + } +} + +impl<'a> IntoIterator for &'a Masks { + type Item = (Ieee1164, &'a u128); + type IntoIter = Iter<'a>; + + fn into_iter(self) -> ::IntoIter { + Iter { mask: self, pos: 0 } + } +} + +impl<'a> IntoIterator for &'a mut Masks { + type Item = (Ieee1164, &'a mut u128); + type IntoIter = IterMut<'a>; + + fn into_iter(self) -> ::IntoIter { + IterMut { mask: self, pos: 0 } + } +} + +pub struct Iter<'a> { + mask: &'a Masks, + pos: u8, +} + +impl<'a> Iterator for Iter<'a> { + type Item = (Ieee1164, &'a u128); + + fn next(&mut self) -> Option<::Item> { + if self.pos < 9 { + let res = match self.pos { + 0 => (Ieee1164::_U, &self.mask._U), + 1 => (Ieee1164::_X, &self.mask._X), + 2 => (Ieee1164::_1, &self.mask._1), + 3 => (Ieee1164::_0, &self.mask._0), + 4 => (Ieee1164::_W, &self.mask._W), + 5 => (Ieee1164::_H, &self.mask._H), + 6 => (Ieee1164::_L, &self.mask._L), + 7 => (Ieee1164::_Z, &self.mask._Z), + 8 => (Ieee1164::_D, &self.mask._D), + _ => unreachable!(), + }; + self.pos += 1; + Some(res) + } else { + None + } + } +} + +pub struct IterMut<'a> { + mask: &'a mut Masks, + pos: u8, +} + +impl<'a> Iterator for IterMut<'a> { + type Item = (Ieee1164, &'a mut u128); + + fn next(&mut self) -> Option<::Item> { + if self.pos < 9 { + let res = match self.pos { + 0 => (Ieee1164::_U, &mut self.mask._U), + 1 => (Ieee1164::_X, &mut self.mask._X), + 2 => (Ieee1164::_1, &mut self.mask._1), + 3 => (Ieee1164::_0, &mut self.mask._0), + 4 => (Ieee1164::_W, &mut self.mask._W), + 5 => (Ieee1164::_H, &mut self.mask._H), + 6 => (Ieee1164::_L, &mut self.mask._L), + 7 => (Ieee1164::_Z, &mut self.mask._Z), + 8 => (Ieee1164::_D, &mut self.mask._D), + _ => unreachable!(), + }; + self.pos += 1; + Some(unsafe { std::mem::transmute(res) }) //FIXME: Get rid of this unsafe! + } else { + None + } + } +} diff --git a/src/logicbit/logicvector/mod.rs b/src/logicbit/logicvector/mod.rs new file mode 100644 index 0000000..369ca0b --- /dev/null +++ b/src/logicbit/logicvector/mod.rs @@ -0,0 +1,617 @@ +mod masks; +use self::masks::{Masks, SanityChecked}; + +use std::cmp::Ordering; +use std::convert::TryFrom; +use std::fmt; +use std::ops::{Add, BitAnd, BitOr, BitXor}; +use std::str::FromStr; + +use crate::{Ieee1164, Ieee1164Value, Resolve}; + +#[allow(unused)] +macro_rules! expand_op_logicvector { + ($func_name:ident, $trait_name:ident, $fn_name:ident) => { + expand_op!( + $func_name, + $trait_name, + $fn_name, + LogicVector, + LogicVector, + LogicVector + ); + }; +} + +macro_rules! unsafe_version { + ($safe_name:ident, $unsafe_name:ident, $lhs:ty, $rhs:ty, $output:ty) => { + fn $unsafe_name(lhs: &$lhs, rhs: &$rhs) -> $output { + $safe_name(lhs, rhs).unwrap() + } + }; +} + +macro_rules! unsafe_version_logicvector { + ($safe_name:ident, $unsafe_name:ident) => { + unsafe_version!($safe_name, $unsafe_name, LogicVector, LogicVector, LogicVector); + }; +} + +#[inline(always)] +fn build_mask(old: u8, new: u8) -> u128 { + use std::u128::MAX; + match (old, new) { + (a, b) if a >= b => panic!("`old` cannot be greater/equal than `new`!"), + (128, 128) => MAX, + (a, 128) => MAX & !((1 << a) - 1), + (a, b) => ((1 << b) - 1) & !((1 << a) - 1), + } +} + +#[inline(always)] +fn gen_mask_from_width(width: u8) -> u128 { + if width != 128 { + ((1 << width) - 1) + } else { + std::u128::MAX + } +} + +#[inline(always)] +fn assert_width(width: u8) -> bool { + width != 0 && width <= 128 +} + +#[derive(Debug, Clone)] +pub struct LogicVector { + masks: Masks, + width: u8, +} + +impl LogicVector { + pub fn from_ieee_value(value: Ieee1164, width: u8) -> Self { + assert!(assert_width(width)); + let mut s = Self { + masks: Masks::default(), + width: width as u8, + }; + s.masks[value] = std::u128::MAX & gen_mask_from_width(width); + debug_assert_eq!(Ok(()), s.sanity_check()); + s + } + + pub fn from_int_value(value: u128, width: u8) -> Option { + let zeros = value.leading_zeros() as u8; + if assert_width(width) && width >= (128 - zeros) { + let mut masks = Masks::default(); + masks[Ieee1164::_1] = value; + masks[Ieee1164::_0] = (!value) & gen_mask_from_width(width); + + let s = Self { masks, width }; + debug_assert_eq!(Ok(()), s.sanity_check()); + Some(s) + } else { + None + } + } + + pub fn with_width(width: u8) -> Self { + assert!(assert_width(width)); + Self::from_ieee_value(Ieee1164::default(), width) + } +} + +impl LogicVector { + pub fn width(&self) -> u8 { + self.width + } + + pub fn set_width(&mut self, new_width: u8) { + self.resize(new_width, Ieee1164::_U); + debug_assert_eq!(Ok(()), self.sanity_check()); + } + + pub fn resize(&mut self, new_width: u8, value: Ieee1164) { + assert!(assert_width(new_width)); + let old_width = self.width(); + self.width = new_width as u8; + + match old_width.cmp(&new_width) { + Ordering::Equal => {} + Ordering::Less => { + let mask = build_mask(old_width, new_width); + + for masks in &mut self.masks { + if masks.0 == value { + *masks.1 |= std::u128::MAX & mask; + } else { + *masks.1 &= !(std::u128::MAX & mask); + } + } + } + Ordering::Greater => { + for mask in &mut self.masks { + *mask.1 &= std::u128::MAX & gen_mask_from_width(new_width); + } + } + }; + debug_assert_eq!(Ok(()), self.sanity_check()); + } + + pub fn set_all_to(&mut self, value: Ieee1164) { + for mask in &mut self.masks { + *mask.1 = if value == mask.0 { std::u128::MAX } else { 0 } + } + debug_assert_eq!(Ok(()), self.sanity_check()); + } + + //TODO introduce proper error type + pub fn set_int_value(&mut self, value: u128) -> Result<(), ()> { + std::mem::replace(self, Self::from_int_value(value, self.width()).ok_or(())?); + Ok(()) + } + + // TODO: maybe not pub? + pub fn as_u128(&self) -> Option { + if self.has_UXZ() { + None + } else { + Some(self.masks[Ieee1164::_1]) + } + } +} + +impl LogicVector { + fn sanity_check(&self) -> Result<(), SanityChecked> { + self.masks.sanity_check(self.width) + } +} + +fn and(lhs: &LogicVector, rhs: &LogicVector) -> Option { + if lhs.width() != rhs.width() { + return None; + } + + let mut masks = Masks::default(); + + if lhs.has_UXZ() || rhs.has_UXZ() { + for _ in 0..lhs.width { + unimplemented!() + } + } else { + let idx_1 = Ieee1164::_1; + let idx_0 = Ieee1164::_0; + masks[idx_1] = lhs.masks[idx_1] & rhs.masks[idx_1]; + masks[idx_0] = lhs.masks[idx_0] & rhs.masks[idx_0]; + } + + Some(LogicVector { + masks, + width: lhs.width, + }) +} +unsafe_version_logicvector!(and, unsafe_and); +expand_op_logicvector!(unsafe_and, BitAnd, bitand); + +fn or(lhs: &LogicVector, rhs: &LogicVector) -> Option { + if lhs.width() != rhs.width() { + return None; + } + + let mut masks = Masks::default(); + + if lhs.has_UXZ() || rhs.has_UXZ() { + for _ in 0..lhs.width { + unimplemented!() + } + } else { + let idx_1 = Ieee1164::_1; + let idx_0 = Ieee1164::_0; + masks[idx_1] = lhs.masks[idx_1] | rhs.masks[idx_1]; + masks[idx_0] = lhs.masks[idx_0] | rhs.masks[idx_0]; + } + + Some(LogicVector { + masks, + width: lhs.width, + }) +} +unsafe_version_logicvector!(or, unsafe_or); +expand_op_logicvector!(unsafe_or, BitOr, bitor); + +fn xor(lhs: &LogicVector, rhs: &LogicVector) -> Option { + if lhs.width() != rhs.width() { + return None; + } + + let mut masks = Masks::default(); + + if lhs.has_UXZ() || rhs.has_UXZ() { + for _ in 0..lhs.width { + unimplemented!() + } + } else { + let idx_1 = Ieee1164::_1; + let idx_0 = Ieee1164::_0; + masks[idx_1] = lhs.masks[idx_1] ^ rhs.masks[idx_1]; + masks[idx_0] = lhs.masks[idx_0] ^ rhs.masks[idx_0]; + } + + Some(LogicVector { + masks, + width: lhs.width, + }) + + //TODO maybe replace by macro and only provide & | ^ +} +unsafe_version_logicvector!(xor, unsafe_xor); +expand_op_logicvector!(unsafe_xor, BitXor, bitxor); + +impl LogicVector { + pub fn safe_add(&self, rhs: &LogicVector) -> Option { + if self.width() != rhs.width() { + return None; + } + let width = self.width(); + if let (Some(a), Some(b)) = (self.as_u128(), rhs.as_u128()) { + LogicVector::from_int_value((a + b) & gen_mask_from_width(width), width) + } else { + Some(LogicVector::with_width(width)) + } + } + + pub fn wrapping_add(&self, _rhs: &LogicVector) -> LogicVector { + unimplemented!() + } +} + +fn add(lhs: &LogicVector, rhs: &LogicVector) -> LogicVector { + //fast, unsafe version + let width = lhs.width(); + assert_eq!(width, rhs.width()); + + LogicVector::from_int_value( + lhs.as_u128().unwrap() + rhs.as_u128().unwrap() & gen_mask_from_width(width), + width, + ) + .unwrap() +} +expand_op_logicvector!(add, Add, add); + +fn resolve(_lhs: &LogicVector, _rhs: &LogicVector) -> LogicVector { + unimplemented!() +} +expand_op!(resolve, Resolve, resolve, LogicVector, LogicVector, LogicVector); + +impl PartialEq for LogicVector { + fn eq(&self, other: &LogicVector) -> bool { + if let (Some(a), Some(b)) = (self.as_u128(), other.as_u128()) { + a == b + } else { + false + } + } +} + +impl Eq for LogicVector {} + +impl PartialEq for LogicVector { + fn eq(&self, other: &u128) -> bool { + if let Some(this) = self.as_u128() { + this == *other + } else { + false + } + } +} + +#[allow(non_snake_case)] +impl LogicVector { + fn contains(&self, value: Ieee1164) -> bool { + self.masks[value] != 0 + } + + fn is_only(&self, value: Ieee1164) -> bool { + self.masks[value] == std::u128::MAX & gen_mask_from_width(self.width) + } + + pub fn has_U(&self) -> bool { + self.contains(Ieee1164::Uninitialized) + } + + pub fn has_X(&self) -> bool { + self.contains(Ieee1164::Strong(Ieee1164Value::Unknown)) + } + + pub fn has_0(&self) -> bool { + self.contains(Ieee1164::Strong(Ieee1164Value::Zero)) + } + + pub fn has_1(&self) -> bool { + self.contains(Ieee1164::Strong(Ieee1164Value::One)) + } + + pub fn has_Z(&self) -> bool { + self.contains(Ieee1164::HighImpedance) + } + + pub fn has_W(&self) -> bool { + self.contains(Ieee1164::Weak(Ieee1164Value::Unknown)) + } + + pub fn has_D(&self) -> bool { + self.contains(Ieee1164::DontCare) + } + + pub fn has_L(&self) -> bool { + self.contains(Ieee1164::Weak(Ieee1164Value::Zero)) + } + + pub fn has_H(&self) -> bool { + self.contains(Ieee1164::Weak(Ieee1164Value::One)) + } + + pub fn has_UXZ(&self) -> bool { + self.has_U() || self.has_D() || self.has_W() || self.has_X() || self.has_Z() + } + + pub fn is_000(&self) -> bool { + self.is_only(Ieee1164::_0) + } + + pub fn is_111(&self) -> bool { + self.is_only(Ieee1164::_1) + } + + pub fn is_ZZZ(&self) -> bool { + self.is_only(Ieee1164::_Z) + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub enum LogicVectorConversionError { + InalidChar(char), + InvalidWidth, +} + +impl From> for LogicVector { + fn from(v: Vec) -> LogicVector { + let len = v.len(); + assert!(assert_width(u8::try_from(len).unwrap())); + + let mut masks = Masks::default(); + for (i, v) in v.into_iter().enumerate() { + masks[v] |= 1 << (len - (i + 1)); + } + + debug_assert_eq!(Ok(()), masks.sanity_check(len as u8)); + + LogicVector { + masks, + width: len as u8, + } + } +} + +impl FromStr for LogicVector { + type Err = LogicVectorConversionError; + + fn from_str(s: &str) -> Result::Err> { + if !assert_width(u8::try_from(s.len()).map_err(|_| LogicVectorConversionError::InvalidWidth)?) { + Err(LogicVectorConversionError::InvalidWidth) + } else { + s.chars() + .try_fold(vec![], |mut v, c| { + v.push(Ieee1164::try_from(c).map_err(|_| LogicVectorConversionError::InalidChar(c))?); + Ok(v) + }) + .map(|v| v.into()) + } + } +} + +impl fmt::Display for LogicVector { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + //TODO real formatting like padding etc + let mut s = String::new(); + for i in (0..self.width).rev() { + for mask in &self.masks { + if (mask.1 >> i) & 1 == 1 { + s.push(mask.0.into()); + continue; + } + } + } + write!(f, "{}", s) + } +} + +impl PartialOrd for LogicVector { + fn partial_cmp(&self, other: &LogicVector) -> Option { + if self.width() != other.width() { + return None; + } + if self.has_UXZ() || other.has_UXZ() { + return None; + } + + self.as_u128().partial_cmp(&other.as_u128()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use proptest::{prop_assert, prop_assert_eq, prop_assume, proptest, proptest_helper}; + + proptest! { + #[test] + fn atm_ctor_value(value in 1u64..) { + let v = LogicVector::from_int_value(value as u128, 128); + prop_assert!(v.is_some()); + let v = v.unwrap(); + prop_assert_eq!(v, value as u128); + } + + #[test] + fn atm_gen_mask(old in 0u8..129, new in 0u8..129) { + prop_assume!(old < new); + prop_assume!(old != 0); + prop_assume!(new != 0); + let mask = build_mask(old, new); + prop_assert_eq!(mask.trailing_zeros() as u8, old); + prop_assert_eq!(mask.leading_zeros() as u8, 128 - new); + prop_assert_eq!(mask.count_ones() as u8, new - old); + prop_assert_eq!(mask.count_zeros() as u8, 128 - new + old); + } + + #[test] + fn atm_as_u128(val in 0u64..) { + let v = LogicVector::from_int_value(val as u128, 64); + prop_assert!(v.is_some()); + let mut v = v.unwrap(); + prop_assert_eq!(Ok(()), v.sanity_check()); + prop_assert_eq!(v.clone(), val as u128); + v.resize(128, Ieee1164::_0); + prop_assert_eq!(v.clone(), val as u128); + v.resize(64, Ieee1164::_0); + v.resize(128, Ieee1164::_1); + prop_assert_eq!(v.clone(), ((std::u64::MAX as u128 )<< 64) | (val as u128)); + } + + #[test] + fn atm_add(a1 in 0u64.., a2 in 0u64.., b1 in 0u64.., b2 in 0u64..) { + let a = (a1 as u128) << 64| (a2) as u128; + let b = (b1 as u128) << 64 | (b2) as u128; + let c = a.checked_add(b); + prop_assume!(c.is_some()); + let c = c.unwrap(); + + let ia = LogicVector::from_int_value(a, 128); + let ib = LogicVector::from_int_value(b, 128); + + prop_assert!(ia.is_some()); + prop_assert!(ib.is_some()); + + let ia = ia.unwrap(); + let ib = ib.unwrap(); + + prop_assert_eq!(ia.as_u128(), Some(a)); + prop_assert_eq!(ib.as_u128(), Some(b)); + + let ic = ia + ib; + prop_assert_eq!(ic.as_u128(), Some(c)); + } + + #[test] + fn atm_to_string(ref a in "[ux10whlzd]{1,128}") { + let lv = a.parse::(); + prop_assert!(lv.is_ok()); + } + } + + #[test] + fn ctor_width() { + for width in 1..=128 { + let v = LogicVector::with_width(width); + assert_eq!(width, v.width()); + assert!(v.has_U(), "{:?}", v); + assert!(!v.has_X(), "{:?}", v); + assert!(!v.has_0(), "{:?}", v); + assert!(!v.has_1(), "{:?}", v); + assert!(!v.has_Z(), "{:?}", v); + assert!(!v.has_W(), "{:?}", v); + assert!(!v.has_D(), "{:?}", v); + assert!(!v.has_L(), "{:?}", v); + assert!(!v.has_H(), "{:?}", v); + } + } + + #[test] + fn ctor_value() { + let v = LogicVector::from_int_value(5, 3); + assert!(v.is_some()); + let v = v.unwrap(); + assert_eq!(v.width(), 3); + assert_eq!(v, 5); + let v = LogicVector::from_int_value(0, 128); + assert!(v.is_some()); + let v = v.unwrap(); + assert_eq!(v.width(), 128); + assert_eq!(v, 0); + let v = LogicVector::from_int_value(5, 8); + assert!(v.is_some()); + let v = v.unwrap(); + assert_eq!(v.width(), 8); + assert_eq!(v, 5); + } + + #[test] + fn resize_smaller() { + let mut v = LogicVector::with_width(5); + assert_eq!(v.width(), 5); + v.set_width(4); + assert_eq!(v.width(), 4); + v.set_width(3); + assert_eq!(v.width(), 3); + v.set_width(2); + assert_eq!(v.width(), 2); + v.set_width(1); + assert_eq!(v.width(), 1); + + let mut v = LogicVector::from_int_value(31, 5).unwrap(); + assert_eq!(v.width(), 5); + assert_eq!(v.as_u128(), Some(0b11111)); + v.set_width(4); + assert_eq!(v.width(), 4); + assert_eq!(v.as_u128(), Some(0b1111)); + v.set_width(3); + assert_eq!(v.width(), 3); + assert_eq!(v.as_u128(), Some(0b111)); + v.set_width(2); + assert_eq!(v.width(), 2); + assert_eq!(v.as_u128(), Some(0b11)); + v.set_width(1); + assert_eq!(v.width(), 1); + assert_eq!(v.as_u128(), Some(0b1)); + } + + #[test] + fn resize_bigger() { + let mut v = LogicVector::with_width(1); + assert_eq!(v.width(), 1); + v.set_width(2); + assert_eq!(v.width(), 2); + v.set_width(3); + assert_eq!(v.width(), 3); + v.set_width(4); + assert_eq!(v.width(), 4); + v.set_width(5); + assert_eq!(v.width(), 5); + + let mut v = LogicVector::from_int_value(0, 1).unwrap(); + assert_eq!(v.width(), 1); + assert_eq!(v, 0); + v.resize(2, Ieee1164::_1); + assert_eq!(v.width(), 2); + assert_eq!(v, 0b10); + v.resize(3, Ieee1164::_0); + assert_eq!(v.width(), 3); + assert_eq!(v, 0b010); + v.resize(4, Ieee1164::_1); + assert_eq!(v.width(), 4); + assert_eq!(v, 0b1010); + v.resize(5, Ieee1164::_0); + assert_eq!(v.width(), 5); + assert_eq!(v, 0b01010); + v.resize(10, Ieee1164::_1); + assert_eq!(v.width(), 10); + assert_eq!(v, 0b1111101010); + } + + #[test] + fn add() {} + + #[test] + fn to_string() {} +} From d160da0823afd93474b274ac61e70284d600531a Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 30 Nov 2018 15:35:34 +0100 Subject: [PATCH 2/3] some small fixes --- benches/logicvector.rs | 6 +++--- src/logicbit/ieee1164.rs | 10 +++++----- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/benches/logicvector.rs b/benches/logicvector.rs index c1ed312..f4d697c 100644 --- a/benches/logicvector.rs +++ b/benches/logicvector.rs @@ -21,7 +21,7 @@ fn create_from_int(b: &mut Bencher) { fn create_from_vec(b: &mut Bencher) { b.iter(|| { for i in 0..NITER { - bb(LogicVector::from(vec![Ieee1164::_U; (i % 128) as usize])); + bb(LogicVector::from(vec![Ieee1164::_U; ((i % 127) + 1) as usize])); } }) } @@ -39,7 +39,7 @@ fn create_from_str(b: &mut Bencher) { fn create_width(b: &mut Bencher) { b.iter(|| { for i in 0..NITER { - bb(LogicVector::with_width((i % 128) as usize + 1)); + bb(LogicVector::with_width(((i % 128) + 1) as u8)); } }) } @@ -51,4 +51,4 @@ fn to_u128(b: &mut Bencher) { assert_eq!(Some(i), bb(LogicVector::from_int_value(i, 128)).unwrap().as_u128()); } }) -} \ No newline at end of file +} diff --git a/src/logicbit/ieee1164.rs b/src/logicbit/ieee1164.rs index d3700d5..e7ba63e 100644 --- a/src/logicbit/ieee1164.rs +++ b/src/logicbit/ieee1164.rs @@ -51,15 +51,15 @@ impl TryFrom for Ieee1164 { 'w' => Ieee1164::Weak(Ieee1164Value::Unknown), 'l' => Ieee1164::Weak(Ieee1164Value::Zero), 'h' => Ieee1164::Weak(Ieee1164Value::One), - '*' | '-' => Ieee1164::DontCare, + '*' | '-' | 'd' => Ieee1164::DontCare, _ => return Err(()), }) } } -impl<'a> From<&'a Ieee1164> for char { - fn from(i: &Ieee1164) -> Self { - match *i { +impl From for char { + fn from(i: Ieee1164) -> Self { + match i { Ieee1164::Uninitialized => 'U', Ieee1164::Strong(Ieee1164Value::Unknown) => 'X', Ieee1164::Strong(Ieee1164Value::Zero) => '0', @@ -188,7 +188,7 @@ expand_op_ieee1164!(resolve, Resolve, resolve); impl fmt::Display for Ieee1164 { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", char::from(self)) + write!(f, "{}", char::from(*self)) } } From a2992cc1e3424dd11d7f4f998c99de1970d2dbe0 Mon Sep 17 00:00:00 2001 From: Marcel Hellwig Date: Fri, 30 Nov 2018 17:10:55 +0100 Subject: [PATCH 3/3] fixed operator precedense --- src/logicbit/logicvector/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/logicbit/logicvector/mod.rs b/src/logicbit/logicvector/mod.rs index 369ca0b..dcd435b 100644 --- a/src/logicbit/logicvector/mod.rs +++ b/src/logicbit/logicvector/mod.rs @@ -271,7 +271,7 @@ fn add(lhs: &LogicVector, rhs: &LogicVector) -> LogicVector { assert_eq!(width, rhs.width()); LogicVector::from_int_value( - lhs.as_u128().unwrap() + rhs.as_u128().unwrap() & gen_mask_from_width(width), + (lhs.as_u128().unwrap() + rhs.as_u128().unwrap()) & gen_mask_from_width(width), width, ) .unwrap()