From 232b026f62ec6302597ad673d12e6c13c4fd13b6 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Mon, 1 Aug 2022 10:11:05 -0400 Subject: [PATCH] things are much faster now, but x/z values not yet supported --- src/vcd/parse.rs | 265 ++++++++++++++++++++++++++-------------- src/vcd/parse/scopes.rs | 8 +- src/vcd/types.rs | 67 ++-------- 3 files changed, 187 insertions(+), 153 deletions(-) diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index 9267a20..8f21be5 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -1,8 +1,5 @@ use std::{fs::File}; use std::collections::HashMap; -use chrono::format::format; -use num::BigInt; -use num::bigint::ToBigInt; use super::*; @@ -22,6 +19,82 @@ use std::num::{IntErrorKind, ParseIntError}; use function_name::named; +fn binary_str_to_vec_u8(binary_str : &str) -> Result, String> { + let mut vec_u8 : Vec = Vec::new(); + let mut binary_str_as_bytes = binary_str.as_bytes(); + + let mut tail_idx = binary_str_as_bytes.len(); + // clamp head if provided binary str is less than 8 long + let mut head_idx = + if tail_idx >= 8 + {binary_str_as_bytes.len() - 8} + else + {0}; + while {tail_idx > 0} { + let curr_b_val = &binary_str_as_bytes[head_idx..tail_idx]; + let val_u8 = base2_str_to_byte(curr_b_val)?; + vec_u8.push(val_u8); + + + if head_idx < 8 { + head_idx = 0 + } + else { + head_idx = head_idx - 8; + } + + if tail_idx < 8 { + tail_idx = 0 + } + else { + tail_idx = tail_idx - 8; + } + + } + Ok(vec_u8) +} + +fn base2_str_to_byte(word : &[u8]) -> Result { + let mut val = 0u8; + + // shouldn't have more than 8 chars in str + let len = word.len(); + if len > 8 { + let (f, l )= (file!(), line!()); + let err = format!( + "Error near {f}:{l}. Base2 string has length {len} > 8."); + return Err(err) + } + + let bit_lut = [ + 0b0000_0001u8, + 0b0000_0010u8, + 0b0000_0100u8, + 0b0000_1000u8, + 0b0001_0000u8, + 0b0010_0000u8, + 0b0100_0000u8, + 0b1000_0000u8 + ]; + + for (idx, chr) in word.iter().rev().enumerate() { + match chr { + b'1' => {val = bit_lut[idx] | val} + b'0' => {} + _ => { + let chr = *chr as char; + let (f, l )= (file!(), line!()); + let err = format!( + "Error near {f}:{l}. Expected 1 or 0 in base2 string but got {chr}"); + return Err(err) + } + } + + } + + Ok(val) +} + /// Sometimes, variables can be listed outside of scopes. /// We call these floating vars. pub(super) fn parse_orphaned_vars<'a>( @@ -118,15 +191,15 @@ fn parse_events<'a>( "#" => { let value = &word[1..]; let (f, l )= (file!(), line!()); - let value = BigInt::parse_bytes(value.as_bytes(), 10).ok_or( - format!("Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}").as_str())?; + let mut value = binary_str_to_vec_u8(value).map_err( + |e| format!("Error near {f}:{l}. Failed to parse {value} as \ + at {cursor:?} with error {e}"))?; // TODO : u32 helps with less memory, but should ideally likely be // configurable. let (f, l )= (file!(), line!()); let start_idx = u32::try_from(vcd.timeline.len()).map_err( |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; vcd.timeline_markers.push(StartIdx(start_idx)); - let (_, mut value) = value.to_bytes_be(); vcd.timeline.append(&mut value); } @@ -136,7 +209,7 @@ fn parse_events<'a>( let hash = &word[1..]; let (f, l )= (file!(), line!()); let Signal_Idx(ref signal_idx) = signal_map.get(hash).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}").as_str())?; + format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}"))?; // account for fact that signal idx could be an alias, so there // could be one step of indirection @@ -158,7 +231,7 @@ fn parse_events<'a>( let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { Signal::Data {name, sig_type, ref mut signal_error, num_bits, - self_idx, timeline, timeline_markers, scope_parent} => { + self_idx, u8_timeline, u8_timeline_markers, ..} => { // if this is a bad signal, go ahead and skip it if signal_error.is_some() {continue;} @@ -175,7 +248,7 @@ fn parse_events<'a>( `{num_bits}`. \ This error occurred while parsing the vcd file at \ {cursor:?}"); - signal_error.insert(msg); + *signal_error = Some(msg); continue; } } @@ -195,13 +268,8 @@ fn parse_events<'a>( |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; let timeline_idx = TimelineIdx(timeline_idx); - let (f, l )= (file!(), line!()); - let start_idx = u32::try_from(timeline.len()).map_err( - |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; - let start_idx = StartIdx(start_idx); - // let pair = (timeline_idx, start_idx); - timeline_markers.push(timeline_idx); - timeline.push(0u8); + u8_timeline_markers.push(timeline_idx); + u8_timeline.push(0u8); Ok(()) } Signal::Alias {..} => { @@ -220,7 +288,7 @@ fn parse_events<'a>( let hash = &word[1..]; let (f, l )= (file!(), line!()); let Signal_Idx(ref signal_idx) = signal_map.get(hash).ok_or( - format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}").as_str())?; + format!("Error near {f}:{l}. Failed to lookup signal {hash} at {cursor:?}"))?; // account for fact that signal idx could be an alias, so there // could be one step of indirection @@ -242,7 +310,7 @@ fn parse_events<'a>( let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); match signal { Signal::Data {name, sig_type, ref mut signal_error, num_bits, - self_idx, timeline, timeline_markers, scope_parent} => { + self_idx, u8_timeline, u8_timeline_markers, scope_parent, ..} => { // if this is a bad signal, go ahead and skip it if signal_error.is_some() {continue;} @@ -259,7 +327,7 @@ fn parse_events<'a>( `{num_bits}`. \ This error occurred while parsing the vcd file at \ {cursor:?}"); - signal_error.insert(msg); + *signal_error = Some(msg); continue; } } @@ -279,13 +347,8 @@ fn parse_events<'a>( |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; let timeline_idx = TimelineIdx(timeline_idx); - let (f, l )= (file!(), line!()); - let start_idx = u32::try_from(timeline.len()).map_err( - |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; - let start_idx = StartIdx(start_idx); - // let pair = (timeline_idx, start_idx); - timeline_markers.push(timeline_idx); - timeline.push(1u8); + u8_timeline_markers.push(timeline_idx); + u8_timeline.push(1u8); Ok(()) } Signal::Alias {..} => { @@ -299,80 +362,94 @@ fn parse_events<'a>( } // handle the case of an n bit signal whose value must be parse - // "b" => { - // // let binary_value = &word[1..]; - // // let (f, l )= (file!(), line!()); - // // let value = BigInt::parse_bytes(binary_value.as_bytes(), 2).ok_or( - // // format!("Error near {f}:{l}. Failed to parse {binary_value} as BigInt at {cursor:?}").as_str())?; - // // let (_, mut value) = value.to_bytes_be(); + "b" => { + let binary_value = &word[1..]; + let observed_num_bits = binary_value.len(); + let (f, l )= (file!(), line!()); + let mut value = binary_str_to_vec_u8(binary_value).map_err( + |e| format!("Error near {f}:{l}. Failed to parse {binary_value} as \ + at {cursor:?} with error {e}"))?; - // // this word should be the signal alias - // let (word, cursor) = word_reader.next_word().unwrap(); + // this word should be the signal alias + let (word, cursor) = word_reader.next_word().unwrap(); - // // lookup signal idx - // let (f, l )= (file!(), line!()); - // let Signal_Idx(ref signal_idx) = signal_map.get(word).ok_or( - // format!("Error near {f}:{l}. Failed to lookup signal {word} at {cursor:?}").as_str())?; + // lookup signal idx + let (f, l )= (file!(), line!()); + let Signal_Idx(ref signal_idx) = signal_map.get(word).ok_or( + format!("Error near {f}:{l}. Failed to lookup signal {word} at {cursor:?}"))?; - // // account for fact that signal idx could be an alias, so there - // // could be one step of indirection - // let signal_idx = - // { - // let signal = vcd.all_signals.get(*signal_idx).unwrap(); - // match signal { - // Signal::Data {..} => {*signal_idx} - // Signal::Alias {name, signal_alias} => { - // let Signal_Idx(ref signal_idx) = signal_alias; - // signal_idx.clone() + // account for fact that signal idx could be an alias, so there + // could be one step of indirection + let signal_idx = + { + let signal = vcd.all_signals.get(*signal_idx).unwrap(); + match signal { + Signal::Data {..} => {*signal_idx} + Signal::Alias {name, signal_alias} => { + let Signal_Idx(ref signal_idx) = signal_alias; + signal_idx.clone() - // } - // } - // }; + } + } + }; - // // after handling potential indirection, go ahead and update the timeline - // // of the signal signal_idx references - // let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); - // match signal { - // Signal::Data {name, sig_type, num_bits, - // self_idx, timeline, timeline_markers, scope_parent} => { - // // get bitwidth, while accounting for the error case when - // // numbits is None - // let num_bits = { - // let (f, l) = (file!(), line!()); - // let msg = format!("\ - // Error near {f}:{l}. The bitwidth for signal {name} \ - // must be specified for a signal of type {sig_type:?}. \ - // This error occurred while parsing the vcd file at \ - // {cursor:?}"); - // num_bits.as_ref().ok_or(msg)? - // }; + // after handling potential indirection, go ahead and update the timeline + // of the signal signal_idx references + let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); + match signal { + Signal::Data {name, sig_type, ref mut signal_error, num_bits, + self_idx, u8_timeline, u8_timeline_markers, scope_parent, ..} => { - // let (f, l )= (file!(), line!()); - // let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( - // |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; - // let timeline_idx = TimelineIdx(timeline_idx); + if signal_error.is_some() {continue;} - // let (f, l )= (file!(), line!()); - // let start_idx = u32::try_from(timeline.len()).map_err( - // |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; - // let start_idx = StartIdx(start_idx); - // let pair = (timeline_idx, start_idx); - // // timeline_markers.push(pair); - // // timeline.append(&mut [0u8, 1u8, 2u8]); - // timeline.push(0u8); - // timeline.push(1u8); - // timeline.push(2u8); - // Ok(()) - // } - // Signal::Alias {..} => { - // let (f, l )= (file!(), line!()); - // let msg = format!( - // "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ - // This error occurred while parsing vcd file at {cursor:?}"); - // Err(msg) - // } - // }?; - // } + // Get the observed number of bits for the value parsed earlier + // and verify that it is not greater than the numbits declared + // when the signal was declared. + // Also account for the error case of a bitwidth of `None` + match num_bits { + Some(ref num_bits) => { + if *num_bits > observed_num_bits { + let (f, l) = (file!(), line!()); + let msg = format!("\ + Error near {f}:{l}. The bitwidth for signal {name} \ + of sig_type {sig_type:?} is expected to be `1` not \ + `{num_bits}`. \ + This error occurred while parsing the vcd file at \ + {cursor:?}"); + println!("Encountered bad signal {name}."); + *signal_error = Some(msg); + continue; + } + } + None => { + let (f, l) = (file!(), line!()); + let msg = format!("\ + Error near {f}:{l}. The bitwidth for signal {name} \ + must be specified for a signal of type {sig_type:?}. \ + This error occurred while parsing the vcd file at \ + {cursor:?}"); + Err(msg)?; + } + }; + + let (f, l )= (file!(), line!()); + let timeline_idx = u32::try_from(vcd.timeline.len()).map_err( + |e| format!("Error near {f}:{l}. Failed to convert from usize to u32."))?; + let timeline_idx = TimelineIdx(timeline_idx); + + u8_timeline_markers.push(timeline_idx); + u8_timeline.append(&mut value); + Ok(()) + } + Signal::Alias {..} => { + let (f, l )= (file!(), line!()); + let msg = format!( + "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ + This error occurred while parsing vcd file at {cursor:?}"); + Err(msg) + } + }?; + } _ => {} } } @@ -404,7 +481,7 @@ pub fn parse_vcd(file : File) -> Result { // parsing scoped vars. let (f, l ) = (file!(), line!()); let msg = format!("Error near {f}:{l}. Current word empty!"); - let (word, cursor) = word_gen.curr_word().ok_or(msg.as_str())?; + let (word, cursor) = word_gen.curr_word().ok_or(msg)?; match word { "$scope" => { parse_scopes(&mut word_gen, None, &mut vcd, &mut signal_map) diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index b275c56..cf2e36f 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -86,8 +86,10 @@ pub(super) fn parse_var<'a>( signal_error: None, num_bits: no_bits, self_idx: signal_idx, - timeline: vec![], - timeline_markers: vec![], + u8_timeline: vec![], + u8_timeline_markers: vec![], + string_timeline: vec![], + string_timeline_markers: vec![], scope_parent: parent_scope_idx }; (signal, signal_idx) } @@ -161,7 +163,7 @@ pub(super) fn parse_signal_tree<'a>( let (word, cursor) = word_reader.next_word().ok_or(&err)?; let ParseResult{matched, residual} = tag(word, "$"); match matched { - // we hope that this word stars with a `$` + // we hope that this word starts with a `$` "$" => { match residual { "scope" => { diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 45be6c2..6c13095 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -1,7 +1,4 @@ -use core::time; -use std::collections::{BTreeMap, HashMap}; use chrono::prelude::*; -use num::BigInt; #[derive(Debug)] pub(super) struct Version(pub String); @@ -30,46 +27,25 @@ pub struct StartIdx(pub(super) u32); #[derive(Debug)] pub(super) enum Sig_Type {Integer, Parameter, Real, Reg, Str, Wire, Tri1, Time} -#[derive(Debug)] -pub(super) enum TimeStamp { - u8(u8), - u16(u16), - u32(u32), - u64(u64), - BigInt(BigInt), -} - -#[derive(Debug, Clone)] -pub(super) enum Value { - u8(u8), - u16(u16), - u32(u32), - u64(u64), - BigInt(BigInt), -} - -pub type BigNum = Vec; - -#[derive(Debug)] -pub(super) enum Sig_Value { - Numeric(u64), - NonNumeric(String)} - #[derive(Debug)] pub(super) enum Signal{ Data{ name : String, sig_type : Sig_Type, // I've seen a 0 bit signal parameter in a xilinx - // simulation before that gets assigne 1 bit values. + // simulation before that gets assigned 1 bit values. // I consider this to be bad behavior. We capture such // errors in the following type. signal_error : Option, num_bits : Option, // TODO : may be able to remove self_idx self_idx : Signal_Idx, - timeline : Vec, - timeline_markers : Vec<(TimelineIdx)>, + // we could encounter a mix of pure values and strings + // for the same signal timeline + u8_timeline : Vec, + u8_timeline_markers : Vec<(TimelineIdx)>, + string_timeline : Vec, + string_timeline_markers : Vec<(TimelineIdx)>, scope_parent : Scope_Idx}, Alias{ name : String, @@ -81,7 +57,6 @@ pub(super) struct Scope { pub(super) name : String, pub(super) parent_idx : Option, - // TODO : may be able to remove self_idx pub(super) self_idx : Scope_Idx, pub(super) child_signals : Vec, @@ -127,11 +102,9 @@ impl VCD { println!(); for scope_idx in &root_scope.child_scopes { - // let Scope_Idx(ref scope_idx_usize) = scope_idx; - // let child_scope = &all_scopes[*scope_idx_usize]; self.print_scope_tree(*scope_idx, depth+1); } - // let root = vcd.all_scopes; + } pub fn print_scopes(&self) { @@ -140,24 +113,6 @@ impl VCD { } } - // pub fn average_len(&self) -> f64{ - // let mut total_lens = 0.0; - // for el in &self.timeline { - // total_lens += el.len() as f64; - // } - - // return total_lens/(self.timeline.len() as f64); - // } - - // pub fn total_len(&self) -> usize{ - // let mut total_lens = 0usize; - // for el in &self.timeline { - // total_lens += el.len(); - // } - - // return total_lens; - // } - pub fn print_longest_signal(&self) { let mut idx = 0usize; let mut max_len = 0usize; @@ -171,10 +126,10 @@ impl VCD { sig_type, num_bits, self_idx, - timeline, + u8_timeline, .. } => { - if timeline.len() > max_len { - max_len = timeline.len(); + if u8_timeline.len() > max_len { + max_len = u8_timeline.len(); let Signal_Idx(idx_usize) = self_idx; idx = *idx_usize; signal_name = name.clone();