Skip to content

Commit

Permalink
support several top-scope variables
Browse files Browse the repository at this point in the history
  • Loading branch information
ekiwi committed Nov 9, 2023
1 parent 578bb9e commit d27bb66
Show file tree
Hide file tree
Showing 4 changed files with 65 additions and 38 deletions.
92 changes: 56 additions & 36 deletions src/hierarchy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,7 @@ pub enum ScopeType {
#[derive(Debug, Clone, Copy)]
pub enum VarType {
Wire,
Reg,
String,
Todo, // placeholder tpe
}
Expand Down Expand Up @@ -179,7 +180,7 @@ pub struct Var {
direction: VarDirection,
length: SignalLength,
signal_idx: SignalRef,
parent: ScopeRef,
parent: Option<ScopeRef>,
next: Option<HierarchyItemId>,
}

Expand All @@ -194,10 +195,15 @@ impl Var {

/// Full hierarchical name of the variable.
pub fn full_name<'a>(&self, hierarchy: &Hierarchy) -> String {
let mut out = hierarchy.get(self.parent).full_name(hierarchy);
out.push(SCOPE_SEPARATOR);
out.push_str(self.name(hierarchy));
out
match self.parent {
None => self.name(hierarchy).to_string(),
Some(parent) => {
let mut out = hierarchy.get(parent).full_name(hierarchy);
out.push(SCOPE_SEPARATOR);
out.push_str(self.name(hierarchy));
out
}
}
}

pub fn var_type(&self) -> VarType {
Expand Down Expand Up @@ -366,6 +372,7 @@ impl<'a> Iterator for HierarchyVarIterator<'a> {
pub struct Hierarchy {
vars: Vec<Var>,
scopes: Vec<Scope>,
first_item: Option<HierarchyItemId>,
strings: Vec<String>,
signal_idx_to_var: Vec<Option<VarRef>>,
meta: HierarchyMetaData,
Expand Down Expand Up @@ -403,7 +410,7 @@ impl Hierarchy {

/// Returns an iterator over all top-level scopes and variables.
pub fn items(&self) -> HierarchyItemIterator {
let start = self.scopes.first().map(|s| HierarchyItem::Scope(s));
let start = self.first_item.map(|id| self.get_item(id));
HierarchyItemIterator::new(&self, start)
}

Expand Down Expand Up @@ -516,6 +523,7 @@ fn interner_to_vec(interner: StringInterner) -> Vec<String> {
pub struct HierarchyBuilder {
vars: Vec<Var>,
scopes: Vec<Scope>,
first_item: Option<HierarchyItemId>,
scope_stack: Vec<ScopeStackEntry>,
strings: StringInterner,
handle_to_node: Vec<Option<VarRef>>,
Expand All @@ -527,10 +535,16 @@ pub struct HierarchyBuilder {

impl Default for HierarchyBuilder {
fn default() -> Self {
// we start with a fake entry in the scope stack to keep track of multiple items in the top scope
let scope_stack = vec![ScopeStackEntry {
scope_id: usize::MAX,
last_child: None,
}];
HierarchyBuilder {
vars: Vec::default(),
scopes: Vec::default(),
scope_stack: Vec::default(),
first_item: None,
scope_stack,
strings: StringInterner::default(),
handle_to_node: Vec::default(),
meta: HierarchyMetaData::default(),
Expand All @@ -545,6 +559,7 @@ impl HierarchyBuilder {
Hierarchy {
vars: self.vars,
scopes: self.scopes,
first_item: self.first_item,
strings: interner_to_vec(self.strings),
signal_idx_to_var: self.handle_to_node,
meta: self.meta,
Expand Down Expand Up @@ -573,38 +588,44 @@ impl HierarchyBuilder {

/// adds a variable or scope to the hierarchy tree
fn add_to_hierarchy_tree(&mut self, node_id: HierarchyItemId) -> Option<ScopeRef> {
match self.scope_stack.last_mut() {
Some(entry) => {
let parent = entry.scope_id;
match entry.last_child {
Some(HierarchyItemId::Var(child)) => {
// add pointer to new node from last child
assert!(self.vars[child.index()].next.is_none());
self.vars[child.index()].next = Some(node_id);
}
Some(HierarchyItemId::Scope(child)) => {
// add pointer to new node from last child
assert!(self.scopes[child.index()].next.is_none());
self.scopes[child.index()].next = Some(node_id);
}
None => {
// otherwise we need to add a pointer from the parent
assert!(self.scopes[parent].child.is_none());
self.scopes[parent].child = Some(node_id);
}
let entry = self.scope_stack.last_mut().unwrap();
let parent = entry.scope_id;
let fake_top_scope_parent = parent == usize::MAX;
match entry.last_child {
Some(HierarchyItemId::Var(child)) => {
// add pointer to new node from last child
assert!(self.vars[child.index()].next.is_none());
self.vars[child.index()].next = Some(node_id);
}
Some(HierarchyItemId::Scope(child)) => {
// add pointer to new node from last child
assert!(self.scopes[child.index()].next.is_none());
self.scopes[child.index()].next = Some(node_id);
}
None => {
if !fake_top_scope_parent {
// otherwise we need to add a pointer from the parent
assert!(self.scopes[parent].child.is_none());
self.scopes[parent].child = Some(node_id);
}
// the new node is now the last child
entry.last_child = Some(node_id);
// return the parent id
Some(ScopeRef::from_index(parent).unwrap())
}
None => None,
}
// the new node is now the last child
entry.last_child = Some(node_id);
// return the parent id if we had a real parent and we aren't at the top scope
if fake_top_scope_parent {
None
} else {
Some(ScopeRef::from_index(parent).unwrap())
}
}

pub fn add_scope(&mut self, name: String, tpe: ScopeType) {
let node_id = self.scopes.len();
let wrapped_id = HierarchyItemId::Scope(ScopeRef::from_index(node_id).unwrap());
if self.first_item.is_none() {
self.first_item = Some(wrapped_id);
}
let parent = self.add_to_hierarchy_tree(wrapped_id);

// new active scope
Expand Down Expand Up @@ -635,11 +656,10 @@ impl HierarchyBuilder {
let node_id = self.vars.len();
let var_id = VarRef::from_index(node_id).unwrap();
let wrapped_id = HierarchyItemId::Var(var_id);
if self.first_item.is_none() {
self.first_item = Some(wrapped_id);
}
let parent = self.add_to_hierarchy_tree(wrapped_id);
assert!(
parent.is_some(),
"Vars cannot be at the top of the hierarchy (not supported for now)"
);

// add lookup
let handle_idx = signal_idx.index();
Expand All @@ -656,7 +676,7 @@ impl HierarchyBuilder {

// now we can build the node data structure and store it
let node = Var {
parent: parent.unwrap(),
parent,
name: self.add_string(name),
tpe,
direction,
Expand Down
2 changes: 1 addition & 1 deletion src/signals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
// author: Kevin Laeufer <[email protected]>

use crate::hierarchy::{Hierarchy, SignalLength, SignalRef};
use crate::vcd::usize_div_ceil;
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};

Expand Down Expand Up @@ -312,6 +311,7 @@ pub(crate) trait SignalSource {
#[cfg(test)]
mod tests {
use super::*;
use crate::vcd::usize_div_ceil;

#[test]
fn test_sizes() {
Expand Down
1 change: 1 addition & 0 deletions src/vcd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,7 @@ fn convert_scope_tpe(tpe: &[u8]) -> ScopeType {
fn convert_var_tpe(tpe: &[u8]) -> VarType {
match tpe {
b"wire" => VarType::Wire,
b"reg" => VarType::Reg,
b"string" => VarType::String,
_ => panic!("TODO: convert {}", String::from_utf8_lossy(tpe)),
}
Expand Down
8 changes: 7 additions & 1 deletion tests/diff_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ fn diff_hierarchy(ours: &Hierarchy, ref_header: &vcd::Header) {
.filter(|i| !matches!(i, vcd::ScopeItem::Comment(_))),
ours.items(),
) {
diff_hierarchy_item(ref_child, our_child, ours)
diff_hierarchy_item(ref_child, our_child, ours);
}
}

Expand Down Expand Up @@ -79,6 +79,7 @@ fn waveform_scope_type_to_string(tpe: ScopeType) -> &'static str {
fn waveform_var_type_to_string(tpe: VarType) -> &'static str {
match tpe {
VarType::Wire => "wire",
VarType::Reg => "reg",
VarType::String => "string",
VarType::Todo => "todo",
}
Expand Down Expand Up @@ -237,6 +238,11 @@ fn diff_amaranth_up_counter() {
);
}

#[test]
fn diff_ghdl_alu() {
run_diff_test("inputs/ghdl/alu.vcd", "inputs/ghdl/alu.vcd.fst");
}

#[test]
fn diff_treadle_gcd() {
run_diff_test("inputs/treadle/GCD.vcd", "inputs/treadle/GCD.vcd.fst");
Expand Down

0 comments on commit d27bb66

Please sign in to comment.