support for strings seems to work

This commit is contained in:
Yehowshua Immanuel 2022-09-08 09:59:24 -04:00
parent cd6ea71b8d
commit 5700db83a0
5 changed files with 271 additions and 33 deletions

View file

@ -75,7 +75,7 @@ Here's a command to test on a malformed VCD:
- [ ] may need to refactor with allow for get_mut for dynamic - [ ] may need to refactor with allow for get_mut for dynamic
compression-decompression for multiple signal structs compression-decompression for multiple signal structs
at once to allow for multi-threading at once to allow for multi-threading
- [ ] add string support for timeline value scanner - [x] add string support for timeline value scanner
- [ ] test against large waveforms from the - [ ] test against large waveforms from the
[verilog-vcd-parser](https://github.com/ben-marshall/verilog-vcd-parser) [verilog-vcd-parser](https://github.com/ben-marshall/verilog-vcd-parser)
tool tool
@ -91,6 +91,7 @@ Here's a command to test on a malformed VCD:
- [ ] Move part of the performance section to another markdown file. - [ ] Move part of the performance section to another markdown file.
## Repairs ## Repairs
- [ ] replace str bracket indices with get(slice)
- [ ] make a custom date parser for possibly up to 18 different versions(that is, for each possible tool). - [ ] make a custom date parser for possibly up to 18 different versions(that is, for each possible tool).
- [ ] Consolidate error messages and add cursors throughout. - [ ] Consolidate error messages and add cursors throughout.
- [ ] Add file and line to the enum errors. - [ ] Add file and line to the enum errors.
@ -98,6 +99,7 @@ Here's a command to test on a malformed VCD:
able to successfully parse all sample VCDs. able to successfully parse all sample VCDs.
## Code Consistency ## Code Consistency
- [ ] split impls in signal.rs into groups
- [ ] Change error messages to line and filenames. Go through all calls to unwrap. - [ ] Change error messages to line and filenames. Go through all calls to unwrap.
- [ ] search for any unwraps or any direct vectors indexing - [ ] search for any unwraps or any direct vectors indexing
- [ ] Handle TODOs - [ ] Handle TODOs

View file

@ -6,7 +6,7 @@ pub mod test;
pub mod vcd; pub mod vcd;
use vcd::*; use vcd::*;
use num::BigUint; use num::{BigUint, traits::sign};
#[derive(Parser)] #[derive(Parser)]
struct Cli { struct Cli {
@ -19,32 +19,48 @@ fn main() -> std::io::Result<()> {
let args = Cli::parse(); let args = Cli::parse();
use std::time::Instant; use std::time::Instant;
let now = Instant::now();
let now = Instant::now();
let file = File::open(&args.path)?; let file = File::open(&args.path)?;
let vcd = parse_vcd(file).unwrap(); let vcd = parse_vcd(file).unwrap();
let elapsed = now.elapsed(); let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
println!("Parsed VCD file {} : {:.2?}", &args.path.as_os_str().to_str().unwrap(), elapsed);
// the following is really only for test-vcd-files/icarus/CPU.vcd // the following is really only for test-vcd-files/icarus/CPU.vcd
// at the moment // at the moment
if args.path.as_os_str().to_str().unwrap() == "test-vcd-files/icarus/CPU.vcd" { if args.path.as_os_str().to_str().unwrap() == "test-vcd-files/icarus/CPU.vcd" {
let signal = &vcd.all_signals[51]; let rs2_data_signal = &vcd.all_signals[51];
let name = match signal { let name = rs2_data_signal.name();
Signal::Data { name, .. } => name, // query testbench -> CPU -> rs2_data[31:0] @ 4687s
_ => "ERROR", let time = BigUint::from(4687u32);
}; let val = rs2_data_signal
let val = signal
.query_num_val_on_tmln( .query_num_val_on_tmln(
BigUint::from(4687u32), &time,
&vcd.tmstmps_encoded_as_u8s, &vcd.tmstmps_encoded_as_u8s,
&vcd.all_signals, &vcd.all_signals,
) )
.unwrap(); .unwrap();
dbg!(format!("{val:#X}")); println!("Signal `{name}` has value `{val}` at time `{time}`");
dbg!(name);
// also need to test testbench -> CPU -> ID_EX_RD[4:0]
} }
// this is to help with testing stringed enums
if args.path.as_os_str().to_str().unwrap() == "test-vcd-files/amaranth/up_counter.vcd" {
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(()) Ok(())
} }

View file

@ -112,7 +112,7 @@ pub(super) fn parse_events<'a>(
) )
})?; })?;
let signal = vcd.try_dereference_alias_mut(signal_idx)?; let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal { match signal {
Signal::Data { Signal::Data {
@ -225,7 +225,7 @@ pub(super) fn parse_events<'a>(
) )
})?; })?;
let signal = vcd.try_dereference_alias_mut(signal_idx)?; let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal { match signal {
Signal::Data { Signal::Data {
@ -316,7 +316,7 @@ pub(super) fn parse_events<'a>(
) )
})?; })?;
let signal = vcd.try_dereference_alias_mut(signal_idx)?; let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal { match signal {
Signal::Data { Signal::Data {
@ -409,7 +409,7 @@ pub(super) fn parse_events<'a>(
) )
})?; })?;
let signal = vcd.try_dereference_alias_mut(signal_idx)?; let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal { match signal {
Signal::Data { Signal::Data {
@ -477,6 +477,55 @@ pub(super) fn parse_events<'a>(
} }
}?; }?;
} }
"s" => {
let val = word[1..].to_string();
let (hash, cursor) = next_word!(word_reader)?;
// lokup signal idx
let signal_idx = signal_map.get(hash).ok_or(()).map_err(|_| {
format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(),
line!()
)
})?;
let signal = vcd.dealiasing_signal_idx_to_signal_lookup_mut(signal_idx)?;
match signal {
Signal::Data {
name,
sig_type,
ref mut signal_error,
num_bits,
string_vals,
byte_len_of_num_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => {
// if this is a bad signal, go ahead and skip it
if signal_error.is_some() {
continue;
}
// record timestamp at which this event occurs
lsb_indxs_of_string_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_string_tmstmp_vals_on_tmln.push(curr_tmstmp_len_u8);
// record string value
string_vals.push(val);
Ok(())
}
Signal::Alias { .. } => {
let (f, l) = (file!(), line!());
let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}");
Err(msg)
}
}?;
}
_ => {} _ => {}
} }
} }

