From cbd8be1708beaf055b94dda46adf4af763fa0052 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Wed, 17 Aug 2022 22:10:00 -0400 Subject: [PATCH] - update README to reflect correct performance numbers - add tasks to README - repair bug in events.rs that prevented most events in VCD file from being parsed - add some initial code for timeline scrubbing --- Cargo.toml | 4 +- README.md | 10 ++- src/main.rs | 2 +- src/vcd/parse.rs | 2 +- src/vcd/parse/events.rs | 6 +- src/vcd/reader.rs | 2 - src/vcd/types.rs | 22 +++++++ src/vcd/utilities.rs | 134 ++++++++++++++++++++++++++-------------- 8 files changed, 125 insertions(+), 57 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 5664131..6996831 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,5 +12,5 @@ debug = 1 num = "0.4" clap = { version = "3.1.8", features = ["derive"] } chrono = "0.4" -itertools = "0.10.3" -backtrace = "0.3" \ No newline at end of file +# TODO : remove itertools once date parser is reworked. +itertools = "0.10.3" \ No newline at end of file diff --git a/README.md b/README.md index fc0ba8d..55d7f90 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ slower. | Software | Time(s) | Memory(MB) | |----------|---------|------------| -| FastWave | ~15.09 | 267.3 | +| FastWave | ~27.30 | 1100+ | | GtkWave | ~30 | 89.8 | @@ -67,7 +67,10 @@ Here's a command to test on a malformed VCD: # TODO -## Features +## Features and Other + - [ ] add timeline value scanner code + - [ ] test against large waveform directly within SpinalHDL + - [ ] (a bit of work) consolidate error messages in validation phase - [ ] be explicit with imports, remove exports as possible once FastWave is known to be fairly stable. - [ ] do a read through all the code @@ -88,5 +91,8 @@ Here's a command to test on a malformed VCD: - [ ] Handle TODOs - [ ] Remove debug code/comments. +## Documentation + - [ ] Document indexing structure with diagram and possibly include the rational + ## Marketing - [ ] Send survey to community \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index d4b5311..b8442c6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -25,7 +25,7 @@ fn main() -> std::io::Result<()> { let elapsed = now.elapsed(); println!("Elapsed: {:.2?}", elapsed); - std::thread::sleep(std::time::Duration::from_secs(10)); + // std::thread::sleep(std::time::Duration::from_secs(10)); Ok(()) } diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index 1ae3bf2..bd6163c 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -1,4 +1,4 @@ -use num::BigInt; +use num::BigUint; use std::collections::HashMap; use std::fs::File; diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index f3c1aa2..f9a3fd6 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -15,7 +15,7 @@ pub(super) fn parse_events<'a>( break; }; - let (word, cursor) = next_word!(word_reader)?; + let (word, cursor) = next_word.unwrap(); let Cursor(Line(_), Word(word_in_line_idx)) = cursor; // we only want to match on the first word in a line if word_in_line_idx != 1 { @@ -26,14 +26,14 @@ pub(super) fn parse_events<'a>( "#" => { let value = &word[1..]; let (f, l) = (file!(), line!()); - let value = BigInt::parse_bytes(value.as_bytes(), 10) + let value = 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.to_bytes_le(); // TODO : u32 helps with less memory, but should ideally likely be // configurable. let (f, l) = (file!(), line!()); diff --git a/src/vcd/reader.rs b/src/vcd/reader.rs index 166a5cd..196b19b 100644 --- a/src/vcd/reader.rs +++ b/src/vcd/reader.rs @@ -5,8 +5,6 @@ use std::io::prelude::*; use std::slice; use std::str; -use backtrace::{Backtrace, BacktraceFrame, BacktraceSymbol}; - #[derive(Debug, Clone)] pub(super) struct Line(pub(super) usize); #[derive(Debug, Clone)] diff --git a/src/vcd/types.rs b/src/vcd/types.rs index cb67d68..b331da3 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -1,4 +1,6 @@ +use super::utilities::{ordered_binary_lookup_u8, LookupErrors}; use chrono::prelude::*; +use num::{BigUint, Zero}; #[derive(Debug)] pub(super) struct Version(pub String); @@ -72,6 +74,26 @@ pub(super) enum Signal { }, } +#[derive(Debug)] +pub(super) enum TimelineQueryResults { + BigUint(BigUint), + String(String), +} + +impl Scope { + pub(super) fn query_value(&self, time: TimelineIdx) -> Result { + // match + // assert + // ordered_binary_lookup_u8( + // &value_sequence_as_bytes_u8, + // 4, + // &timeline_cursors, + // TimelineIdx(scrubbing_cursor), + // ); + Ok(TimelineQueryResults::String("".to_string())) + } +} + #[derive(Debug)] pub(super) struct Scope { pub(super) name: String, diff --git a/src/vcd/utilities.rs b/src/vcd/utilities.rs index a0c1abb..2c11b42 100644 --- a/src/vcd/utilities.rs +++ b/src/vcd/utilities.rs @@ -1,7 +1,5 @@ use super::*; -use std::cmp::Ordering; - #[derive(Debug)] pub(super) enum BinaryParserErrTypes { XValue, @@ -77,61 +75,105 @@ pub(super) fn binary_str_to_vec_u8(binary_str: &str) -> Result, BinaryPa Ok(vec_u8) } -// TODO : modify ordered_binary_lookup to support VCD timeline lookup -// and return time in signature -fn compare_strs(a: &str, b: &str) -> Ordering { - // choose the smaller of the two indices - let upper_bound = if a.len() > b.len() { b.len() } else { a.len() }; - let a_as_bytes = a.as_bytes(); - let b_as_bytes = b.as_bytes(); +use num::{BigUint, Zero}; - for i in 0..upper_bound { - let a_byte = a_as_bytes[i]; - let b_byte = b_as_bytes[i]; - if a_byte > b_byte { - return Ordering::Greater; - } - if b_byte > a_byte { - return Ordering::Less; - } - } - - if a.len() > b.len() { - return Ordering::Greater; - } - - if a.len() < b.len() { - return Ordering::Less; - } - - return Ordering::Equal; +#[derive(Debug)] +pub(super) enum LookupErrors { + PreTimeline { + desired_time: TimelineIdx, + timeline_start_time: TimelineIdx, + }, + EmptyTimeline, + TimelineNotMultiple, + OrderingFailure, } -fn ordered_binary_lookup(map: &Vec<(String, SignalIdx)>, key: &str) -> Result { - let mut upper_idx = map.len() - 1; - let mut lower_idx = 0usize; +pub(super) fn ordered_binary_lookup_u8( + value_sequence_as_bytes: &Vec, + bytes_per_value: usize, + timeline_cursors: &Vec, + desired_time: TimelineIdx, +) -> Result { + // timeline must not be empty + if timeline_cursors.is_empty() { + return Err(LookupErrors::EmptyTimeline); + } + // assertion that value_sequence is a proper multiple of + // timeline_markers + if value_sequence_as_bytes.len() != (timeline_cursors.len() * bytes_per_value) { + return Err(LookupErrors::TimelineNotMultiple); + } + + let TimelineIdx(desired_time) = desired_time; + + // check if we're requesting a value that occurs before the recorded + // start of the timeline + let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap(); + if desired_time < *timeline_start_time { + return Err(LookupErrors::PreTimeline { + desired_time: TimelineIdx(desired_time), + timeline_start_time: TimelineIdx(*timeline_start_time), + }); + } + + let mut lower_idx = 0usize; + let mut upper_idx = timeline_cursors.len() - 1; + + // check if we're requesting a value that occurs beyond the end of the timeline, + // if so, return the last value in this timeline + let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap(); + if desired_time > *timeline_end_time { + let range = (value_sequence_as_bytes.len() - bytes_per_value)..; + let value_by_bytes = &value_sequence_as_bytes[range]; + let value = BigUint::from_bytes_le(value_by_bytes); + + return Ok(value); + } + + // This while loop is the meat of the lookup. Performance is log2(n), + // where n is the number of events on the timeline. + // We can assume that by the time we get here, that the desired_time + // is an event that occurs on the timeline, given that we handle any events + // occuring after or before the recorded tiimeline in the code above. while lower_idx <= upper_idx { let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2); - let (str_val, signal_idx) = map.get(mid_idx).unwrap(); - let ordering = compare_strs(key, str_val.as_str()); + let TimelineIdx(curr_time) = timeline_cursors[mid_idx]; + let ordering = curr_time.cmp(&desired_time); match ordering { - Ordering::Less => { - upper_idx = mid_idx - 1; - } - Ordering::Equal => { - return Ok(*signal_idx); - } - Ordering::Greater => { + std::cmp::Ordering::Less => { lower_idx = mid_idx + 1; } + std::cmp::Ordering::Equal => { + let u8_timeline_start_idx = mid_idx * bytes_per_value; + let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value; + let range = u8_timeline_start_idx..u8_timeline_end_idx; + let value_by_bytes = &value_sequence_as_bytes[range]; + let value = BigUint::from_bytes_le(value_by_bytes); + return Ok(value); + } + std::cmp::Ordering::Greater => { + upper_idx = mid_idx - 1; + } } } - return Err(format!( - "Error near {}:{}. Unable to find key: `{key}` in the map.", - file!(), - line!() - )); + let idx = lower_idx - 1; + let TimelineIdx(left_time) = timeline_cursors[idx]; + let TimelineIdx(right_time) = timeline_cursors[idx + 1]; + + let ordered_left = left_time < desired_time; + let ordered_right = desired_time < right_time; + if !(ordered_left && ordered_right) { + return Err(LookupErrors::OrderingFailure); + } + + let u8_timeline_start_idx = idx * bytes_per_value; + let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value; + let range = u8_timeline_start_idx..u8_timeline_end_idx; + let value_by_bytes = &value_sequence_as_bytes[range]; + let value = BigUint::from_bytes_le(value_by_bytes); + + return Ok(value); }