Skip to content

Commit 578bbb1

Browse files
authored
Add comb depth analysis (#5)
* add comb depth analysis * small api change * add some panic docs * fix issue * fix the docs a bit * make ex compile
1 parent 96143fa commit 578bbb1

File tree

3 files changed

+205
-13
lines changed

3 files changed

+205
-13
lines changed

src/bin/main.rs

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ fn simple_example() -> Netlist<Gate> {
5151

5252
fn harder_example() -> Netlist<Gate> {
5353
let netlist = Netlist::new("harder_example".to_string());
54-
let bitwidth = 8;
54+
let bitwidth = 9;
5555

5656
// Add the the inputs
5757
let a_vec = netlist.insert_input_escaped_logic_bus("a".to_string(), bitwidth);
@@ -94,6 +94,20 @@ fn harder_example() -> Netlist<Gate> {
9494
fn main() {
9595
let netlist = harder_example();
9696
print!("{}", netlist);
97+
98+
let logic_levels = netlist
99+
.get_analysis::<circuit::graph::SimpleCombDepth<_>>()
100+
.unwrap();
101+
println!("Logic levels: {}", logic_levels.get_max_depth());
102+
for n in netlist.objects() {
103+
for n in n.nets() {
104+
println!(
105+
"{}: {}",
106+
n.get_identifier(),
107+
logic_levels.get_comb_depth(&n).unwrap()
108+
);
109+
}
110+
}
97111
// let fo = netlist
98112
// .get_analysis::<circuit::graph::FanOutTable<_>>()
99113
// .unwrap();
@@ -106,16 +120,10 @@ fn main() {
106120
// for inst in netlist.objects() {
107121
// println!("{}", inst);
108122
// }
109-
let pg = netlist
110-
.get_analysis::<circuit::graph::MultiDiGraph<_>>()
111-
.unwrap();
112-
let graph = pg.get_graph();
113-
for c in graph.edge_references() {
114-
let w = c.weight();
115-
if let circuit::graph::Edge::Connection(c) = w {
116-
c.src().get_net_mut().set_identifier("lolled".into());
117-
}
118-
}
123+
// let pg = netlist
124+
// .get_analysis::<circuit::graph::MultiDiGraph<_>>()
125+
// .unwrap();
126+
// let graph = pg.get_graph();
119127
// println!("{}", petgraph::dot::Dot::with_config(&graph, &[]));
120128
}
121129

src/graph.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use crate::circuit::{Instantiable, Net};
88
#[cfg(feature = "graph")]
99
use crate::netlist::Connection;
10+
use crate::netlist::iter::DFSIterator;
1011
use crate::netlist::{NetRef, Netlist};
1112
#[cfg(feature = "graph")]
1213
use petgraph::graph::DiGraph;
@@ -81,6 +82,78 @@ where
8182
}
8283
}
8384

85+
/// An simple example to analyze the logic levels of a netlist.
86+
/// This analysis checks for cycles, but it doesn't check for registers.
87+
pub struct SimpleCombDepth<'a, I: Instantiable> {
88+
// A reference to the underlying netlist
89+
_netlist: &'a Netlist<I>,
90+
// Maps a net to its logic level as a DAG
91+
comb_depth: HashMap<Net, usize>,
92+
/// The maximum depth of the circuit
93+
max_depth: usize,
94+
}
95+
96+
impl<I> SimpleCombDepth<'_, I>
97+
where
98+
I: Instantiable,
99+
{
100+
/// Returns the logic level of a net in the circuit.
101+
pub fn get_comb_depth(&self, net: &Net) -> Option<usize> {
102+
self.comb_depth.get(net).cloned()
103+
}
104+
105+
/// Returns the maximum logic level of the circuit.
106+
pub fn get_max_depth(&self) -> usize {
107+
self.max_depth
108+
}
109+
}
110+
111+
impl<'a, I> Analysis<'a, I> for SimpleCombDepth<'a, I>
112+
where
113+
I: Instantiable,
114+
{
115+
fn build(netlist: &'a Netlist<I>) -> Result<Self, String> {
116+
let mut comb_depth: HashMap<Net, usize> = HashMap::new();
117+
118+
let mut nodes = Vec::new();
119+
for (driven, _) in netlist.outputs() {
120+
let mut dfs = DFSIterator::new(netlist, driven.unwrap());
121+
while let Some(n) = dfs.next() {
122+
if dfs.check_cycles() {
123+
return Err("Cycle detected in the netlist".to_string());
124+
}
125+
nodes.push(n);
126+
}
127+
}
128+
nodes.reverse();
129+
130+
for node in nodes {
131+
if node.is_an_input() {
132+
comb_depth.insert(node.as_net().clone(), 0);
133+
} else {
134+
// TODO(matth2k): get_driver_net() relies on a weak reference. Rewrite without it.
135+
let max_depth: usize = (0..node.get_num_input_ports())
136+
.filter_map(|i| netlist.get_driver_net(node.clone(), i))
137+
.filter_map(|n| comb_depth.get(&n))
138+
.max()
139+
.cloned()
140+
.unwrap_or(usize::MAX);
141+
for net in node.nets() {
142+
comb_depth.insert(net, max_depth + 1);
143+
}
144+
}
145+
}
146+
147+
let max_depth = comb_depth.values().max().cloned().unwrap_or(0);
148+
149+
Ok(SimpleCombDepth {
150+
_netlist: netlist,
151+
comb_depth,
152+
max_depth,
153+
})
154+
}
155+
}
156+
84157
/// An enum to provide pseudo-nodes for any misc user-programmable behavior.
85158
#[cfg(feature = "graph")]
86159
#[derive(Debug, Clone)]

src/netlist.rs

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,10 @@ where
315315
}
316316

317317
/// Get driving net using the weak reference
318+
///
319+
/// # Panics
320+
///
321+
/// Panics if the reference to the netlist is lost.
318322
fn get_driver_net(&self, index: usize) -> Option<Net> {
319323
let operand = &self.operands[index];
320324
match operand {
@@ -387,12 +391,18 @@ where
387391
}
388392

389393
/// Returns a borrow to the [Net] at this circuit node.
394+
///
395+
/// # Panics
396+
///
390397
/// Panics if the circuit node has multiple outputs.
391398
pub fn as_net(&self) -> Ref<Net> {
392399
Ref::map(self.netref.borrow(), |f| f.as_net())
393400
}
394401

395402
/// Returns a mutable borrow to the [Net] at this circuit node.
403+
///
404+
/// # Panics
405+
///
396406
/// Panics if the circuit node has multiple outputs.
397407
pub fn as_net_mut(&self) -> RefMut<Net> {
398408
RefMut::map(self.netref.borrow_mut(), |f| f.as_net_mut())
@@ -422,12 +432,18 @@ where
422432
}
423433

424434
/// Returns the name of the net at this circuit node.
435+
///
436+
/// # Panics
437+
///
425438
/// Panics if the circuit node has multiple outputs.
426439
pub fn get_identifier(&self) -> Identifier {
427440
self.as_net().get_identifier().clone()
428441
}
429442

430443
/// Changes the identifier of the net at this circuit node.
444+
///
445+
/// # Panics
446+
///
431447
/// Panics if the circuit node has multiple outputs.
432448
pub fn set_identifier(&self, identifier: Identifier) {
433449
self.as_net_mut().set_identifier(identifier)
@@ -465,7 +481,10 @@ where
465481
}
466482

467483
/// Updates the name of the instance, if the circuit node is an instance.
468-
/// Panics if the circuit node is not an instance.
484+
///
485+
/// # Panics
486+
///
487+
/// Panics if the circuit node is a principal input.
469488
pub fn set_instance_name(&self, name: Identifier) {
470489
match self.netref.borrow_mut().get_mut() {
471490
Object::Instance(_, inst_name, _) => *inst_name = name,
@@ -474,7 +493,12 @@ where
474493
}
475494

476495
/// Exposes this circuit node as a top-level output in the netlist.
477-
/// Panics if cell is a multi-output circuit node. Errors if circuit node is a principal input.
496+
/// Returns an error if the circuit node is a principal input.
497+
///
498+
/// # Panics
499+
///
500+
/// Panics if cell is a multi-output circuit node.
501+
/// Panics if the reference to the netlist is lost.
478502
pub fn expose_as_output(self) -> Result<Self, String> {
479503
let netlist = self
480504
.netref
@@ -487,6 +511,11 @@ where
487511
}
488512

489513
/// Exposes this circuit node as a top-level output in the netlist with a specific port name.
514+
///
515+
/// # Panics
516+
///
517+
/// Panics if the cell is a multi-output circuit node.
518+
/// Panics if the reference to the netlist is lost.
490519
pub fn expose_with_name(self, name: String) -> Self {
491520
let netlist = self
492521
.netref
@@ -500,6 +529,9 @@ where
500529

501530
/// Exposes the `net` driven by this circuit node as a top-level output.
502531
/// Errors if `net` is not driven by this circuit node.
532+
///
533+
/// # Panics
534+
/// Panics if the reference to the netlist is lost.
503535
pub fn expose_net(&self, net: &Net) -> Result<(), String> {
504536
let netlist = self
505537
.netref
@@ -522,12 +554,20 @@ where
522554
}
523555

524556
/// Returns the net that drives the `index`th input
557+
///
558+
/// # Panics
559+
///
560+
/// Panics if the reference to the netlist is lost.
525561
pub fn get_driver_net(&self, index: usize) -> Option<Net> {
526562
self.netref.borrow().get_driver_net(index)
527563
}
528564

529565
/// Returns a request to mutably borrow the operand net
530566
/// This requires another borrow in the form of [MutBorrowReq]
567+
///
568+
/// # Panics
569+
///
570+
/// Panics if the reference to the netlist is lost.
531571
pub fn req_driver_net(&self, index: usize) -> Option<MutBorrowReq<I>> {
532572
let net = self.get_driver_net(index)?;
533573
let operand = self.get_driver(index).unwrap();
@@ -599,6 +639,9 @@ where
599639
}
600640

601641
/// Returns `true` if this circuit node drives a top-level output.
642+
///
643+
/// # Panics
644+
/// Panics if the weak reference to the netlist is lost.
602645
pub fn drives_an_top_output(&self) -> bool {
603646
let netlist = self
604647
.netref
@@ -620,6 +663,10 @@ where
620663
}
621664

622665
/// Deletes the uses of this circuit node from the netlist.
666+
///
667+
/// # Panics
668+
///
669+
/// Panics if the reference to the netlist is lost.
623670
pub fn delete_uses(self) -> Result<Object<I>, String> {
624671
let netlist = self
625672
.netref
@@ -631,7 +678,11 @@ where
631678
}
632679

633680
/// Replaces the uses of this circuit node in the netlist with another circuit node.
681+
///
682+
/// # Panics
683+
///
634684
/// Panics if either `self` or `other` is a multi-output circuit node.
685+
/// Panics if the weak reference to the netlist is lost.
635686
pub fn replace_uses_with(self, other: &Self) -> Result<Object<I>, String> {
636687
let netlist = self
637688
.netref
@@ -1053,6 +1104,21 @@ where
10531104
Ok(NetRef::wrap(owned_object))
10541105
}
10551106

1107+
/// Returns the driving net at input position `index` for `netref`
1108+
///
1109+
/// # Panics
1110+
///
1111+
/// Panics if `index` is out of bounds
1112+
pub fn get_driver_net(&self, netref: NetRef<I>, index: usize) -> Option<Net> {
1113+
let op = netref.unwrap().borrow().operands[index].clone()?;
1114+
Some(
1115+
self.index_weak(&op.root())
1116+
.borrow()
1117+
.get_net(op.secondary())
1118+
.clone(),
1119+
)
1120+
}
1121+
10561122
/// Set an added object as a top-level output.
10571123
/// Panics if `net`` is a multi-output node.
10581124
pub fn expose_net_with_name(&self, net: DrivenNet<I>, name: String) -> DrivenNet<I> {
@@ -1513,10 +1579,28 @@ pub mod iter {
15131579
}
15141580

15151581
/// A depth-first iterator over the circuit nodes in a netlist
1582+
/// # Examples
1583+
///
1584+
/// ```
1585+
/// use circuit::netlist::iter::DFSIterator;
1586+
/// use circuit::netlist::GateNetlist;
1587+
///
1588+
/// let netlist = GateNetlist::new("example".to_string());
1589+
/// netlist.insert_input("input1".into());
1590+
/// let mut nodes = Vec::new();
1591+
/// let mut dfs = DFSIterator::new(&netlist, netlist.last().unwrap());
1592+
/// while let Some(n) = dfs.next() {
1593+
/// if dfs.check_cycles() {
1594+
/// panic!("Cycle detected in the netlist");
1595+
/// }
1596+
/// nodes.push(n);
1597+
/// }
1598+
/// ```
15161599
pub struct DFSIterator<'a, I: Instantiable> {
15171600
netlist: &'a Netlist<I>,
15181601
stack: Vec<NetRef<I>>,
15191602
visited: HashSet<usize>,
1603+
cycles: bool,
15201604
}
15211605

15221606
impl<'a, I> DFSIterator<'a, I>
@@ -1529,7 +1613,33 @@ pub mod iter {
15291613
netlist,
15301614
stack: vec![from],
15311615
visited: HashSet::new(),
1616+
cycles: false,
1617+
}
1618+
}
1619+
}
1620+
1621+
impl<I> DFSIterator<'_, I>
1622+
where
1623+
I: Instantiable,
1624+
{
1625+
/// Check if the DFS traversal has encountered a cycle yet.
1626+
pub fn check_cycles(&self) -> bool {
1627+
self.cycles
1628+
}
1629+
1630+
/// Consumes the iterator to detect cycles in the netlist.
1631+
pub fn detect_cycles(mut self) -> bool {
1632+
if self.cycles {
1633+
return true;
15321634
}
1635+
1636+
while let Some(_) = self.next() {
1637+
if self.cycles {
1638+
return true;
1639+
}
1640+
}
1641+
1642+
self.cycles
15331643
}
15341644
}
15351645

@@ -1544,6 +1654,7 @@ pub mod iter {
15441654
let uw = item.clone().unwrap();
15451655
let index = uw.borrow().get_index();
15461656
if !self.visited.insert(index) {
1657+
self.cycles = true;
15471658
return self.next();
15481659
}
15491660
let operands = &uw.borrow().operands;

0 commit comments

Comments
 (0)