From b3fbdf742b88ca1aeb042f3b04609575d359ed78 Mon Sep 17 00:00:00 2001 From: matth2k Date: Wed, 2 Jul 2025 21:31:42 -0400 Subject: [PATCH] Give netref hashing with pointer-like semantics --- src/bin/main.rs | 8 +----- src/graph.rs | 68 ++++++++++++++++++++++++++++++++++--------------- src/netlist.rs | 33 +++++++++++++++++------- 3 files changed, 72 insertions(+), 37 deletions(-) diff --git a/src/bin/main.rs b/src/bin/main.rs index f06ce31..27338b7 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -100,13 +100,7 @@ fn main() { .unwrap(); println!("Logic levels: {}", logic_levels.get_max_depth()); for n in netlist.objects() { - for n in n.nets() { - println!( - "{}: {}", - n.get_identifier(), - logic_levels.get_comb_depth(&n).unwrap() - ); - } + println!("{}: {}", n, logic_levels.get_comb_depth(&n).unwrap()); } // let fo = netlist // .get_analysis::>() diff --git a/src/graph.rs b/src/graph.rs index e85a08f..cdb2086 100644 --- a/src/graph.rs +++ b/src/graph.rs @@ -15,11 +15,12 @@ use std::collections::hash_map::Entry; use std::collections::{HashMap, HashSet}; /// A common trait of analyses than can be performed on a netlist. +/// An analysis becomes stale when the netlist is modified. pub trait Analysis<'a, I: Instantiable> where Self: Sized + 'a, { - /// Construct the analysis + /// Construct the analysis to the current state of the netlist. fn build(netlist: &'a Netlist) -> Result; } @@ -27,8 +28,10 @@ where pub struct FanOutTable<'a, I: Instantiable> { // A reference to the underlying netlist _netlist: &'a Netlist, - // Maps a net to the list of nets it drives - fan_out: HashMap>>, + // Maps a net to the list of nodes it drives + net_fan_out: HashMap>>, + /// Maps a node to the list of nodes it drives + node_fan_out: HashMap, Vec>>, /// Contains nets which are outputs is_an_output: HashSet, } @@ -38,17 +41,25 @@ where I: Instantiable, { /// Returns an iterator to the circuit nodes that use `net`. - pub fn get_users(&self, net: &Net) -> impl Iterator> { - self.fan_out + pub fn get_net_users(&self, net: &Net) -> impl Iterator> { + self.net_fan_out .get(net) .into_iter() .flat_map(|users| users.iter().cloned()) } + /// Returns an iterator to the circuit nodes that use `node`. + pub fn get_node_users(&self, node: &NetRef) -> impl Iterator> { + self.node_fan_out + .get(node) + .into_iter() + .flat_map(|users| users.iter().cloned()) + } + /// Returns `true` if the net has any used by any cells in the circuit /// This does incude nets that are only used as outputs. - pub fn has_uses(&self, net: &Net) -> bool { - (self.fan_out.contains_key(net) && !self.fan_out.get(net).unwrap().is_empty()) + pub fn net_has_uses(&self, net: &Net) -> bool { + (self.net_fan_out.contains_key(net) && !self.net_fan_out.get(net).unwrap().is_empty()) || self.is_an_output.contains(net) } } @@ -58,14 +69,28 @@ where I: Instantiable, { fn build(netlist: &'a Netlist) -> Result { - let mut fan_out: HashMap>> = HashMap::new(); + let mut net_fan_out: HashMap>> = HashMap::new(); + #[allow(clippy::mutable_key_type)] + let mut node_fan_out: HashMap, Vec>> = HashMap::new(); let mut is_an_output: HashSet = HashSet::new(); for c in netlist.connections() { - if let Entry::Vacant(e) = fan_out.entry(c.net()) { + if let Entry::Vacant(e) = net_fan_out.entry(c.net()) { + e.insert(vec![c.target().unwrap()]); + } else { + net_fan_out + .get_mut(&c.net()) + .unwrap() + .push(c.target().unwrap()); + } + + if let Entry::Vacant(e) = node_fan_out.entry(c.src().unwrap()) { e.insert(vec![c.target().unwrap()]); } else { - fan_out.get_mut(&c.net()).unwrap().push(c.target().unwrap()); + node_fan_out + .get_mut(&c.src().unwrap()) + .unwrap() + .push(c.target().unwrap()); } } @@ -76,7 +101,8 @@ where Ok(FanOutTable { _netlist: netlist, - fan_out, + net_fan_out, + node_fan_out, is_an_output, }) } @@ -88,7 +114,7 @@ pub struct SimpleCombDepth<'a, I: Instantiable> { // A reference to the underlying netlist _netlist: &'a Netlist, // Maps a net to its logic level as a DAG - comb_depth: HashMap, + comb_depth: HashMap, usize>, /// The maximum depth of the circuit max_depth: usize, } @@ -97,9 +123,9 @@ impl SimpleCombDepth<'_, I> where I: Instantiable, { - /// Returns the logic level of a net in the circuit. - pub fn get_comb_depth(&self, net: &Net) -> Option { - self.comb_depth.get(net).cloned() + /// Returns the logic level of a node in the circuit. + pub fn get_comb_depth(&self, node: &NetRef) -> Option { + self.comb_depth.get(node).cloned() } /// Returns the maximum logic level of the circuit. @@ -113,7 +139,8 @@ where I: Instantiable, { fn build(netlist: &'a Netlist) -> Result { - let mut comb_depth: HashMap = HashMap::new(); + #[allow(clippy::mutable_key_type)] + let mut comb_depth: HashMap, usize> = HashMap::new(); let mut nodes = Vec::new(); for (driven, _) in netlist.outputs() { @@ -129,18 +156,17 @@ where for node in nodes { if node.is_an_input() { - comb_depth.insert(node.as_net().clone(), 0); + comb_depth.insert(node.clone(), 0); } else { // TODO(matth2k): get_driver_net() relies on a weak reference. Rewrite without it. let max_depth: usize = (0..node.get_num_input_ports()) - .filter_map(|i| netlist.get_driver_net(node.clone(), i)) + .filter_map(|i| netlist.get_driver(node.clone(), i)) .filter_map(|n| comb_depth.get(&n)) .max() .cloned() .unwrap_or(usize::MAX); - for net in node.nets() { - comb_depth.insert(net, max_depth + 1); - } + + comb_depth.insert(node, max_depth + 1); } } diff --git a/src/netlist.rs b/src/netlist.rs index 7326c7a..2a7b8f8 100644 --- a/src/netlist.rs +++ b/src/netlist.rs @@ -376,6 +376,26 @@ where netref: NetRefT, } +impl PartialEq for NetRef +where + I: Instantiable, +{ + fn eq(&self, other: &Self) -> bool { + Rc::ptr_eq(&self.netref, &other.netref) + } +} + +impl Eq for NetRef where I: Instantiable {} + +impl std::hash::Hash for NetRef +where + I: Instantiable, +{ + fn hash(&self, state: &mut H) { + Rc::as_ptr(&self.netref).hash(state); + } +} + impl NetRef where I: Instantiable, @@ -1104,19 +1124,14 @@ where Ok(NetRef::wrap(owned_object)) } - /// Returns the driving net at input position `index` for `netref` + /// Returns the driving node at input position `index` for `netref` /// /// # Panics /// /// Panics if `index` is out of bounds - pub fn get_driver_net(&self, netref: NetRef, index: usize) -> Option { + pub fn get_driver(&self, netref: NetRef, index: usize) -> Option> { let op = netref.unwrap().borrow().operands[index].clone()?; - Some( - self.index_weak(&op.root()) - .borrow() - .get_net(op.secondary()) - .clone(), - ) + Some(NetRef::wrap(self.index_weak(&op.root()).clone())) } /// Set an added object as a top-level output. @@ -1294,7 +1309,7 @@ where let mut is_dead = true; for net in obj.nets() { // This should account for outputs - if fan_out.has_uses(&net) { + if fan_out.net_has_uses(&net) { is_dead = false; break; }