add docs and change examples to reflect changing interfaces
This commit is contained in:
parent
320b0d348d
commit
a0713c1f38
11
README.md
11
README.md
|
@ -2,6 +2,11 @@ Copyright - Yehowshua Immanuel
|
||||||
|
|
||||||
# A High performance, VCD Parser written in Rust
|
# 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:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
# Current Features
|
# Current Features
|
||||||
|
|
||||||
- Robust Error Handling
|
- 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``
|
``cargo run --release --example parse_vcd tests/vcd-files/aldec/SPI_Write.vcd``
|
||||||
|
|
||||||
You can also run the vcd1 or vcd2 example with:
|
You can also run the vcd example with: cargo run --release --example vcd1.
|
||||||
- cargo run --release --example vcd1
|
|
||||||
- cargo run --release --example vcd2
|
|
||||||
|
|
||||||
You can run all the tests with ``cargo test``
|
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
|
## Features and Other
|
||||||
- [ ] add documenting comments
|
- [ ] 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 looking up signal values on the VCD class
|
||||||
- [ ] perhaps we should be returning signal trees from 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
|
- [ ] add lz4 compression support and compare memory perf before and after
|
||||||
|
|
BIN
assets/viewer.png
Normal file
BIN
assets/viewer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 253 KiB |
61
examples/vcd.rs
Normal file
61
examples/vcd.rs
Normal file
|
@ -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(())
|
||||||
|
}
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -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(())
|
|
||||||
}
|
|
|
@ -1,2 +1,4 @@
|
||||||
mod vcd;
|
mod vcd;
|
||||||
pub use vcd::*;
|
pub use vcd::*;
|
||||||
|
|
||||||
|
pub use num::BigUint;
|
|
@ -2,7 +2,7 @@ mod reader;
|
||||||
use reader::*;
|
use reader::*;
|
||||||
|
|
||||||
mod types;
|
mod types;
|
||||||
use types::*;
|
pub use types::*;
|
||||||
|
|
||||||
mod parse;
|
mod parse;
|
||||||
pub use parse::*;
|
pub use parse::*;
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
//! part of the vcd parser that handles parsing the signal tree and
|
/// part of the vcd parser that handles parsing the signal tree and
|
||||||
//! building the resulting signal tree
|
/// building the resulting signal tree
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
pub(super) fn parse_var<'a>(
|
pub(super) fn parse_var<'a>(
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use super::{ScopeIdx, SignalIdx};
|
use super::{ScopeIdx, SignalIdx};
|
||||||
use num::{BigUint, Zero};
|
use num::{BigUint};
|
||||||
|
|
||||||
// Index to the least significant byte of a timestamp
|
// Index to the least significant byte of a timestamp
|
||||||
// value on the timeline
|
// value on the timeline
|
||||||
|
|
|
@ -24,7 +24,7 @@ pub(super) struct Metadata {
|
||||||
|
|
||||||
// We do a lot of arena allocation in this codebase.
|
// We do a lot of arena allocation in this codebase.
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
pub struct ScopeIdx(pub(super) usize);
|
pub struct ScopeIdx(pub usize);
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq)]
|
#[derive(Debug, Copy, Clone, PartialEq)]
|
||||||
pub struct SignalIdx(pub(super) usize);
|
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
|
// 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
|
// keep track of all timestamp values, a given signal only needs to keep
|
||||||
// track of the timestamps at which the given signal value changes.
|
// track of the timestamps at which the given signal value changes.
|
||||||
pub tmstmps_encoded_as_u8s: Vec<u8>,
|
pub(super) tmstmps_encoded_as_u8s: Vec<u8>,
|
||||||
pub all_signals: Vec<Signal>,
|
pub(super) all_signals: Vec<Signal>,
|
||||||
pub(super) all_scopes: Vec<Scope>,
|
pub(super) all_scopes: Vec<Scope>,
|
||||||
pub(super) root_scopes: Vec<ScopeIdx>,
|
pub(super) root_scopes: Vec<ScopeIdx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl VCD {
|
impl VCD {
|
||||||
|
pub fn root_scopes_by_idx(&self) -> Vec<ScopeIdx> {
|
||||||
|
self.root_scopes.clone()
|
||||||
|
}
|
||||||
|
pub fn child_scopes_by_idx(&self, scope_idx: ScopeIdx) -> Vec<ScopeIdx> {
|
||||||
|
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
|
/// 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
|
/// 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``
|
/// another alias, that's an error. Otherwise, we return the ``Signal::Data``
|
||||||
|
@ -125,10 +138,7 @@ impl VCD {
|
||||||
/// into the signal arena in the all_signals field of the vcd, and returns
|
/// 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,
|
/// the resulting signal if that signal is a Signal::Data variant, else,
|
||||||
/// this function returns an Err.
|
/// this function returns an Err.
|
||||||
pub fn dealiasing_signal_lookup<'a>(
|
pub fn dealiasing_signal_lookup<'a>(&'a self, signal: &Signal) -> Result<&'a Signal, String> {
|
||||||
&'a self,
|
|
||||||
signal: &Signal
|
|
||||||
) -> Result<&'a Signal, String> {
|
|
||||||
// dereference signal if Signal::Alias, or keep idx if Signal::Data
|
// dereference signal if Signal::Alias, or keep idx if Signal::Data
|
||||||
let signal_idx = match signal {
|
let signal_idx = match signal {
|
||||||
Signal::Data { self_idx, .. } => *self_idx,
|
Signal::Data { self_idx, .. } => *self_idx,
|
||||||
|
|
Loading…
Reference in a new issue