Skip to main content

prjunnamed_generic/
split.rs

1use std::collections::BTreeSet;
2
3use prjunnamed_netlist::{Cell, Const, Design, FlipFlop, Net, Value};
4
5pub fn split(design: &mut Design) -> bool {
6    let mut live_nets = BTreeSet::<Net>::new();
7    let mut queue = BTreeSet::<Net>::new();
8
9    // Find roots.
10    for cell_ref in design.iter_cells() {
11        let cell = cell_ref.get();
12        if cell.has_effects(design) {
13            cell.visit(|net| {
14                queue.insert(net);
15            })
16        }
17    }
18
19    // Mark all live nets.
20    while let Some(net) = queue.pop_first() {
21        if live_nets.contains(&net) {
22            continue;
23        }
24        live_nets.insert(net);
25        let (cell_ref, offset) = design.find_cell(net);
26        match &*cell_ref.get() {
27            Cell::Buf(arg) | Cell::Not(arg) => {
28                queue.insert(arg[offset]);
29            }
30            Cell::And(arg1, arg2) | Cell::Or(arg1, arg2) | Cell::Xor(arg1, arg2) => {
31                queue.insert(arg1[offset]);
32                queue.insert(arg2[offset]);
33            }
34            Cell::Mux(arg1, arg2, arg3) => {
35                queue.insert(*arg1);
36                queue.insert(arg2[offset]);
37                queue.insert(arg3[offset]);
38            }
39            Cell::Adc(arg1, arg2, arg3) => {
40                queue.insert(*arg3);
41                for index in 0..(offset + 1).min(arg1.len()) {
42                    queue.insert(arg1[index]);
43                    queue.insert(arg2[index]);
44                }
45            }
46
47            Cell::Shl(arg1, arg2, _) => {
48                for index in 0..(offset + 1) {
49                    queue.insert(arg1[index]);
50                }
51                for net in arg2 {
52                    queue.insert(net);
53                }
54            }
55            Cell::UShr(arg1, arg2, _) | Cell::SShr(arg1, arg2, _) | Cell::XShr(arg1, arg2, _) => {
56                for index in offset..(arg1.len()) {
57                    queue.insert(arg1[index]);
58                }
59                for net in arg2 {
60                    queue.insert(net);
61                }
62            }
63
64            Cell::Mul(arg1, arg2) => {
65                for index in 0..(offset + 1) {
66                    queue.insert(arg1[index]);
67                    queue.insert(arg2[index]);
68                }
69            }
70
71            Cell::Dff(ff) => {
72                queue.insert(ff.data[offset]);
73                queue.insert(ff.load_data[offset]);
74                queue.insert(ff.clock.net());
75                queue.insert(ff.reset.net());
76                queue.insert(ff.load.net());
77                queue.insert(ff.clear.net());
78                queue.insert(ff.enable.net());
79            }
80
81            Cell::Debug(..) => (),
82
83            cell => cell.visit(|net| {
84                queue.insert(net);
85            }),
86        }
87    }
88
89    // Split partially live cells.
90    for cell_ref in design.iter_cells() {
91        let _guard = design.use_metadata_from(&[cell_ref]);
92        let cell = cell_ref.get();
93        let cell_output = cell_ref.output();
94        let count_live = cell_output.iter().filter(|net| live_nets.contains(net)).count();
95        if cell.has_effects(design) {
96            continue; // root
97        } else if count_live == cell_ref.output_len() {
98            continue; // fully live
99        } else if count_live == 0 {
100            continue; // fully dead; removed by `design.compact()`
101        }
102        // might be partially live; candidate for splitting
103        let out_low_dead_count = cell_output.iter().position(|net| live_nets.contains(&net)).unwrap();
104        let out_high_dead_count = cell_output.iter().rev().position(|net| live_nets.contains(&net)).unwrap();
105        let out_live_nets = Value::from_iter(cell_output.iter().filter(|net| live_nets.contains(net)));
106        let arg_live_nets = |arg: &Value| {
107            Value::from_iter(
108                cell_output
109                    .iter()
110                    .enumerate()
111                    .filter(|&(_offset, out_net)| live_nets.contains(&out_net))
112                    .map(|(offset, _out_net)| arg[offset]),
113            )
114        };
115        let arg_live_trits = |arg: &Const| {
116            Const::from_iter(
117                cell_output
118                    .iter()
119                    .enumerate()
120                    .filter(|&(_offset, out_net)| live_nets.contains(&out_net))
121                    .map(|(offset, _out_net)| arg[offset]),
122            )
123        };
124        let (from_live_nets, to_live_nets) = match &*cell {
125            Cell::Buf(arg) => (out_live_nets, design.add_buf(arg_live_nets(arg))),
126            Cell::Not(arg) => (out_live_nets, design.add_not(arg_live_nets(arg))),
127            Cell::And(arg1, arg2) => (out_live_nets, design.add_and(arg_live_nets(arg1), arg_live_nets(arg2))),
128            Cell::Or(arg1, arg2) => (out_live_nets, design.add_or(arg_live_nets(arg1), arg_live_nets(arg2))),
129            Cell::Xor(arg1, arg2) => (out_live_nets, design.add_xor(arg_live_nets(arg1), arg_live_nets(arg2))),
130            Cell::Mux(arg1, arg2, arg3) => {
131                (out_live_nets, design.add_mux(*arg1, arg_live_nets(arg2), arg_live_nets(arg3)))
132            }
133            Cell::Adc(arg1, arg2, arg3) if out_high_dead_count > 1 => {
134                let new_width = arg1.len() - (out_high_dead_count - 1);
135                (
136                    cell_output.slice(..new_width),
137                    design.add_adc(arg1.slice(..new_width), arg2.slice(..new_width), *arg3).slice(..new_width),
138                )
139            }
140            Cell::Shl(arg1, arg2, stride) if out_high_dead_count > 0 => {
141                let new_width = arg1.len() - out_high_dead_count;
142                (cell_output.slice(..new_width), design.add_shl(arg1.slice(..new_width), arg2, *stride))
143            }
144            Cell::UShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
145                cell_output.slice(out_low_dead_count..),
146                design.add_ushr(arg1.slice(out_low_dead_count..), arg2, *stride),
147            ),
148            Cell::SShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
149                cell_output.slice(out_low_dead_count..),
150                design.add_sshr(arg1.slice(out_low_dead_count..), arg2, *stride),
151            ),
152            Cell::XShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
153                cell_output.slice(out_low_dead_count..),
154                design.add_xshr(arg1.slice(out_low_dead_count..), arg2, *stride),
155            ),
156            Cell::Mul(arg1, arg2) if out_high_dead_count > 0 => {
157                let new_width = arg1.len() - out_high_dead_count;
158                (cell_output.slice(..new_width), design.add_mul(arg1.slice(..new_width), arg2.slice(..new_width)))
159            }
160            Cell::Dff(flip_flop) => (
161                out_live_nets,
162                design.add_dff(FlipFlop {
163                    data: arg_live_nets(&flip_flop.data),
164                    clock: flip_flop.clock,
165                    clear: flip_flop.clear,
166                    load: flip_flop.load,
167                    load_data: arg_live_nets(&flip_flop.load_data),
168                    reset: flip_flop.reset,
169                    enable: flip_flop.enable,
170                    reset_over_enable: flip_flop.reset_over_enable,
171                    clear_value: arg_live_trits(&flip_flop.clear_value),
172                    reset_value: arg_live_trits(&flip_flop.reset_value),
173                    init_value: arg_live_trits(&flip_flop.init_value),
174                }),
175            ),
176            _ => continue,
177        };
178        if cfg!(feature = "trace") {
179            eprintln!(">split {} => {}", design.display_value(&from_live_nets), design.display_value(&to_live_nets));
180        }
181        design.replace_value(from_live_nets, to_live_nets);
182    }
183
184    design.compact()
185}