diff --git a/src/fst.rs b/src/fst.rs index 932b1b3..aa64813 100644 --- a/src/fst.rs +++ b/src/fst.rs @@ -152,6 +152,7 @@ fn read_hierarchy(reader: &mut FstReader) -> Hierarchy { convert_var_tpe(tpe), convert_var_direction(direction), length, + None, SignalRef::from_index(handle.get_index()).unwrap(), ); } diff --git a/src/hierarchy.rs b/src/hierarchy.rs index 37d987c..083406a 100644 --- a/src/hierarchy.rs +++ b/src/hierarchy.rs @@ -123,6 +123,7 @@ pub enum ScopeType { pub enum VarType { Wire, Reg, + Parameter, String, Todo, // placeholder tpe } @@ -133,6 +134,12 @@ pub enum VarDirection { Todo, // placeholder tpe } +#[derive(Debug, Clone, Copy)] +pub struct VarIndex { + pub msb: i32, + pub lsb: i32, +} + /// Signal identifier in the waveform (VCD, FST, etc.) file. #[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)] pub struct SignalRef(NonZeroU32); @@ -179,6 +186,7 @@ pub struct Var { tpe: VarType, direction: VarDirection, length: SignalLength, + index: Option, signal_idx: SignalRef, parent: Option, next: Option, @@ -212,6 +220,9 @@ impl Var { pub fn direction(&self) -> VarDirection { self.direction } + pub fn index(&self) -> Option { + self.index + } pub fn signal_idx(&self) -> SignalRef { self.signal_idx } @@ -651,6 +662,7 @@ impl HierarchyBuilder { tpe: VarType, direction: VarDirection, raw_length: u32, + index: Option, signal_idx: SignalRef, ) { let node_id = self.vars.len(); @@ -681,6 +693,7 @@ impl HierarchyBuilder { tpe, direction, length, + index, signal_idx, next: None, }; @@ -742,12 +755,13 @@ mod tests { + 1 // tpe + 1 // direction + 4 // length + + 12 // bit index TODO: try to save some space here! + std::mem::size_of::() // handle + std::mem::size_of::() // parent + std::mem::size_of::() // next ); - // currently this all comes out to 24 bytes (~= 3x 64-bit pointers) - assert_eq!(std::mem::size_of::(), 24); + // currently this all comes out to 36 bytes (~= 4x 64-bit pointers) + assert_eq!(std::mem::size_of::(), 36); // Scope assert_eq!( diff --git a/src/vcd.rs b/src/vcd.rs index e45c6d0..dacb81f 100644 --- a/src/vcd.rs +++ b/src/vcd.rs @@ -72,9 +72,10 @@ fn read_hierarchy(input: &mut (impl BufRead + Seek)) -> (usize, Hierarchy) { convert_var_tpe(tpe), VarDirection::Todo, u32::from_str_radix(std::str::from_utf8(size).unwrap(), 10).unwrap(), + None, SignalRef::from_index(id_to_int(id).unwrap() as usize).unwrap(), ), - HeaderCmd::VectorVar(tpe, size, id, name, _) => { + HeaderCmd::VectorVar(tpe, size, id, name, index) => { let length = match u32::from_str_radix(std::str::from_utf8(size).unwrap(), 10) { Ok(len) => len, Err(_) => { @@ -90,6 +91,7 @@ fn read_hierarchy(input: &mut (impl BufRead + Seek)) -> (usize, Hierarchy) { convert_var_tpe(tpe), VarDirection::Todo, length, + parse_index(index), SignalRef::from_index(id_to_int(id).unwrap() as usize).unwrap(), ); } @@ -115,6 +117,32 @@ fn read_hierarchy(input: &mut (impl BufRead + Seek)) -> (usize, Hierarchy) { ((end - start) as usize, hierarchy) } +fn parse_index(index: &[u8]) -> Option { + if index.len() < 3 { + return None; + } + assert_eq!(index[0], b'['); + assert_eq!(*index.last().unwrap(), b']'); + let sep = index.iter().position(|b| *b == b':'); + match sep { + None => { + let inner = &index[1..(index.len() - 1)]; + let inner_str = std::str::from_utf8(inner).unwrap(); + let bit = i32::from_str_radix(inner_str, 10).unwrap(); + Some(VarIndex { msb: bit, lsb: bit }) + } + Some(pos) => { + let msb_bytes = &index[1..pos]; + let msb_str = std::str::from_utf8(msb_bytes).unwrap(); + let msb = i32::from_str_radix(msb_str, 10).unwrap(); + let lsb_bytes = &index[(pos + 1)..(index.len() - 1)]; + let lsb_str = std::str::from_utf8(lsb_bytes).unwrap(); + let lsb = i32::from_str_radix(lsb_str, 10).unwrap(); + Some(VarIndex { msb, lsb }) + } + } +} + fn convert_timescale_unit(name: &[u8]) -> TimescaleUnit { match name { b"fs" => TimescaleUnit::FemtoSeconds, @@ -138,6 +166,7 @@ fn convert_var_tpe(tpe: &[u8]) -> VarType { match tpe { b"wire" => VarType::Wire, b"reg" => VarType::Reg, + b"parameter" => VarType::Parameter, b"string" => VarType::String, _ => panic!("TODO: convert {}", String::from_utf8_lossy(tpe)), } diff --git a/src/wavemem.rs b/src/wavemem.rs index e84e520..64355d7 100644 --- a/src/wavemem.rs +++ b/src/wavemem.rs @@ -679,22 +679,21 @@ fn expand_special_vector_cases(value: &[u8], len: usize) -> Option> { return None; } - // sometimes an all z or all x vector is written with only a single digit - if value.len() == 1 && matches!(value[0], b'x' | b'X' | b'z' | b'Z') { - let mut repeated = Vec::with_capacity(len); - repeated.resize(len, value[0]); - return Some(repeated); - } - - // check if we might want to zero extend the value - if matches!(value[0], b'1' | b'0') { - let mut zero_extended = Vec::with_capacity(len); - zero_extended.resize(len - value.len(), b'0'); - zero_extended.extend_from_slice(value); - return Some(zero_extended); + // zero, x or z extend + match value[0] { + b'1' | b'0' => { + let mut zero_extended = Vec::with_capacity(len); + zero_extended.resize(len - value.len(), b'0'); + zero_extended.extend_from_slice(value); + Some(zero_extended) + } + b'x' | b'X' | b'z' | b'Z' => { + let mut repeated = Vec::with_capacity(len); + repeated.resize(len, value[0]); + Some(repeated) + } + _ => None, // failed } - - None // failed } #[inline] diff --git a/tests/diff_tests.rs b/tests/diff_tests.rs index 3983fa6..2391636 100644 --- a/tests/diff_tests.rs +++ b/tests/diff_tests.rs @@ -80,6 +80,7 @@ fn waveform_var_type_to_string(tpe: VarType) -> &'static str { match tpe { VarType::Wire => "wire", VarType::Reg => "reg", + VarType::Parameter => "parameter", VarType::String => "string", VarType::Todo => "todo", } @@ -113,7 +114,17 @@ fn diff_hierarchy_item(ref_item: &vcd::ScopeItem, our_item: HierarchyItem, our_h SignalLength::Variable => {} // nothing to check SignalLength::Fixed(size) => assert_eq!(ref_var.size, size.get()), } - assert!(ref_var.index.is_none(), "TODO: expose index"); + match ref_var.index { + None => assert!(our_var.index().is_none()), + Some(vcd::ReferenceIndex::BitSelect(bit)) => { + assert_eq!(our_var.index().unwrap().msb, bit); + assert_eq!(our_var.index().unwrap().lsb, bit); + } + Some(vcd::ReferenceIndex::Range(msb, lsb)) => { + assert_eq!(our_var.index().unwrap().msb, msb); + assert_eq!(our_var.index().unwrap().lsb, lsb); + } + } } (vcd::ScopeItem::Comment(_), _) => {} // we do not care about comments (other_ref, our) => panic!( @@ -163,9 +174,12 @@ fn diff_signals(ref_reader: &mut vcd::Parser, our: &mut Waveform) let suffix: String = our_value_str.chars().skip(prefix_len).collect(); assert_eq!(suffix, value.to_string()); let is_x_extended = suffix.chars().next().unwrap() == 'x'; + let is_z_extended = suffix.chars().next().unwrap() == 'z'; for c in our_value_str.chars().take(prefix_len) { if is_x_extended { assert_eq!(c, 'x'); + } else if is_z_extended { + assert_eq!(c, 'z'); } else { assert_eq!(c, '0'); }