1use bitloom_hir::{FrozenHir, PortValues};
12
13use crate::{
14 AbstractionView, EquivStatus, FormalEquivProduct, PortMismatch, Sim, check_functional_equiv,
15 compare_port_values,
16};
17
18#[derive(Debug, Clone, Default)]
24pub struct SyncFifoFunctional {
25 ram: [u64; 4],
26 wr_ptr: u8,
27 rd_ptr: u8,
28 count: u8,
29 dout: u64,
30}
31
32impl SyncFifoFunctional {
33 pub fn new() -> Self {
34 Self::default()
35 }
36}
37
38impl AbstractionView for SyncFifoFunctional {
39 fn cycle(&mut self, inputs: &PortValues) -> PortValues {
40 let rst = inputs.get("rst").unwrap_or(0) != 0;
41 let wr_en = inputs.get("wr_en").unwrap_or(0) != 0;
42 let rd_en = inputs.get("rd_en").unwrap_or(0) != 0;
43 let data_in = inputs.get("data_in").unwrap_or(0);
44
45 if rst {
46 self.ram = [0; 4];
47 self.wr_ptr = 0;
48 self.rd_ptr = 0;
49 self.count = 0;
50 self.dout = 0;
51 } else {
52 let full = self.count >= 4;
55 let empty = self.count == 0;
56 let can_wr = wr_en && !full;
57 let can_rd = rd_en && !empty;
58
59 if can_wr {
60 self.ram[self.wr_ptr as usize % 4] = data_in & 0xff;
61 }
62 self.dout = self.ram[self.rd_ptr as usize % 4];
64
65 if can_wr {
66 self.wr_ptr = self.wr_ptr.wrapping_add(1) & 0b11;
67 }
68 if can_rd {
69 self.rd_ptr = self.rd_ptr.wrapping_add(1) & 0b11;
70 }
71 match (can_wr, can_rd) {
72 (true, true) => {}
73 (true, false) => self.count = self.count.saturating_add(1).min(4),
74 (false, true) => self.count = self.count.saturating_sub(1),
75 (false, false) => {}
76 }
77 }
78
79 let mut out = inputs.clone();
80 out.set("full", u64::from(self.count >= 4));
81 out.set("empty", u64::from(self.count == 0));
82 out.set("data_out", self.dout);
83 out
84 }
85}
86
87pub fn sync_fifo_dual_stimulus() -> Vec<PortValues> {
89 let mut out = Vec::new();
90 let mut frame = |rst: u64, wr_en: u64, rd_en: u64, data_in: u64| {
91 let mut pv = PortValues::default();
92 pv.set("rst", rst);
93 pv.set("wr_en", wr_en);
94 pv.set("rd_en", rd_en);
95 pv.set("data_in", data_in);
96 out.push(pv);
97 };
98 frame(1, 0, 0, 0);
99 frame(0, 1, 0, 0x11);
100 frame(0, 1, 0, 0x22);
101 frame(0, 0, 1, 0);
102 frame(0, 0, 1, 0);
103 frame(0, 0, 0, 0);
104 out
105}
106
107#[derive(Debug, Clone, Default)]
113pub struct GpioFunctional {
114 out_r: u64,
115}
116
117impl GpioFunctional {
118 pub fn new() -> Self {
119 Self::default()
120 }
121}
122
123impl AbstractionView for GpioFunctional {
124 fn cycle(&mut self, inputs: &PortValues) -> PortValues {
125 let rst = inputs.get("rst").unwrap_or(0) != 0;
126 let dir = inputs.get("dir").unwrap_or(0) & 0xff;
127 let wr_en = inputs.get("wr_en").unwrap_or(0) != 0;
128 let wr_data = inputs.get("wr_data").unwrap_or(0) & 0xff;
129 let wr_mask = inputs.get("wr_mask").unwrap_or(0) & 0xff;
130 let pad_in = inputs.get("pad_in").unwrap_or(0) & 0xff;
131
132 if rst {
133 self.out_r = 0;
134 } else if wr_en {
135 let kept = self.out_r & (!wr_mask & 0xff);
136 let newt = wr_data & wr_mask;
137 self.out_r = (kept | newt) & 0xff;
138 }
139
140 let pad_out = self.out_r & dir;
141 let rd_data = (self.out_r & dir) | (pad_in & (!dir & 0xff));
142
143 let mut out = inputs.clone();
144 out.set("pad_out", pad_out);
145 out.set("rd_data", rd_data);
146 out
147 }
148}
149
150pub fn gpio_dual_stimulus() -> Vec<PortValues> {
152 let mut out = Vec::new();
153 let mut frame = |rst: u64, dir: u64, wr_en: u64, wr_data: u64, wr_mask: u64, pad_in: u64| {
154 let mut pv = PortValues::default();
155 pv.set("rst", rst);
156 pv.set("dir", dir);
157 pv.set("wr_en", wr_en);
158 pv.set("wr_data", wr_data);
159 pv.set("wr_mask", wr_mask);
160 pv.set("pad_in", pad_in);
161 out.push(pv);
162 };
163 frame(1, 0xff, 0, 0, 0, 0);
164 frame(0, 0xff, 1, 0xa5, 0xff, 0);
165 frame(0, 0xff, 0, 0, 0, 0);
166 frame(0, 0x0f, 0, 0, 0, 0xf0); frame(0, 0x0f, 1, 0x03, 0x0f, 0xf0);
168 frame(0, 0x0f, 0, 0, 0, 0xaa);
169 out
170}
171
172#[derive(Debug, Clone, Default)]
177pub struct UartTxFunctional {
178 hold: u64,
179 shift_reg: u64,
180 busy: u64,
181 bit_idx: u64,
182 baud_cnt: u64,
183}
184
185impl UartTxFunctional {
186 pub fn new() -> Self {
187 Self::default()
188 }
189}
190
191impl AbstractionView for UartTxFunctional {
192 fn cycle(&mut self, inputs: &PortValues) -> PortValues {
193 let rst = inputs.get("rst").unwrap_or(0) != 0;
194 let wr_en = inputs.get("wr_en").unwrap_or(0) != 0;
195 let wr_data = inputs.get("wr_data").unwrap_or(0) & 0xff;
196 let baud_div = inputs.get("baud_div").unwrap_or(0) & 0xff;
197
198 if rst {
199 self.hold = 0;
200 self.shift_reg = 0;
201 self.busy = 0;
202 self.bit_idx = 0;
203 self.baud_cnt = 0;
204 } else {
205 let busy = self.busy != 0;
206 let accept = wr_en && !busy;
207 let is_start = self.bit_idx == 0;
208 let is_stop = self.bit_idx == 9;
209 let baud_eq = self.baud_cnt == baud_div;
210 let baud_tick = busy && baud_eq;
211
212 let baud_cnt_busy = if baud_eq {
213 0
214 } else {
215 self.baud_cnt.wrapping_add(1) & 0xff
216 };
217 let baud_cnt_busy_or_idle = if busy { baud_cnt_busy } else { 0 };
218 let next_baud_cnt = if accept { 0 } else { baud_cnt_busy_or_idle };
219
220 let busy_after_tick = if is_stop { 0 } else { 1 };
221 let busy_when_busy = if baud_tick { busy_after_tick } else { 1 };
222 let busy_when_busy_or_idle = if busy { busy_when_busy } else { 0 };
223 let next_busy = if accept { 1 } else { busy_when_busy_or_idle };
224
225 let bit_idx_p1 = (self.bit_idx.wrapping_add(1)) & 0xf;
226 let bit_after_tick = if is_stop { 0 } else { bit_idx_p1 };
227 let bit_when_busy = if baud_tick {
228 bit_after_tick
229 } else {
230 self.bit_idx
231 };
232 let bit_when_busy_or_idle = if busy { bit_when_busy } else { 0 };
233 let next_bit_idx = if accept { 0 } else { bit_when_busy_or_idle };
234
235 let do_shift = busy && !is_start && !is_stop;
236 let do_shift_tick = do_shift && baud_eq;
237 let shift_shr = (self.shift_reg >> 1) & 0xff;
238 let shift_after_tick = if do_shift_tick {
239 shift_shr
240 } else {
241 self.shift_reg
242 };
243 let next_shift_busy = if busy {
244 shift_after_tick
245 } else {
246 self.shift_reg
247 };
248 let next_shift_final = if accept { wr_data } else { next_shift_busy };
249 let next_hold = if accept { wr_data } else { self.hold };
250
251 self.busy = next_busy;
252 self.bit_idx = next_bit_idx;
253 self.shift_reg = next_shift_final;
254 self.hold = next_hold;
255 self.baud_cnt = next_baud_cnt;
256 }
257
258 let busy = self.busy != 0;
259 let is_start = self.bit_idx == 0;
260 let is_stop = self.bit_idx == 9;
261 let data_bit = (self.shift_reg & 1) != 0;
262 let tx_data_or_stop = if is_stop { 1 } else { u64::from(data_bit) };
263 let tx_active = if is_start { 0 } else { tx_data_or_stop };
264 let tx = if busy { tx_active } else { 1 };
265
266 let mut out = inputs.clone();
267 out.set("tx", tx);
268 out.set("tx_byte", self.hold & 0xff);
269 out.set("tx_busy", self.busy & 1);
270 out
271 }
272}
273
274pub fn uart_tx_dual_stimulus() -> Vec<PortValues> {
276 let mut out = Vec::new();
277 let mut frame = |rst: u64, wr_en: u64, wr_data: u64, baud_div: u64| {
278 let mut pv = PortValues::default();
279 pv.set("rst", rst);
280 pv.set("wr_en", wr_en);
281 pv.set("wr_data", wr_data);
282 pv.set("baud_div", baud_div);
283 out.push(pv);
284 };
285 frame(1, 0, 0, 0);
287 frame(0, 0, 0, 0);
288 frame(0, 1, 0xa5, 0); for _ in 0..9 {
290 frame(0, 0, 0, 0); }
292 frame(0, 0, 0, 0); frame(1, 0, 0, 1);
295 frame(0, 1, 0x01, 1);
296 frame(0, 0, 0, 1); frame(0, 0, 0, 1); frame(1, 0, 0, 0);
300 frame(0, 1, 0x3c, 0);
301 frame(0, 1, 0xff, 0);
302 frame(0, 0, 0, 0);
303 out
304}
305
306const SYNC_FIFO_ARCH_PORTS: &[&str] = &["full", "empty", "data_out"];
308
309const GPIO_ARCH_PORTS: &[&str] = &["pad_out", "rd_data"];
311
312const UART_TX_ARCH_PORTS: &[&str] = &["tx", "tx_byte", "tx_busy"];
314#[derive(Debug, Clone, Default)]
316pub struct IpDualModelMatrix;
317
318impl IpDualModelMatrix {
319 pub fn new() -> Self {
320 Self
321 }
322
323 pub fn verify_sync_fifo(&self, hir: FrozenHir) -> EquivStatus {
325 let mut sim = Sim::new(hir);
326 let mut fl = SyncFifoFunctional::new();
327 let mut cycles = 0usize;
328 for inputs in sync_fifo_dual_stimulus() {
329 sim.set_inputs(inputs.clone());
330 sim.settle();
331 sim.tick();
332 let abs_out = fl.cycle(&inputs);
333 if let Err(mismatches) =
334 compare_named_ports(sim.ports(), &abs_out, SYNC_FIFO_ARCH_PORTS)
335 {
336 return EquivStatus::Fail {
337 cycle: cycles,
338 mismatches,
339 };
340 }
341 cycles += 1;
342 }
343 EquivStatus::Pass { cycles }
344 }
345
346 pub fn verify_sync_fifo_with<A: AbstractionView>(
348 &self,
349 hir: FrozenHir,
350 abs: &mut A,
351 ) -> EquivStatus {
352 let mut sim = Sim::new(hir);
353 let mut cycles = 0usize;
354 for inputs in sync_fifo_dual_stimulus() {
355 sim.set_inputs(inputs.clone());
356 sim.settle();
357 sim.tick();
358 let abs_out = abs.cycle(&inputs);
359 if let Err(mismatches) =
360 compare_named_ports(sim.ports(), &abs_out, SYNC_FIFO_ARCH_PORTS)
361 {
362 return EquivStatus::Fail {
363 cycle: cycles,
364 mismatches,
365 };
366 }
367 cycles += 1;
368 }
369 EquivStatus::Pass { cycles }
370 }
371
372 pub fn verify_gpio_handwritten(&self, hir: FrozenHir) -> EquivStatus {
374 let mut sim = Sim::new(hir);
375 let mut fl = GpioFunctional::new();
376 let mut cycles = 0usize;
377 for inputs in gpio_dual_stimulus() {
378 sim.set_inputs(inputs.clone());
379 sim.settle();
380 sim.tick();
381 let abs_out = fl.cycle(&inputs);
382 if let Err(mismatches) = compare_named_ports(sim.ports(), &abs_out, GPIO_ARCH_PORTS) {
383 return EquivStatus::Fail {
384 cycle: cycles,
385 mismatches,
386 };
387 }
388 cycles += 1;
389 }
390 EquivStatus::Pass { cycles }
391 }
392
393 pub fn verify_uart_tx_handwritten(&self, hir: FrozenHir) -> EquivStatus {
395 let mut sim = Sim::new(hir);
396 let mut fl = UartTxFunctional::new();
397 let mut cycles = 0usize;
398 for inputs in uart_tx_dual_stimulus() {
399 sim.set_inputs(inputs.clone());
400 sim.settle();
401 sim.tick();
402 let abs_out = fl.cycle(&inputs);
403 if let Err(mismatches) = compare_named_ports(sim.ports(), &abs_out, UART_TX_ARCH_PORTS)
404 {
405 return EquivStatus::Fail {
406 cycle: cycles,
407 mismatches,
408 };
409 }
410 cycles += 1;
411 }
412 EquivStatus::Pass { cycles }
413 }
414
415 pub fn verify_uart_tx_handwritten_with<A: AbstractionView>(
417 &self,
418 hir: FrozenHir,
419 abs: &mut A,
420 ) -> EquivStatus {
421 let mut sim = Sim::new(hir);
422 let mut cycles = 0usize;
423 for inputs in uart_tx_dual_stimulus() {
424 sim.set_inputs(inputs.clone());
425 sim.settle();
426 sim.tick();
427 let abs_out = abs.cycle(&inputs);
428 if let Err(mismatches) = compare_named_ports(sim.ports(), &abs_out, UART_TX_ARCH_PORTS)
429 {
430 return EquivStatus::Fail {
431 cycle: cycles,
432 mismatches,
433 };
434 }
435 cycles += 1;
436 }
437 EquivStatus::Pass { cycles }
438 }
439
440 pub fn verify_generated_rst_compare(&self, hir: FrozenHir) -> EquivStatus {
442 FormalEquivProduct::new(0xC0FFEE, 8)
443 .with_boolean_ports(&["rst"])
444 .check_random_compare(hir)
445 }
446
447 pub fn verify_handwritten<A: AbstractionView>(
449 &self,
450 hir: FrozenHir,
451 abs: &mut A,
452 stimuli: impl IntoIterator<Item = PortValues>,
453 ) -> EquivStatus {
454 check_functional_equiv(hir, abs, stimuli)
455 }
456}
457
458fn compare_named_ports(
459 left: &PortValues,
460 right: &PortValues,
461 names: &[&str],
462) -> Result<(), Vec<PortMismatch>> {
463 let mut l = PortValues::default();
464 let mut r = PortValues::default();
465 for name in names {
466 if let Some(v) = left.get(name) {
467 l.set(*name, v);
468 }
469 if let Some(v) = right.get(name) {
470 r.set(*name, v);
471 }
472 }
473 compare_port_values(&l, &r)
474}