Compare commits

..

No commits in common. "main" and "name_width_separation" have entirely different histories.

5 changed files with 113 additions and 268 deletions

View file

@ -1,4 +1,4 @@
Copyright(2023) - Yehowshua Immanuel Copyright - Yehowshua Immanuel
# Vision # Vision
Imagine being able to visualize a CPU pipeline diagram by merely loading a simulation waveform dump, sprinkling in a bit of code, and dragging and dropping some diagram blocks into the visualizer. This project aims to offer such an experience. Imagine being able to visualize a CPU pipeline diagram by merely loading a simulation waveform dump, sprinkling in a bit of code, and dragging and dropping some diagram blocks into the visualizer. This project aims to offer such an experience.
@ -6,7 +6,10 @@ Imagine being able to visualize a CPU pipeline diagram by merely loading a simul
Since this project is written in Rust, it should also be able to run in the browser via web-assembly. Since this project is written in Rust, it should also be able to run in the browser via web-assembly.
# Status # Status
As of January 2024, work on the Fastwave Backend is stalled. It has been a fun journey watching Fastwave enable the first iterations of the [surfer waveform viewer](https://surfer-project.org). Now surfer uses an even better backend called [Wellen](https://github.com/ekiwi/wellen?tab=readme-ov-file). Go check it out! I hear it's really good. Perhaps I will soon archive the Fastwave Backend. I hope to work on this more actively again soon.
The Zoq is is working on an excellent frontend call the Surfer. Check it out
[here](https://gitlab.com/surfer-project/surfer)!
Browser demo: https://app.surfer-project.org/ Browser demo: https://app.surfer-project.org/

View file

@ -5,7 +5,7 @@
mod vcd; mod vcd;
pub use vcd::parse::parse_vcd; pub use vcd::parse::parse_vcd;
pub use vcd::signal::{Signal, SignalType, SignalValue, SignalErrors}; pub use vcd::signal::{Signal, SignalType, SignalValue};
pub use vcd::types::{Metadata, Timescale, Version}; pub use vcd::types::{Metadata, Timescale, Version};
pub use vcd::types::{ScopeIdx, SignalIdx, VCD}; pub use vcd::types::{ScopeIdx, SignalIdx, VCD};

View file

@ -289,10 +289,7 @@ fn parse_scopes_inner<R: std::io::Read>(
// $scope module reg_mag_i $end // $scope module reg_mag_i $end
// ^^^^^^^^^ - scope name // ^^^^^^^^^ - scope name
let (scope_name, _) = next_word!(word_reader)?; let (scope_name, _) = next_word!(word_reader)?;
// In some cases there are VCD files which have scopes without names.
// since these occur in the wild, we'll tolerate them even if it is unclear
// if it is supported or not by the spec.
if scope_name != "$end" {
let mut path = path.clone(); let mut path = path.clone();
path.push(scope_name.to_string()); path.push(scope_name.to_string());
@ -376,70 +373,6 @@ fn parse_scopes_inner<R: std::io::Read>(
} }
} }
} }
} else {
// We'll be conservative and only allow new scopes in this case, and make the nameless
// scope completely transparent. I.e.
// $scope module a $end
// $scope module $end
// $scope module b $end
// ...
// $upscope
// $upscope
// $upscope
// will create `a.b`
loop {
let (word, cursor) = next_word!(word_reader)?;
let ParseResult { matched, residual } = tag(word, "$");
match matched {
// we hope that this word starts with a `$`
"$" => {
match residual {
"scope" => {
// recursive - parse inside of current scope tree
parse_scopes_inner(
word_reader,
parent_scope_idx,
vcd,
signal_map,
&path,
)?;
}
"upscope" => {
ident(word_reader, "$end")?;
break;
}
// we ignore comments
"comment" => loop {
if ident(word_reader, "$end").is_ok() {
break;
}
},
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{residual}` in annonyoums scope but expected \
`$scope`, `$comment`, or `$upscope` \
on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
_ => {
let err = format!(
"Error near {}:{}. \
found keyword `{matched}` but \
expected `$` on {cursor:?}",
file!(),
line!()
);
return Err(err);
}
}
}
}
Ok(()) Ok(())
} }

View file

@ -11,7 +11,7 @@ use num::BigUint;
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct LsbIdxOfTmstmpValOnTmln(pub(super) u32); pub struct LsbIdxOfTmstmpValOnTmln(pub(super) u32);
#[derive(Debug, Eq, PartialEq, Clone)] #[derive(Debug, Eq, PartialEq)]
pub enum SignalType { pub enum SignalType {
Event, Event,
Integer, Integer,
@ -40,11 +40,6 @@ pub enum SignalValue {
String(String), String(String),
} }
pub struct QueryResult<T> {
pub current: Option<(TimeStamp, T)>,
pub next: Option<TimeStamp>,
}
pub struct Signal<'a>(pub(super) &'a SignalEnum); pub struct Signal<'a>(pub(super) &'a SignalEnum);
impl<'a> Signal<'a> { impl<'a> Signal<'a> {
@ -53,16 +48,11 @@ impl<'a> Signal<'a> {
signal_enum.name() signal_enum.name()
} }
pub fn name_with_index(&self) -> String { pub fn name_with_size(&self) -> String {
let Signal(signal_enum) = &self; let Signal(signal_enum) = &self;
signal_enum.name_with_index() signal_enum.name_with_index()
} }
pub fn index(&self) -> Option<String> {
let Signal(signal_enum) = &self;
signal_enum.index()
}
pub fn path(&self) -> &[String] { pub fn path(&self) -> &[String] {
match self.0 { match self.0 {
SignalEnum::Data { path, .. } => path, SignalEnum::Data { path, .. } => path,
@ -87,9 +77,6 @@ impl<'a> Signal<'a> {
signal_enum.bits_required() signal_enum.bits_required()
} }
// NOTE: (zoq) I am removing thse because they aren't used in Surfer so I can't test them
// properly
/*
pub fn query_string_val_on_tmln( pub fn query_string_val_on_tmln(
&self, &self,
desired_time: &BigUint, desired_time: &BigUint,
@ -98,33 +85,32 @@ impl<'a> Signal<'a> {
let Signal(signal_enum) = &self; let Signal(signal_enum) = &self;
signal_enum signal_enum
.query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) .query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|QueryResult{current, next: _}| current.map(|c| c.1)) .map(|(val, _)| val)
} }
pub fn query_num_val_on_tmln( pub fn query_num_val_on_tmln(
&self, &self,
desired_time: &BigUint, desired_time: &BigUint,
vcd: &types::VCD, vcd: &types::VCD,
) -> Result<Option<BigUint>, SignalErrors> { ) -> Result<BigUint, SignalErrors> {
let Signal(signal_enum) = &self; let Signal(signal_enum) = &self;
signal_enum signal_enum
.query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) .query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals)
.map(|QueryResult{current, next: _}| current.map(|c| c.1)) .map(|(val, _)| val)
} }
*/
pub fn query_val_on_tmln( pub fn query_val_on_tmln(
&self, &self,
desired_time: &BigUint, desired_time: &BigUint,
vcd: &types::VCD, vcd: &types::VCD,
) -> Result<QueryResult<SignalValue>, SignalErrors> { ) -> Result<(TimeStamp, SignalValue), SignalErrors> {
let Signal(signal_enum) = &self; let Signal(signal_enum) = &self;
let num_query_out = signal_enum.query_num_val_on_tmln( let num_val = signal_enum.query_num_val_on_tmln(
desired_time, desired_time,
&vcd.tmstmps_encoded_as_u8s, &vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals, &vcd.all_signals,
); );
let str_query_out = signal_enum.query_string_val_on_tmln( let str_val = signal_enum.query_string_val_on_tmln(
desired_time, desired_time,
&vcd.tmstmps_encoded_as_u8s, &vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals, &vcd.all_signals,
@ -133,44 +119,16 @@ impl<'a> Signal<'a> {
// Both num and str will return the newest value that is closest to // Both num and str will return the newest value that is closest to
// the desired time. If both have valid values, select the most recent // the desired time. If both have valid values, select the most recent
// one // one
match (num_query_out, str_query_out) { match (num_val, str_val) {
(Ok(num_result), Ok(str_result)) => { (Ok((num_val, num_time)), Ok((str_val, str_time))) => {
let next = match (num_result.next, str_result.next) {
(Some(n), Some(s)) => Some(n.min(s)),
(Some(n), None) => Some(n),
(None, Some(s)) => Some(s),
(None, None) => None,
};
match (num_result.current, str_result.current) {
(Some((num_time, num_value)), Some((str_time, str_value))) => {
if num_time > str_time { if num_time > str_time {
Ok(QueryResult { Ok((num_time, SignalValue::BigUint(num_val)))
current: Some((num_time, SignalValue::BigUint(num_value))),
next,
})
} else { } else {
Ok(QueryResult { Ok((str_time, SignalValue::String(str_val)))
current: Some((str_time, SignalValue::String(str_value))),
next,
})
} }
} }
(Some((num_time, num_val)), None) => Ok(QueryResult { (Ok((num_val, time)), Err(_)) => Ok((time, SignalValue::BigUint(num_val))),
current: Some((num_time, SignalValue::BigUint(num_val))), (Err(_), Ok((str_val, time))) => Ok((time, SignalValue::String(str_val))),
next,
}),
(None, Some((str_time, str_value))) => Ok(QueryResult {
current: Some((str_time, SignalValue::String(str_value))),
next,
}),
(None, None) => Ok(QueryResult {
current: None,
next,
}),
}
}
(_e, Err(e)) => Err(e),
(Err(e), _e) => Err(e), (Err(e), _e) => Err(e),
} }
} }
@ -229,6 +187,10 @@ pub(super) enum SignalEnum {
#[derive(Debug)] #[derive(Debug)]
pub enum SignalErrors { pub enum SignalErrors {
PreTimeline {
desired_time: BigUint,
timeline_start_time: BigUint,
},
EmptyTimeline, EmptyTimeline,
TimelineNotMultiple, TimelineNotMultiple,
StrTmlnLenMismatch, StrTmlnLenMismatch,
@ -278,13 +240,6 @@ impl SignalEnum {
SignalEnum::Alias { name, .. } => name.clone(), SignalEnum::Alias { name, .. } => name.clone(),
} }
} }
pub fn index(&self) -> Option<String> {
match self {
SignalEnum::Data { index, .. } => index.clone(),
SignalEnum::Alias { .. } => None,
}
}
} }
// helper functions ultimately used by Signal's query functions later on // helper functions ultimately used by Signal's query functions later on
@ -429,7 +384,7 @@ impl SignalEnum {
desired_time: &BigUint, desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>, tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>, all_signals: &Vec<SignalEnum>,
) -> Result<QueryResult<String>, SignalErrors> { ) -> Result<(String, TimeStamp), SignalErrors> {
let signal_idx = match self { let signal_idx = match self {
Self::Data { self_idx, .. } => { Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx; let SignalIdx(idx) = self_idx;
@ -463,10 +418,7 @@ impl SignalEnum {
// this signal should at least have some events, otherwise, trying to index into // this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail // an empty vector later on would fail
if lsb_indxs_of_string_tmstmp_vals_on_tmln.is_empty() { if lsb_indxs_of_string_tmstmp_vals_on_tmln.is_empty() {
return Ok(QueryResult { return Err(SignalErrors::EmptyTimeline);
current: None,
next: None
});
} }
// the vector of string timeline lsb indices should have the same // the vector of string timeline lsb indices should have the same
@ -480,9 +432,9 @@ impl SignalEnum {
let (timeline_start_time, _) = let (timeline_start_time, _) =
self.time_and_str_val_at_event_idx(0, tmstmps_encoded_as_u8s)?; self.time_and_str_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time { if *desired_time < timeline_start_time {
return Ok(QueryResult { return Err(SignalErrors::PreTimeline {
current: None, desired_time: desired_time.clone(),
next: Some(timeline_start_time), timeline_start_time,
}); });
} }
@ -494,10 +446,7 @@ impl SignalEnum {
// check if we're requesting a value that occurs beyond the end of the timeline, // check if we're requesting a value that occurs beyond the end of the timeline,
// if so, return the last value in this timeline // if so, return the last value in this timeline
if *desired_time > timeline_end_time { if *desired_time > timeline_end_time {
return Ok(QueryResult { return Ok((timeline_end_val.to_string(), timeline_end_time));
current: Some((timeline_end_time, timeline_end_val.to_string())),
next: None,
});
} }
// This while loop is the meat of the lookup. Performance is log2(n), // This while loop is the meat of the lookup. Performance is log2(n),
@ -516,21 +465,7 @@ impl SignalEnum {
lower_idx = mid_idx + 1; lower_idx = mid_idx + 1;
} }
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1 { return Ok((curr_val.to_string(), curr_time));
Some(
self.time_and_str_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok(QueryResult {
current: Some((curr_time, curr_val.to_string())),
next: next_time,
});
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1; upper_idx = mid_idx - 1;
@ -553,17 +488,14 @@ impl SignalEnum {
}); });
} }
Ok(QueryResult { Ok((left_val.to_string(), left_time))
current: Some((left_time, left_val.to_string())),
next: Some(right_time),
})
} }
pub fn query_num_val_on_tmln( pub fn query_num_val_on_tmln(
&self, &self,
desired_time: &BigUint, desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>, tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<SignalEnum>, all_signals: &Vec<SignalEnum>,
) -> Result<QueryResult<BigUint>, SignalErrors> { ) -> Result<(BigUint, TimeStamp), SignalErrors> {
let signal_idx = match self { let signal_idx = match self {
Self::Data { self_idx, .. } => { Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx; let SignalIdx(idx) = self_idx;
@ -608,10 +540,7 @@ impl SignalEnum {
// this signal should at least have some events, otherwise, trying to index into // this signal should at least have some events, otherwise, trying to index into
// an empty vector later on would fail // an empty vector later on would fail
if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() { if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() {
return Ok(QueryResult { return Err(SignalErrors::EmptyTimeline);
current: None,
next: None
});
} }
// assertion that value_sequence is a proper multiple of // assertion that value_sequence is a proper multiple of
@ -634,9 +563,9 @@ impl SignalEnum {
let (timeline_start_time, _) = let (timeline_start_time, _) =
self.time_and_num_val_at_event_idx(0, tmstmps_encoded_as_u8s)?; self.time_and_num_val_at_event_idx(0, tmstmps_encoded_as_u8s)?;
if *desired_time < timeline_start_time { if *desired_time < timeline_start_time {
return Ok(QueryResult { return Err(SignalErrors::PreTimeline {
current: None, desired_time: desired_time.clone(),
next: Some(timeline_start_time), timeline_start_time,
}); });
} }
@ -648,10 +577,7 @@ impl SignalEnum {
// check if we're requesting a value that occurs beyond the end of the timeline, // check if we're requesting a value that occurs beyond the end of the timeline,
// if so, return the last value in this timeline // if so, return the last value in this timeline
if *desired_time > timeline_end_time { if *desired_time > timeline_end_time {
return Ok(QueryResult { return Ok((timeline_end_val, timeline_end_time));
current: Some((timeline_end_time, timeline_end_val)),
next: None,
});
} }
// This while loop is the meat of the lookup. Performance is log2(n), // This while loop is the meat of the lookup. Performance is log2(n),
@ -670,21 +596,7 @@ impl SignalEnum {
lower_idx = mid_idx + 1; lower_idx = mid_idx + 1;
} }
std::cmp::Ordering::Equal => { std::cmp::Ordering::Equal => {
let next_time = if mid_idx < lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1 { return Ok((curr_val, curr_time));
Some(
self.time_and_num_val_at_event_idx(
mid_idx + 1,
tmstmps_encoded_as_u8s,
)?
.0,
)
} else {
None
};
return Ok(QueryResult {
current: Some((curr_time, curr_val)),
next: next_time,
});
} }
std::cmp::Ordering::Greater => { std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1; upper_idx = mid_idx - 1;
@ -707,9 +619,6 @@ impl SignalEnum {
}); });
} }
return Ok(QueryResult { Ok((left_val, left_time))
current: Some((left_time, left_val)),
next: Some(right_time),
});
} }
} }

View file

@ -9,7 +9,7 @@ use chrono::prelude::{DateTime, Utc};
use num::BigUint; use num::BigUint;
use std::fmt; use std::fmt;
#[derive(Debug, Clone)] #[derive(Debug)]
pub struct Version(pub String); pub struct Version(pub String);
#[derive(Debug, Clone, Copy, Eq, PartialEq)] #[derive(Debug, Clone, Copy, Eq, PartialEq)]