non trivial re-factor

This commit is contained in:
Yehowshua Immanuel 2022-08-19 20:13:46 -04:00
parent cbd8be1708
commit 15a2564f13
8 changed files with 668 additions and 528 deletions

View file

@ -7,5 +7,8 @@ pub(super) use types::*;
mod parse; mod parse;
pub(super) use parse::*; pub(super) use parse::*;
mod signal;
pub(super) use signal::*;
mod utilities; mod utilities;
use utilities::*; use utilities::*;

View file

@ -31,11 +31,10 @@ pub fn parse_vcd(file: File) -> Result<VCD, String> {
// after we parse metadata, we form the VCD object // after we parse metadata, we form the VCD object
let mut vcd = VCD { let mut vcd = VCD {
metadata: header, metadata: header,
timeline: vec![], tmstmps_encoded_as_u8s: vec![],
timeline_markers: vec![],
all_signals: vec![], all_signals: vec![],
all_scopes: vec![], all_scopes: vec![],
scope_roots: vec![], root_scopes: vec![],
}; };
parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?; parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?;

View file

@ -5,6 +5,8 @@ pub(super) fn parse_events<'a>(
vcd: &'a mut VCD, vcd: &'a mut VCD,
signal_map: &mut HashMap<String, SignalIdx>, signal_map: &mut HashMap<String, SignalIdx>,
) -> Result<(), String> { ) -> Result<(), String> {
let mut curr_tmstmp_lsb_idx = 0u32;
let mut curr_tmstmp_len_u8 = 0u8;
loop { loop {
let next_word = word_reader.next_word(); let next_word = word_reader.next_word();
@ -36,18 +38,36 @@ pub(super) fn parse_events<'a>(
let mut value = value.to_bytes_le(); let mut value = value.to_bytes_le();
// TODO : u32 helps with less memory, but should ideally likely be // TODO : u32 helps with less memory, but should ideally likely be
// configurable. // configurable.
let (f, l) = (file!(), line!()); curr_tmstmp_len_u8 = u8::try_from(value.len()).map_err(|_| {
let start_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { format!(
format!("Error near {f}:{l}. Failed to convert from usize to u32.") "Error near {}:{}. Failed to convert from usize to u8.",
file!(),
line!()
)
})?; })?;
vcd.timeline_markers.push(StartIdx(start_idx)); vcd.tmstmps_encoded_as_u8s.append(&mut value);
vcd.timeline.append(&mut value); curr_tmstmp_lsb_idx =
u32::try_from(vcd.tmstmps_encoded_as_u8s.len()).map_err(|_| {
format!(
"Error near {}:{}. Failed to convert from usize to u8.",
file!(),
line!()
)
})?;
// curr_tmstmp_lsb_idx = vcd.tmstmps_encoded_as_u8s.len();
} }
// handle the case of an n bit signal whose value must be parsed // handle the case of an n bit signal whose value must be parsed
"b" => { "b" => {
let binary_value = &word[1..]; let binary_value = &word[1..];
let observed_num_bits = binary_value.len(); let observed_num_bits = u16::try_from(binary_value.len()).map_err(|_| {
format!(
"Error near {}:{}, {cursor:?}. \
Found signal with more than 2^16 - 1 bits.",
file!(),
line!()
)
})?;
let mut value_u8: Vec<u8> = Vec::new(); let mut value_u8: Vec<u8> = Vec::new();
let mut value_string = String::new(); let mut value_string = String::new();
@ -81,7 +101,7 @@ pub(super) fn parse_events<'a>(
let (word, cursor) = next_word!(word_reader)?; let (word, cursor) = next_word!(word_reader)?;
// lookup signal idx // lookup signal idx
let SignalIdx(ref signal_idx) = signal_map.get(word).ok_or(()).map_err(|_| { let signal_idx = signal_map.get(word).ok_or(()).map_err(|_| {
format!( format!(
"Error near {}:{}. Failed to lookup signal {word} at {cursor:?}", "Error near {}:{}. Failed to lookup signal {word} at {cursor:?}",
file!(), file!(),
@ -89,34 +109,29 @@ pub(super) fn parse_events<'a>(
) )
})?; })?;
// account for fact that signal idx could be an alias, so there let signal = vcd.try_dereference_alias_mut(signal_idx)?;
// could be one step of indirection
let signal_idx = { // we may have to dereference a signal if it's pointing at an alias
let signal = vcd.all_signals.get(*signal_idx).unwrap(); // let signal = &vcd.all_signals[*signal_idx];
match signal { // let signal = signal.try_dereference_alias_mut(&vcd.all_signals)?;
Signal::Data { .. } => *signal_idx,
Signal::Alias { signal_alias, .. } => {
let SignalIdx(ref signal_idx) = signal_alias;
signal_idx.clone()
}
}
};
// after handling potential indirection, go ahead and update the timeline
// of the signal signal_idx references
let signal = vcd.all_signals.get_mut(signal_idx).unwrap();
match signal { match signal {
Signal::Data { Signal::Data {
name, name,
sig_type, sig_type,
ref mut signal_error, ref mut signal_error,
num_bits, num_bits,
u8_timeline, self_idx,
u8_timeline_markers, nums_encoded_as_fixed_width_le_u8,
string_timeline, string_vals,
string_timeline_markers, lsb_indxs_of_num_tmstmp_vals_on_tmln,
.. byte_len_of_num_tmstmp_vals_on_tmln,
lsb_indxs_of_string_tmstmp_vals_on_tmln,
byte_len_of_string_tmstmp_vals_on_tmln,
scope_parent,
} => { } => {
// we've already identified in a prior loop iteration that the signal has
// an error
if signal_error.is_some() { if signal_error.is_some() {
continue; continue;
} }
@ -152,29 +167,31 @@ pub(super) fn parse_events<'a>(
} }
}; };
let (f, l) = (file!(), line!());
let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| {
format!("Error near {f}:{l}. Failed to convert from usize to u32.")
})?;
let timeline_idx = TimelineIdx(timeline_idx);
if store_as_string { if store_as_string {
string_timeline_markers.push(timeline_idx); lsb_indxs_of_string_tmstmp_vals_on_tmln
string_timeline.push(value_string); .push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
string_vals.push(value_string);
Ok(()) Ok(())
} else { } else {
u8_timeline_markers.push(timeline_idx); let mut curr_num_bytes =
u8::try_from(value_u8.len()).map_err(|_| {
let mut curr_num_bytes = value_u8.len(); format!(
u8_timeline.append(&mut value_u8); "Error near {}:{}. \
Found signal {name} with with value change of greater \
than 2^16 - 1 bits on {cursor:?}.",
file!(),
line!()
)
})?;
lsb_indxs_of_num_tmstmp_vals_on_tmln
.push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx));
byte_len_of_num_tmstmp_vals_on_tmln.push(curr_num_bytes);
// we may need to zero extend values // we may need to zero extend values
// so that we end up storing all values // so that we end up storing all values
// of a particular signal in a consistent // of a particular signal in a consistent
// amount of bytes // amount of bytes
let num_bits = num_bits.unwrap(); let bytes_required = signal.bytes_required()?;
let bytes_required =
(num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
while curr_num_bytes < bytes_required { while curr_num_bytes < bytes_required {
// TODO: remove once library is known to be stable // TODO: remove once library is known to be stable
@ -187,7 +204,7 @@ pub(super) fn parse_events<'a>(
// for signal {name}"); // for signal {name}");
// Err(err)?; // Err(err)?;
u8_timeline.push(0u8); byte_len_of_num_tmstmp_vals_on_tmln.push(0u8);
curr_num_bytes += 1; curr_num_bytes += 1;
} }
Ok(()) Ok(())
@ -204,286 +221,288 @@ pub(super) fn parse_events<'a>(
} }
// handle the case of a one bit signal whose value is set to `0` // handle the case of a one bit signal whose value is set to `0`
"0" => { // "0" => {
// lookup signal idx // // lookup signal idx
let hash = &word[1..]; // let hash = &word[1..];
let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| {
format!( // format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(), // file!(),
line!() // line!()
) // )
})?; // })?;
// account for fact that signal idx could be an alias, so there // // account for fact that signal idx could be an alias, so there
// could be one step of indirection // // could be one step of indirection
let signal_idx = { // let signal_idx = {
let signal = vcd.all_signals.get(*signal_idx).unwrap(); // let signal = vcd.all_signals.get(*signal_idx).unwrap();
match signal { // match signal {
Signal::Data { .. } => *signal_idx, // Signal::Data { .. } => *signal_idx,
Signal::Alias { signal_alias, .. } => { // Signal::Alias { signal_alias, .. } => {
let SignalIdx(ref signal_idx) = signal_alias; // let SignalIdx(ref signal_idx) = signal_alias;
signal_idx.clone() // signal_idx.clone()
} // }
} // }
}; // };
// after handling potential indirection, go ahead and update the timeline // // after handling potential indirection, go ahead and update the timeline
// of the signal signal_idx references // // of the signal signal_idx references
let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); // let signal = vcd.all_signals.get_mut(signal_idx).unwrap();
match signal { // match signal {
Signal::Data { // Signal::Data {
name, // name,
sig_type, // sig_type,
ref mut signal_error, // ref mut signal_error,
num_bits, // num_bits,
u8_timeline, // u8_timeline,
u8_timeline_markers, // u8_timeline_markers,
.. // ..
} => { // } => {
// if this is a bad signal, go ahead and skip it // // if this is a bad signal, go ahead and skip it
if signal_error.is_some() { // if signal_error.is_some() {
continue; // continue;
} // }
// Get bitwidth and verify that it is 1. // // Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None` // // Also account for the error case of a bitwidth of `None`
match num_bits { // match num_bits {
Some(ref num_bits) => { // Some(ref num_bits) => {
if *num_bits != 1 { // if *num_bits != 1 {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
of sig_type {sig_type:?} is expected to be `1` not \ // of sig_type {sig_type:?} is expected to be `1` not \
`{num_bits}`. \ // `{num_bits}`. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
*signal_error = Some(msg); // *signal_error = Some(msg);
continue; // continue;
} // }
} // }
None => { // None => {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {sig_type:?}. \ // must be specified for a signal of type {sig_type:?}. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
Err(msg)?; // Err(msg)?;
} // }
}; // };
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len())
format!("Error near {f}:{l}. Failed to convert from usize to u32.") // .map_err(|_| {
})?; // format!("Error near {f}:{l}. Failed to convert from usize to u32.")
let timeline_idx = TimelineIdx(timeline_idx); // })?;
// let timeline_idx = TimelineIdx(timeline_idx);
u8_timeline_markers.push(timeline_idx); // u8_timeline_markers.push(timeline_idx);
u8_timeline.push(0u8); // u8_timeline.push(0u8);
Ok(()) // Ok(())
} // }
Signal::Alias { .. } => { // Signal::Alias { .. } => {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ // "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}"); // This error occurred while parsing vcd file at {cursor:?}");
Err(msg) // Err(msg)
} // }
}?; // }?;
} // }
"1" => { // "1" => {
// lokup signal idx // // lokup signal idx
let hash = &word[1..]; // let hash = &word[1..];
let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| {
format!( // format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(), // file!(),
line!() // line!()
) // )
})?; // })?;
// account for fact that signal idx could be an alias, so there // // account for fact that signal idx could be an alias, so there
// could be one step of indirection // // could be one step of indirection
let signal_idx = { // let signal_idx = {
let signal = vcd.all_signals.get(*signal_idx).unwrap(); // let signal = vcd.all_signals.get(*signal_idx).unwrap();
match signal { // match signal {
Signal::Data { .. } => *signal_idx, // Signal::Data { .. } => *signal_idx,
Signal::Alias { signal_alias, .. } => { // Signal::Alias { signal_alias, .. } => {
let SignalIdx(ref signal_idx) = signal_alias; // let SignalIdx(ref signal_idx) = signal_alias;
signal_idx.clone() // signal_idx.clone()
} // }
} // }
}; // };
// after handling potential indirection, go ahead and update the timeline // // after handling potential indirection, go ahead and update the timeline
// of the signal signal_idx references // // of the signal signal_idx references
let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); // let signal = vcd.all_signals.get_mut(signal_idx).unwrap();
match signal { // match signal {
Signal::Data { // Signal::Data {
name, // name,
sig_type, // sig_type,
ref mut signal_error, // ref mut signal_error,
num_bits, // num_bits,
u8_timeline, // u8_timeline,
u8_timeline_markers, // u8_timeline_markers,
.. // ..
} => { // } => {
// if this is a bad signal, go ahead and skip it // // if this is a bad signal, go ahead and skip it
if signal_error.is_some() { // if signal_error.is_some() {
continue; // continue;
} // }
// Get bitwidth and verify that it is 1. // // Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None` // // Also account for the error case of a bitwidth of `None`
match num_bits { // match num_bits {
Some(ref num_bits) => { // Some(ref num_bits) => {
if *num_bits != 1 { // if *num_bits != 1 {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
of sig_type {sig_type:?} is expected to be `1` not \ // of sig_type {sig_type:?} is expected to be `1` not \
`{num_bits}`. \ // `{num_bits}`. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
*signal_error = Some(msg); // *signal_error = Some(msg);
continue; // continue;
} // }
} // }
None => { // None => {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {sig_type:?}. \ // must be specified for a signal of type {sig_type:?}. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
Err(msg)?; // Err(msg)?;
} // }
}; // };
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len())
format!("Error near {f}:{l}. Failed to convert from usize to u32.") // .map_err(|_| {
})?; // format!("Error near {f}:{l}. Failed to convert from usize to u32.")
let timeline_idx = TimelineIdx(timeline_idx); // })?;
// let timeline_idx = TimelineIdx(timeline_idx);
u8_timeline_markers.push(timeline_idx); // u8_timeline_markers.push(timeline_idx);
u8_timeline.push(1u8); // u8_timeline.push(1u8);
Ok(()) // Ok(())
} // }
Signal::Alias { .. } => { // Signal::Alias { .. } => {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"Error near {f}:{l}, a signal alias should not point to a signal alias.\n\ // "Error near {f}:{l}, a signal alias should not point to a signal alias.\n\
This error occurred while parsing vcd file at {cursor:?}"); // This error occurred while parsing vcd file at {cursor:?}");
Err(msg) // Err(msg)
} // }
}?; // }?;
} // }
// other one bit cases // // other one bit cases
"x" | "X" | "z" | "Z" | "u" | "U" => { // "x" | "X" | "z" | "Z" | "u" | "U" => {
let val = word.to_string(); // let val = word.to_string();
// lokup signal idx // // lokup signal idx
let hash = &word[1..]; // let hash = &word[1..];
let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| {
format!( // format!(
"Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}",
file!(), // file!(),
line!() // line!()
) // )
})?; // })?;
// account for fact that signal idx could be an alias, so there // // account for fact that signal idx could be an alias, so there
// could be one step of indirection // // could be one step of indirection
let signal_idx = { // let signal_idx = {
let signal = vcd.all_signals.get(*signal_idx).unwrap(); // let signal = vcd.all_signals.get(*signal_idx).unwrap();
match signal { // match signal {
Signal::Data { .. } => *signal_idx, // Signal::Data { .. } => *signal_idx,
Signal::Alias { signal_alias, .. } => { // Signal::Alias { signal_alias, .. } => {
let SignalIdx(ref signal_idx) = signal_alias; // let SignalIdx(ref signal_idx) = signal_alias;
signal_idx.clone() // signal_idx.clone()
} // }
} // }
}; // };
// after handling potential indirection, go ahead and update the timeline // // after handling potential indirection, go ahead and update the timeline
// of the signal signal_idx references // // of the signal signal_idx references
let signal = vcd.all_signals.get_mut(signal_idx).unwrap(); // let signal = vcd.all_signals.get_mut(signal_idx).unwrap();
match signal { // match signal {
Signal::Data { // Signal::Data {
name, // name,
sig_type, // sig_type,
ref mut signal_error, // ref mut signal_error,
num_bits, // num_bits,
string_timeline, // string_timeline,
string_timeline_markers, // string_timeline_markers,
.. // ..
} => { // } => {
// if this is a bad signal, go ahead and skip it // // if this is a bad signal, go ahead and skip it
if signal_error.is_some() { // if signal_error.is_some() {
continue; // continue;
} // }
// Get bitwidth and verify that it is 1. // // Get bitwidth and verify that it is 1.
// Also account for the error case of a bitwidth of `None` // // Also account for the error case of a bitwidth of `None`
match num_bits { // match num_bits {
Some(ref num_bits) => { // Some(ref num_bits) => {
if *num_bits != 1 { // if *num_bits != 1 {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
of sig_type {sig_type:?} is expected to be `1` not \ // of sig_type {sig_type:?} is expected to be `1` not \
`{num_bits}`. \ // `{num_bits}`. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
*signal_error = Some(msg); // *signal_error = Some(msg);
continue; // continue;
} // }
} // }
None => { // None => {
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let msg = format!( // let msg = format!(
"\ // "\
Error near {f}:{l}. The bitwidth for signal {name} \ // Error near {f}:{l}. The bitwidth for signal {name} \
must be specified for a signal of type {sig_type:?}. \ // must be specified for a signal of type {sig_type:?}. \
This error occurred while parsing the vcd file at \ // This error occurred while parsing the vcd file at \
{cursor:?}" // {cursor:?}"
); // );
Err(msg)?; // Err(msg)?;
} // }
}; // };
let (f, l) = (file!(), line!()); // let (f, l) = (file!(), line!());
let timeline_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len())
format!("Error near {f}:{l}. Failed to convert from usize to u32.") // .map_err(|_| {
})?; // format!("Error near {f}:{l}. Failed to convert from usize to u32.")
let timeline_idx = TimelineIdx(timeline_idx); // })?;
// let timeline_idx = TimelineIdx(timeline_idx);
string_timeline_markers.push(timeline_idx);
string_timeline.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)
}
}?;
}
// string_timeline_markers.push(timeline_idx);
// string_timeline.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

