From b886e5d26df7bdc778feb025aced54385dc79a9a Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Tue, 10 Oct 2023 16:08:09 +0200 Subject: [PATCH 01/12] Add support for parsing real values (to a String) --- src/vcd/parse/events.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 32c8f7a..4cdd947 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -492,7 +492,8 @@ pub(super) fn parse_events( } }?; } - "s" => { + // Store real values as a string as well and let the user parse it to an f64 + "s" | " S" | "r" | "R" => { let val = word[1..].to_string(); let (hash, cursor) = next_word!(word_reader)?; // lokup signal idx From 9c54c3a295c55ec8988fe9153b5fa39178c01339 Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Wed, 11 Oct 2023 09:54:59 +0200 Subject: [PATCH 02/12] Increase maximum bit width --- src/vcd/parse/events.rs | 8 ++++---- src/vcd/parse/scopes.rs | 4 ++-- src/vcd/signal.rs | 14 +++++++------- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index 4cdd947..3f42048 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -71,10 +71,10 @@ pub(super) fn parse_events( // handle the case of an n bit signal whose value must be parsed "b" => { let binary_value = &word[1..]; - let observed_num_bits = u16::try_from(binary_value.len()).map_err(|_| { + let observed_num_bits = u32::try_from(binary_value.len()).map_err(|_| { format!( "Error near {}:{}, {cursor:?}. \ - Found signal with more than 2^16 - 1 bits.", + Found signal with more than 2^32 - 1 bits.", file!(), line!() ) @@ -200,11 +200,11 @@ pub(super) fn parse_events( format!("Error near {}:{}. num_bytes empty.", file!(), line!()) })?; let mut curr_num_bytes = - u8::try_from(value_u8.len()).map_err(|_| { + u16::try_from(value_u8.len()).map_err(|_| { format!( "Error near {}:{}. \ Found signal {name} with with value change of greater \ - than 2^16 - 1 bits on {cursor:?}.", + than 2^32 - 1 bits on {cursor:?}.", file!(), line!() ) diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index a84a282..9931cbd 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -104,10 +104,10 @@ pub(super) fn parse_var( let num_bits = word .parse::() .unwrap_or_else(|_| panic!("{}", parse_err)); - let num_bits = u16::try_from(num_bits).map_err(|_| { + let num_bits = u32::try_from(num_bits).map_err(|_| { format!( "Error near {}:{} while parsing vcd file at {cursor:?}. \ - This signal has {num_bits} > 2^16 - 1 bits.", + This signal has {num_bits} > 2^32 - 1 bits.", file!(), line!() ) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index e498bfb..d0dc9b5 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -67,7 +67,7 @@ impl<'a> Signal<'a> { } } - pub fn num_bits(&self) -> Option { + pub fn num_bits(&self) -> Option { let Signal(signal_enum) = &self; signal_enum.bits_required() } @@ -140,8 +140,8 @@ pub(super) enum SignalEnum { /// I consider this to be bad behavior. We capture such /// errors in the following type: signal_error: Option, - num_bits: Option, - num_bytes: Option, + num_bits: Option, + num_bytes: Option, /// TODO : may be able to remove self_idx self_idx: SignalIdx, /// A signal may take on a new value and hold that value @@ -225,12 +225,12 @@ impl SignalEnum { /// Computes the bytes required to store a signal's numerical value /// using the num_bits which another function would provide from /// the num_bits field of the Signal::Data variant. - pub(super) fn bytes_required(num_bits: u16, name: &String) -> Result { + pub(super) fn bytes_required(num_bits: u32, name: &String) -> Result { let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 }; - let bytes_required = u8::try_from(bytes_required).map_err(|_| { + let bytes_required = u16::try_from(bytes_required).map_err(|_| { format!( "Error near {}:{}. Signal {name} of length num_bits requires \ - {bytes_required} > 256 bytes.", + {bytes_required} > 65536 bytes.", file!(), line!() ) @@ -343,7 +343,7 @@ impl SignalEnum { Ok((timestamp, signal_val)) } - fn bits_required(&self) -> Option { + fn bits_required(&self) -> Option { match self { SignalEnum::Data { num_bits, .. } => *num_bits, // TODO: Follow aliases? From a8ee52a11e87b4d8c14b90d726c14a767ecae87b Mon Sep 17 00:00:00 2001 From: Oscar Gustafsson Date: Sun, 15 Oct 2023 22:05:52 +0200 Subject: [PATCH 03/12] Bump dependencies --- Cargo.toml | 4 ++-- examples/parse_vcd.rs | 1 - examples/vcd.rs | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 6b0390b..0b4dd2c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ debug = 1 [dependencies] num = "0.4" -clap = { version = "3.1.8", features = ["derive"] } +clap = { version = "4.4.6", features = ["derive"] } chrono = "0.4" # TODO : remove itertools once date parser is reworked. -itertools = "0.10.3" +itertools = "0.11" diff --git a/examples/parse_vcd.rs b/examples/parse_vcd.rs index 66b6146..b1478c3 100644 --- a/examples/parse_vcd.rs +++ b/examples/parse_vcd.rs @@ -10,7 +10,6 @@ use fastwave_backend::parse_vcd; #[derive(Parser)] struct Cli { /// The path to the file to read - #[clap(parse(from_os_str))] path: std::path::PathBuf, } diff --git a/examples/vcd.rs b/examples/vcd.rs index 968bf09..eb6b660 100644 --- a/examples/vcd.rs +++ b/examples/vcd.rs @@ -27,7 +27,7 @@ fn visit_all_scopes(vcd: &VCD) { let SignalIdx(idx) = signal_idx; indented_print(indent + 1, &format!("{},{}", signal.name(), idx)); } - visit_all_scope_children(child_scope_idx, vcd.clone(), indent + 1); + visit_all_scope_children(child_scope_idx, vcd, indent + 1); } } } From e7f2f661dfad09a833ebe11f145768da07475573 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Thu, 5 Oct 2023 17:57:06 +0200 Subject: [PATCH 04/12] Separate name from width --- src/vcd/parse/scopes.rs | 3 +++ src/vcd/signal.rs | 22 ++++++++++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index 9931cbd..5da96ea 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -126,10 +126,12 @@ pub(super) fn parse_var( // $var parameter 3 a IDLE $end // ^^^^ - full_signal_name(can extend until $end) let mut full_signal_name = Vec::::new(); + let mut size = None; loop { let (word, _) = next_word!(word_reader)?; match word { "$end" => break, + other if other.starts_with('[') => size = Some(other.to_string()), _ => full_signal_name.push(word.to_string()), } } @@ -170,6 +172,7 @@ pub(super) fn parse_var( .chain([full_signal_name]) .collect::>(), signal_type: var_type, + index: size, signal_error: None, num_bits, num_bytes, diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index d0dc9b5..541221e 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -48,6 +48,11 @@ impl<'a> Signal<'a> { signal_enum.name() } + pub fn name_with_size(&self) -> String { + let Signal(signal_enum) = &self; + signal_enum.name_with_index() + } + pub fn path(&self) -> &[String] { match self.0 { SignalEnum::Data { path, .. } => path, @@ -135,6 +140,9 @@ pub(super) enum SignalEnum { name: String, path: Vec, signal_type: SignalType, + /// The optional [start:end] part of the signal name that is sometimes + /// added to signals + index: Option, /// I've seen a 0 bit signal parameter in a xilinx /// simulation before that gets assigned 1 bit values. /// I consider this to be bad behavior. We capture such @@ -218,6 +226,20 @@ impl SignalEnum { } .clone() } + + pub fn name_with_index(&self) -> String { + match self { + SignalEnum::Data { + name, index: None, .. + } => format!("{name}"), + SignalEnum::Data { + name, + index: Some(size), + .. + } => format!("{name} {size}"), + SignalEnum::Alias { name, .. } => name.clone(), + } + } } // helper functions ultimately used by Signal's query functions later on From d42d01f9c344c86d15ada772f09177c5d50f1623 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Mon, 23 Oct 2023 18:15:30 +0200 Subject: [PATCH 05/12] Expose index --- src/vcd/signal.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index 541221e..7f40451 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -48,11 +48,16 @@ impl<'a> Signal<'a> { signal_enum.name() } - pub fn name_with_size(&self) -> String { + pub fn name_with_index(&self) -> String { let Signal(signal_enum) = &self; signal_enum.name_with_index() } + pub fn index(&self) -> Option { + let Signal(signal_enum) = &self; + signal_enum.index() + } + pub fn path(&self) -> &[String] { match self.0 { SignalEnum::Data { path, .. } => path, @@ -240,6 +245,13 @@ impl SignalEnum { SignalEnum::Alias { name, .. } => name.clone(), } } + + pub fn index(&self) -> Option { + match self { + SignalEnum::Data { index, .. } => index.clone(), + SignalEnum::Alias { .. } => None, + } + } } // helper functions ultimately used by Signal's query functions later on From 7a2bed42a3371e3661c648b9e744435507401a35 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Wed, 25 Oct 2023 17:55:05 +0200 Subject: [PATCH 06/12] Add some clones and expose SignalErrors --- src/lib.rs | 2 +- src/vcd/signal.rs | 2 +- src/vcd/types.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index c4caa1f..6bdb055 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ mod vcd; pub use vcd::parse::parse_vcd; -pub use vcd::signal::{Signal, SignalType, SignalValue}; +pub use vcd::signal::{Signal, SignalType, SignalValue, SignalErrors}; pub use vcd::types::{Metadata, Timescale, Version}; pub use vcd::types::{ScopeIdx, SignalIdx, VCD}; diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index 7f40451..c1522f0 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -11,7 +11,7 @@ use num::BigUint; #[derive(Debug, Copy, Clone)] pub struct LsbIdxOfTmstmpValOnTmln(pub(super) u32); -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Eq, PartialEq, Clone)] pub enum SignalType { Event, Integer, diff --git a/src/vcd/types.rs b/src/vcd/types.rs index 53e067e..7ef3a54 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -9,7 +9,7 @@ use chrono::prelude::{DateTime, Utc}; use num::BigUint; use std::fmt; -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct Version(pub String); #[derive(Debug, Clone, Copy, Eq, PartialEq)] From 9ba53df728064c862ce29cbb70dd92961f7e9de7 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Fri, 3 Nov 2023 19:49:06 +0100 Subject: [PATCH 07/12] Add workaround for files produced by verilator which have annonymous scopes --- src/vcd/parse/scopes.rs | 207 ++++++++++++++++++++++++++-------------- 1 file changed, 137 insertions(+), 70 deletions(-) diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index 5da96ea..5fe106b 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -289,87 +289,154 @@ fn parse_scopes_inner( // $scope module reg_mag_i $end // ^^^^^^^^^ - scope name 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(); + path.push(scope_name.to_string()); - let mut path = path.clone(); - path.push(scope_name.to_string()); + let curr_scope_idx = ScopeIdx(vcd.all_scopes.len()); - let curr_scope_idx = ScopeIdx(vcd.all_scopes.len()); - - // register this scope as a child of the current parent scope - // if there is a parent scope, or else we register this scope as - // root scope - match parent_scope_idx { - Some(ScopeIdx(parent_scope_idx)) => { - let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap(); - parent_scope.child_scopes.push(curr_scope_idx); + // register this scope as a child of the current parent scope + // if there is a parent scope, or else we register this scope as + // root scope + match parent_scope_idx { + Some(ScopeIdx(parent_scope_idx)) => { + let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap(); + parent_scope.child_scopes.push(curr_scope_idx); + } + None => vcd.root_scopes.push(curr_scope_idx), } - None => vcd.root_scopes.push(curr_scope_idx), - } - // add this scope to list of existing scopes - vcd.all_scopes.push(Scope { - name: scope_name.to_string(), - self_idx: curr_scope_idx, - child_signals: vec![], - child_scopes: vec![], - }); + // add this scope to list of existing scopes + vcd.all_scopes.push(Scope { + name: scope_name.to_string(), + self_idx: curr_scope_idx, + child_signals: vec![], + child_scopes: vec![], + }); - // $scope module reg_mag_i $end - // ^^^^ - end keyword - ident(word_reader, "$end")?; + // $scope module reg_mag_i $end + // ^^^^ - end keyword + ident(word_reader, "$end")?; - 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, - Some(curr_scope_idx), - vcd, - signal_map, - &path, - )?; - } - "var" => { - parse_var(word_reader, curr_scope_idx, vcd, signal_map, &path)?; - } - "upscope" => { - ident(word_reader, "$end")?; - break; - } - // we ignore comments - "comment" => loop { - if ident(word_reader, "$end").is_ok() { + 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, + Some(curr_scope_idx), + vcd, + signal_map, + &path, + )?; + } + "var" => { + parse_var(word_reader, curr_scope_idx, vcd, signal_map, &path)?; + } + "upscope" => { + ident(word_reader, "$end")?; break; } - }, - _ => { - let err = format!( - "Error near {}:{}. \ - found keyword `{residual}` but expected \ - `$scope`, `$var`, `$comment`, or `$upscope` \ - on {cursor:?}", - file!(), - line!() - ); - return Err(err); + // we ignore comments + "comment" => loop { + if ident(word_reader, "$end").is_ok() { + break; + } + }, + _ => { + let err = format!( + "Error near {}:{}. \ + found keyword `{residual}` but expected \ + `$scope`, `$var`, `$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); + } } - _ => { - let err = format!( - "Error near {}:{}. \ - found keyword `{matched}` but \ - expected `$` on {cursor:?}", - file!(), - line!() - ); - return Err(err); + } + } 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); + } } } } From 9a8c2a03eb6eed808280b344f6f8c9d45aaed3c3 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Tue, 5 Dec 2023 14:25:36 +0100 Subject: [PATCH 08/12] Don't throw away the right hand time of changes --- src/vcd/signal.rs | 50 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 16 deletions(-) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index c1522f0..5093dff 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -90,7 +90,7 @@ impl<'a> Signal<'a> { let Signal(signal_enum) = &self; signal_enum .query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) - .map(|(val, _)| val) + .map(|(val, _, _)| val) } pub fn query_num_val_on_tmln( @@ -101,14 +101,14 @@ impl<'a> Signal<'a> { let Signal(signal_enum) = &self; signal_enum .query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) - .map(|(val, _)| val) + .map(|(val, _, _)| val) } pub fn query_val_on_tmln( &self, desired_time: &BigUint, vcd: &types::VCD, - ) -> Result<(TimeStamp, SignalValue), SignalErrors> { + ) -> Result<(TimeStamp, SignalValue, Option), SignalErrors> { let Signal(signal_enum) = &self; let num_val = signal_enum.query_num_val_on_tmln( desired_time, @@ -125,15 +125,21 @@ impl<'a> Signal<'a> { // the desired time. If both have valid values, select the most recent // one match (num_val, str_val) { - (Ok((num_val, num_time)), Ok((str_val, str_time))) => { + (Ok((num_val, num_time, num_next)), Ok((str_val, str_time, str_next))) => { + let next = match (num_next, str_next) { + (Some(n), Some(s)) => Some(n.min(s)), + (Some(n), None) => Some(n), + (None, Some(s)) => Some(s), + (None, None) => None + }; if num_time > str_time { - Ok((num_time, SignalValue::BigUint(num_val))) + Ok((num_time, SignalValue::BigUint(num_val), next)) } else { - Ok((str_time, SignalValue::String(str_val))) + Ok((str_time, SignalValue::String(str_val), next)) } } - (Ok((num_val, time)), Err(_)) => Ok((time, SignalValue::BigUint(num_val))), - (Err(_), Ok((str_val, time))) => Ok((time, SignalValue::String(str_val))), + (Ok((num_val, time, next)), Err(_)) => Ok((time, SignalValue::BigUint(num_val), next)), + (Err(_), Ok((str_val, time, next))) => Ok((time, SignalValue::String(str_val), next)), (Err(e), _e) => Err(e), } } @@ -396,7 +402,7 @@ impl SignalEnum { desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result<(String, TimeStamp), SignalErrors> { + ) -> Result<(String, TimeStamp, Option), SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -458,7 +464,7 @@ impl SignalEnum { // 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 *desired_time > timeline_end_time { - return Ok((timeline_end_val.to_string(), timeline_end_time)); + return Ok((timeline_end_val.to_string(), timeline_end_time, None)); } // This while loop is the meat of the lookup. Performance is log2(n), @@ -477,7 +483,13 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - return Ok((curr_val.to_string(), curr_time)); + let next_time = if mid_idx >= lsb_indxs_of_string_tmstmp_vals_on_tmln.len() { + Some(self.time_and_str_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) + } + else { + None + }; + return Ok((curr_val.to_string(), curr_time, next_time)); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -500,14 +512,14 @@ impl SignalEnum { }); } - Ok((left_val.to_string(), left_time)) + Ok((left_val.to_string(), left_time, Some(right_time))) } pub fn query_num_val_on_tmln( &self, desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result<(BigUint, TimeStamp), SignalErrors> { + ) -> Result<(BigUint, TimeStamp, Option), SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -589,7 +601,7 @@ impl SignalEnum { // 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 *desired_time > timeline_end_time { - return Ok((timeline_end_val, timeline_end_time)); + return Ok((timeline_end_val, timeline_end_time, None)); } // This while loop is the meat of the lookup. Performance is log2(n), @@ -608,7 +620,13 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - return Ok((curr_val, curr_time)); + let next_time = if mid_idx >= lsb_indxs_of_num_tmstmp_vals_on_tmln.len() { + Some(self.time_and_num_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) + } + else { + None + }; + return Ok((curr_val, curr_time, next_time)); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -631,6 +649,6 @@ impl SignalEnum { }); } - Ok((left_val, left_time)) + Ok((left_val, left_time, Some(right_time))) } } From 7d414f36ddc8817a640787c3279b71cf36b54938 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Tue, 5 Dec 2023 14:59:04 +0100 Subject: [PATCH 09/12] Correctly compute next index --- src/vcd/signal.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index 5093dff..7d0cfc4 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -483,7 +483,7 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - let next_time = if mid_idx >= lsb_indxs_of_string_tmstmp_vals_on_tmln.len() { + let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len()-1 { Some(self.time_and_str_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) } else { @@ -620,7 +620,7 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - let next_time = if mid_idx >= lsb_indxs_of_num_tmstmp_vals_on_tmln.len() { + let next_time = if mid_idx < lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1 { Some(self.time_and_num_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) } else { From e2e3541e3f5632be33a69f646293f47c2ea8e2c0 Mon Sep 17 00:00:00 2001 From: TheZoq2 Date: Tue, 5 Dec 2023 16:44:19 +0100 Subject: [PATCH 10/12] Refactor the double-sided-query to make it less error-prone --- src/vcd/signal.rs | 147 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 104 insertions(+), 43 deletions(-) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs index 7d0cfc4..b87e1a2 100644 --- a/src/vcd/signal.rs +++ b/src/vcd/signal.rs @@ -40,6 +40,11 @@ pub enum SignalValue { String(String), } +pub struct QueryResult { + pub current: Option<(TimeStamp, T)>, + pub next: Option, +} + pub struct Signal<'a>(pub(super) &'a SignalEnum); impl<'a> Signal<'a> { @@ -82,6 +87,9 @@ impl<'a> Signal<'a> { 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( &self, desired_time: &BigUint, @@ -90,32 +98,33 @@ impl<'a> Signal<'a> { let Signal(signal_enum) = &self; signal_enum .query_string_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) - .map(|(val, _, _)| val) + .map(|QueryResult{current, next: _}| current.map(|c| c.1)) } pub fn query_num_val_on_tmln( &self, desired_time: &BigUint, vcd: &types::VCD, - ) -> Result { + ) -> Result, SignalErrors> { let Signal(signal_enum) = &self; signal_enum .query_num_val_on_tmln(desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals) - .map(|(val, _, _)| val) + .map(|QueryResult{current, next: _}| current.map(|c| c.1)) } + */ pub fn query_val_on_tmln( &self, desired_time: &BigUint, vcd: &types::VCD, - ) -> Result<(TimeStamp, SignalValue, Option), SignalErrors> { + ) -> Result, SignalErrors> { let Signal(signal_enum) = &self; - let num_val = signal_enum.query_num_val_on_tmln( + let num_query_out = signal_enum.query_num_val_on_tmln( desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals, ); - let str_val = signal_enum.query_string_val_on_tmln( + let str_query_out = signal_enum.query_string_val_on_tmln( desired_time, &vcd.tmstmps_encoded_as_u8s, &vcd.all_signals, @@ -124,22 +133,44 @@ impl<'a> Signal<'a> { // 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 // one - match (num_val, str_val) { - (Ok((num_val, num_time, num_next)), Ok((str_val, str_time, str_next))) => { - let next = match (num_next, str_next) { + match (num_query_out, str_query_out) { + (Ok(num_result), Ok(str_result)) => { + 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 + (None, None) => None, }; - if num_time > str_time { - Ok((num_time, SignalValue::BigUint(num_val), next)) - } else { - Ok((str_time, SignalValue::String(str_val), next)) + + match (num_result.current, str_result.current) { + (Some((num_time, num_value)), Some((str_time, str_value))) => { + if num_time > str_time { + Ok(QueryResult { + current: Some((num_time, SignalValue::BigUint(num_value))), + next, + }) + } else { + Ok(QueryResult { + current: Some((str_time, SignalValue::String(str_value))), + next, + }) + } + } + (Some((num_time, num_val)), None) => Ok(QueryResult { + current: Some((num_time, SignalValue::BigUint(num_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, + }), } } - (Ok((num_val, time, next)), Err(_)) => Ok((time, SignalValue::BigUint(num_val), next)), - (Err(_), Ok((str_val, time, next))) => Ok((time, SignalValue::String(str_val), next)), + (_e, Err(e)) => Err(e), (Err(e), _e) => Err(e), } } @@ -198,10 +229,6 @@ pub(super) enum SignalEnum { #[derive(Debug)] pub enum SignalErrors { - PreTimeline { - desired_time: BigUint, - timeline_start_time: BigUint, - }, EmptyTimeline, TimelineNotMultiple, StrTmlnLenMismatch, @@ -402,7 +429,7 @@ impl SignalEnum { desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result<(String, TimeStamp, Option), SignalErrors> { + ) -> Result, SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -436,7 +463,10 @@ impl SignalEnum { // this signal should at least have some events, otherwise, trying to index into // an empty vector later on would fail if lsb_indxs_of_string_tmstmp_vals_on_tmln.is_empty() { - return Err(SignalErrors::EmptyTimeline); + return Ok(QueryResult { + current: None, + next: None + }); } // the vector of string timeline lsb indices should have the same @@ -450,9 +480,9 @@ impl SignalEnum { let (timeline_start_time, _) = self.time_and_str_val_at_event_idx(0, tmstmps_encoded_as_u8s)?; if *desired_time < timeline_start_time { - return Err(SignalErrors::PreTimeline { - desired_time: desired_time.clone(), - timeline_start_time, + return Ok(QueryResult { + current: None, + next: Some(timeline_start_time), }); } @@ -464,7 +494,10 @@ impl SignalEnum { // 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 *desired_time > timeline_end_time { - return Ok((timeline_end_val.to_string(), timeline_end_time, None)); + return Ok(QueryResult { + 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), @@ -483,13 +516,21 @@ impl SignalEnum { lower_idx = mid_idx + 1; } std::cmp::Ordering::Equal => { - let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len()-1 { - Some(self.time_and_str_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) - } - else { + let next_time = if mid_idx < lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1 { + Some( + self.time_and_str_val_at_event_idx( + mid_idx + 1, + tmstmps_encoded_as_u8s, + )? + .0, + ) + } else { None }; - return Ok((curr_val.to_string(), curr_time, next_time)); + return Ok(QueryResult { + current: Some((curr_time, curr_val.to_string())), + next: next_time, + }); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -512,14 +553,17 @@ impl SignalEnum { }); } - Ok((left_val.to_string(), left_time, Some(right_time))) + Ok(QueryResult { + current: Some((left_time, left_val.to_string())), + next: Some(right_time), + }) } pub fn query_num_val_on_tmln( &self, desired_time: &BigUint, tmstmps_encoded_as_u8s: &Vec, all_signals: &Vec, - ) -> Result<(BigUint, TimeStamp, Option), SignalErrors> { + ) -> Result, SignalErrors> { let signal_idx = match self { Self::Data { self_idx, .. } => { let SignalIdx(idx) = self_idx; @@ -564,7 +608,10 @@ impl SignalEnum { // this signal should at least have some events, otherwise, trying to index into // an empty vector later on would fail if lsb_indxs_of_num_tmstmp_vals_on_tmln.is_empty() { - return Err(SignalErrors::EmptyTimeline); + return Ok(QueryResult { + current: None, + next: None + }); } // assertion that value_sequence is a proper multiple of @@ -587,9 +634,9 @@ impl SignalEnum { let (timeline_start_time, _) = self.time_and_num_val_at_event_idx(0, tmstmps_encoded_as_u8s)?; if *desired_time < timeline_start_time { - return Err(SignalErrors::PreTimeline { - desired_time: desired_time.clone(), - timeline_start_time, + return Ok(QueryResult { + current: None, + next: Some(timeline_start_time), }); } @@ -601,7 +648,10 @@ impl SignalEnum { // 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 *desired_time > timeline_end_time { - return Ok((timeline_end_val, timeline_end_time, None)); + return Ok(QueryResult { + current: Some((timeline_end_time, timeline_end_val)), + next: None, + }); } // This while loop is the meat of the lookup. Performance is log2(n), @@ -621,12 +671,20 @@ impl SignalEnum { } std::cmp::Ordering::Equal => { let next_time = if mid_idx < lsb_indxs_of_num_tmstmp_vals_on_tmln.len() - 1 { - Some(self.time_and_num_val_at_event_idx(mid_idx+1, tmstmps_encoded_as_u8s)?.0) - } - else { + Some( + self.time_and_num_val_at_event_idx( + mid_idx + 1, + tmstmps_encoded_as_u8s, + )? + .0, + ) + } else { None }; - return Ok((curr_val, curr_time, next_time)); + return Ok(QueryResult { + current: Some((curr_time, curr_val)), + next: next_time, + }); } std::cmp::Ordering::Greater => { upper_idx = mid_idx - 1; @@ -649,6 +707,9 @@ impl SignalEnum { }); } - Ok((left_val, left_time, Some(right_time))) + return Ok(QueryResult { + current: Some((left_time, left_val)), + next: Some(right_time), + }); } } From 133b1a2693e801a27abadf5bfb201050a6eebb74 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel <103011002+ThePerfectComputer@users.noreply.github.com> Date: Tue, 12 Mar 2024 18:50:29 -0400 Subject: [PATCH 11/12] Sunsetting --- README.md | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/README.md b/README.md index 5dfa11b..019280d 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,7 @@ 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. # Status -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)! +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. Browser demo: https://app.surfer-project.org/ From f2676673cd54c143d389fc42a686f719c9748208 Mon Sep 17 00:00:00 2001 From: Yehowshua Immanuel Date: Sat, 16 Mar 2024 23:29:10 -0400 Subject: [PATCH 12/12] small update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 019280d..7890a1e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Copyright - Yehowshua Immanuel +Copyright(2023) - Yehowshua Immanuel # 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.