- 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"
clap = { version = "3.1.8", features = ["derive"] }
chrono = "0.4"
# TODO : remove itertools once date parser is reworked.
itertools = "0.10.3"
backtrace = "0.3"

View file

@ -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

View file

@ -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(())
}

View file

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

View file

@ -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!());

View file

@ -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)]

View file

@ -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<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)]
pub(super) struct Scope {
pub(super) name: String,

View file

@ -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<Vec<u8>, 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<SignalIdx, String> {
let mut upper_idx = map.len() - 1;
let mut lower_idx = 0usize;
pub(super) fn ordered_binary_lookup_u8(
value_sequence_as_bytes: &Vec<u8>,
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 {
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);
}