@ -100,12 +100,14 @@ pub(super) fn parse_var<'a>(
name: full_signal_name, name: full_signal_name,
sig_type: var_type, sig_type: var_type,
signal_error: None, signal_error: None,
num_bits: no_bits, num_bits: None,
self_idx: signal_idx, self_idx: signal_idx,
u8_timeline: vec![], nums_encoded_as_fixed_width_le_u8: vec![],
u8_timeline_markers: vec![], string_vals: vec![],
string_timeline: vec![], lsb_indxs_of_num_tmstmp_vals_on_tmln: vec![],
string_timeline_markers: vec![], byte_len_of_num_tmstmp_vals_on_tmln: vec![],
lsb_indxs_of_string_tmstmp_vals_on_tmln: vec![],
byte_len_of_string_tmstmp_vals_on_tmln: vec![],
scope_parent: parent_scope_idx, scope_parent: parent_scope_idx,
}; };
(signal, signal_idx) (signal, signal_idx)
@ -155,7 +157,7 @@ fn parse_orphaned_vars<'a>(
child_signals: vec![], child_signals: vec![],
child_scopes: vec![], child_scopes: vec![],
}); });
vcd.scope_roots.push(scope_idx); vcd.root_scopes.push(scope_idx);
} }
// we can go ahead and parse the current var as we've already encountered // we can go ahead and parse the current var as we've already encountered
@ -224,7 +226,7 @@ fn parse_scopes_inner<'a>(
let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap(); let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap();
parent_scope.child_scopes.push(curr_scope_idx); parent_scope.child_scopes.push(curr_scope_idx);
} }
None => vcd.scope_roots.push(curr_scope_idx), None => vcd.root_scopes.push(curr_scope_idx),
} }
// add this scope to list of existing scopes // add this scope to list of existing scopes

