- 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
This commit is contained in:
Yehowshua Immanuel 2022-08-17 22:10:00 -04:00
parent 3a4ed56532
commit cbd8be1708
8 changed files with 125 additions and 57 deletions

View file

@ -12,5 +12,5 @@ debug = 1
num = "0.4" num = "0.4"
clap = { version = "3.1.8", features = ["derive"] } clap = { version = "3.1.8", features = ["derive"] }
chrono = "0.4" chrono = "0.4"
itertools = "0.10.3" # TODO : remove itertools once date parser is reworked.
backtrace = "0.3" itertools = "0.10.3"

View file

@ -37,7 +37,7 @@ slower.
| Software | Time(s) | Memory(MB) | | Software | Time(s) | Memory(MB) |
|----------|---------|------------| |----------|---------|------------|
| FastWave | ~15.09 | 267.3 | | FastWave | ~27.30 | 1100+ |
| GtkWave | ~30 | 89.8 | | GtkWave | ~30 | 89.8 |
@ -67,7 +67,10 @@ Here's a command to test on a malformed VCD:
# TODO # 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 - [ ] be explicit with imports, remove exports as possible
once FastWave is known to be fairly stable. once FastWave is known to be fairly stable.
- [ ] do a read through all the code - [ ] do a read through all the code
@ -88,5 +91,8 @@ Here's a command to test on a malformed VCD:
- [ ] Handle TODOs - [ ] Handle TODOs
- [ ] Remove debug code/comments. - [ ] Remove debug code/comments.
## Documentation
- [ ] Document indexing structure with diagram and possibly include the rational
## Marketing ## Marketing
- [ ] Send survey to community - [ ] Send survey to community

View file

@ -25,7 +25,7 @@ fn main() -> std::io::Result<()> {
let elapsed = now.elapsed(); let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed); println!("Elapsed: {:.2?}", elapsed);
std::thread::sleep(std::time::Duration::from_secs(10)); // std::thread::sleep(std::time::Duration::from_secs(10));
Ok(()) Ok(())
} }

View file

@ -1,4 +1,4 @@
use num::BigInt; use num::BigUint;
use std::collections::HashMap; use std::collections::HashMap;
use std::fs::File; use std::fs::File;

View file

@ -15,7 +15,7 @@ pub(super) fn parse_events<'a>(
break; break;
}; };
let (word, cursor) = next_word!(word_reader)?; let (word, cursor) = next_word.unwrap();
let Cursor(Line(_), Word(word_in_line_idx)) = cursor; let Cursor(Line(_), Word(word_in_line_idx)) = cursor;
// we only want to match on the first word in a line // we only want to match on the first word in a line
if word_in_line_idx != 1 { if word_in_line_idx != 1 {
@ -26,14 +26,14 @@ pub(super) fn parse_events<'a>(
"#" => { "#" => {
let value = &word[1..]; let value = &word[1..];
let (f, l) = (file!(), line!()); 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(()) .ok_or(())
.map_err(|_| { .map_err(|_| {
format!( format!(
"Error near {f}:{l}. Failed to parse {value} as BigInt at {cursor:?}" "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 // TODO : u32 helps with less memory, but should ideally likely be
// configurable. // configurable.
let (f, l) = (file!(), line!()); let (f, l) = (file!(), line!());

View file

@ -5,8 +5,6 @@ use std::io::prelude::*;
use std::slice; use std::slice;
use std::str; use std::str;
use backtrace::{Backtrace, BacktraceFrame, BacktraceSymbol};
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(super) struct Line(pub(super) usize); pub(super) struct Line(pub(super) usize);
#[derive(Debug, Clone)] #[derive(Debug, Clone)]

View file

@ -1,4 +1,6 @@
use super::utilities::{ordered_binary_lookup_u8, LookupErrors};
use chrono::prelude::*; use chrono::prelude::*;
use num::{BigUint, Zero};
#[derive(Debug)] #[derive(Debug)]
pub(super) struct Version(pub String); 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<TimelineQueryResults, String> {
// match
// assert
// ordered_binary_lookup_u8(
// &value_sequence_as_bytes_u8,
// 4,
// &timeline_cursors,
// TimelineIdx(scrubbing_cursor),
// );
Ok(TimelineQueryResults::String("".to_string()))
}
}
#[derive(Debug)] #[derive(Debug)]
pub(super) struct Scope { pub(super) struct Scope {
pub(super) name: String, pub(super) name: String,

View file

@ -1,7 +1,5 @@
use super::*; use super::*;
use std::cmp::Ordering;
#[derive(Debug)] #[derive(Debug)]
pub(super) enum BinaryParserErrTypes { pub(super) enum BinaryParserErrTypes {
XValue, XValue,
@ -77,61 +75,105 @@ pub(super) fn binary_str_to_vec_u8(binary_str: &str) -> Result<Vec<u8>, BinaryPa
Ok(vec_u8) Ok(vec_u8)
} }
// TODO : modify ordered_binary_lookup to support VCD timeline lookup use num::{BigUint, Zero};
// 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();
for i in 0..upper_bound { #[derive(Debug)]
let a_byte = a_as_bytes[i]; pub(super) enum LookupErrors {
let b_byte = b_as_bytes[i]; PreTimeline {
if a_byte > b_byte { desired_time: TimelineIdx,
return Ordering::Greater; timeline_start_time: TimelineIdx,
} },
if b_byte > a_byte { EmptyTimeline,
return Ordering::Less; TimelineNotMultiple,
} OrderingFailure,
}
if a.len() > b.len() {
return Ordering::Greater;
}
if a.len() < b.len() {
return Ordering::Less;
}
return Ordering::Equal;
} }
fn ordered_binary_lookup(map: &Vec<(String, SignalIdx)>, key: &str) -> Result<SignalIdx, String> { pub(super) fn ordered_binary_lookup_u8(
let mut upper_idx = map.len() - 1; value_sequence_as_bytes: &Vec<u8>,
let mut lower_idx = 0usize; bytes_per_value: usize,
timeline_cursors: &Vec<TimelineIdx>,
desired_time: TimelineIdx,
) -> Result<BigUint, LookupErrors> {
// 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 { while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2); let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (str_val, signal_idx) = map.get(mid_idx).unwrap(); let TimelineIdx(curr_time) = timeline_cursors[mid_idx];
let ordering = compare_strs(key, str_val.as_str()); let ordering = curr_time.cmp(&desired_time);
match ordering { match ordering {
Ordering::Less => { std::cmp::Ordering::Less => {
upper_idx = mid_idx - 1;
}
Ordering::Equal => {
return Ok(*signal_idx);
}
Ordering::Greater => {
lower_idx = mid_idx + 1; 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!( let idx = lower_idx - 1;
"Error near {}:{}. Unable to find key: `{key}` in the map.", let TimelineIdx(left_time) = timeline_cursors[idx];
file!(), let TimelineIdx(right_time) = timeline_cursors[idx + 1];
line!()
)); 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);
} }