Skip to content

Commit

Permalink
diff: actually compare some value changes
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Nov 9, 2023
1 parent 7303235 commit 190f2c9
Show file tree
Hide file tree
Showing 3 changed files with 114 additions and 12 deletions.
2 changes: 1 addition & 1 deletion src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ pub struct SignalRef(NonZeroU32);

impl SignalRef {
#[inline]
pub(crate) fn from_index(index: usize) -> Option<Self> {
pub fn from_index(index: usize) -> Option<Self> {
match NonZeroU32::new(index as u32 + 1) {
None => None,
Some(value) => Some(Self(value)),
Expand Down
75 changes: 70 additions & 5 deletions src/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};

pub type Time = u64;
pub type TimeTableIdx = u32;

#[derive(Debug, Clone, Copy)]
pub enum SignalValue<'a> {
Expand All @@ -33,7 +34,7 @@ impl<'a> Display for SignalValue<'a> {
}

impl<'a> SignalValue<'a> {
fn to_bit_string(&self) -> Option<String> {
pub fn to_bit_string(&self) -> Option<String> {
match &self {
SignalValue::Binary(data, bits) => Some(two_state_to_bit_string(data, *bits)),
SignalValue::FourValue(data, bits) => Some(four_state_to_bit_string(data, *bits)),
Expand Down Expand Up @@ -115,14 +116,14 @@ pub(crate) enum SignalEncoding {

pub(crate) struct Signal {
idx: SignalRef,
time_indices: Vec<u32>,
time_indices: Vec<TimeTableIdx>,
data: SignalChangeData,
}

impl Signal {
pub(crate) fn new_fixed_len(
idx: SignalRef,
time_indices: Vec<u32>,
time_indices: Vec<TimeTableIdx>,
encoding: SignalEncoding,
width: u32,
bytes: Vec<u8>,
Expand All @@ -142,7 +143,7 @@ impl Signal {

pub(crate) fn new_var_len(
idx: SignalRef,
time_indices: Vec<u32>,
time_indices: Vec<TimeTableIdx>,
strings: Vec<String>,
) -> Self {
assert_eq!(time_indices.len(), strings.len());
Expand All @@ -156,7 +157,7 @@ impl Signal {

pub(crate) fn size_in_memory(&self) -> usize {
let base = std::mem::size_of::<Self>();
let time = self.time_indices.len() * std::mem::size_of::<u32>();
let time = self.time_indices.len() * std::mem::size_of::<TimeTableIdx>();
let data = match &self.data {
SignalChangeData::FixedLength { bytes, .. } => bytes.len(),
SignalChangeData::VariableLength(strings) => strings
Expand Down Expand Up @@ -225,6 +226,41 @@ impl Waveform {
pub fn get_signal_size_in_memory(&self, id: SignalRef) -> Option<usize> {
Some((self.signals.get(&id)?).size_in_memory())
}

pub fn get_signal_value_at(
&self,
signal_ref: SignalRef,
time_table_idx: TimeTableIdx,
) -> SignalValue {
assert!((time_table_idx as usize) < self.time_table.len());
let signal: &Signal = &self.signals[&signal_ref];
let offset = find_offset_from_time_table_idx(&signal.time_indices, time_table_idx);
signal.data.get_value_at(offset)
}
}

/// Finds the index that is the same or less than the needle and returns the position of it.
/// Note that `indices` needs to sorted from smallest to largest.
/// Essentially implements a binary search!
fn find_offset_from_time_table_idx(indices: &[TimeTableIdx], needle: TimeTableIdx) -> usize {
let mut lower_idx = 0usize;
let mut upper_idx = indices.len() - 1;
while lower_idx <= upper_idx {
let mid_idx = lower_idx + ((upper_idx - lower_idx) / 2);

match indices[mid_idx].cmp(&needle) {
std::cmp::Ordering::Less => {
lower_idx = mid_idx + 1;
}
std::cmp::Ordering::Equal => {
return mid_idx;
}
std::cmp::Ordering::Greater => {
upper_idx = mid_idx - 1;
}
}
}
lower_idx - 1
}

enum SignalChangeData {
Expand All @@ -236,6 +272,35 @@ enum SignalChangeData {
VariableLength(Vec<String>),
}

impl SignalChangeData {
fn get_value_at(&self, offset: usize) -> SignalValue {
match &self {
SignalChangeData::FixedLength {
encoding,
width,
bytes,
} => {
let start = offset * (*width as usize);
let data = &bytes[start..(start + (*width as usize))];
match encoding {
SignalEncoding::Binary(bits) => SignalValue::Binary(data, *bits),
SignalEncoding::FourValue(bits) => SignalValue::FourValue(data, *bits),
SignalEncoding::FixedLength => {
SignalValue::String(std::str::from_utf8(data).unwrap())
}
SignalEncoding::Float => {
SignalValue::Float(f64::from_le_bytes(<[u8; 8]>::try_from(data).unwrap()))
}
SignalEncoding::VariableLength => {
panic!("Variable length signals need to be variable length encoded!")
}
}
}
SignalChangeData::VariableLength(strings) => SignalValue::String(&strings[offset]),
}
}
}

pub(crate) trait SignalSource {
/// Loads new signals.
/// Many implementations take advantage of loading multiple signals at a time.
Expand Down
49 changes: 43 additions & 6 deletions tests/diff_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::io::{BufRead, BufReader};
use waveform::{
Hierarchy, HierarchyItem, ScopeType, SignalLength, TimescaleUnit, VarType, Waveform,
Hierarchy, HierarchyItem, ScopeType, SignalLength, SignalRef, TimescaleUnit, VarType, Waveform,
};

fn run_diff_test(vcd_filename: &str, fst_filename: &str) {
Expand Down Expand Up @@ -91,7 +91,6 @@ fn diff_hierarchy_item(ref_item: &vcd::ScopeItem, our_item: HierarchyItem, our_h
ref_scope.scope_type.to_string(),
waveform_scope_type_to_string(our_scope.scope_type())
);
println!("Scope");
for (ref_child, our_child) in itertools::zip_eq(
ref_scope
.items
Expand Down Expand Up @@ -144,17 +143,55 @@ fn diff_signals<R: BufRead>(ref_reader: &mut vcd::Parser<R>, our: &mut Waveform)
time_table_idx += 1;
assert_eq!(current_time, time_table[time_table_idx]);
}
vcd::Command::ChangeScalar(_, _) => {}
vcd::Command::ChangeScalar(id, value) => {
let signal_ref = vcd_lib_id_to_signal_ref(id);
let our_value = our.get_signal_value_at(signal_ref, time_table_idx as u32);
let our_value_str = our_value.to_bit_string().unwrap();
assert_eq!(our_value_str, value.to_string());
}
vcd::Command::ChangeVector(_, _) => {}
vcd::Command::ChangeReal(_, _) => {}
vcd::Command::ChangeString(_, _) => {}
vcd::Command::ChangeReal(_, _) => {
todo!("compare real")
}
vcd::Command::ChangeString(_, _) => {
todo!("compare string")
}
vcd::Command::Begin(_) => {} // ignore
vcd::Command::End(_) => {} // ignore
other => panic!("Unhandled command: {:?}", other),
}
}
}

fn vcd_lib_id_to_signal_ref(id: vcd::IdCode) -> SignalRef {
let num = id_to_int(id.to_string().as_bytes()).unwrap();
SignalRef::from_index(num as usize).unwrap()
}

const ID_CHAR_MIN: u8 = b'!';
const ID_CHAR_MAX: u8 = b'~';
const NUM_ID_CHARS: u64 = (ID_CHAR_MAX - ID_CHAR_MIN + 1) as u64;

println!("TODO")
/// Copied from https://github.com/kevinmehall/rust-vcd, licensed under MIT
fn id_to_int(id: &[u8]) -> Option<u64> {
if id.is_empty() {
return None;
}
let mut result = 0u64;
for &i in id.iter().rev() {
if !(ID_CHAR_MIN..=ID_CHAR_MAX).contains(&i) {
return None;
}
let c = ((i - ID_CHAR_MIN) as u64) + 1;
result = match result
.checked_mul(NUM_ID_CHARS)
.and_then(|x| x.checked_add(c))
{
None => return None,
Some(value) => value,
};
}
Some(result - 1)
}

#[test]
Expand Down

0 comments on commit 190f2c9

Please sign in to comment.