View file

@ -115,7 +115,8 @@ macro_rules! curr_word {
($word_reader:ident) => { ($word_reader:ident) => {
$word_reader.curr_word().ok_or(()).map_err(|_| { $word_reader.curr_word().ok_or(()).map_err(|_| {
format!( format!(
"Error near {}:{}. A call to curr_word! shouldn't fail unless next_word has not yet been invoked.", "Error near {}:{}. A call to curr_word! shouldn't \
fail unless next_word has not yet been invoked.",
file!(), file!(),
line!() line!()
) )

193
src/vcd/signal.rs Normal file
View file

@ -0,0 +1,193 @@
// use super::utilities::{ordered_binary_lookup_u8, LookupErrors};
use super::{ScopeIdx, SignalIdx};
use num::{BigUint, Zero};
// Index to the least significant byte of a timestamp
// value on the timeline
#[derive(Debug, Copy, Clone)]
pub struct LsbIdxOfTmstmpValOnTmln(pub(super) u32);
#[derive(Debug)]
pub(super) enum SigType {
Integer,
Parameter,
Real,
Reg,
Str,
Wire,
Tri1,
Time,
}
#[derive(Debug)]
pub(super) enum TimelineQueryResults {
BigUint(BigUint),
String(String),
}
#[derive(Debug)]
pub(super) enum Signal {
Data {
name: String,
sig_type: SigType,
// 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
// errors in the following type:
signal_error: Option<String>,
num_bits: Option<u16>,
// TODO : may be able to remove self_idx
self_idx: SignalIdx,
// A signal may take on a new value and hold that value
// for sometime. We only need to record the value of a signal
// when it changes(the is what VCDs tend to do).
// A signal may need x amount of bytes to record its largest possible
// value, so we record every single value of a given signal as a sequence
// of x number of u8s.
// For example, we might find that `my_signal.nums_encoded_as_fixed_width_le_u8`
// has two 32 bit values, namely, 1 and 2, encoded as follows:
// my_signal.nums_encoded_as_fixed_width_le_u8 = vec![1u8, 0u8, 0u8, 0u8, 2u8, 0u8, 0u8, 0u8];
nums_encoded_as_fixed_width_le_u8: Vec<u8>,
string_vals: Vec<String>,
// we could do Vec<(LsbIdxOfTmstmpValOnTmln, u8)>, but I suspect that
// Vec<LsbIdxOfTmstmpValOnTmln> is more cache friendly.
// We use ``LsbIdxOfTmstmpValOnTmln`` to index into the LSB of a particular
// timestamp encoded as the minimu length u8 sequence within
// ``vcd.tmstmps_encoded_as_u8s``, and we use the values in
// ``byte_len_of_num_tmstmp_vals_on_tmln`` to determine how many u8 values
// a particular timestamp is composed of.
lsb_indxs_of_num_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
byte_len_of_num_tmstmp_vals_on_tmln: Vec<u8>,
lsb_indxs_of_string_tmstmp_vals_on_tmln: Vec<LsbIdxOfTmstmpValOnTmln>,
byte_len_of_string_tmstmp_vals_on_tmln: Vec<u8>,
scope_parent: ScopeIdx,
},
Alias {
name: String,
signal_alias: SignalIdx,
},
}
impl Signal {
pub(super) fn try_dereference_alias<'a>(
&'a self,
signals: &'a Vec<Signal>,
) -> Result<&Signal, String> {
// dereference a signal if we need to and return a signal, else return
// the signal itself
let signal = match self {
Signal::Data { .. } => self,
Signal::Alias { name, signal_alias } => {
let SignalIdx(idx) = signal_alias;
&signals[*idx]
}
};
match signal {
Signal::Data { .. } => Ok(signal),
Signal::Alias { .. } => Err(format!(
"Error near {}:{}. A signal alias shouldn't \
point to a signal alias.",
file!(),
line!()
)),
}
}
// pub(super) fn try_dereference_alias_mut<'a>(
// &'a self,
// signals: &'a mut Vec<Signal>,
// ) -> Result<&mut Signal, String> {
// // dereference a signal if we need to and return a signal, else return
// // the signal itself
// let signal = match self {
// Signal::Data {
// name,
// sig_type,
// signal_error,
// num_bits,
// self_idx,
// ..
// } => {
// let SignalIdx(idx) = self_idx;
// signals.get(*idx).unwrap()
// }
// Signal::Alias { name, signal_alias } => {
// let SignalIdx(idx) = signal_alias;
// 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!()
// )),
// }
// }
pub(super) fn bytes_required(&self) -> Result<u8, String> {
match self {
Signal::Data {
name,
sig_type,
signal_error,
num_bits,
..
} => {
let num_bits = num_bits.ok_or_else(|| {
format!("Error near {}:{}. num_bits empty.", file!(), line!())
})?;
let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
let bytes_required = u8::try_from(bytes_required).map_err(|_| {
format!(
"Error near {}:{}. Signal {name} of length num_bits requires \
{bytes_required} > 256 bytes.",
file!(),
line!()
)
})?;
Ok(bytes_required)
}
Signal::Alias { name, signal_alias } => {
let msg = format!(
"Error near {}:{}. Bytes required should not be called on the signal alias {name}",
file!(),
line!()
);
Err(msg)
}
}
// let bytes_required = (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 };
}
// fn u8_tmstmp_to_biguint(&self, idx: usize) -> BigUint {
// // let lsb_idx = self.
// match self {
// Signal::Data {
// name,
// sig_type,
// signal_error,
// num_bits,
// self_idx,
// nums_encoded_as_fixed_width_le_u8,
// string_vals,
// lsb_indxs_of_num_tmstmp_vals_on_tmln,
// byte_len_of_num_tmstmp_vals_on_tmln,
// lsb_indxs_of_string_tmstmp_vals_on_tmln,
// byte_len_of_string_tmstmp_vals_on_tmln,
// scope_parent,
// } => {}
// }
// BigUint::zero()
// }
pub(super) fn query_value(&self, time: BigUint) -> Result<TimelineQueryResults, String> {
// match
// assert
// ordered_binary_lookup_u8(
// &value_sequence_as_bytes_u8,
// 4,
// &timeline_cursors,
// TimelineIdx(scrubbing_cursor),
// );
Ok(TimelineQueryResults::String("".to_string()))
}
}

View file

@ -1,6 +1,5 @@
use super::utilities::{ordered_binary_lookup_u8, LookupErrors}; use super::Signal;
use chrono::prelude::*; use chrono::prelude::*;
use num::{BigUint, Zero};
#[derive(Debug)] #[derive(Debug)]
pub(super) struct Version(pub String); pub(super) struct Version(pub String);
@ -23,77 +22,13 @@ pub(super) struct Metadata {
pub(super) timescale: (Option<u32>, Timescale), pub(super) timescale: (Option<u32>, Timescale),
} }
// We do a lot of arena allocation in this codebase.
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub(super) struct ScopeIdx(pub(super) usize); pub(super) struct ScopeIdx(pub(super) usize);
#[derive(Debug, Copy, Clone, PartialEq)] #[derive(Debug, Copy, Clone, PartialEq)]
pub(super) struct SignalIdx(pub(super) usize); pub(super) struct SignalIdx(pub(super) usize);
#[derive(Debug, Copy, Clone)]
pub(super) struct TimelineIdx(pub(super) u32);
#[derive(Debug, Copy, Clone)]
pub struct StartIdx(pub(super) u32);
#[derive(Debug)]
pub(super) enum SigType {
Integer,
Parameter,
Real,
Reg,
Str,
Wire,
Tri1,
Time,
}
#[derive(Debug)]
pub(super) enum Signal {
Data {
name: String,
sig_type: SigType,
// 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
// errors in the following type.
signal_error: Option<String>,
num_bits: Option<usize>,
// TODO : may be able to remove self_idx
self_idx: SignalIdx,
// we could encounter a mix of pure values and strings
// for the same signal timeline
u8_timeline: Vec<u8>,
u8_timeline_markers: Vec<TimelineIdx>,
string_timeline: Vec<String>,
string_timeline_markers: Vec<TimelineIdx>,
scope_parent: ScopeIdx,
},
Alias {
name: String,
signal_alias: SignalIdx,
},
}
#[derive(Debug)]
pub(super) enum TimelineQueryResults {
BigUint(BigUint),
String(String),
}
impl Scope {
pub(super) fn query_value(&self, time: TimelineIdx) -> Result<TimelineQueryResults, String> {
// match
// assert
// ordered_binary_lookup_u8(
// &value_sequence_as_bytes_u8,
// 4,
// &timeline_cursors,
// TimelineIdx(scrubbing_cursor),
// );
Ok(TimelineQueryResults::String("".to_string()))
}
}
#[derive(Debug)] #[derive(Debug)]
pub(super) struct Scope { pub(super) struct Scope {
pub(super) name: String, pub(super) name: String,
@ -105,82 +40,63 @@ pub(super) struct Scope {
pub(super) child_scopes: Vec<ScopeIdx>, pub(super) child_scopes: Vec<ScopeIdx>,
} }
// TODO: document how timeline is represented
#[derive(Debug)] #[derive(Debug)]
pub struct VCD { pub struct VCD {
pub(super) metadata: Metadata, pub(super) metadata: Metadata,
// since we only need to store values when there is an actual change // Since we only need to store values when there is an actual change
// in the timeline, we keep a vector that stores the time at which an // in the timeline, we keep a vector that stores the time at which an
// event occurs. Time t is always stored as the minimum length sequence // event occurs. Time t is always stored/encoded as the minimum length sequence
// of u8. // of u8.
pub timeline: Vec<u8>, // We essentially fill ``tmstmps_encoded_as_u8s`` with BigUints converted
// we need to keep track of where a given time t sequence of u8 begins // to sequences of little endian u8s.
// and ends in the timeline vector. // It is up to the signals to keep track of the start/stop indices in the
pub timeline_markers: Vec<StartIdx>, // 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
// track of the timestamps at which the given signal value changes.
pub tmstmps_encoded_as_u8s: Vec<u8>,
pub(super) all_signals: Vec<Signal>, pub(super) all_signals: Vec<Signal>,
pub(super) all_scopes: Vec<Scope>, pub(super) all_scopes: Vec<Scope>,
pub(super) scope_roots: Vec<ScopeIdx>, pub(super) root_scopes: Vec<ScopeIdx>,
} }
impl VCD { impl VCD {
// TODO : make this a generic traversal function that applies specified /// We take in a Signal and attempt to dereference that signal if it is of
// functions upon encountering scopes and signals /// variant Signal::Alias. If it is of variant Signal::Alias and points to
fn print_scope_tree(&self, root_scope_idx: ScopeIdx, depth: usize) { /// another alias, that's an error. Otherwise, we return the Signal::Data
let all_scopes = &self.all_scopes; /// pointed to by the Signal::Alias.
let all_signals = &self.all_signals; /// If the Signal is of varint Signal::Data, then that can be returned directly.
pub(super) fn try_dereference_alias_mut<'a>(
&'a mut self,
idx: &SignalIdx,
) -> Result<&'a mut Signal, String> {
// get the signal pointed to be SignalIdx from the arena
let SignalIdx(idx) = idx;
let signal = &self.all_signals[*idx];
let indent = " ".repeat(depth * 4); // dereference signal if Signal::Alias, or keep idx if Signal::Data
let ScopeIdx(root_scope_idx) = root_scope_idx; let signal_idx = match signal {
let root_scope = &all_scopes[root_scope_idx]; Signal::Data {
let root_scope_name = &root_scope.name; name,
sig_type,
signal_error,
num_bits,
self_idx,
..
} => *self_idx,
Signal::Alias { name, signal_alias } => *signal_alias,
};
println!("{indent}scope: {root_scope_name}"); // Should now point to Signal::Data variant, or else there's an error
let SignalIdx(idx) = signal_idx;
for SignalIdx(ref signal_idx) in &root_scope.child_signals { let signal = self.all_signals.get_mut(idx).unwrap();
let child_signal = &all_signals[*signal_idx]; match signal {
let name = match child_signal { Signal::Data { .. } => Ok(signal),
Signal::Data { name, .. } => name, Signal::Alias { .. } => Err(format!(
Signal::Alias { name, .. } => name, "Error near {}:{}. A signal alias shouldn't \
}; point to a signal alias.",
println!("{indent} - sig: {name}") file!(),
line!()
)),
} }
println!();
for scope_idx in &root_scope.child_scopes {
self.print_scope_tree(*scope_idx, depth + 1);
}
}
pub fn print_scopes(&self) {
for scope_root in &self.scope_roots {
self.print_scope_tree(*scope_root, 0);
}
}
pub fn print_longest_signal(&self) {
let mut idx = 0usize;
let mut max_len = 0usize;
let mut signal_name = String::new();
for signal in &self.all_signals {
match signal {
Signal::Alias { .. } => {}
Signal::Data {
name,
self_idx,
u8_timeline,
..
} => {
if u8_timeline.len() > max_len {
max_len = u8_timeline.len();
let SignalIdx(idx_usize) = self_idx;
idx = *idx_usize;
signal_name = name.clone();
}
}
}
}
dbg!((idx, max_len, signal_name));
} }
} }

View file

@ -80,100 +80,107 @@ use num::{BigUint, Zero};
#[derive(Debug)] #[derive(Debug)]
pub(super) enum LookupErrors { pub(super) enum LookupErrors {
PreTimeline { PreTimeline {
desired_time: TimelineIdx, desired_time: BigUint,
timeline_start_time: TimelineIdx, timeline_start_time: BigUint,
}, },
EmptyTimeline, EmptyTimeline,
TimelineNotMultiple, TimelineNotMultiple,
OrderingFailure, OrderingFailure,
} }
pub(super) fn ordered_binary_lookup_u8( // pub(super) fn ordered_binary_lookup_u8(
value_sequence_as_bytes: &Vec<u8>, // //(REMOVE THIS COMMENT)below is from self
bytes_per_value: usize, // lsb_indxs_of_num_tmstmp_vals_on_tmln: &Vec<LsbIdxOfTmstmpValOnTmln>,
timeline_cursors: &Vec<TimelineIdx>, // tmstmps_encoded_as_u8s: &Vec<u8>,
desired_time: TimelineIdx, // //(REMOVE THIS COMMENT)below is from self
) -> Result<BigUint, LookupErrors> { // nums_encoded_as_fixed_width_le_u8: &Vec<u8>,
// timeline must not be empty // // TODO : should this be usize?
if timeline_cursors.is_empty() { // bytes_per_value: usize,
return Err(LookupErrors::EmptyTimeline); // desired_time: BigUint,
} // ) -> Result<BigUint, LookupErrors> {
// // 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(LookupErrors::EmptyTimeline);
// }
// assertion that value_sequence is a proper multiple of // // assertion that value_sequence is a proper multiple of
// timeline_markers // // timeline_markers
if value_sequence_as_bytes.len() != (timeline_cursors.len() * bytes_per_value) { // if lsb_indxs_of_num_tmstmp_vals_on_tmln.len()
return Err(LookupErrors::TimelineNotMultiple); // != (nums_encoded_as_fixed_width_le_u8.len() * bytes_per_value)
} // {
// return Err(LookupErrors::TimelineNotMultiple);
// }
let TimelineIdx(desired_time) = desired_time; // // let TimelineIdx(desired_time) = desired_time;
// check if we're requesting a value that occurs before the recorded // // check if we're requesting a value that occurs before the recorded
// start of the timeline // // start of the timeline
let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap(); // let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap();
if desired_time < *timeline_start_time { // if desired_time < *timeline_start_time {
return Err(LookupErrors::PreTimeline { // return Err(LookupErrors::PreTimeline {
desired_time: TimelineIdx(desired_time), // desired_time: TimelineIdx(desired_time),
timeline_start_time: TimelineIdx(*timeline_start_time), // timeline_start_time: TimelineIdx(*timeline_start_time),
}); // });
} // }
let mut lower_idx = 0usize; // let mut lower_idx = 0usize;
let mut upper_idx = timeline_cursors.len() - 1; // let mut upper_idx = timeline_cursors.len() - 1;
// 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
let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap(); // let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap();
if desired_time > *timeline_end_time { // if desired_time > *timeline_end_time {
let range = (value_sequence_as_bytes.len() - bytes_per_value)..; // let range = (value_sequence_as_bytes.len() - bytes_per_value)..;
let value_by_bytes = &value_sequence_as_bytes[range]; // let value_by_bytes = &value_sequence_as_bytes[range];
let value = BigUint::from_bytes_le(value_by_bytes); // let value = BigUint::from_bytes_le(value_by_bytes);
return Ok(value); // return Ok(value);
} // }
// 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),
// where n is the number of events on the timeline. // // where n is the number of events on the timeline.
// We can assume that by the time we get here, that the desired_time // // 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 // // 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. // // occuring after or before the recorded tiimeline in the code above.
while lower_idx <= upper_idx { // while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2); // let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);
let TimelineIdx(curr_time) = timeline_cursors[mid_idx]; // let TimelineIdx(curr_time) = timeline_cursors[mid_idx];
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 => {
lower_idx = mid_idx + 1; // lower_idx = mid_idx + 1;
} // }
std::cmp::Ordering::Equal => { // std::cmp::Ordering::Equal => {
let u8_timeline_start_idx = mid_idx * bytes_per_value; // let u8_timeline_start_idx = mid_idx * bytes_per_value;
let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value; // let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value;
let range = u8_timeline_start_idx..u8_timeline_end_idx; // let range = u8_timeline_start_idx..u8_timeline_end_idx;
let value_by_bytes = &value_sequence_as_bytes[range]; // let value_by_bytes = &value_sequence_as_bytes[range];
let value = BigUint::from_bytes_le(value_by_bytes); // let value = BigUint::from_bytes_le(value_by_bytes);
return Ok(value); // return Ok(value);
} // }
std::cmp::Ordering::Greater => { // std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1; // upper_idx = mid_idx - 1;
} // }
} // }
} // }
let idx = lower_idx - 1; // let idx = lower_idx - 1;
let TimelineIdx(left_time) = timeline_cursors[idx]; // let TimelineIdx(left_time) = timeline_cursors[idx];
let TimelineIdx(right_time) = timeline_cursors[idx + 1]; // let TimelineIdx(right_time) = timeline_cursors[idx + 1];
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(LookupErrors::OrderingFailure); // return Err(LookupErrors::OrderingFailure);
} // }
let u8_timeline_start_idx = idx * bytes_per_value; // let u8_timeline_start_idx = idx * bytes_per_value;
let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value; // let u8_timeline_end_idx = u8_timeline_start_idx + bytes_per_value;
let range = u8_timeline_start_idx..u8_timeline_end_idx; // let range = u8_timeline_start_idx..u8_timeline_end_idx;
let value_by_bytes = &value_sequence_as_bytes[range]; // let value_by_bytes = &value_sequence_as_bytes[range];
let value = BigUint::from_bytes_le(value_by_bytes); // let value = BigUint::from_bytes_le(value_by_bytes);
return Ok(value); // return Ok(value);
} // }