- 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:
parent
3a4ed56532
commit
cbd8be1708
|
@ -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"
|
10
README.md
10
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
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use num::BigInt;
|
||||
use num::BigUint;
|
||||
use std::collections::HashMap;
|
||||
use std::fs::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!());
|
||||
|
|
|
@ -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)]
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
#[derive(Debug)]
|
||||
pub(super) enum LookupErrors {
|
||||
PreTimeline {
|
||||
desired_time: TimelineIdx,
|
||||
timeline_start_time: TimelineIdx,
|
||||
},
|
||||
EmptyTimeline,
|
||||
TimelineNotMultiple,
|
||||
OrderingFailure,
|
||||
}
|
||||
|
||||
if a.len() > b.len() {
|
||||
return Ordering::Greater;
|
||||
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);
|
||||
}
|
||||
|
||||
if a.len() < b.len() {
|
||||
return Ordering::Less;
|
||||
// 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);
|
||||
}
|
||||
|
||||
return Ordering::Equal;
|
||||
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),
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
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);
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue