support for strings seems to work
This commit is contained in:
parent
cd6ea71b8d
commit
5700db83a0
|
@ -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
|
||||
compression-decompression for multiple signal structs
|
||||
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
|
||||
[verilog-vcd-parser](https://github.com/ben-marshall/verilog-vcd-parser)
|
||||
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.
|
||||
|
||||
## 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).
|
||||
- [ ] Consolidate error messages and add cursors throughout.
|
||||
- [ ] 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.
|
||||
|
||||
## Code Consistency
|
||||
- [ ] split impls in signal.rs into groups
|
||||
- [ ] Change error messages to line and filenames. Go through all calls to unwrap.
|
||||
- [ ] search for any unwraps or any direct vectors indexing
|
||||
- [ ] Handle TODOs
|
||||
|
|
42
src/main.rs
42
src/main.rs
|
@ -6,7 +6,7 @@ pub mod test;
|
|||
pub mod vcd;
|
||||
use vcd::*;
|
||||
|
||||
use num::BigUint;
|
||||
use num::{BigUint, traits::sign};
|
||||
|
||||
#[derive(Parser)]
|
||||
struct Cli {
|
||||
|
@ -19,32 +19,48 @@ fn main() -> std::io::Result<()> {
|
|||
let args = Cli::parse();
|
||||
|
||||
use std::time::Instant;
|
||||
|
||||
let now = Instant::now();
|
||||
|
||||
let file = File::open(&args.path)?;
|
||||
let vcd = parse_vcd(file).unwrap();
|
||||
|
||||
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
|
||||
// at the moment
|
||||
if args.path.as_os_str().to_str().unwrap() == "test-vcd-files/icarus/CPU.vcd" {
|
||||
let signal = &vcd.all_signals[51];
|
||||
let name = match signal {
|
||||
Signal::Data { name, .. } => name,
|
||||
_ => "ERROR",
|
||||
};
|
||||
let val = signal
|
||||
let rs2_data_signal = &vcd.all_signals[51];
|
||||
let name = rs2_data_signal.name();
|
||||
// query testbench -> CPU -> rs2_data[31:0] @ 4687s
|
||||
let time = BigUint::from(4687u32);
|
||||
let val = rs2_data_signal
|
||||
.query_num_val_on_tmln(
|
||||
BigUint::from(4687u32),
|
||||
&time,
|
||||
&vcd.tmstmps_encoded_as_u8s,
|
||||
&vcd.all_signals,
|
||||
)
|
||||
.unwrap();
|
||||
dbg!(format!("{val:#X}"));
|
||||
dbg!(name);
|
||||
println!("Signal `{name}` has value `{val}` at time `{time}`");
|
||||
|
||||
// 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(())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
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 {
|
||||
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 {
|
||||
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 {
|
||||
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)
|
||||
}
|
||||
}?;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -76,6 +76,7 @@ pub enum SignalErrors {
|
|||
},
|
||||
EmptyTimeline,
|
||||
TimelineNotMultiple,
|
||||
StrTmlnLenMismatch,
|
||||
OrderingFailure {
|
||||
lhs_time: BigUint,
|
||||
mid_time: BigUint,
|
||||
|
@ -90,6 +91,29 @@ pub enum SignalErrors {
|
|||
type TimeStamp = 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 {
|
||||
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 };
|
||||
|
@ -187,9 +211,124 @@ impl Signal {
|
|||
|
||||
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(
|
||||
&self,
|
||||
desired_time: BigUint,
|
||||
desired_time: &BigUint,
|
||||
tmstmps_encoded_as_u8s: &Vec<u8>,
|
||||
all_signals: &Vec<Signal>,
|
||||
) -> 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) =
|
||||
match &all_signals[signal_idx] {
|
||||
Signal::Data {
|
||||
|
@ -244,10 +390,6 @@ impl Signal {
|
|||
if nums_encoded_as_fixed_width_le_u8.len()
|
||||
!= (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);
|
||||
}
|
||||
|
||||
|
@ -255,9 +397,9 @@ impl Signal {
|
|||
// start of the timeline
|
||||
let (timeline_start_time, _) =
|
||||
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 {
|
||||
desired_time: desired_time,
|
||||
desired_time: desired_time.clone(),
|
||||
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,
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
@ -282,7 +424,7 @@ impl Signal {
|
|||
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
|
||||
let (curr_time, curr_val) =
|
||||
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 {
|
||||
std::cmp::Ordering::Less => {
|
||||
|
@ -302,12 +444,12 @@ impl Signal {
|
|||
let (right_time, _) =
|
||||
self.time_and_num_val_at_event_idx(lower_idx, tmstmps_encoded_as_u8s)?;
|
||||
|
||||
let ordered_left = left_time < desired_time;
|
||||
let ordered_right = desired_time < right_time;
|
||||
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,
|
||||
mid_time: desired_time.clone(),
|
||||
rhs_time: right_time,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -60,12 +60,12 @@ pub struct 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
|
||||
/// another alias, that's an error. Otherwise, we return the ``Signal::Data``
|
||||
/// pointed to by the ``Signal::Alias``.
|
||||
/// 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,
|
||||
idx: &SignalIdx,
|
||||
) -> 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,
|
||||
idx: &SignalIdx,
|
||||
) -> Result<&'a Signal, String> {
|
||||
|
@ -106,6 +106,35 @@ impl VCD {
|
|||
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
|
||||
let SignalIdx(idx) = signal_idx;
|
||||
let signal = self.all_signals.get(idx).unwrap();
|
||||
|
|
Loading…
Reference in a new issue