View file

@ -76,6 +76,7 @@ pub enum SignalErrors {
}, },
EmptyTimeline, EmptyTimeline,
TimelineNotMultiple, TimelineNotMultiple,
StrTmlnLenMismatch,
OrderingFailure { OrderingFailure {
lhs_time: BigUint, lhs_time: BigUint,
mid_time: BigUint, mid_time: BigUint,
@ -90,6 +91,29 @@ pub enum SignalErrors {
type TimeStamp = BigUint; type TimeStamp = BigUint;
type SignalValNum = BigUint; type SignalValNum = BigUint;
// getter functions
impl Signal {
pub fn self_idx(&self) -> Result<SignalIdx, String> {
match self {
Signal::Data { self_idx, ..} => {return Ok(self_idx.clone())},
Signal::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
pub fn name(&self) -> String {
match self {
Signal::Data { name, ..} => name,
Signal::Alias { name, .. } => name
}.clone()
}
}
impl Signal { impl Signal {
pub(super) fn bytes_required(num_bits: u16, name: &String) -> Result<u8, String> { pub(super) fn bytes_required(num_bits: u16, name: &String) -> Result<u8, String> {
let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 }; let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
@ -187,9 +211,124 @@ impl Signal {
Ok((timestamp, signal_val)) Ok((timestamp, signal_val))
} }
pub fn query_string_val_on_tmln(
&self,
desired_time: &BigUint,
tmstmps_encoded_as_u8s: &Vec<u8>,
all_signals: &Vec<Signal>,
) -> Result<String, SignalErrors> {
let signal_idx = match self {
Self::Data { self_idx, .. } => {
let SignalIdx(idx) = self_idx;
*idx
}
Self::Alias {
name: _,
signal_alias,
} => {
let SignalIdx(idx) = signal_alias;
*idx
}
};
// if the signal idx points to data variant of the signal,
// extract:
// 1. the vector of string values
// 2. the vector of indices into timeline where events occur
// for this signal
// else we propagate Err(..).
let (string_vals, lsb_indxs_of_string_tmstmp_vals_on_tmln) =
match &all_signals[signal_idx] {
Signal::Data {
ref string_vals,
ref lsb_indxs_of_string_tmstmp_vals_on_tmln,
..
} => {
Ok((
string_vals,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
))
}
Signal::Alias { .. } => Err(SignalErrors::PointsToAlias),
}?;
// 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);
}
// the vector of string timeline lsb indices should have the same
// length as the vector of string values
if string_vals.len() != lsb_indxs_of_string_tmstmp_vals_on_tmln.len() {
return Err(SignalErrors::StrTmlnLenMismatch);
}
// check if we're requesting a value that occurs before the recorded
// start of the timeline
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: timeline_start_time,
});
}
let mut lower_idx = 0usize;
let mut upper_idx = lsb_indxs_of_string_tmstmp_vals_on_tmln.len() - 1;
let (timeline_end_time, timeline_end_val) =
self.time_and_str_val_at_event_idx(upper_idx, tmstmps_encoded_as_u8s)?;
// 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());
}
// This while loop is the meat of the lookup. Performance is log2(n),
// where n is the number of events on the timeline.
// We can assume that by the time we get here, that the desired_time
// is an event that occurs on the timeline, given that we handle any events
// occuring after or before the recorded tiimeline in the code above.
while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (curr_time, curr_val) =
self.time_and_str_val_at_event_idx(mid_idx, tmstmps_encoded_as_u8s)?;
let ordering = curr_time.cmp(desired_time);
match ordering {
std::cmp::Ordering::Less => {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
return Ok(curr_val.to_string());
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
}
}
}
let (left_time, left_val) =
self.time_and_str_val_at_event_idx(lower_idx - 1, tmstmps_encoded_as_u8s)?;
let (right_time, _) =
self.time_and_str_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?;
let ordered_left = left_time < *desired_time;
let ordered_right = *desired_time < right_time;
if !(ordered_left && ordered_right) {
return Err(SignalErrors::OrderingFailure {
lhs_time: left_time,
mid_time: desired_time.clone(),
rhs_time: right_time,
});
}
return Ok(left_val.to_string());
}
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<Signal>, all_signals: &Vec<Signal>,
) -> Result<BigUint, SignalErrors> { ) -> Result<BigUint, SignalErrors> {
@ -207,6 +346,13 @@ impl Signal {
} }
}; };
// if the signal idx points to data variant of the signal,
// extract:
// 1. the vector of LE u8 compressed values
// 2. the vector of indices into timeline where events occur
// for this signal
// 3. the number of bytes per value for this signal
// else we propagate Err(..).
let (nums_encoded_as_fixed_width_le_u8, lsb_indxs_of_num_tmstmp_vals_on_tmln, num_bytes) = let (nums_encoded_as_fixed_width_le_u8, lsb_indxs_of_num_tmstmp_vals_on_tmln, num_bytes) =
match &all_signals[signal_idx] { match &all_signals[signal_idx] {
Signal::Data { Signal::Data {
@ -244,10 +390,6 @@ impl Signal {
if nums_encoded_as_fixed_width_le_u8.len() if nums_encoded_as_fixed_width_le_u8.len()
!= (lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize)) != (lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize))
{ {
dbg!((
nums_encoded_as_fixed_width_le_u8.len(),
(lsb_indxs_of_num_tmstmp_vals_on_tmln.len() * (bytes_required as usize))
));
return Err(SignalErrors::TimelineNotMultiple); return Err(SignalErrors::TimelineNotMultiple);
} }
@ -255,9 +397,9 @@ impl Signal {
// start of the timeline // start of the timeline
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 Err(SignalErrors::PreTimeline { return Err(SignalErrors::PreTimeline {
desired_time: desired_time, desired_time: desired_time.clone(),
timeline_start_time: timeline_start_time, timeline_start_time: timeline_start_time,
}); });
} }
@ -269,7 +411,7 @@ impl Signal {
// 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(timeline_end_val); return Ok(timeline_end_val);
} }
@ -282,7 +424,7 @@ impl Signal {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2); let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let (curr_time, curr_val) = let (curr_time, curr_val) =
self.time_and_num_val_at_event_idx(mid_idx, tmstmps_encoded_as_u8s)?; self.time_and_num_val_at_event_idx(mid_idx, tmstmps_encoded_as_u8s)?;
let ordering = curr_time.cmp(&desired_time); let ordering = curr_time.cmp(desired_time);
match ordering { match ordering {
std::cmp::Ordering::Less => { std::cmp::Ordering::Less => {
@ -302,12 +444,12 @@ impl Signal {
let (right_time, _) = let (right_time, _) =
self.time_and_num_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?; self.time_and_num_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?;
let ordered_left = left_time < desired_time; let ordered_left = left_time < *desired_time;
let ordered_right = desired_time < right_time; let ordered_right = *desired_time < right_time;
if !(ordered_left && ordered_right) { if !(ordered_left && ordered_right) {
return Err(SignalErrors::OrderingFailure { return Err(SignalErrors::OrderingFailure {
lhs_time: left_time, lhs_time: left_time,
mid_time: desired_time, mid_time: desired_time.clone(),
rhs_time: right_time, rhs_time: right_time,
}); });
} }

View file

@ -60,12 +60,12 @@ pub struct VCD {
} }
impl VCD { impl VCD {
/// We take in a Signal and attempt to dereference 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``
/// pointed to by the ``Signal::Alias``. /// pointed to by the ``Signal::Alias``.
/// If the Signal is of varint ``Signal::Data``, then that can be returned directly. /// If the Signal is of varint ``Signal::Data``, then that can be returned directly.
pub(super) fn try_dereference_alias_mut<'a>( pub(super) fn dealiasing_signal_idx_to_signal_lookup_mut<'a>(
&'a mut self, &'a mut self,
idx: &SignalIdx, idx: &SignalIdx,
) -> Result<&'a mut Signal, String> { ) -> Result<&'a mut Signal, String> {
@ -92,7 +92,7 @@ impl VCD {
)), )),
} }
} }
pub(super) fn try_dereference_alias<'a>( pub(super) fn dealiasing_signal_idx_to_signal_lookup<'a>(
&'a self, &'a self,
idx: &SignalIdx, idx: &SignalIdx,
) -> Result<&'a Signal, String> { ) -> Result<&'a Signal, String> {
@ -106,6 +106,35 @@ impl VCD {
Signal::Alias { name, signal_alias } => *signal_alias, Signal::Alias { name, signal_alias } => *signal_alias,
}; };
// Should now point to Signal::Data variant, or else there's an error
let SignalIdx(idx) = signal_idx;
let signal = self.all_signals.get(idx).unwrap();
match signal {
Signal::Data { .. } => Ok(signal),
Signal::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
/// 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> {
// dereference signal if Signal::Alias, or keep idx if Signal::Data
let signal_idx = match signal {
Signal::Data { self_idx, .. } => *self_idx,
Signal::Alias { name, signal_alias } => *signal_alias,
};
// Should now point to Signal::Data variant, or else there's an error // Should now point to Signal::Data variant, or else there's an error
let SignalIdx(idx) = signal_idx; let SignalIdx(idx) = signal_idx;
let signal = self.all_signals.get(idx).unwrap(); let signal = self.all_signals.get(idx).unwrap();