1use std::fmt::Display;
2use std::collections::{BTreeMap, BTreeSet};
3
4use crate::{Cell, Design, IoNet, Net, Value};
5
6#[derive(Debug)]
7pub enum NotIsomorphic {
8 NoOutputLeft(String),
9 NoOutputRight(String),
10 OutputSizeMismatch(String),
11 IoSizeMismatch(String),
12 NameSizeMismatch(String),
13 ValueSizeMismatch(Value, Value),
14 NetMismatch(Net, Net),
15 IoNetMismatch(IoNet, IoNet),
16}
17
18impl Display for NotIsomorphic {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 NotIsomorphic::NoOutputLeft(name) => write!(f, "output {:?} is missing in the left design", name),
22 NotIsomorphic::NoOutputRight(name) => write!(f, "output {:?} is missing in the right design", name),
23 NotIsomorphic::OutputSizeMismatch(name) => write!(f, "size of output {:?} does not match", name),
24 NotIsomorphic::IoSizeMismatch(name) => write!(f, "size of IO {:?} does not match", name),
25 NotIsomorphic::NameSizeMismatch(name) => write!(f, "size of name cell {:?} does not match", name),
26 NotIsomorphic::ValueSizeMismatch(value_l, value_r) => {
27 write!(f, "size of values {} and {} do not match", value_l, value_r)
28 }
29 NotIsomorphic::NetMismatch(net_l, net_r) => write!(f, "nets {} and {} are not isomorphic", net_l, net_r),
30 NotIsomorphic::IoNetMismatch(io_net_l, io_net_r) => {
31 write!(f, "IO nets {} and {} are not isomorphic", io_net_l, io_net_r)
32 }
33 }
34 }
35}
36
37pub fn isomorphic(lft: &Design, rgt: &Design) -> Result<(), NotIsomorphic> {
39 let mut queue: BTreeSet<(Net, Net)> = BTreeSet::new();
40 fn queue_vals(queue: &mut BTreeSet<(Net, Net)>, val_l: &Value, val_r: &Value) -> Result<(), NotIsomorphic> {
41 if val_l.len() != val_r.len() {
42 return Err(NotIsomorphic::ValueSizeMismatch(val_l.clone(), val_r.clone()));
43 }
44 for (net_l, net_r) in val_l.iter().zip(val_r) {
45 queue.insert((net_l, net_r));
46 }
47 Ok(())
48 }
49
50 let mut visited: BTreeSet<(Net, Net)> = BTreeSet::new();
51 visited.insert((Net::UNDEF, Net::UNDEF));
52 visited.insert((Net::ZERO, Net::ZERO));
53 visited.insert((Net::ONE, Net::ONE));
54 let mut outputs_l = BTreeMap::new();
55 let mut names_l = BTreeMap::new();
56 for cell in lft.iter_cells() {
57 match &*cell.get() {
58 Cell::Output(name, value) => {
59 outputs_l.insert(name.clone(), value.clone());
60 }
61 Cell::Name(name, value) => {
62 names_l.insert(name.clone(), value.clone());
63 }
64 _ => (),
65 }
66 }
67 let mut outputs_r = BTreeMap::new();
68 let mut names_r = BTreeMap::new();
69 for cell in rgt.iter_cells() {
70 match &*cell.get() {
71 Cell::Output(name, value) => {
72 outputs_r.insert(name.clone(), value.clone());
73 }
74 Cell::Name(name, value) => {
75 names_r.insert(name.clone(), value.clone());
76 }
77 _ => (),
78 }
79 }
80 for (name, value_l) in &outputs_l {
81 if let Some(value_r) = outputs_r.get(name) {
82 if value_l.len() != value_r.len() {
83 return Err(NotIsomorphic::OutputSizeMismatch(name.clone()));
84 }
85 for (net_l, net_r) in value_l.iter().zip(value_r) {
86 queue.insert((net_l, net_r));
87 }
88 } else {
89 return Err(NotIsomorphic::NoOutputRight(name.clone()));
90 }
91 }
92 for name in outputs_r.keys() {
93 if !outputs_l.contains_key(name) {
94 return Err(NotIsomorphic::NoOutputLeft(name.clone()));
95 }
96 }
97 for (name, value_l) in &names_l {
98 if let Some(value_r) = names_r.get(name) {
99 if value_l.len() != value_r.len() {
100 return Err(NotIsomorphic::NameSizeMismatch(name.clone()));
101 }
102 for (net_l, net_r) in value_l.iter().zip(value_r) {
103 queue.insert((net_l, net_r));
104 }
105 }
106 }
107 let mut ios = BTreeSet::new();
108 ios.insert((IoNet::FLOATING, IoNet::FLOATING));
109 for (name, _) in lft.iter_ios() {
110 if let (Some(io_l), Some(io_r)) = (lft.get_io(name), rgt.get_io(name)) {
111 if io_l.len() != io_r.len() {
112 return Err(NotIsomorphic::IoSizeMismatch(name.to_owned()));
113 }
114 for (ionet_l, ionet_r) in io_l.iter().zip(io_r.iter()) {
115 ios.insert((ionet_l, ionet_r));
116 }
117 }
118 }
119 while let Some((net_l, net_r)) = queue.pop_first() {
120 if visited.contains(&(net_l, net_r)) {
121 continue;
122 }
123 if net_l.as_const().is_some() || net_r.as_const().is_some() {
124 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
126 }
127 let (cell_l, bit_l) = lft.find_cell(net_l).unwrap();
128 let (cell_r, bit_r) = rgt.find_cell(net_r).unwrap();
129 let out_l = cell_l.output();
130 let out_r = cell_r.output();
131 if bit_l != bit_r || out_l.len() != out_r.len() {
132 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
133 }
134 for (net_l, net_r) in out_l.iter().zip(out_r) {
135 visited.insert((net_l, net_r));
136 }
137 match (&*cell_l.get(), &*cell_r.get()) {
138 (Cell::Buf(val_l), Cell::Buf(val_r)) | (Cell::Not(val_l), Cell::Not(val_r)) => {
139 queue_vals(&mut queue, val_l, val_r)?
140 }
141 (Cell::And(arg1_l, arg2_l), Cell::And(arg1_r, arg2_r))
142 | (Cell::Or(arg1_l, arg2_l), Cell::Or(arg1_r, arg2_r))
143 | (Cell::Xor(arg1_l, arg2_l), Cell::Xor(arg1_r, arg2_r))
144 | (Cell::Eq(arg1_l, arg2_l), Cell::Eq(arg1_r, arg2_r))
145 | (Cell::ULt(arg1_l, arg2_l), Cell::ULt(arg1_r, arg2_r))
146 | (Cell::SLt(arg1_l, arg2_l), Cell::SLt(arg1_r, arg2_r))
147 | (Cell::Mul(arg1_l, arg2_l), Cell::Mul(arg1_r, arg2_r))
148 | (Cell::UDiv(arg1_l, arg2_l), Cell::UDiv(arg1_r, arg2_r))
149 | (Cell::UMod(arg1_l, arg2_l), Cell::UMod(arg1_r, arg2_r))
150 | (Cell::SDivTrunc(arg1_l, arg2_l), Cell::SDivTrunc(arg1_r, arg2_r))
151 | (Cell::SDivFloor(arg1_l, arg2_l), Cell::SDivFloor(arg1_r, arg2_r))
152 | (Cell::SModTrunc(arg1_l, arg2_l), Cell::SModTrunc(arg1_r, arg2_r))
153 | (Cell::SModFloor(arg1_l, arg2_l), Cell::SModFloor(arg1_r, arg2_r)) => {
154 queue_vals(&mut queue, arg1_l, arg1_r)?;
155 queue_vals(&mut queue, arg2_l, arg2_r)?;
156 }
157 (Cell::Mux(arg1_l, arg2_l, arg3_l), Cell::Mux(sel_r, arg2_r, arg3_r)) => {
158 queue.insert((*arg1_l, *sel_r));
159 queue_vals(&mut queue, arg2_l, arg2_r)?;
160 queue_vals(&mut queue, arg3_l, arg3_r)?;
161 }
162 (Cell::Adc(arg1_l, arg2_l, arg3_l), Cell::Adc(arg1_r, arg2_r, arg3_r)) => {
163 queue_vals(&mut queue, arg1_l, arg1_r)?;
164 queue_vals(&mut queue, arg2_l, arg2_r)?;
165 queue.insert((*arg3_l, *arg3_r));
166 }
167 (Cell::Aig(arg1_l, arg2_l), Cell::Aig(arg1_r, arg2_r)) => {
168 queue.insert((arg1_l.net(), arg1_r.net()));
169 queue.insert((arg2_l.net(), arg2_r.net()));
170 if arg1_l.is_positive() != arg1_r.is_positive() || arg2_l.is_positive() != arg2_r.is_positive() {
171 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
172 }
173 }
174 (Cell::Shl(arg1_l, arg2_l, stride_l), Cell::Shl(arg1_r, arg2_r, stride_r))
175 | (Cell::UShr(arg1_l, arg2_l, stride_l), Cell::UShr(arg1_r, arg2_r, stride_r))
176 | (Cell::SShr(arg1_l, arg2_l, stride_l), Cell::SShr(arg1_r, arg2_r, stride_r))
177 | (Cell::XShr(arg1_l, arg2_l, stride_l), Cell::XShr(arg1_r, arg2_r, stride_r)) => {
178 queue_vals(&mut queue, arg1_l, arg1_r)?;
179 queue_vals(&mut queue, arg2_l, arg2_r)?;
180 if stride_l != stride_r {
181 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
182 }
183 }
184 (Cell::Dff(ff_l), Cell::Dff(ff_r)) => {
185 queue_vals(&mut queue, &ff_l.data, &ff_r.data)?;
186 queue.insert((ff_l.clock.net(), ff_r.clock.net()));
187 queue.insert((ff_l.clear.net(), ff_r.clear.net()));
188 queue.insert((ff_l.reset.net(), ff_r.reset.net()));
189 queue.insert((ff_l.enable.net(), ff_r.enable.net()));
190 if ff_l.clock.is_positive() != ff_r.clock.is_positive()
191 || ff_l.clear.is_positive() != ff_r.clear.is_positive()
192 || ff_l.reset.is_positive() != ff_r.reset.is_positive()
193 || ff_l.enable.is_positive() != ff_r.enable.is_positive()
194 || (ff_l.reset_over_enable != ff_r.reset_over_enable
195 && !ff_l.reset.is_always(false)
196 && !ff_l.enable.is_always(true))
197 || ff_l.clear_value != ff_r.clear_value
198 || ff_l.reset_value != ff_r.reset_value
199 || ff_l.init_value != ff_r.init_value
200 {
201 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
202 }
203 }
204 (Cell::IoBuf(iobuf_l), Cell::IoBuf(iobuf_r)) => {
205 for (io_net_l, io_net_r) in iobuf_l.io.iter().zip(iobuf_r.io.iter()) {
206 if !ios.contains(&(io_net_l, io_net_r)) {
207 return Err(NotIsomorphic::IoNetMismatch(io_net_l, io_net_r));
208 }
209 }
210 queue_vals(&mut queue, &iobuf_l.output, &iobuf_r.output)?;
211 queue.insert((iobuf_l.enable.net(), iobuf_r.enable.net()));
212 if iobuf_l.enable.is_positive() != iobuf_r.enable.is_positive() {
213 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
214 }
215 }
216 (Cell::Memory(memory_l), Cell::Memory(memory_r)) => {
217 if memory_l.depth != memory_r.depth
218 || memory_l.width != memory_r.width
219 || memory_l.init_value != memory_r.init_value
220 || memory_l.write_ports.len() != memory_r.write_ports.len()
221 || memory_l.read_ports.len() != memory_r.read_ports.len()
222 {
223 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
224 }
225 for (write_port_l, write_port_r) in memory_l.write_ports.iter().zip(memory_r.write_ports.iter()) {
226 queue_vals(&mut queue, &write_port_l.addr, &write_port_r.addr)?;
227 queue_vals(&mut queue, &write_port_l.data, &write_port_r.data)?;
228 queue_vals(&mut queue, &write_port_l.mask, &write_port_r.mask)?;
229 queue.insert((write_port_l.clock.net(), write_port_r.clock.net()));
230 if write_port_l.clock.is_positive() != write_port_r.clock.is_positive() {
231 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
232 }
233 }
234 for (read_port_l, read_port_r) in memory_l.read_ports.iter().zip(memory_r.read_ports.iter()) {
235 queue_vals(&mut queue, &read_port_l.addr, &read_port_r.addr)?;
236 if read_port_l.data_len != read_port_r.data_len {
237 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
238 }
239 match (&read_port_l.flip_flop, &read_port_r.flip_flop) {
240 (None, None) => (),
241 (Some(ff_l), Some(ff_r)) => {
242 queue.insert((ff_l.clock.net(), ff_r.clock.net()));
243 queue.insert((ff_l.clear.net(), ff_r.clear.net()));
244 queue.insert((ff_l.reset.net(), ff_r.reset.net()));
245 queue.insert((ff_l.enable.net(), ff_r.enable.net()));
246 if ff_l.clock.is_positive() != ff_r.clock.is_positive()
247 || ff_l.clear.is_positive() != ff_r.clear.is_positive()
248 || ff_l.reset.is_positive() != ff_r.reset.is_positive()
249 || ff_l.enable.is_positive() != ff_r.enable.is_positive()
250 || (ff_l.reset_over_enable != ff_r.reset_over_enable
251 && !ff_l.reset.is_always(false)
252 && !ff_l.enable.is_always(true))
253 || ff_l.clear_value != ff_r.clear_value
254 || ff_l.reset_value != ff_r.reset_value
255 || ff_l.init_value != ff_r.init_value
256 || ff_l.relations != ff_r.relations
257 {
258 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
259 }
260 }
261 _ => return Err(NotIsomorphic::NetMismatch(net_l, net_r)),
262 }
263 }
264 }
265 (Cell::Target(target_cell_l), Cell::Target(target_cell_r)) => {
266 for (io_net_l, io_net_r) in target_cell_l.ios.iter().zip(target_cell_r.ios.iter()) {
267 if !ios.contains(&(io_net_l, io_net_r)) {
268 return Err(NotIsomorphic::IoNetMismatch(io_net_l, io_net_r));
269 }
270 }
271 if target_cell_l.kind != target_cell_r.kind || target_cell_l.params != target_cell_r.params {
272 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
273 }
274 queue_vals(&mut queue, &target_cell_l.inputs, &target_cell_r.inputs)?;
275 }
276 (Cell::Other(inst_l), Cell::Other(inst_r)) => {
277 if inst_l.kind != inst_r.kind || inst_l.params != inst_r.params || inst_l.outputs != inst_r.outputs {
278 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
279 }
280 for (name, value_l) in &inst_l.inputs {
281 let Some(value_r) = inst_r.inputs.get(name) else {
282 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
283 };
284 queue_vals(&mut queue, value_l, value_r)?;
285 }
286 for name in inst_r.inputs.keys() {
287 if !inst_l.inputs.contains_key(name) {
288 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
289 }
290 }
291 for (name, io_value_l) in &inst_l.ios {
292 let Some(io_value_r) = inst_r.ios.get(name) else {
293 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
294 };
295 for (io_net_l, io_net_r) in io_value_l.iter().zip(io_value_r.iter()) {
296 if !ios.contains(&(io_net_l, io_net_r)) {
297 return Err(NotIsomorphic::IoNetMismatch(io_net_l, io_net_r));
298 }
299 }
300 }
301 for name in inst_r.ios.keys() {
302 if !inst_l.ios.contains_key(name) {
303 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
304 }
305 }
306 }
307 (Cell::Input(name_l, _), Cell::Input(name_r, _)) => {
308 if name_l != name_r {
309 return Err(NotIsomorphic::NetMismatch(net_l, net_r));
310 }
311 }
312 _ => return Err(NotIsomorphic::NetMismatch(net_l, net_r)),
313 }
314 }
315 Ok(())
316}
317
318#[macro_export]
319macro_rules! assert_isomorphic {
320 ( $lft:ident, $rgt:ident ) => {
321 $lft.apply();
322 $rgt.apply();
323 let result = prjunnamed_netlist::isomorphic(&$lft, &$rgt);
324 if let Err(error) = result {
325 panic!("{}\nleft design:\n{}\nright design:\n{}", error, $lft, $rgt);
326 }
327 };
328}