diff --git a/README.md b/README.md index f2a56dd..73b9429 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ Copyright - Yehowshua Immanuel # A High performance, VCD Parser written in Rust +Below is a screenshot of the early stages of the proprietary +viewer frontend that builds on this backend: + +![](assets/viewer.png) + # Current Features - Robust Error Handling @@ -55,9 +60,7 @@ The first build of the program may take some time. ``cargo run --release --example parse_vcd tests/vcd-files/aldec/SPI_Write.vcd`` -You can also run the vcd1 or vcd2 example with: - - cargo run --release --example vcd1 - - cargo run --release --example vcd2 +You can also run the vcd example with: cargo run --release --example vcd1. You can run all the tests with ``cargo test`` @@ -73,6 +76,8 @@ Here's a command to test on a malformed VCD: ## Features and Other - [ ] add documenting comments + - [ ] make signal query private until later? + - [ ] change crate name to vcd_fast_parse - [ ] perhaps we should be looking up signal values on the VCD class - [ ] perhaps we should be returning signal trees from the VCD class - [ ] add lz4 compression support and compare memory perf before and after diff --git a/assets/viewer.png b/assets/viewer.png new file mode 100644 index 0000000..9314b0e Binary files /dev/null and b/assets/viewer.png differ diff --git a/examples/vcd.rs b/examples/vcd.rs new file mode 100644 index 0000000..9ccd9cb --- /dev/null +++ b/examples/vcd.rs @@ -0,0 +1,61 @@ +use std::fs::File; + +use fastwave::*; + +use num::{BigUint}; + +fn indented_print(indent : u8, name : &String) { + for _ in 0..indent {print!(" ");} + print!(" |"); + print!(" "); + println!("{name}"); +} + +fn print_root_scope_tree(root_idx: ScopeIdx, vcd: &VCD, indent : u8) { + if vcd.child_scopes_by_idx(root_idx).is_empty() { + indented_print(indent, vcd.scope_name_by_idx(root_idx)); + } else { + for child_scope_idx in vcd.child_scopes_by_idx(root_idx) { + indented_print(indent, vcd.scope_name_by_idx(child_scope_idx)); + let ScopeIdx(idx) = child_scope_idx; + print_root_scope_tree(child_scope_idx, vcd.clone(), indent + 1); + } + } +} + +fn ui_all_scopes(vcd: &VCD) { + for root_scope_idx in vcd.root_scopes_by_idx() { + print_root_scope_tree(root_scope_idx, vcd, 0u8); + } +} + +fn main() -> std::io::Result<()> { + + use std::time::Instant; + + let now = Instant::now(); + let file_path = "tests/vcd-files/icarus/CPU.vcd"; + let file = File::open(file_path)?; + let vcd = parse_vcd(file).unwrap(); + let elapsed = now.elapsed(); + println!("Parsed VCD file {} : {:.2?}", file_path, elapsed); + + println!("Printing Scopes"); + ui_all_scopes(&vcd); + + + // let state_signal = vcd. + // let name = state_signal.name(); + // let time = BigUint::from(57760000u32); + // let val = state_signal + // .query_string_val_on_tmln( + // &time, + // &vcd.tmstmps_encoded_as_u8s, + // &vcd.all_signals, + // ) + // .unwrap(); + // println!("Signal `{name}` has value `{val}` at time `{time}`"); + + + Ok(()) +} diff --git a/examples/vcd1.rs b/examples/vcd1.rs deleted file mode 100644 index 7e4fceb..0000000 --- a/examples/vcd1.rs +++ /dev/null @@ -1,34 +0,0 @@ -use clap::Parser; -use std::fs::File; - -use fastwave::*; - -use num::{BigUint}; - -fn main() -> std::io::Result<()> { - - use std::time::Instant; - - let now = Instant::now(); - let file_path = "tests/vcd-files/icarus/CPU.vcd"; - let file = File::open(file_path).unwrap(); - let vcd = parse_vcd(file).unwrap(); - let elapsed = now.elapsed(); - - println!("Parsed VCD file {} : {:.2?}", file_path, elapsed); - - // testbench -> CPU -> rs2_data[31:0] @ 4687s - let rs2_data_signal = &vcd.all_signals[51]; - let name = rs2_data_signal.name(); - let time = BigUint::from(4687u32); - let val = rs2_data_signal - .query_num_val_on_tmln( - &time, - &vcd.tmstmps_encoded_as_u8s, - &vcd.all_signals, - ) - .unwrap(); - println!("Signal `{name}` has value `{val}` at time `{time}`"); - - Ok(()) -} diff --git a/examples/vcd2.rs b/examples/vcd2.rs deleted file mode 100644 index 5235e6f..0000000 --- a/examples/vcd2.rs +++ /dev/null @@ -1,34 +0,0 @@ -use clap::Parser; -use std::fs::File; - -use fastwave::*; - -use num::{BigUint}; - -fn main() -> std::io::Result<()> { - - use std::time::Instant; - - let now = Instant::now(); - let file_path = "tests/vcd-files/amaranth/up_counter.vcd"; - let file = File::open(file_path)?; - let vcd = parse_vcd(file).unwrap(); - let elapsed = now.elapsed(); - - println!("Parsed VCD file {} : {:.2?}", file_path, elapsed); - - let state_signal = &vcd.all_signals[4]; - let name = state_signal.name(); - let time = BigUint::from(57760000u32); - let val = state_signal - .query_string_val_on_tmln( - &time, - &vcd.tmstmps_encoded_as_u8s, - &vcd.all_signals, - ) - .unwrap(); - println!("Signal `{name}` has value `{val}` at time `{time}`"); - - - Ok(()) -} diff --git a/src/lib.rs b/src/lib.rs index e7b9df3..0e8c12c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,4 @@ mod vcd; -pub use vcd::*; \ No newline at end of file +pub use vcd::*; + +pub use num::BigUint; \ No newline at end of file diff --git a/src/vcd.rs b/src/vcd.rs index 029b934..bee6f58 100644 --- a/src/vcd.rs +++ b/src/vcd.rs @@ -2,7 +2,7 @@ mod reader; use reader::*; mod types; -use types::*; +pub use types::*; mod parse; pub use parse::*; diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index c48888c..9f52c55 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -1,5 +1,5 @@ -//! part of the vcd parser that handles parsing the signal tree and -//! building the resulting signal tree +/// part of the vcd parser that handles parsing the signal tree and +/// building the resulting signal tree use super::*; pub(super) fn parse_var<'a>( diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index eeeec52..65c5d62 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -1,5 +1,5 @@ use super::{ScopeIdx, SignalIdx}; -use num::{BigUint, Zero}; +use num::{BigUint}; // Index to the least significant byte of a timestamp // value on the timeline diff --git a/src/vcd/types.rs b/src/vcd/types.rs index d0bdf5d..f47fd1d 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -24,7 +24,7 @@ pub(super) struct Metadata { // We do a lot of arena allocation in this codebase. #[derive(Debug, Copy, Clone)] -pub struct ScopeIdx(pub(super) usize); +pub struct ScopeIdx(pub usize); #[derive(Debug, Copy, Clone, PartialEq)] pub struct SignalIdx(pub(super) usize); @@ -53,13 +53,26 @@ pub struct VCD { // vector of u8s that constitute a timestamp value. Signals don't have to // keep track of all timestamp values, a given signal only needs to keep // track of the timestamps at which the given signal value changes. - pub tmstmps_encoded_as_u8s: Vec, - pub all_signals: Vec, + pub(super) tmstmps_encoded_as_u8s: Vec, + pub(super) all_signals: Vec, pub(super) all_scopes: Vec, pub(super) root_scopes: Vec, } impl VCD { + pub fn root_scopes_by_idx(&self) -> Vec { + self.root_scopes.clone() + } + pub fn child_scopes_by_idx(&self, scope_idx: ScopeIdx) -> Vec { + let ScopeIdx(idx) = scope_idx; + let scope = &self.all_scopes[idx]; + scope.child_scopes.clone() + } + pub fn scope_name_by_idx(&self, scope_idx: ScopeIdx) -> &String { + let ScopeIdx(idx) = scope_idx; + let scope = &self.all_scopes[idx]; + &scope.name + } /// We take in a Signal and attempt to de-alias that signal if it is of /// variant ``Signal::Alias``. If it is of variant ``Signal::Alias`` and points to /// another alias, that's an error. Otherwise, we return the ``Signal::Data`` @@ -119,16 +132,13 @@ impl VCD { )), } } - /// Takes a signal as input and returns the signal if the signal is of the - /// Signal::Data variant, else the function follows follows the uses the + /// Takes a signal as input and returns the signal if the signal is of the + /// Signal::Data variant, else the function follows follows the uses the /// SignalIdx in the signal_alias field of Signal::Alias variant to index /// into the signal arena in the all_signals field of the vcd, and returns /// the resulting signal if that signal is a Signal::Data variant, else, /// this function returns an Err. - pub fn dealiasing_signal_lookup<'a>( - &'a self, - signal: &Signal - ) -> Result<&'a Signal, String> { + pub fn dealiasing_signal_lookup<'a>(&'a self, signal: &Signal) -> Result<&'a Signal, String> { // dereference signal if Signal::Alias, or keep idx if Signal::Data let signal_idx = match signal { Signal::Data { self_idx, .. } => *self_idx,