diff --git a/src/vcd.rs b/src/vcd.rs index b4dcc83..5d228f8 100644 --- a/src/vcd.rs +++ b/src/vcd.rs @@ -7,5 +7,8 @@ pub(super) use types::*; mod parse; pub(super) use parse::*; +mod signal; +pub(super) use signal::*; + mod utilities; use utilities::*; diff --git a/src/vcd/parse.rs b/src/vcd/parse.rs index bd6163c..e3771d9 100644 --- a/src/vcd/parse.rs +++ b/src/vcd/parse.rs @@ -31,11 +31,10 @@ pub fn parse_vcd(file: File) -> Result { // after we parse metadata, we form the VCD object let mut vcd = VCD { metadata: header, - timeline: vec![], - timeline_markers: vec![], + tmstmps_encoded_as_u8s: vec![], all_signals: vec![], all_scopes: vec![], - scope_roots: vec![], + root_scopes: vec![], }; parse_scopes(&mut word_gen, &mut vcd, &mut signal_map)?; diff --git a/src/vcd/parse/events.rs b/src/vcd/parse/events.rs index f9a3fd6..efb4408 100644 --- a/src/vcd/parse/events.rs +++ b/src/vcd/parse/events.rs @@ -5,6 +5,8 @@ pub(super) fn parse_events<'a>( vcd: &'a mut VCD, signal_map: &mut HashMap, ) -> Result<(), String> { + let mut curr_tmstmp_lsb_idx = 0u32; + let mut curr_tmstmp_len_u8 = 0u8; loop { let next_word = word_reader.next_word(); @@ -36,18 +38,36 @@ pub(super) fn parse_events<'a>( let mut value = value.to_bytes_le(); // TODO : u32 helps with less memory, but should ideally likely be // configurable. - let (f, l) = (file!(), line!()); - let start_idx = u32::try_from(vcd.timeline.len()).map_err(|_| { - format!("Error near {f}:{l}. Failed to convert from usize to u32.") + curr_tmstmp_len_u8 = u8::try_from(value.len()).map_err(|_| { + format!( + "Error near {}:{}. Failed to convert from usize to u8.", + file!(), + line!() + ) })?; - vcd.timeline_markers.push(StartIdx(start_idx)); - vcd.timeline.append(&mut value); + vcd.tmstmps_encoded_as_u8s.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 "b" => { 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 = Vec::new(); let mut value_string = String::new(); @@ -81,7 +101,7 @@ pub(super) fn parse_events<'a>( let (word, cursor) = next_word!(word_reader)?; // 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!( "Error near {}:{}. Failed to lookup signal {word} at {cursor:?}", file!(), @@ -89,34 +109,29 @@ pub(super) fn parse_events<'a>( ) })?; - // account for fact that signal idx could be an alias, so there - // could be one step of indirection - let signal_idx = { - let signal = vcd.all_signals.get(*signal_idx).unwrap(); - match signal { - Signal::Data { .. } => *signal_idx, - Signal::Alias { signal_alias, .. } => { - let SignalIdx(ref signal_idx) = signal_alias; - signal_idx.clone() - } - } - }; + let signal = vcd.try_dereference_alias_mut(signal_idx)?; + + // we may have to dereference a signal if it's pointing at an alias + // let signal = &vcd.all_signals[*signal_idx]; + // let signal = signal.try_dereference_alias_mut(&vcd.all_signals)?; - // 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 { Signal::Data { name, sig_type, ref mut signal_error, num_bits, - u8_timeline, - u8_timeline_markers, - string_timeline, - string_timeline_markers, - .. + 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, } => { + // we've already identified in a prior loop iteration that the signal has + // an error if signal_error.is_some() { 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 { - string_timeline_markers.push(timeline_idx); - string_timeline.push(value_string); + lsb_indxs_of_string_tmstmp_vals_on_tmln + .push(LsbIdxOfTmstmpValOnTmln(curr_tmstmp_lsb_idx)); + string_vals.push(value_string); Ok(()) } else { - u8_timeline_markers.push(timeline_idx); - - let mut curr_num_bytes = value_u8.len(); - u8_timeline.append(&mut value_u8); + let mut curr_num_bytes = + u8::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:?}.", + 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 // so that we end up storing all values // of a particular signal in a consistent // amount of bytes - let num_bits = num_bits.unwrap(); - let bytes_required = - (num_bits / 8) + if (num_bits % 8) > 0 { 1 } else { 0 }; + let bytes_required = signal.bytes_required()?; while curr_num_bytes < bytes_required { // TODO: remove once library is known to be stable @@ -187,7 +204,7 @@ pub(super) fn parse_events<'a>( // for signal {name}"); // Err(err)?; - u8_timeline.push(0u8); + byte_len_of_num_tmstmp_vals_on_tmln.push(0u8); curr_num_bytes += 1; } 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` - "0" => { - // lookup signal idx - let hash = &word[1..]; - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { - format!( - "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", - file!(), - line!() - ) - })?; + // "0" => { + // // lookup signal idx + // let hash = &word[1..]; + // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + // format!( + // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + // file!(), + // line!() + // ) + // })?; - // account for fact that signal idx could be an alias, so there - // could be one step of indirection - let signal_idx = { - let signal = vcd.all_signals.get(*signal_idx).unwrap(); - match signal { - Signal::Data { .. } => *signal_idx, - Signal::Alias { signal_alias, .. } => { - let SignalIdx(ref signal_idx) = signal_alias; - signal_idx.clone() - } - } - }; + // // account for fact that signal idx could be an alias, so there + // // could be one step of indirection + // let signal_idx = { + // let signal = vcd.all_signals.get(*signal_idx).unwrap(); + // match signal { + // 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 { - Signal::Data { - name, - sig_type, - ref mut signal_error, - num_bits, - u8_timeline, - u8_timeline_markers, - .. - } => { - // if this is a bad signal, go ahead and skip it - if signal_error.is_some() { - continue; - } + // // 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 { + // Signal::Data { + // name, + // sig_type, + // ref mut signal_error, + // num_bits, + // u8_timeline, + // u8_timeline_markers, + // .. + // } => { + // // if this is a bad signal, go ahead and skip it + // if signal_error.is_some() { + // continue; + // } - // Get bitwidth and verify that it is 1. - // Also account for the error case of a bitwidth of `None` - match num_bits { - Some(ref num_bits) => { - if *num_bits != 1 { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - of sig_type {sig_type:?} is expected to be `1` not \ - `{num_bits}`. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - *signal_error = Some(msg); - continue; - } - } - None => { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - must be specified for a signal of type {sig_type:?}. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - Err(msg)?; - } - }; + // // Get bitwidth and verify that it is 1. + // // Also account for the error case of a bitwidth of `None` + // match num_bits { + // Some(ref num_bits) => { + // if *num_bits != 1 { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // of sig_type {sig_type:?} is expected to be `1` not \ + // `{num_bits}`. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // *signal_error = Some(msg); + // continue; + // } + // } + // None => { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // must be specified for a signal of type {sig_type:?}. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // Err(msg)?; + // } + // }; - 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); + // let (f, l) = (file!(), line!()); + // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len()) + // .map_err(|_| { + // format!("Error near {f}:{l}. Failed to convert from usize to u32.") + // })?; + // let timeline_idx = TimelineIdx(timeline_idx); - u8_timeline_markers.push(timeline_idx); - u8_timeline.push(0u8); - 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) - } - }?; - } + // u8_timeline_markers.push(timeline_idx); + // u8_timeline.push(0u8); + // 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) + // } + // }?; + // } - "1" => { - // lokup signal idx - let hash = &word[1..]; - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { - format!( - "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", - file!(), - line!() - ) - })?; + // "1" => { + // // lokup signal idx + // let hash = &word[1..]; + // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + // format!( + // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + // file!(), + // line!() + // ) + // })?; - // account for fact that signal idx could be an alias, so there - // could be one step of indirection - let signal_idx = { - let signal = vcd.all_signals.get(*signal_idx).unwrap(); - match signal { - Signal::Data { .. } => *signal_idx, - Signal::Alias { signal_alias, .. } => { - let SignalIdx(ref signal_idx) = signal_alias; - signal_idx.clone() - } - } - }; + // // account for fact that signal idx could be an alias, so there + // // could be one step of indirection + // let signal_idx = { + // let signal = vcd.all_signals.get(*signal_idx).unwrap(); + // match signal { + // 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 { - Signal::Data { - name, - sig_type, - ref mut signal_error, - num_bits, - u8_timeline, - u8_timeline_markers, - .. - } => { - // if this is a bad signal, go ahead and skip it - if signal_error.is_some() { - continue; - } + // // 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 { + // Signal::Data { + // name, + // sig_type, + // ref mut signal_error, + // num_bits, + // u8_timeline, + // u8_timeline_markers, + // .. + // } => { + // // if this is a bad signal, go ahead and skip it + // if signal_error.is_some() { + // continue; + // } - // Get bitwidth and verify that it is 1. - // Also account for the error case of a bitwidth of `None` - match num_bits { - Some(ref num_bits) => { - if *num_bits != 1 { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - of sig_type {sig_type:?} is expected to be `1` not \ - `{num_bits}`. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - *signal_error = Some(msg); - continue; - } - } - None => { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - must be specified for a signal of type {sig_type:?}. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - Err(msg)?; - } - }; + // // Get bitwidth and verify that it is 1. + // // Also account for the error case of a bitwidth of `None` + // match num_bits { + // Some(ref num_bits) => { + // if *num_bits != 1 { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // of sig_type {sig_type:?} is expected to be `1` not \ + // `{num_bits}`. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // *signal_error = Some(msg); + // continue; + // } + // } + // None => { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // must be specified for a signal of type {sig_type:?}. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // Err(msg)?; + // } + // }; - 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); + // let (f, l) = (file!(), line!()); + // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len()) + // .map_err(|_| { + // format!("Error near {f}:{l}. Failed to convert from usize to u32.") + // })?; + // let timeline_idx = TimelineIdx(timeline_idx); - u8_timeline_markers.push(timeline_idx); - u8_timeline.push(1u8); - 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) - } - }?; - } - // other one bit cases - "x" | "X" | "z" | "Z" | "u" | "U" => { - let val = word.to_string(); - // lokup signal idx - let hash = &word[1..]; - let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { - format!( - "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", - file!(), - line!() - ) - })?; + // u8_timeline_markers.push(timeline_idx); + // u8_timeline.push(1u8); + // 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) + // } + // }?; + // } + // // other one bit cases + // "x" | "X" | "z" | "Z" | "u" | "U" => { + // let val = word.to_string(); + // // lokup signal idx + // let hash = &word[1..]; + // let SignalIdx(ref signal_idx) = signal_map.get(hash).ok_or(()).map_err(|_| { + // format!( + // "Error near {}:{}. Failed to lookup signal {hash} at {cursor:?}", + // file!(), + // line!() + // ) + // })?; - // account for fact that signal idx could be an alias, so there - // could be one step of indirection - let signal_idx = { - let signal = vcd.all_signals.get(*signal_idx).unwrap(); - match signal { - Signal::Data { .. } => *signal_idx, - Signal::Alias { signal_alias, .. } => { - let SignalIdx(ref signal_idx) = signal_alias; - signal_idx.clone() - } - } - }; + // // account for fact that signal idx could be an alias, so there + // // could be one step of indirection + // let signal_idx = { + // let signal = vcd.all_signals.get(*signal_idx).unwrap(); + // match signal { + // 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 { - Signal::Data { - name, - sig_type, - ref mut signal_error, - num_bits, - string_timeline, - string_timeline_markers, - .. - } => { - // if this is a bad signal, go ahead and skip it - if signal_error.is_some() { - continue; - } + // // 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 { + // Signal::Data { + // name, + // sig_type, + // ref mut signal_error, + // num_bits, + // string_timeline, + // string_timeline_markers, + // .. + // } => { + // // if this is a bad signal, go ahead and skip it + // if signal_error.is_some() { + // continue; + // } - // Get bitwidth and verify that it is 1. - // Also account for the error case of a bitwidth of `None` - match num_bits { - Some(ref num_bits) => { - if *num_bits != 1 { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - of sig_type {sig_type:?} is expected to be `1` not \ - `{num_bits}`. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - *signal_error = Some(msg); - continue; - } - } - None => { - let (f, l) = (file!(), line!()); - let msg = format!( - "\ - Error near {f}:{l}. The bitwidth for signal {name} \ - must be specified for a signal of type {sig_type:?}. \ - This error occurred while parsing the vcd file at \ - {cursor:?}" - ); - Err(msg)?; - } - }; + // // Get bitwidth and verify that it is 1. + // // Also account for the error case of a bitwidth of `None` + // match num_bits { + // Some(ref num_bits) => { + // if *num_bits != 1 { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // of sig_type {sig_type:?} is expected to be `1` not \ + // `{num_bits}`. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // *signal_error = Some(msg); + // continue; + // } + // } + // None => { + // let (f, l) = (file!(), line!()); + // let msg = format!( + // "\ + // Error near {f}:{l}. The bitwidth for signal {name} \ + // must be specified for a signal of type {sig_type:?}. \ + // This error occurred while parsing the vcd file at \ + // {cursor:?}" + // ); + // Err(msg)?; + // } + // }; - 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); - - 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) - } - }?; - } + // let (f, l) = (file!(), line!()); + // let timeline_idx = u32::try_from(vcd.tmstmps_encoded_as_u8s.len()) + // .map_err(|_| { + // format!("Error near {f}:{l}. Failed to convert from usize to u32.") + // })?; + // 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) + // } + // }?; + // } _ => {} } } diff --git a/src/vcd/parse/scopes.rs b/src/vcd/parse/scopes.rs index 09e1f05..6dfe96a 100644 --- a/src/vcd/parse/scopes.rs +++ b/src/vcd/parse/scopes.rs @@ -100,12 +100,14 @@ pub(super) fn parse_var<'a>( name: full_signal_name, sig_type: var_type, signal_error: None, - num_bits: no_bits, + num_bits: None, self_idx: signal_idx, - u8_timeline: vec![], - u8_timeline_markers: vec![], - string_timeline: vec![], - string_timeline_markers: vec![], + nums_encoded_as_fixed_width_le_u8: vec![], + string_vals: vec![], + lsb_indxs_of_num_tmstmp_vals_on_tmln: 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, }; (signal, signal_idx) @@ -155,7 +157,7 @@ fn parse_orphaned_vars<'a>( child_signals: 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 @@ -224,7 +226,7 @@ fn parse_scopes_inner<'a>( let parent_scope = vcd.all_scopes.get_mut(parent_scope_idx).unwrap(); 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 diff --git a/src/vcd/reader.rs b/src/vcd/reader.rs index 196b19b..bd14094 100644 --- a/src/vcd/reader.rs +++ b/src/vcd/reader.rs @@ -115,7 +115,8 @@ macro_rules! curr_word { ($word_reader:ident) => { $word_reader.curr_word().ok_or(()).map_err(|_| { 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!(), line!() ) diff --git a/src/vcd/signal.rs b/src/vcd/signal.rs new file mode 100644 index 0000000..832ba51 --- /dev/null +++ b/src/vcd/signal.rs @@ -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, + num_bits: Option, + // 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, + string_vals: Vec, + // we could do Vec<(LsbIdxOfTmstmpValOnTmln, u8)>, but I suspect that + // Vec 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, + 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: ScopeIdx, + }, + Alias { + name: String, + signal_alias: SignalIdx, + }, +} + +impl Signal { + pub(super) fn try_dereference_alias<'a>( + &'a self, + signals: &'a Vec, + ) -> 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, + // ) -> 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 { + 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 { + // match + // assert + // ordered_binary_lookup_u8( + // &value_sequence_as_bytes_u8, + // 4, + // &timeline_cursors, + // TimelineIdx(scrubbing_cursor), + // ); + Ok(TimelineQueryResults::String("".to_string())) + } +} diff --git a/src/vcd/types.rs b/src/vcd/types.rs index b331da3..04ab293 100644 --- a/src/vcd/types.rs +++ b/src/vcd/types.rs @@ -1,6 +1,5 @@ -use super::utilities::{ordered_binary_lookup_u8, LookupErrors}; +use super::Signal; use chrono::prelude::*; -use num::{BigUint, Zero}; #[derive(Debug)] pub(super) struct Version(pub String); @@ -23,77 +22,13 @@ pub(super) struct Metadata { pub(super) timescale: (Option, Timescale), } +// We do a lot of arena allocation in this codebase. #[derive(Debug, Copy, Clone)] pub(super) struct ScopeIdx(pub(super) usize); #[derive(Debug, Copy, Clone, PartialEq)] 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, - num_bits: Option, - // 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_timeline_markers: Vec, - string_timeline: Vec, - string_timeline_markers: Vec, - 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 { - // match - // assert - // ordered_binary_lookup_u8( - // &value_sequence_as_bytes_u8, - // 4, - // &timeline_cursors, - // TimelineIdx(scrubbing_cursor), - // ); - Ok(TimelineQueryResults::String("".to_string())) - } -} - #[derive(Debug)] pub(super) struct Scope { pub(super) name: String, @@ -105,82 +40,63 @@ pub(super) struct Scope { pub(super) child_scopes: Vec, } -// TODO: document how timeline is represented #[derive(Debug)] pub struct VCD { 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 - // 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. - pub timeline: Vec, - // we need to keep track of where a given time t sequence of u8 begins - // and ends in the timeline vector. - pub timeline_markers: Vec, + // We essentially fill ``tmstmps_encoded_as_u8s`` with BigUints converted + // to sequences of little endian u8s. + // It is up to the signals to keep track of the start/stop indices in the + // 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, pub(super) all_signals: Vec, pub(super) all_scopes: Vec, - pub(super) scope_roots: Vec, + pub(super) root_scopes: Vec, } impl VCD { - // TODO : make this a generic traversal function that applies specified - // functions upon encountering scopes and signals - fn print_scope_tree(&self, root_scope_idx: ScopeIdx, depth: usize) { - let all_scopes = &self.all_scopes; - let all_signals = &self.all_signals; + /// We take in a Signal and attempt to dereference 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>( + &'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); - let ScopeIdx(root_scope_idx) = root_scope_idx; - let root_scope = &all_scopes[root_scope_idx]; - let root_scope_name = &root_scope.name; + // dereference signal if Signal::Alias, or keep idx if Signal::Data + let signal_idx = match signal { + Signal::Data { + name, + sig_type, + signal_error, + num_bits, + self_idx, + .. + } => *self_idx, + Signal::Alias { name, signal_alias } => *signal_alias, + }; - println!("{indent}scope: {root_scope_name}"); - - for SignalIdx(ref signal_idx) in &root_scope.child_signals { - let child_signal = &all_signals[*signal_idx]; - let name = match child_signal { - Signal::Data { name, .. } => name, - Signal::Alias { name, .. } => name, - }; - println!("{indent} - sig: {name}") + // Should now point to Signal::Data variant, or else there's an error + let SignalIdx(idx) = signal_idx; + let signal = self.all_signals.get_mut(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!() + )), } - 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)); } } diff --git a/src/vcd/utilities.rs b/src/vcd/utilities.rs index 2c11b42..a8e20aa 100644 --- a/src/vcd/utilities.rs +++ b/src/vcd/utilities.rs @@ -80,100 +80,107 @@ use num::{BigUint, Zero}; #[derive(Debug)] pub(super) enum LookupErrors { PreTimeline { - desired_time: TimelineIdx, - timeline_start_time: TimelineIdx, + desired_time: BigUint, + timeline_start_time: BigUint, }, EmptyTimeline, TimelineNotMultiple, OrderingFailure, } -pub(super) fn ordered_binary_lookup_u8( - value_sequence_as_bytes: &Vec, - bytes_per_value: usize, - timeline_cursors: &Vec, - desired_time: TimelineIdx, -) -> Result { - // timeline must not be empty - if timeline_cursors.is_empty() { - return Err(LookupErrors::EmptyTimeline); - } +// pub(super) fn ordered_binary_lookup_u8( +// //(REMOVE THIS COMMENT)below is from self +// lsb_indxs_of_num_tmstmp_vals_on_tmln: &Vec, +// tmstmps_encoded_as_u8s: &Vec, +// //(REMOVE THIS COMMENT)below is from self +// nums_encoded_as_fixed_width_le_u8: &Vec, +// // TODO : should this be usize? +// bytes_per_value: usize, +// desired_time: BigUint, +// ) -> Result { +// // 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 - // timeline_markers - if value_sequence_as_bytes.len() != (timeline_cursors.len() * bytes_per_value) { - return Err(LookupErrors::TimelineNotMultiple); - } +// // assertion that value_sequence is a proper multiple of +// // timeline_markers +// if lsb_indxs_of_num_tmstmp_vals_on_tmln.len() +// != (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 - // start of the timeline - let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap(); - if desired_time < *timeline_start_time { - return Err(LookupErrors::PreTimeline { - desired_time: TimelineIdx(desired_time), - timeline_start_time: TimelineIdx(*timeline_start_time), - }); - } +// // check if we're requesting a value that occurs before the recorded +// // start of the timeline +// let TimelineIdx(timeline_start_time) = timeline_cursors.first().unwrap(); +// if desired_time < *timeline_start_time { +// return Err(LookupErrors::PreTimeline { +// desired_time: TimelineIdx(desired_time), +// timeline_start_time: TimelineIdx(*timeline_start_time), +// }); +// } - let mut lower_idx = 0usize; - let mut upper_idx = timeline_cursors.len() - 1; +// let mut lower_idx = 0usize; +// let mut upper_idx = timeline_cursors.len() - 1; - // check if we're requesting a value that occurs beyond the end of the timeline, - // if so, return the last value in this timeline - let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap(); - if desired_time > *timeline_end_time { - let range = (value_sequence_as_bytes.len() - bytes_per_value)..; - let value_by_bytes = &value_sequence_as_bytes[range]; - let value = BigUint::from_bytes_le(value_by_bytes); +// // check if we're requesting a value that occurs beyond the end of the timeline, +// // if so, return the last value in this timeline +// let TimelineIdx(timeline_end_time) = timeline_cursors.last().unwrap(); +// if desired_time > *timeline_end_time { +// let range = (value_sequence_as_bytes.len() - bytes_per_value)..; +// let value_by_bytes = &value_sequence_as_bytes[range]; +// 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), - // 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 TimelineIdx(curr_time) = timeline_cursors[mid_idx]; - let ordering = curr_time.cmp(&desired_time); +// // 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 TimelineIdx(curr_time) = timeline_cursors[mid_idx]; +// let ordering = curr_time.cmp(&desired_time); - match ordering { - std::cmp::Ordering::Less => { - lower_idx = mid_idx + 1; - } - std::cmp::Ordering::Equal => { - let u8_timeline_start_idx = mid_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 value_by_bytes = &value_sequence_as_bytes[range]; - let value = BigUint::from_bytes_le(value_by_bytes); - return Ok(value); - } - std::cmp::Ordering::Greater => { - upper_idx = mid_idx - 1; - } - } - } +// match ordering { +// std::cmp::Ordering::Less => { +// lower_idx = mid_idx + 1; +// } +// std::cmp::Ordering::Equal => { +// let u8_timeline_start_idx = mid_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 value_by_bytes = &value_sequence_as_bytes[range]; +// let value = BigUint::from_bytes_le(value_by_bytes); +// return Ok(value); +// } +// std::cmp::Ordering::Greater => { +// upper_idx = mid_idx - 1; +// } +// } +// } - let idx = lower_idx - 1; - let TimelineIdx(left_time) = timeline_cursors[idx]; - let TimelineIdx(right_time) = timeline_cursors[idx + 1]; +// let idx = lower_idx - 1; +// let TimelineIdx(left_time) = timeline_cursors[idx]; +// let TimelineIdx(right_time) = timeline_cursors[idx + 1]; - let ordered_left = left_time < desired_time; - let ordered_right = desired_time < right_time; - if !(ordered_left && ordered_right) { - return Err(LookupErrors::OrderingFailure); - } +// let ordered_left = left_time < desired_time; +// let ordered_right = desired_time < right_time; +// if !(ordered_left && ordered_right) { +// return Err(LookupErrors::OrderingFailure); +// } - let u8_timeline_start_idx = 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 value_by_bytes = &value_sequence_as_bytes[range]; - let value = BigUint::from_bytes_le(value_by_bytes); +// let u8_timeline_start_idx = 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 value_by_bytes = &value_sequence_as_bytes[range]; +// let value = BigUint::from_bytes_le(value_by_bytes); - return Ok(value); -} +// return Ok(value); +// }