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        if let Ok((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.clock.net());
74                    queue.insert(ff.reset.net());
75                    queue.insert(ff.clear.net());
76                    queue.insert(ff.enable.net());
77                }
78
79                Cell::Debug(..) => (),
80
81                cell => cell.visit(|net| {
82                    queue.insert(net);
83                }),
84            }
85        }
86    }
87
88    // Split partially live cells.
89    for cell_ref in design.iter_cells() {
90        let _guard = design.use_metadata_from(&[cell_ref]);
91        let cell = cell_ref.get();
92        let cell_output = cell_ref.output();
93        let count_live = cell_output.iter().filter(|net| live_nets.contains(&net)).count();
94        if cell.has_effects(design) {
95            continue; // root
96        } else if count_live == cell_ref.output_len() {
97            continue; // fully live
98        } else if count_live == 0 {
99            continue; // fully dead; removed by `design.compact()`
100        }
101        // might be partially live; candidate for splitting
102        let out_low_dead_count = cell_output.iter().position(|net| live_nets.contains(&net)).unwrap();
103        let out_high_dead_count = cell_output.iter().rev().position(|net| live_nets.contains(&net)).unwrap();
104        let out_live_nets = Value::from_iter(cell_output.iter().filter(|net| live_nets.contains(net)));
105        let arg_live_nets = |arg: &Value| {
106            Value::from_iter(
107                cell_output
108                    .iter()
109                    .enumerate()
110                    .filter(|&(_offset, out_net)| live_nets.contains(&out_net))
111                    .map(|(offset, _out_net)| arg[offset]),
112            )
113        };
114        let arg_live_trits = |arg: &Const| {
115            Const::from_iter(
116                cell_output
117                    .iter()
118                    .enumerate()
119                    .filter(|&(_offset, out_net)| live_nets.contains(&out_net))
120                    .map(|(offset, _out_net)| arg[offset]),
121            )
122        };
123        let (from_live_nets, to_live_nets) = match &*cell {
124            Cell::Buf(arg) => (out_live_nets, design.add_buf(arg_live_nets(arg))),
125            Cell::Not(arg) => (out_live_nets, design.add_not(arg_live_nets(arg))),
126            Cell::And(arg1, arg2) => (out_live_nets, design.add_and(arg_live_nets(arg1), arg_live_nets(arg2))),
127            Cell::Or(arg1, arg2) => (out_live_nets, design.add_or(arg_live_nets(arg1), arg_live_nets(arg2))),
128            Cell::Xor(arg1, arg2) => (out_live_nets, design.add_xor(arg_live_nets(arg1), arg_live_nets(arg2))),
129            Cell::Mux(arg1, arg2, arg3) => {
130                (out_live_nets, design.add_mux(*arg1, arg_live_nets(arg2), arg_live_nets(arg3)))
131            }
132            Cell::Adc(arg1, arg2, arg3) if out_high_dead_count > 1 => {
133                let new_width = arg1.len() - (out_high_dead_count - 1);
134                (
135                    cell_output.slice(..new_width),
136                    design.add_adc(arg1.slice(..new_width), arg2.slice(..new_width), *arg3).slice(..new_width),
137                )
138            }
139            Cell::Shl(arg1, arg2, stride) if out_high_dead_count > 0 => {
140                let new_width = arg1.len() - out_high_dead_count;
141                (cell_output.slice(..new_width), design.add_shl(arg1.slice(..new_width), arg2, *stride))
142            }
143            Cell::UShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
144                cell_output.slice(out_low_dead_count..),
145                design.add_ushr(arg1.slice(out_low_dead_count..), arg2, *stride),
146            ),
147            Cell::SShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
148                cell_output.slice(out_low_dead_count..),
149                design.add_sshr(arg1.slice(out_low_dead_count..), arg2, *stride),
150            ),
151            Cell::XShr(arg1, arg2, stride) if out_low_dead_count > 0 => (
152                cell_output.slice(out_low_dead_count..),
153                design.add_xshr(arg1.slice(out_low_dead_count..), arg2, *stride),
154            ),
155            Cell::Mul(arg1, arg2) if out_high_dead_count > 0 => {
156                let new_width = arg1.len() - out_high_dead_count;
157                (cell_output.slice(..new_width), design.add_mul(arg1.slice(..new_width), arg2.slice(..new_width)))
158            }
159            Cell::Dff(flip_flop) => (
160                out_live_nets,
161                design.add_dff(FlipFlop {
162                    data: arg_live_nets(&flip_flop.data),
163                    clock: flip_flop.clock,
164                    clear: flip_flop.clear,
165                    reset: flip_flop.reset,
166                    enable: flip_flop.enable,
167                    reset_over_enable: flip_flop.reset_over_enable,
168                    clear_value: arg_live_trits(&flip_flop.clear_value),
169                    reset_value: arg_live_trits(&flip_flop.reset_value),
170                    init_value: arg_live_trits(&flip_flop.init_value),
171                }),
172            ),
173            _ => continue,
174        };
175        if cfg!(feature = "trace") {
176            eprintln!(">split {} => {}", design.display_value(&from_live_nets), design.display_value(&to_live_nets));
177        }
178        design.replace_value(from_live_nets, to_live_nets);
179    }
180
181    design.compact()
182}