Test ordered binary lookup #4
|
@ -4,7 +4,7 @@ Copyright - Yehowshua Immanuel
|
||||||
|
|
||||||
## Current Features
|
## Current Features
|
||||||
- pretty fast, parses 3.04 GB VCD file in ~54s on M1 Macbook Air with
|
- pretty fast, parses 3.04 GB VCD file in ~54s on M1 Macbook Air with
|
||||||
respect to 50s with GTKWave on the same device. FastWave currently
|
respect to 30s with GTKWave on the same device. FastWave currently
|
||||||
offers highly robust error handling which GTKWave doesn't have.
|
offers highly robust error handling which GTKWave doesn't have.
|
||||||
|
|
||||||
I noticed that when running FastWave in the VsCode terminal as opposed
|
I noticed that when running FastWave in the VsCode terminal as opposed
|
||||||
|
@ -40,6 +40,7 @@ Here's a command to test on a malformed VCD:
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
- [ ] macro for getting line number when propagating errors
|
- [ ] macro for getting line number when propagating errors
|
||||||
|
- [ ] search for any unwraps or any direct vectors indexing
|
||||||
- [ ] re-order all signal timelines as binary balanced trees with respect to timestamps
|
- [ ] re-order all signal timelines as binary balanced trees with respect to timestamps
|
||||||
- support multithreaded re-ordering
|
- support multithreaded re-ordering
|
||||||
- [ ] looks into making a macro for filename and linenumber later
|
- [ ] looks into making a macro for filename and linenumber later
|
||||||
|
|
106
src/vcd/parse.rs
106
src/vcd/parse.rs
|
@ -1,6 +1,6 @@
|
||||||
use std::{fs::File};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use num::BigInt;
|
use num::BigInt;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::fs::File;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
@ -19,7 +19,44 @@ use scopes::*;
|
||||||
mod events;
|
mod events;
|
||||||
use events::*;
|
use events::*;
|
||||||
|
|
||||||
pub fn parse_vcd(file : File) -> Result<VCD, String> {
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
|
fn compare_strs(a: &str, b: &str) -> Ordering {
|
||||||
|
let last_idx = if a.len() > b.len() { a.len() } else { b.len() };
|
||||||
|
// let last_idx += -1;
|
||||||
|
Ordering::Less
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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 = key.partial_cmp(str_val.as_str()).unwrap();
|
||||||
|
|
||||||
|
match ordering {
|
||||||
|
Ordering::Less => {
|
||||||
|
upper_idx = mid_idx - 1;
|
||||||
|
}
|
||||||
|
Ordering::Equal => {
|
||||||
|
return Ok(*signal_idx);
|
||||||
|
}
|
||||||
|
Ordering::Greater => {
|
||||||
|
lower_idx = mid_idx + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Err(format!(
|
||||||
|
"Error near {}:{}. Unable to find key: `{key}` in the map.",
|
||||||
|
file!(),
|
||||||
|
line!()
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn parse_vcd(file: File) -> Result<VCD, String> {
|
||||||
let mut word_gen = WordReader::new(file);
|
let mut word_gen = WordReader::new(file);
|
||||||
|
|
||||||
let header = parse_metadata(&mut word_gen)?;
|
let header = parse_metadata(&mut word_gen)?;
|
||||||
|
@ -29,17 +66,48 @@ pub fn parse_vcd(file : File) -> Result<VCD, String> {
|
||||||
let mut signal_map = std::collections::HashMap::new();
|
let mut signal_map = std::collections::HashMap::new();
|
||||||
|
|
||||||
// after we parse metadata, we form the VCD object
|
// after we parse metadata, we form the VCD object
|
||||||
let mut vcd = VCD{
|
let mut vcd = VCD {
|
||||||
metadata : header,
|
metadata: header,
|
||||||
timeline : vec![],
|
timeline: vec![],
|
||||||
timeline_markers : vec![],
|
timeline_markers: vec![],
|
||||||
all_signals : vec![],
|
all_signals: vec![],
|
||||||
all_scopes : vec![],
|
all_scopes: vec![],
|
||||||
scope_roots : vec![],
|
scope_roots: vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?;
|
parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?;
|
||||||
parse_events(&mut word_gen, &mut vcd, &mut signal_map)?;
|
|
||||||
|
// the signal map should not contain any empty string
|
||||||
|
for (k, v) in &signal_map {
|
||||||
|
if k.len() == 0 {
|
||||||
|
return Err(format!("Critical error near {}:{}. There should be no empty strings in vcd string -> SignalIdx hashmap.", file!(), line!()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that we've parsed all scopes and filled the hashmap
|
||||||
|
// with signals, we convert hashmap to an ordered vector
|
||||||
|
let mut signal_map1: Vec<(String, SignalIdx)> = signal_map
|
||||||
|
.iter()
|
||||||
|
.map(|(string, idx)| (string.clone(), idx.clone()))
|
||||||
|
.collect();
|
||||||
|
signal_map1.sort_by(|a: &(String, SignalIdx), b: &(String, SignalIdx)| {
|
||||||
|
let a = &a.0;
|
||||||
|
let b = &b.0;
|
||||||
|
a.partial_cmp(&b).unwrap()
|
||||||
|
});
|
||||||
|
|
||||||
|
let now = std::time::Instant::now();
|
||||||
|
for (k, v) in &signal_map1 {
|
||||||
|
let signal_idx = ordered_binary_lookup(&signal_map1, k.as_str())?;
|
||||||
|
assert!(*v == signal_idx);
|
||||||
|
}
|
||||||
|
let ordered_binary_search_elapsed = now.elapsed();
|
||||||
|
println!(
|
||||||
|
"ordered_binary_search_elapsed: {:.2?}",
|
||||||
|
ordered_binary_search_elapsed
|
||||||
|
);
|
||||||
|
|
||||||
|
// parse_events(&mut wosrd_gen, &mut vcd, &mut signal_map)?;
|
||||||
|
|
||||||
Ok(vcd)
|
Ok(vcd)
|
||||||
}
|
}
|
||||||
|
@ -55,29 +123,18 @@ mod tests {
|
||||||
// two loops
|
// two loops
|
||||||
// testing dates
|
// testing dates
|
||||||
for file in test::GOOD_DATE_FILES {
|
for file in test::GOOD_DATE_FILES {
|
||||||
let metadata = parse_metadata(
|
let metadata = parse_metadata(&mut WordReader::new(File::open(file).unwrap()));
|
||||||
&mut WordReader::new(
|
|
||||||
File::open(file)
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert!(metadata.is_ok());
|
assert!(metadata.is_ok());
|
||||||
assert!(metadata.unwrap().date.is_some());
|
assert!(metadata.unwrap().date.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
for file in test::FILES {
|
for file in test::FILES {
|
||||||
let metadata = parse_metadata(
|
let metadata = parse_metadata(&mut WordReader::new(File::open(file).unwrap()));
|
||||||
&mut WordReader::new(
|
|
||||||
File::open(file)
|
|
||||||
.unwrap()
|
|
||||||
)
|
|
||||||
);
|
|
||||||
assert!(metadata.is_ok());
|
assert!(metadata.is_ok());
|
||||||
|
|
||||||
let (scalar, _timescale) = metadata.unwrap().timescale;
|
let (scalar, _timescale) = metadata.unwrap().timescale;
|
||||||
assert!(scalar.is_some());
|
assert!(scalar.is_some());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -94,6 +151,5 @@ mod tests {
|
||||||
|
|
||||||
// assert!(vcd.is_ok());
|
// assert!(vcd.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
134
src/vcd/types.rs
134
src/vcd/types.rs
|
@ -4,18 +4,27 @@ use chrono::prelude::*;
|
||||||
pub(super) struct Version(pub String);
|
pub(super) struct Version(pub String);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum Timescale {Fs, Ps, Ns, Us, Ms, S, Unit}
|
pub(super) enum Timescale {
|
||||||
|
Fs,
|
||||||
|
Ps,
|
||||||
|
Ns,
|
||||||
|
Us,
|
||||||
|
Ms,
|
||||||
|
S,
|
||||||
|
Unit,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct Metadata {
|
pub(super) struct Metadata {
|
||||||
pub(super) date : Option<DateTime<Utc>>,
|
pub(super) date: Option<DateTime<Utc>>,
|
||||||
pub(super) version : Option<Version>,
|
pub(super) version: Option<Version>,
|
||||||
pub(super) timescale : (Option<u32>, Timescale)}
|
pub(super) timescale: (Option<u32>, Timescale),
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub(super) struct ScopeIdx(pub(super) usize);
|
pub(super) struct ScopeIdx(pub(super) usize);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub(super) struct SignalIdx(pub(super) usize);
|
pub(super) struct SignalIdx(pub(super) usize);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -25,63 +34,71 @@ pub(super) struct TimelineIdx(pub(super) u32);
|
||||||
pub struct StartIdx(pub(super) u32);
|
pub struct StartIdx(pub(super) u32);
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum SigType {Integer, Parameter, Real, Reg, Str, Wire, Tri1, Time}
|
pub(super) enum SigType {
|
||||||
|
Integer,
|
||||||
|
Parameter,
|
||||||
|
Real,
|
||||||
|
Reg,
|
||||||
|
Str,
|
||||||
|
Wire,
|
||||||
|
Tri1,
|
||||||
|
Time,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) enum Signal{
|
pub(super) enum Signal {
|
||||||
Data{
|
Data {
|
||||||
name : String,
|
name: String,
|
||||||
sig_type : SigType,
|
sig_type: SigType,
|
||||||
// I've seen a 0 bit signal parameter in a xilinx
|
// I've seen a 0 bit signal parameter in a xilinx
|
||||||
// simulation before that gets assigned 1 bit values.
|
// simulation before that gets assigned 1 bit values.
|
||||||
// I consider this to be bad behavior. We capture such
|
// I consider this to be bad behavior. We capture such
|
||||||
// errors in the following type.
|
// errors in the following type.
|
||||||
signal_error : Option<String>,
|
signal_error: Option<String>,
|
||||||
num_bits : Option<usize>,
|
num_bits: Option<usize>,
|
||||||
// TODO : may be able to remove self_idx
|
// TODO : may be able to remove self_idx
|
||||||
self_idx : SignalIdx,
|
self_idx: SignalIdx,
|
||||||
// we could encounter a mix of pure values and strings
|
// we could encounter a mix of pure values and strings
|
||||||
// for the same signal timeline
|
// for the same signal timeline
|
||||||
u8_timeline : Vec<u8>,
|
u8_timeline: Vec<u8>,
|
||||||
u8_timeline_markers : Vec<TimelineIdx>,
|
u8_timeline_markers: Vec<TimelineIdx>,
|
||||||
string_timeline : Vec<String>,
|
string_timeline: Vec<String>,
|
||||||
string_timeline_markers : Vec<TimelineIdx>,
|
string_timeline_markers: Vec<TimelineIdx>,
|
||||||
scope_parent : ScopeIdx},
|
scope_parent: ScopeIdx,
|
||||||
Alias{
|
},
|
||||||
name : String,
|
Alias {
|
||||||
signal_alias : SignalIdx}
|
name: String,
|
||||||
|
signal_alias: SignalIdx,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub(super) struct Scope {
|
pub(super) struct Scope {
|
||||||
pub(super) name : String,
|
pub(super) name: String,
|
||||||
|
|
||||||
pub(super) parent_idx : Option<ScopeIdx>,
|
pub(super) parent_idx: Option<ScopeIdx>,
|
||||||
pub(super) self_idx : ScopeIdx,
|
pub(super) self_idx: ScopeIdx,
|
||||||
|
|
||||||
pub(super) child_signals : Vec<SignalIdx>,
|
|
||||||
pub(super) child_scopes : Vec<ScopeIdx>}
|
|
||||||
|
|
||||||
|
pub(super) child_signals: Vec<SignalIdx>,
|
||||||
|
pub(super) child_scopes: Vec<ScopeIdx>,
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: document how timeline is represented
|
// TODO: document how timeline is represented
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct VCD {
|
pub struct VCD {
|
||||||
pub(super) metadata : Metadata,
|
pub(super) metadata: Metadata,
|
||||||
pub timeline : Vec<u8>,
|
pub timeline: Vec<u8>,
|
||||||
pub timeline_markers : Vec<StartIdx>,
|
pub timeline_markers: Vec<StartIdx>,
|
||||||
pub(super) all_signals : Vec<Signal>,
|
pub(super) all_signals: Vec<Signal>,
|
||||||
pub(super) all_scopes : Vec<Scope>,
|
pub(super) all_scopes: Vec<Scope>,
|
||||||
pub(super) scope_roots : Vec<ScopeIdx>}
|
pub(super) scope_roots: Vec<ScopeIdx>,
|
||||||
|
}
|
||||||
|
|
||||||
impl VCD {
|
impl VCD {
|
||||||
// TODO : make this a generic traversal function that applies specified
|
// TODO : make this a generic traversal function that applies specified
|
||||||
// functions upon encountering scopes and signals
|
// functions upon encountering scopes and signals
|
||||||
fn print_scope_tree(
|
fn print_scope_tree(&self, root_scope_idx: ScopeIdx, depth: usize) {
|
||||||
&self,
|
let all_scopes = &self.all_scopes;
|
||||||
root_scope_idx : ScopeIdx,
|
|
||||||
depth : usize)
|
|
||||||
{
|
|
||||||
let all_scopes = &self.all_scopes;
|
|
||||||
let all_signals = &self.all_signals;
|
let all_signals = &self.all_signals;
|
||||||
|
|
||||||
let indent = " ".repeat(depth * 4);
|
let indent = " ".repeat(depth * 4);
|
||||||
|
@ -94,17 +111,16 @@ impl VCD {
|
||||||
for SignalIdx(ref signal_idx) in &root_scope.child_signals {
|
for SignalIdx(ref signal_idx) in &root_scope.child_signals {
|
||||||
let child_signal = &all_signals[*signal_idx];
|
let child_signal = &all_signals[*signal_idx];
|
||||||
let name = match child_signal {
|
let name = match child_signal {
|
||||||
Signal::Data{name, ..} => {name}
|
Signal::Data { name, .. } => name,
|
||||||
Signal::Alias{name, ..} => {name}
|
Signal::Alias { name, .. } => name,
|
||||||
};
|
};
|
||||||
println!("{indent} - sig: {name}")
|
println!("{indent} - sig: {name}")
|
||||||
}
|
}
|
||||||
println!();
|
println!();
|
||||||
|
|
||||||
for scope_idx in &root_scope.child_scopes {
|
for scope_idx in &root_scope.child_scopes {
|
||||||
self.print_scope_tree(*scope_idx, depth+1);
|
self.print_scope_tree(*scope_idx, depth + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_scopes(&self) {
|
pub fn print_scopes(&self) {
|
||||||
|
@ -120,20 +136,20 @@ impl VCD {
|
||||||
|
|
||||||
for signal in &self.all_signals {
|
for signal in &self.all_signals {
|
||||||
match signal {
|
match signal {
|
||||||
Signal::Alias {..} => {}
|
Signal::Alias { .. } => {}
|
||||||
Signal::Data {
|
Signal::Data {
|
||||||
name,
|
name,
|
||||||
self_idx,
|
self_idx,
|
||||||
u8_timeline,
|
u8_timeline,
|
||||||
.. } => {
|
..
|
||||||
if u8_timeline.len() > max_len {
|
} => {
|
||||||
max_len = u8_timeline.len();
|
if u8_timeline.len() > max_len {
|
||||||
let SignalIdx(idx_usize) = self_idx;
|
max_len = u8_timeline.len();
|
||||||
idx = *idx_usize;
|
let SignalIdx(idx_usize) = self_idx;
|
||||||
signal_name = name.clone();
|
idx = *idx_usize;
|
||||||
}
|
signal_name = name.clone();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue