diff --git a/src/lib.rs b/src/lib.rs index f73512a..1ab2b83 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,6 +7,6 @@ mod vcd; pub use vcd::parse::parse_vcd; pub use vcd::types::{ScopeIdx, SignalIdx, VCD}; pub use vcd::types::{Metadata, Timescale, Version}; -pub use vcd::signal::{Signal}; +pub use vcd::signal::{Signal, SignalValue}; pub use num::BigUint; diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index c89361c..4a5e3b0 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -27,10 +27,11 @@ pub fn parse_vcd(file: File) -> Result { all_signals: vec![], all_scopes: vec![], root_scopes: vec![], + largest_timestamp: None }; scopes::parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?; events::parse_events(&mut word_gen, &mut vcd, &mut signal_map)?; Ok(vcd) -} \ No newline at end of file +} diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 05e24ed..49bff94 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -40,14 +40,14 @@ pub(super) fn parse_events<'a>( "#" => { let value = &word[1..]; let (f, l) = (file!(), line!()); - let value = BigUint::parse_bytes(value.as_bytes(), 10) + let value_biguint = BigUint::parse_bytes(value.as_bytes(), 10) .ok_or(()) .map_err(|_| { format!( "Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}" ) })?; - let mut value = value.to_bytes_le(); + let mut value = value_biguint.to_bytes_le(); // TODO : u32 helps with less memory, but should ideally likely be // configurable. curr_tmstmp_len_u8 = u8::try_from(value.len()).map_err(|_| { @@ -66,6 +66,7 @@ pub(super) fn parse_events<'a>( ) })?; vcd.tmstmps_encoded_as_u8s.append(&mut value); + vcd.largest_timestamp = Some(value_biguint); } // handle the case of an n bit signal whose value must be parsed @@ -134,6 +135,7 @@ pub(super) fn parse_events<'a>( lsb_indxs_of_num_tmstmp_vals_on_tmln, byte_len_of_num_tmstmp_vals_on_tmln, lsb_indxs_of_string_tmstmp_vals_on_tmln, + byte_len_of_string_tmstmp_vals_on_tmln, .. } => { // we've already identified in a prior loop iteration that the signal has @@ -176,6 +178,7 @@ pub(super) fn parse_events<'a>( if store_as_string { lsb_indxs_of_string_tmstmp_vals_on_tmln .push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx)); + byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8); string_vals.push(value_string); Ok(()) } else { diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index 1688f92..0e8dc9d 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -23,8 +23,8 @@ pub enum SigType { Time, } -#[derive(Debug)] -pub(super) enum TimelineQueryResults { +#[derive(Debug, PartialEq)] +pub enum SignalValue { BigUint(BigUint), String(String), } @@ -36,13 +36,19 @@ impl<'a> Signal<'a> { let Signal(signal_enum) = &self; signal_enum.name() } + + pub fn num_bits(&self) -> Option { + let Signal(signal_enum) = &self; + signal_enum.bits_required() + } + pub fn query_string_val_on_tmln( &self, desired_time: &BigUint, vcd: &types::VCD, ) -> Result { let Signal(signal_enum) = &self; - signal_enum.query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) + signal_enum.query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals).map(|(val, _)| val) } pub fn query_num_val_on_tmln( &self, @@ -50,7 +56,44 @@ impl<'a> Signal<'a> { vcd: &types::VCD, ) -> Result { let Signal(signal_enum) = &self; - signal_enum.query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) + signal_enum.query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals).map(|(val, _)| val) + } + + pub fn query_val_on_tmln( + &self, + desired_time: &BigUint, + vcd: &types::VCD, + ) -> Result { + let Signal(signal_enum) = &self; + let num_val = signal_enum + .query_num_val_on_tmln( + desired_time, + &vcd.tmstmps_encoded_as_u8s, + &vcd.all_signals + ); + let str_val = signal_enum + .query_string_val_on_tmln( + desired_time, + &vcd.tmstmps_encoded_as_u8s, + &vcd.all_signals + ); + + // Both num and str will return the newest value that is closest to + // the desired time. If both have valid values, select the most recent + // one + match (num_val, str_val) { + (Ok((num_val, num_time)), Ok((str_val, str_time))) => { + if num_time > str_time { + Ok(SignalValue::BigUint(num_val)) + } + else { + Ok(SignalValue::String(str_val)) + } + } + (Ok((num_val, _)), Err(_)) => Ok(SignalValue::BigUint(num_val)), + (Err(_), Ok((str_val, _))) => Ok(SignalValue::String(str_val)), + (Err(e), _e) => Err(e) + } } } @@ -187,6 +230,10 @@ impl SignalEnum { lsb_indxs_of_string_tmstmp_vals_on_tmln[event_idx]; let timestamp_idx = timestamp_idx as usize; + if byte_len_of_string_tmstmp_vals_on_tmln.is_empty() { + return Err(SignalErrors::EmptyTimeline); + } + // form timestamp let byte_len = byte_len_of_string_tmstmp_vals_on_tmln[event_idx] as usize; let timestamp = &tmstmps_encoded_as_u8s[timestamp_idx..(timestamp_idx + byte_len)]; @@ -253,6 +300,14 @@ impl SignalEnum { Ok((timestamp, signal_val)) } + + fn bits_required(&self) -> Option { + match self { + SignalEnum::Data {num_bits, ..} => num_bits.clone(), + // TODO: Follow aliases? + SignalEnum::Alias { name, signal_alias } => None, + } + } } // Val and string query functions. @@ -265,7 +320,7 @@ impl SignalEnum { desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result { + ) -> Result<(String, TimeStamp), SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -319,7 +374,7 @@ impl SignalEnum { if *desired_time < timeline_start_time { return Err(SignalErrors::PreTimeline { desired_time: desired_time.clone(), - timeline_start_time: timeline_start_time, + timeline_start_time, }); } @@ -331,7 +386,7 @@ impl SignalEnum { // check if we're requesting a value that occurs beyond the end of the timeline, // if so, return the last value in this timeline if *desired_time > timeline_end_time { - return Ok(timeline_end_val.to_string()); + return Ok((timeline_end_val.to_string(), timeline_end_time)); } // This while loop is the meat of the lookup. Performance is log2(n), @@ -350,7 +405,7 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - return Ok(curr_val.to_string()); + return Ok((curr_val.to_string(), curr_time)); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -373,14 +428,14 @@ impl SignalEnum { }); } - return Ok(left_val.to_string()); + Ok((left_val.to_string(), left_time)) } pub fn query_num_val_on_tmln( &self, desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result { + ) -> Result<(BigUint, TimeStamp), SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -449,7 +504,7 @@ impl SignalEnum { if *desired_time < timeline_start_time { return Err(SignalErrors::PreTimeline { desired_time: desired_time.clone(), - timeline_start_time: timeline_start_time, + timeline_start_time, }); } @@ -461,7 +516,7 @@ impl SignalEnum { // check if we're requesting a value that occurs beyond the end of the timeline, // if so, return the last value in this timeline if *desired_time > timeline_end_time { - return Ok(timeline_end_val); + return Ok((timeline_end_val, timeline_end_time)); } // This while loop is the meat of the lookup. Performance is log2(n), @@ -480,7 +535,7 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - return Ok(curr_val); + return Ok((curr_val, curr_time)); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -503,6 +558,6 @@ impl SignalEnum { }); } - return Ok(left_val); + Ok((left_val, left_time)) } } diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 3b2979a..d589e73 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -5,6 +5,7 @@ // and the YEHOWSHUA license, both of which can be found at // the root of the folder containing the sources for this program. use chrono::prelude::{DateTime, Utc}; +use num::BigUint; use super::signal::{Signal, SignalEnum}; #[derive(Debug)] @@ -63,6 +64,7 @@ pub struct VCD { pub(super) all_signals: Vec, pub(super) all_scopes: Vec, pub(super) root_scopes: Vec, + pub(super) largest_timestamp: Option } impl VCD { @@ -121,4 +123,8 @@ impl VCD { )), } } + + pub fn max_timestamp(&self) -> &Option { + &self.largest_timestamp + } }