1use super::sva_model::SvaExpr;
8use std::collections::{HashMap, HashSet};
9
10#[derive(Debug, Clone, PartialEq)]
13pub enum BoundedExpr {
14 Var(String),
16 Bool(bool),
18 Int(i64),
20 And(Box<BoundedExpr>, Box<BoundedExpr>),
22 Or(Box<BoundedExpr>, Box<BoundedExpr>),
24 Not(Box<BoundedExpr>),
26 Implies(Box<BoundedExpr>, Box<BoundedExpr>),
28 Eq(Box<BoundedExpr>, Box<BoundedExpr>),
30 Lt(Box<BoundedExpr>, Box<BoundedExpr>),
32 Gt(Box<BoundedExpr>, Box<BoundedExpr>),
34 Lte(Box<BoundedExpr>, Box<BoundedExpr>),
36 Gte(Box<BoundedExpr>, Box<BoundedExpr>),
38 Unsupported(String),
40
41 BitVecConst { width: u32, value: u64 },
45 BitVecVar(String, u32),
47 BitVecBinary { op: BitVecBoundedOp, left: Box<BoundedExpr>, right: Box<BoundedExpr> },
49 BitVecExtract { high: u32, low: u32, operand: Box<BoundedExpr> },
51 BitVecConcat(Box<BoundedExpr>, Box<BoundedExpr>),
53 ArraySelect { array: Box<BoundedExpr>, index: Box<BoundedExpr> },
55 ArrayStore { array: Box<BoundedExpr>, index: Box<BoundedExpr>, value: Box<BoundedExpr> },
57 IntBinary { op: ArithBoundedOp, left: Box<BoundedExpr>, right: Box<BoundedExpr> },
59 Comparison { op: CmpBoundedOp, left: Box<BoundedExpr>, right: Box<BoundedExpr> },
61 ForAll { var: String, sort: BoundedSort, body: Box<BoundedExpr> },
63 Exists { var: String, sort: BoundedSort, body: Box<BoundedExpr> },
65 Apply { name: String, args: Vec<BoundedExpr> },
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
71pub enum BitVecBoundedOp {
72 And, Or, Xor, Not, Shl, Shr, AShr, Add, Sub, Mul, ULt, SLt, Eq,
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum ArithBoundedOp {
78 Add, Sub, Mul, Div,
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq)]
83pub enum CmpBoundedOp {
84 Gt, Lt, Gte, Lte,
85}
86
87#[derive(Debug, Clone, PartialEq, Eq)]
89pub enum BoundedSort {
90 Bool,
91 Int,
92 BitVec(u32),
93 Real,
95}
96
97#[derive(Debug, Clone)]
106pub struct SequenceMatch {
107 pub condition: BoundedExpr,
109 pub length: u32,
111}
112
113pub struct TranslateResult {
115 pub expr: BoundedExpr,
116 pub declarations: Vec<String>, }
118
119pub struct DirectiveResult {
121 pub expr: BoundedExpr,
122 pub declarations: Vec<String>,
123 pub role: DirectiveRole,
124}
125
126pub struct SvaTranslator {
128 pub bound: u32,
129 pub(crate) declarations: HashSet<String>,
130 local_bindings: HashMap<String, BoundedExpr>,
135 queue_timestep: Option<u32>,
139}
140
141impl SvaTranslator {
142 pub fn new(bound: u32) -> Self {
143 Self {
144 bound,
145 declarations: HashSet::new(),
146 local_bindings: HashMap::new(),
147 queue_timestep: None,
148 }
149 }
150
151 pub fn translate(&mut self, expr: &SvaExpr, t: u32) -> BoundedExpr {
153 match expr {
154 SvaExpr::Signal(name) => {
155 let var_name = format!("{}@{}", name, t);
156 self.declarations.insert(var_name.clone());
157 BoundedExpr::Var(var_name)
158 }
159
160 SvaExpr::Const(value, _width) => BoundedExpr::Int(*value as i64),
161
162 SvaExpr::Rose(inner) => {
163 if t == 0 {
164 self.translate(inner, 0)
166 } else {
167 let current = self.translate(inner, t);
168 let previous = self.translate(inner, t - 1);
169 BoundedExpr::And(
170 Box::new(current),
171 Box::new(BoundedExpr::Not(Box::new(previous))),
172 )
173 }
174 }
175
176 SvaExpr::Fell(inner) => {
177 if t == 0 {
178 BoundedExpr::Not(Box::new(self.translate(inner, 0)))
179 } else {
180 let current = self.translate(inner, t);
181 let previous = self.translate(inner, t - 1);
182 BoundedExpr::And(
183 Box::new(BoundedExpr::Not(Box::new(current))),
184 Box::new(previous),
185 )
186 }
187 }
188
189 SvaExpr::Past(inner, n) => {
190 if t >= *n {
191 self.translate(inner, t - n)
192 } else {
193 self.translate(inner, t)
197 }
198 }
199
200 SvaExpr::And(left, right) => {
201 let l = self.translate(left, t);
202 let r = self.translate(right, t);
203 BoundedExpr::And(Box::new(l), Box::new(r))
204 }
205
206 SvaExpr::Or(left, right) => {
207 let l = self.translate(left, t);
208 let r = self.translate(right, t);
209 BoundedExpr::Or(Box::new(l), Box::new(r))
210 }
211
212 SvaExpr::Not(inner) => {
213 let i = self.translate(inner, t);
214 BoundedExpr::Not(Box::new(i))
215 }
216
217 SvaExpr::Eq(left, right) => {
218 let l = self.translate(left, t);
219 let r = self.translate(right, t);
220 BoundedExpr::Eq(Box::new(l), Box::new(r))
221 }
222
223 SvaExpr::Implication {
224 antecedent,
225 consequent,
226 overlapping,
227 } => {
228 let ante = self.translate(antecedent, t);
229 let cons_t = if *overlapping { t } else { t + 1 };
230 let prev_queue = self.queue_timestep;
232 self.queue_timestep = Some(t);
233 let cons = self.translate(consequent, cons_t);
234 self.queue_timestep = prev_queue;
235 BoundedExpr::Implies(Box::new(ante), Box::new(cons))
236 }
237
238 SvaExpr::Delay { body, min, max } => match max {
239 Some(max_val) if max_val == min => {
241 self.translate(body, t + min)
243 }
244 Some(max_val) => {
245 let mut result: Option<BoundedExpr> = None;
247 for offset in *min..=*max_val {
248 let step = t + offset;
249 if step > t + self.bound {
250 break;
251 }
252 let b = self.translate(body, step);
253 result = Some(match result {
254 None => b,
255 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(b)),
256 });
257 }
258 result.unwrap_or(BoundedExpr::Bool(false))
259 }
260 None => {
261 let mut result: Option<BoundedExpr> = None;
263 for offset in *min..=self.bound {
264 let step = t + offset;
265 if step > t + self.bound {
266 break;
267 }
268 let b = self.translate(body, step);
269 result = Some(match result {
270 None => b,
271 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(b)),
272 });
273 }
274 result.unwrap_or(BoundedExpr::Bool(false))
275 }
276 },
277
278 SvaExpr::SEventually(inner) => {
279 let mut result: Option<BoundedExpr> = None;
281 for offset in 1..=self.bound {
282 let b = self.translate(inner, t + offset);
283 result = Some(match result {
284 None => b,
285 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(b)),
286 });
287 }
288 result.unwrap_or(BoundedExpr::Bool(false))
289 }
290
291 SvaExpr::Stable(inner) => {
292 if t == 0 {
295 BoundedExpr::Bool(true)
296 } else {
297 let current = self.translate(inner, t);
298 let previous = self.translate(inner, t - 1);
299 BoundedExpr::Eq(Box::new(current), Box::new(previous))
300 }
301 }
302
303 SvaExpr::Changed(inner) => {
304 if t == 0 {
307 BoundedExpr::Bool(false)
308 } else {
309 let current = self.translate(inner, t);
310 let previous = self.translate(inner, t - 1);
311 BoundedExpr::Not(Box::new(BoundedExpr::Eq(
312 Box::new(current),
313 Box::new(previous),
314 )))
315 }
316 }
317
318 SvaExpr::Nexttime(inner, n) => {
319 self.translate(inner, t + n)
321 }
322
323 SvaExpr::DisableIff { condition, body } => {
324 let cond = self.translate(condition, t);
327 let prop = self.translate(body, t);
328 BoundedExpr::Implies(
329 Box::new(BoundedExpr::Not(Box::new(cond))),
330 Box::new(prop),
331 )
332 }
333
334 SvaExpr::Repetition { body, min, max } => {
335 let effective_max = match max {
336 Some(m) => *m,
337 None => (*min).max(1) + self.bound,
338 };
339 if *min == effective_max {
340 let mut result: Option<BoundedExpr> = None;
342 for offset in 0..*min {
343 let b = self.translate(body, t + offset);
344 result = Some(match result {
345 None => b,
346 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
347 });
348 }
349 result.unwrap_or(BoundedExpr::Bool(true))
350 } else {
351 let mut outer: Option<BoundedExpr> = None;
353 for len in *min..=effective_max {
354 if len > self.bound + t {
355 break;
356 }
357 let mut inner_conj: Option<BoundedExpr> = None;
358 for offset in 0..len {
359 let b = self.translate(body, t + offset);
360 inner_conj = Some(match inner_conj {
361 None => b,
362 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
363 });
364 }
365 let conj = inner_conj.unwrap_or(BoundedExpr::Bool(true));
366 outer = Some(match outer {
367 None => conj,
368 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(conj)),
369 });
370 }
371 outer.unwrap_or(BoundedExpr::Bool(false))
372 }
373 }
374
375 SvaExpr::SAlways(inner) => {
376 let remaining = if self.bound > t { self.bound - t } else { 1 };
378 let mut result: Option<BoundedExpr> = None;
379 for offset in 0..remaining {
380 let b = self.translate(inner, t + offset);
381 result = Some(match result {
382 None => b,
383 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
384 });
385 }
386 result.unwrap_or(BoundedExpr::Bool(true))
387 }
388
389 SvaExpr::IfElse { condition, then_expr, else_expr } => {
390 let c = self.translate(condition, t);
392 let p = self.translate(then_expr, t);
393 let q = self.translate(else_expr, t);
394 BoundedExpr::And(
395 Box::new(BoundedExpr::Implies(Box::new(c.clone()), Box::new(p))),
396 Box::new(BoundedExpr::Implies(
397 Box::new(BoundedExpr::Not(Box::new(c))),
398 Box::new(q),
399 )),
400 )
401 }
402
403 SvaExpr::NotEq(left, right) => {
406 let l = self.translate(left, t);
408 let r = self.translate(right, t);
409 BoundedExpr::Not(Box::new(BoundedExpr::Eq(Box::new(l), Box::new(r))))
410 }
411
412 SvaExpr::LessThan(left, right) => {
413 let l = self.translate(left, t);
414 let r = self.translate(right, t);
415 BoundedExpr::Lt(Box::new(l), Box::new(r))
416 }
417
418 SvaExpr::GreaterThan(left, right) => {
419 let l = self.translate(left, t);
420 let r = self.translate(right, t);
421 BoundedExpr::Gt(Box::new(l), Box::new(r))
422 }
423
424 SvaExpr::LessEqual(left, right) => {
425 let l = self.translate(left, t);
426 let r = self.translate(right, t);
427 BoundedExpr::Lte(Box::new(l), Box::new(r))
428 }
429
430 SvaExpr::GreaterEqual(left, right) => {
431 let l = self.translate(left, t);
432 let r = self.translate(right, t);
433 BoundedExpr::Gte(Box::new(l), Box::new(r))
434 }
435
436 SvaExpr::Ternary { condition, then_expr, else_expr } => {
437 let c = self.translate(condition, t);
439 let a = self.translate(then_expr, t);
440 let b = self.translate(else_expr, t);
441 BoundedExpr::Or(
442 Box::new(BoundedExpr::And(Box::new(c.clone()), Box::new(a))),
443 Box::new(BoundedExpr::And(
444 Box::new(BoundedExpr::Not(Box::new(c))),
445 Box::new(b),
446 )),
447 )
448 }
449
450 SvaExpr::Throughout { signal, sequence } => {
451 let seq_matches = self.translate_sequence(sequence, t);
456 let mut outer: Option<BoundedExpr> = None;
457 for sm in &seq_matches {
458 let mut sig_conj: Option<BoundedExpr> = None;
460 for offset in 0..=sm.length {
461 let s = self.translate(signal, t + offset);
462 sig_conj = Some(match sig_conj {
463 None => s,
464 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(s)),
465 });
466 }
467 let sig_all = sig_conj.unwrap_or(BoundedExpr::Bool(true));
468 let case = BoundedExpr::And(
470 Box::new(sig_all),
471 Box::new(sm.condition.clone()),
472 );
473 outer = Some(match outer {
474 None => case,
475 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(case)),
476 });
477 }
478 outer.unwrap_or(BoundedExpr::Bool(false))
479 }
480
481 SvaExpr::Within { inner, outer } => {
482 let outer_matches = self.translate_sequence(outer, t);
488 let mut result: Option<BoundedExpr> = None;
489 for om in &outer_matches {
490 for inner_start in 0..=om.length {
492 let inner_matches = self.translate_sequence(inner, t + inner_start);
493 for im in &inner_matches {
494 if inner_start + im.length <= om.length {
496 let case = BoundedExpr::And(
497 Box::new(om.condition.clone()),
498 Box::new(im.condition.clone()),
499 );
500 result = Some(match result {
501 None => case,
502 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(case)),
503 });
504 }
505 }
506 }
507 }
508 result.unwrap_or(BoundedExpr::Bool(false))
509 }
510
511 SvaExpr::FirstMatch(inner) => {
512 let matches = self.translate_sequence(inner, t);
516 if matches.is_empty() {
517 return BoundedExpr::Bool(false);
518 }
519 let min_length = matches.iter().map(|m| m.length).min().unwrap_or(0);
520 let mut result: Option<BoundedExpr> = None;
521 for m in &matches {
522 if m.length == min_length {
523 result = Some(match result {
524 None => m.condition.clone(),
525 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(m.condition.clone())),
526 });
527 }
528 }
529 result.unwrap_or(BoundedExpr::Bool(false))
530 }
531
532 SvaExpr::Intersect { left, right } => {
533 let left_matches = self.translate_sequence(left, t);
537 let right_matches = self.translate_sequence(right, t);
538 let mut outer: Option<BoundedExpr> = None;
539 for lm in &left_matches {
540 for rm in &right_matches {
541 if lm.length == rm.length {
542 let both = BoundedExpr::And(
544 Box::new(lm.condition.clone()),
545 Box::new(rm.condition.clone()),
546 );
547 outer = Some(match outer {
548 None => both,
549 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(both)),
550 });
551 }
552 }
553 }
554 outer.unwrap_or(BoundedExpr::Bool(false))
556 }
557
558 SvaExpr::OneHot0(inner) => {
561 let sig = self.translate(inner, t);
562 BoundedExpr::Apply {
563 name: "onehot0".to_string(),
564 args: vec![sig],
565 }
566 }
567
568 SvaExpr::OneHot(inner) => {
569 let sig = self.translate(inner, t);
570 BoundedExpr::Apply {
571 name: "onehot".to_string(),
572 args: vec![sig],
573 }
574 }
575
576 SvaExpr::CountOnes(inner) => {
577 let sig = self.translate(inner, t);
578 BoundedExpr::Apply {
579 name: "countones".to_string(),
580 args: vec![sig],
581 }
582 }
583
584 SvaExpr::IsUnknown(_) => {
585 BoundedExpr::Bool(false)
587 }
588
589 SvaExpr::Sampled(inner) => {
590 self.translate(inner, t)
592 }
593
594 SvaExpr::Bits(inner) => {
595 let sig = self.translate(inner, t);
596 BoundedExpr::Apply {
597 name: "bits".to_string(),
598 args: vec![sig],
599 }
600 }
601
602 SvaExpr::Clog2(inner) => {
603 let val = self.translate(inner, t);
604 if let BoundedExpr::Int(n) = &val {
605 let n = *n;
606 let result = if n <= 1 {
607 0i64
608 } else {
609 let u = n as u64;
612 u.next_power_of_two().trailing_zeros() as i64
617 };
618 BoundedExpr::Int(result)
619 } else {
620 BoundedExpr::Apply {
621 name: "clog2".to_string(),
622 args: vec![val],
623 }
624 }
625 }
626
627 SvaExpr::CountBits(inner, control_chars) => {
630 let sig = self.translate(inner, t);
631 BoundedExpr::Apply {
632 name: format!("countbits_{}", control_chars.iter().collect::<String>()),
633 args: vec![sig],
634 }
635 }
636
637 SvaExpr::IsUnbounded(_) => {
638 BoundedExpr::Bool(false)
641 }
642
643 SvaExpr::GotoRepetition { body, count } => {
646 if *count == 0 {
647 BoundedExpr::Bool(true)
648 } else if *count > self.bound {
649 BoundedExpr::Bool(false)
650 } else {
651 let combos = combinations(self.bound, *count);
653 let mut disj: Option<BoundedExpr> = None;
654 for combo in combos {
655 let mut conj: Option<BoundedExpr> = None;
656 for &pos in &combo {
657 let b = self.translate(body, t + pos);
658 conj = Some(match conj {
659 None => b,
660 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
661 });
662 }
663 let term = conj.unwrap_or(BoundedExpr::Bool(true));
664 disj = Some(match disj {
665 None => term,
666 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(term)),
667 });
668 }
669 disj.unwrap_or(BoundedExpr::Bool(false))
670 }
671 }
672
673 SvaExpr::NonConsecRepetition { body, min, max } => {
674 let effective_max = match max {
675 Some(m) => *m,
676 None => self.bound,
677 };
678 if *min > self.bound {
679 BoundedExpr::Bool(false)
680 } else {
681 let capped_max = effective_max.min(self.bound);
682 let all_positions: Vec<u32> = (1..=self.bound).collect();
683 let mut outer_disj: Option<BoundedExpr> = None;
684 for count in *min..=capped_max {
685 let combos = combinations(self.bound, count);
686 for combo in combos {
687 let mut conj: Option<BoundedExpr> = None;
689 for &pos in &all_positions {
690 let b = self.translate(body, t + pos);
691 let term = if combo.contains(&pos) {
692 b
693 } else {
694 BoundedExpr::Not(Box::new(b))
695 };
696 conj = Some(match conj {
697 None => term,
698 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(term)),
699 });
700 }
701 let term = conj.unwrap_or(BoundedExpr::Bool(true));
702 outer_disj = Some(match outer_disj {
703 None => term,
704 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(term)),
705 });
706 }
707 }
708 outer_disj.unwrap_or(BoundedExpr::Bool(false))
709 }
710 }
711
712 SvaExpr::AcceptOn { condition, body } => {
715 let c = self.translate(condition, t);
717 let p = self.translate(body, t);
718 BoundedExpr::Or(Box::new(c), Box::new(p))
719 }
720
721 SvaExpr::RejectOn { condition, body } => {
722 let c = self.translate(condition, t);
724 let p = self.translate(body, t);
725 BoundedExpr::And(
726 Box::new(BoundedExpr::Not(Box::new(c))),
727 Box::new(p),
728 )
729 }
730
731 SvaExpr::PropertyNot(inner) => {
734 let i = self.translate(inner, t);
735 BoundedExpr::Not(Box::new(i))
736 }
737
738 SvaExpr::PropertyImplies(left, right) => {
739 let l = self.translate(left, t);
740 let r = self.translate(right, t);
741 BoundedExpr::Implies(Box::new(l), Box::new(r))
742 }
743
744 SvaExpr::PropertyIff(left, right) => {
745 let l = self.translate(left, t);
747 let r = self.translate(right, t);
748 BoundedExpr::And(
749 Box::new(BoundedExpr::Implies(Box::new(l.clone()), Box::new(r.clone()))),
750 Box::new(BoundedExpr::Implies(Box::new(r), Box::new(l))),
751 )
752 }
753
754 SvaExpr::Always(inner) => {
757 let remaining = if self.bound > t { self.bound - t } else { 1 };
759 let mut result: Option<BoundedExpr> = None;
760 for offset in 0..remaining {
761 let b = self.translate(inner, t + offset);
762 result = Some(match result {
763 None => b,
764 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
765 });
766 }
767 result.unwrap_or(BoundedExpr::Bool(true))
768 }
769
770 SvaExpr::AlwaysBounded { body, min, max } => {
771 let effective_max = match max {
773 Some(m) => *m,
774 None => self.bound.saturating_sub(t), };
776 let mut result: Option<BoundedExpr> = None;
777 for i in *min..=effective_max {
778 if t + i >= self.bound + t { break; } let b = self.translate(body, t + i);
780 result = Some(match result {
781 None => b,
782 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
783 });
784 }
785 result.unwrap_or(BoundedExpr::Bool(true)) }
787
788 SvaExpr::SAlwaysBounded { body, min, max } => {
789 let mut result: Option<BoundedExpr> = None;
791 for i in *min..=*max {
792 let b = self.translate(body, t + i);
793 result = Some(match result {
794 None => b,
795 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
796 });
797 }
798 result.unwrap_or(BoundedExpr::Bool(true))
799 }
800
801 SvaExpr::EventuallyBounded { body, min, max } => {
802 let mut result: Option<BoundedExpr> = None;
804 for i in *min..=*max {
805 let b = self.translate(body, t + i);
806 result = Some(match result {
807 None => b,
808 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(b)),
809 });
810 }
811 result.unwrap_or(BoundedExpr::Bool(false))
812 }
813
814 SvaExpr::SEventuallyBounded { body, min, max } => {
815 let effective_max = match max {
817 Some(m) => *m,
818 None => self.bound.saturating_sub(t),
819 };
820 let mut result: Option<BoundedExpr> = None;
821 for i in *min..=effective_max {
822 let b = self.translate(body, t + i);
823 result = Some(match result {
824 None => b,
825 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(b)),
826 });
827 }
828 result.unwrap_or(BoundedExpr::Bool(false))
829 }
830
831 SvaExpr::Until { lhs, rhs, strong, inclusive } => {
832 let max_k = t + self.bound;
838 let mut outer: Option<BoundedExpr> = None;
839 for k in t..max_k {
840 let rhs_at_k = self.translate(rhs, k);
841 let end = if *inclusive { k + 1 } else { k };
843 let mut lhs_conj: Option<BoundedExpr> = None;
844 for j in t..end {
845 let l = self.translate(lhs, j);
846 lhs_conj = Some(match lhs_conj {
847 None => l,
848 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(l)),
849 });
850 }
851 let lhs_all = lhs_conj.unwrap_or(BoundedExpr::Bool(true));
852 let case = BoundedExpr::And(Box::new(rhs_at_k), Box::new(lhs_all));
853 outer = Some(match outer {
854 None => case,
855 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(case)),
856 });
857 }
858 if !strong {
859 let mut all_lhs: Option<BoundedExpr> = None;
861 for j in t..max_k {
862 let l = self.translate(lhs, j);
863 all_lhs = Some(match all_lhs {
864 None => l,
865 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(l)),
866 });
867 }
868 let fallback = all_lhs.unwrap_or(BoundedExpr::Bool(true));
869 outer = Some(match outer {
870 None => fallback,
871 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(fallback)),
872 });
873 }
874 outer.unwrap_or(BoundedExpr::Bool(false))
875 }
876
877 SvaExpr::Strong(inner) => {
880 let matches = self.translate_sequence(inner, t);
884 if matches.is_empty() {
885 return BoundedExpr::Bool(false);
886 }
887 let mut result: Option<BoundedExpr> = None;
888 for m in &matches {
889 let cond = m.condition.clone();
890 result = Some(match result {
891 None => cond,
892 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(cond)),
893 });
894 }
895 result.unwrap_or(BoundedExpr::Bool(false))
896 }
897
898 SvaExpr::Weak(inner) => {
899 let matches = self.translate_sequence(inner, t);
906 if matches.is_empty() {
907 return BoundedExpr::Bool(true);
908 }
909 let mut any_match: Option<BoundedExpr> = None;
911 for m in &matches {
912 let cond = m.condition.clone();
913 any_match = Some(match any_match {
914 None => cond,
915 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(cond)),
916 });
917 }
918 let max_length = matches.iter().map(|m| m.length).max().unwrap_or(0);
926 if t + max_length > self.bound {
927 BoundedExpr::Bool(true)
929 } else {
930 any_match.unwrap_or(BoundedExpr::Bool(true))
932 }
933 }
934
935 SvaExpr::SNexttime(inner, n) => {
936 if t + n >= self.bound {
940 BoundedExpr::Bool(false)
941 } else {
942 self.translate(inner, t + n)
943 }
944 }
945
946 SvaExpr::FollowedBy { antecedent, consequent, overlapping } => {
947 let impl_expr = SvaExpr::Implication {
950 antecedent: antecedent.clone(),
951 consequent: Box::new(SvaExpr::Not(consequent.clone())),
952 overlapping: *overlapping,
953 };
954 let impl_result = self.translate(&impl_expr, t);
955 BoundedExpr::Not(Box::new(impl_result))
956 }
957
958 SvaExpr::PropertyCase { expression, items, default } => {
959 let expr_val = self.translate(expression, t);
961 let mut result = match default {
962 Some(d) => self.translate(d, t),
963 None => BoundedExpr::Bool(true), };
965 for (vals, prop) in items.iter().rev() {
967 let prop_translated = self.translate(prop, t);
968 let mut cond: Option<BoundedExpr> = None;
970 for v in vals {
971 let v_translated = self.translate(v, t);
972 let eq = BoundedExpr::Eq(Box::new(expr_val.clone()), Box::new(v_translated));
973 cond = Some(match cond {
974 None => eq,
975 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(eq)),
976 });
977 }
978 let condition = cond.unwrap_or(BoundedExpr::Bool(false));
979 result = BoundedExpr::And(
981 Box::new(BoundedExpr::Implies(Box::new(condition.clone()), Box::new(prop_translated))),
982 Box::new(BoundedExpr::Implies(
983 Box::new(BoundedExpr::Not(Box::new(condition))),
984 Box::new(result),
985 )),
986 );
987 }
988 result
989 }
990
991 SvaExpr::SyncAcceptOn { condition, body } => {
992 let c = self.translate(condition, t);
995 let p = self.translate(body, t);
996 BoundedExpr::Or(Box::new(c), Box::new(p))
997 }
998
999 SvaExpr::SyncRejectOn { condition, body } => {
1000 let c = self.translate(condition, t);
1002 let p = self.translate(body, t);
1003 BoundedExpr::And(
1004 Box::new(BoundedExpr::Not(Box::new(c))),
1005 Box::new(p),
1006 )
1007 }
1008
1009 SvaExpr::SequenceAnd(left, right) => {
1012 let left_matches = self.translate_sequence(left, t);
1015 let right_matches = self.translate_sequence(right, t);
1016 let mut outer: Option<BoundedExpr> = None;
1017 for lm in &left_matches {
1018 for rm in &right_matches {
1019 let _composite_length = lm.length.max(rm.length);
1021 let both = BoundedExpr::And(
1022 Box::new(lm.condition.clone()),
1023 Box::new(rm.condition.clone()),
1024 );
1025 outer = Some(match outer {
1026 None => both,
1027 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(both)),
1028 });
1029 }
1030 }
1031 outer.unwrap_or(BoundedExpr::Bool(false))
1032 }
1033
1034 SvaExpr::SequenceOr(left, right) => {
1035 let left_matches = self.translate_sequence(left, t);
1038 let right_matches = self.translate_sequence(right, t);
1039 let mut outer: Option<BoundedExpr> = None;
1040 for m in left_matches.iter().chain(right_matches.iter()) {
1041 outer = Some(match outer {
1042 None => m.condition.clone(),
1043 Some(acc) => BoundedExpr::Or(Box::new(acc), Box::new(m.condition.clone())),
1044 });
1045 }
1046 outer.unwrap_or(BoundedExpr::Bool(false))
1047 }
1048
1049 SvaExpr::ImmediateAssert { expression, .. } => {
1052 self.translate(expression, t)
1054 }
1055
1056 SvaExpr::FieldAccess { signal, field } => {
1059 let sig = self.translate(signal, t);
1060 match &sig {
1062 BoundedExpr::Var(name) => {
1063 let field_var = format!("{}.{}", name.split('@').next().unwrap_or(name), field);
1064 let var_name = format!("{}@{}", field_var, t);
1065 self.declarations.insert(var_name.clone());
1066 BoundedExpr::Var(var_name)
1067 }
1068 _ => sig, }
1070 }
1071
1072 SvaExpr::EnumLiteral { value, .. } => {
1073 BoundedExpr::Var(value.clone())
1075 }
1076
1077 SvaExpr::Triggered(name) => {
1080 let var_name = format!("{}.triggered@{}", name, t);
1081 self.declarations.insert(var_name.clone());
1082 BoundedExpr::Var(var_name)
1083 }
1084
1085 SvaExpr::Matched(name) => {
1086 let var_name = format!("{}.matched@{}", name, t);
1087 self.declarations.insert(var_name.clone());
1088 BoundedExpr::Var(var_name)
1089 }
1090
1091 SvaExpr::BitAnd(l, r) => {
1094 let left = self.translate(l, t);
1095 let right = self.translate(r, t);
1096 BoundedExpr::BitVecBinary { op: BitVecBoundedOp::And, left: Box::new(left), right: Box::new(right) }
1097 }
1098
1099 SvaExpr::BitOr(l, r) => {
1100 let left = self.translate(l, t);
1101 let right = self.translate(r, t);
1102 BoundedExpr::BitVecBinary { op: BitVecBoundedOp::Or, left: Box::new(left), right: Box::new(right) }
1103 }
1104
1105 SvaExpr::BitXor(l, r) => {
1106 let left = self.translate(l, t);
1107 let right = self.translate(r, t);
1108 BoundedExpr::BitVecBinary { op: BitVecBoundedOp::Xor, left: Box::new(left), right: Box::new(right) }
1109 }
1110
1111 SvaExpr::BitNot(inner) => {
1112 let i = self.translate(inner, t);
1113 BoundedExpr::BitVecBinary { op: BitVecBoundedOp::Not, left: Box::new(i.clone()), right: Box::new(i) }
1114 }
1115
1116 SvaExpr::ReductionAnd(inner) => {
1117 let sig = self.translate(inner, t);
1118 BoundedExpr::Apply { name: "reduction_and".to_string(), args: vec![sig] }
1119 }
1120
1121 SvaExpr::ReductionOr(inner) => {
1122 let sig = self.translate(inner, t);
1123 BoundedExpr::Apply { name: "reduction_or".to_string(), args: vec![sig] }
1124 }
1125
1126 SvaExpr::ReductionXor(inner) => {
1127 let sig = self.translate(inner, t);
1128 BoundedExpr::Apply { name: "reduction_xor".to_string(), args: vec![sig] }
1129 }
1130
1131 SvaExpr::BitSelect { signal, index } => {
1132 let s = self.translate(signal, t);
1133 let i = self.translate(index, t);
1134 BoundedExpr::ArraySelect { array: Box::new(s), index: Box::new(i) }
1135 }
1136
1137 SvaExpr::PartSelect { signal, high, low } => {
1138 let s = self.translate(signal, t);
1139 BoundedExpr::BitVecExtract { high: *high, low: *low, operand: Box::new(s) }
1140 }
1141
1142 SvaExpr::Concat(items) => {
1143 if items.len() < 2 {
1144 return items.first().map(|i| self.translate(i, t)).unwrap_or(BoundedExpr::Bool(false));
1145 }
1146 let mut result = self.translate(&items[0], t);
1147 for item in &items[1..] {
1148 let r = self.translate(item, t);
1149 result = BoundedExpr::BitVecConcat(Box::new(result), Box::new(r));
1150 }
1151 result
1152 }
1153
1154 SvaExpr::SequenceAction { expression, assignments } => {
1157 let expr_cond = self.translate(expression, t);
1161 for (name, rhs) in assignments {
1162 let bound_value = self.translate(rhs, t);
1163 self.local_bindings.insert(name.clone(), bound_value);
1164 }
1165 expr_cond
1166 }
1167
1168 SvaExpr::LocalVar(name) => {
1169 if let Some(bound_value) = self.local_bindings.get(name) {
1172 bound_value.clone()
1173 } else {
1174 let var_name = format!("{}@{}", name, t);
1176 self.declarations.insert(var_name.clone());
1177 BoundedExpr::Var(var_name)
1178 }
1179 }
1180
1181 SvaExpr::ConstCast(inner) => {
1184 let freeze_t = self.queue_timestep.unwrap_or(t);
1187 self.translate(inner, freeze_t)
1188 }
1189
1190 SvaExpr::Clocked { body, clock, .. } => {
1192 let inner = self.translate(body, t);
1196 for decl in self.declarations.clone() {
1199 if !decl.contains("__clk_") {
1200 let tagged = format!("__clk_{}__{}", clock, decl);
1201 self.declarations.insert(tagged);
1202 }
1203 }
1204 inner
1205 }
1206
1207 SvaExpr::ArrayMap { .. } => {
1209 BoundedExpr::Unsupported("array map with unknown size".to_string())
1210 }
1211 SvaExpr::TypeThis => {
1212 BoundedExpr::Unsupported("type(this) in class scope".to_string())
1213 }
1214 SvaExpr::RealConst(_) => {
1215 BoundedExpr::Unsupported("real constant outside checker context".to_string())
1218 }
1219 }
1220 }
1221
1222 pub fn translate_sequence(&mut self, expr: &SvaExpr, t: u32) -> Vec<SequenceMatch> {
1233 match expr {
1234 SvaExpr::Delay { body, min, max } => {
1236 match max {
1237 Some(max_val) if max_val == min => {
1239 let body_matches = self.translate_sequence(body, t + min);
1241 body_matches.into_iter().map(|bm| SequenceMatch {
1242 condition: bm.condition,
1243 length: min + bm.length,
1244 }).collect()
1245 }
1246 Some(max_val) => {
1247 let mut matches = Vec::new();
1249 for offset in *min..=*max_val {
1250 if t + offset > t + self.bound { break; }
1251 let body_matches = self.translate_sequence(body, t + offset);
1252 for bm in body_matches {
1253 matches.push(SequenceMatch {
1254 condition: bm.condition,
1255 length: offset + bm.length,
1256 });
1257 }
1258 }
1259 if matches.is_empty() {
1260 matches.push(SequenceMatch {
1261 condition: BoundedExpr::Bool(false),
1262 length: *min,
1263 });
1264 }
1265 matches
1266 }
1267 None => {
1268 let mut matches = Vec::new();
1270 for offset in *min..=self.bound {
1271 if t + offset > t + self.bound { break; }
1272 let body_matches = self.translate_sequence(body, t + offset);
1273 for bm in body_matches {
1274 matches.push(SequenceMatch {
1275 condition: bm.condition,
1276 length: offset + bm.length,
1277 });
1278 }
1279 }
1280 if matches.is_empty() {
1281 matches.push(SequenceMatch {
1282 condition: BoundedExpr::Bool(false),
1283 length: *min,
1284 });
1285 }
1286 matches
1287 }
1288 }
1289 }
1290
1291 SvaExpr::Repetition { body, min, max } => {
1293 let effective_max = match max {
1294 Some(m) => *m,
1295 None => (*min).max(1) + self.bound,
1296 };
1297 let mut all_matches = Vec::new();
1298 for count in *min..=effective_max {
1299 if count > self.bound + 1 { break; }
1300 if count == 0 {
1301 all_matches.push(SequenceMatch {
1303 condition: BoundedExpr::Bool(true),
1304 length: 0,
1305 });
1306 continue;
1307 }
1308 let total_length = count - 1;
1312 if t + total_length > t + self.bound { break; }
1313 let mut cond: Option<BoundedExpr> = None;
1314 for offset in 0..count {
1315 let b = self.translate(body, t + offset);
1316 cond = Some(match cond {
1317 None => b,
1318 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
1319 });
1320 }
1321 all_matches.push(SequenceMatch {
1322 condition: cond.unwrap_or(BoundedExpr::Bool(true)),
1323 length: total_length,
1324 });
1325 }
1326 if all_matches.is_empty() {
1327 all_matches.push(SequenceMatch {
1328 condition: BoundedExpr::Bool(false),
1329 length: 0,
1330 });
1331 }
1332 all_matches
1333 }
1334
1335 SvaExpr::Implication { antecedent, consequent, overlapping } => {
1339 let ante_matches = self.translate_sequence(antecedent, t);
1340 let mut all_matches = Vec::new();
1341 for am in &ante_matches {
1342 let gap = if *overlapping { 0u32 } else { 1u32 };
1343 let cons_start = t + am.length + gap;
1344 if cons_start > t + self.bound { continue; }
1345 let cons_matches = self.translate_sequence(consequent, cons_start);
1346 for cm in &cons_matches {
1347 let total_length = am.length + gap + cm.length;
1348 if total_length > self.bound { continue; }
1349 all_matches.push(SequenceMatch {
1350 condition: BoundedExpr::And(
1351 Box::new(am.condition.clone()),
1352 Box::new(cm.condition.clone()),
1353 ),
1354 length: total_length,
1355 });
1356 }
1357 }
1358 if all_matches.is_empty() {
1359 all_matches.push(SequenceMatch {
1360 condition: BoundedExpr::Bool(false),
1361 length: 0,
1362 });
1363 }
1364 all_matches
1365 }
1366
1367 SvaExpr::GotoRepetition { body, count } => {
1369 if *count == 0 {
1370 return vec![SequenceMatch { condition: BoundedExpr::Bool(true), length: 0 }];
1371 }
1372 if *count > self.bound {
1373 return vec![SequenceMatch { condition: BoundedExpr::Bool(false), length: 0 }];
1374 }
1375 let combos = combinations(self.bound, *count);
1376 let mut matches = Vec::new();
1377 for combo in combos {
1378 let end_pos = combo.last().copied().unwrap_or(0);
1379 let mut conj: Option<BoundedExpr> = None;
1380 for &pos in &combo {
1381 let b = self.translate(body, t + pos);
1382 conj = Some(match conj {
1383 None => b,
1384 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(b)),
1385 });
1386 }
1387 matches.push(SequenceMatch {
1388 condition: conj.unwrap_or(BoundedExpr::Bool(true)),
1389 length: end_pos,
1390 });
1391 }
1392 matches
1393 }
1394
1395 SvaExpr::NonConsecRepetition { body, min, max } => {
1397 let effective_max = match max {
1398 Some(m) => *m,
1399 None => self.bound,
1400 };
1401 if *min > self.bound {
1402 return vec![SequenceMatch { condition: BoundedExpr::Bool(false), length: 0 }];
1403 }
1404 let capped_max = effective_max.min(self.bound);
1405 let all_positions: Vec<u32> = (1..=self.bound).collect();
1406 let mut all_matches = Vec::new();
1407 for count in *min..=capped_max {
1408 let combos = combinations(self.bound, count);
1409 for combo in combos {
1410 let mut conj: Option<BoundedExpr> = None;
1411 for &pos in &all_positions {
1412 let b = self.translate(body, t + pos);
1413 let term = if combo.contains(&pos) {
1414 b
1415 } else {
1416 BoundedExpr::Not(Box::new(b))
1417 };
1418 conj = Some(match conj {
1419 None => term,
1420 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(term)),
1421 });
1422 }
1423 all_matches.push(SequenceMatch {
1425 condition: conj.unwrap_or(BoundedExpr::Bool(true)),
1426 length: self.bound,
1427 });
1428 }
1429 }
1430 if all_matches.is_empty() {
1431 all_matches.push(SequenceMatch {
1432 condition: BoundedExpr::Bool(true),
1433 length: 0,
1434 });
1435 }
1436 all_matches
1437 }
1438
1439 SvaExpr::Strong(inner) | SvaExpr::Weak(inner) => {
1441 self.translate_sequence(inner, t)
1442 }
1443
1444 SvaExpr::FirstMatch(inner) => {
1446 let matches = self.translate_sequence(inner, t);
1447 if matches.is_empty() {
1448 return vec![SequenceMatch {
1449 condition: BoundedExpr::Bool(false),
1450 length: 0,
1451 }];
1452 }
1453 let min_length = matches.iter().map(|m| m.length).min().unwrap_or(0);
1454 matches.into_iter().filter(|m| m.length == min_length).collect()
1455 }
1456
1457 SvaExpr::Intersect { left, right } => {
1459 let left_matches = self.translate_sequence(left, t);
1460 let right_matches = self.translate_sequence(right, t);
1461 let mut combined = Vec::new();
1462 for lm in &left_matches {
1463 for rm in &right_matches {
1464 if lm.length == rm.length {
1465 combined.push(SequenceMatch {
1466 condition: BoundedExpr::And(
1467 Box::new(lm.condition.clone()),
1468 Box::new(rm.condition.clone()),
1469 ),
1470 length: lm.length,
1471 });
1472 }
1473 }
1474 }
1475 if combined.is_empty() {
1476 combined.push(SequenceMatch {
1477 condition: BoundedExpr::Bool(false),
1478 length: 0,
1479 });
1480 }
1481 combined
1482 }
1483
1484 SvaExpr::SequenceAnd(left, right) => {
1486 let left_matches = self.translate_sequence(left, t);
1487 let right_matches = self.translate_sequence(right, t);
1488 let mut combined = Vec::new();
1489 for lm in &left_matches {
1490 for rm in &right_matches {
1491 combined.push(SequenceMatch {
1492 condition: BoundedExpr::And(
1493 Box::new(lm.condition.clone()),
1494 Box::new(rm.condition.clone()),
1495 ),
1496 length: lm.length.max(rm.length),
1497 });
1498 }
1499 }
1500 if combined.is_empty() {
1501 combined.push(SequenceMatch {
1502 condition: BoundedExpr::Bool(false),
1503 length: 0,
1504 });
1505 }
1506 combined
1507 }
1508
1509 SvaExpr::SequenceOr(left, right) => {
1511 let mut left_matches = self.translate_sequence(left, t);
1512 let right_matches = self.translate_sequence(right, t);
1513 left_matches.extend(right_matches);
1514 if left_matches.is_empty() {
1515 left_matches.push(SequenceMatch {
1516 condition: BoundedExpr::Bool(false),
1517 length: 0,
1518 });
1519 }
1520 left_matches
1521 }
1522
1523 _ => {
1525 vec![SequenceMatch {
1526 condition: self.translate(expr, t),
1527 length: 0,
1528 }]
1529 }
1530 }
1531 }
1532
1533 fn sequence_span(&self, expr: &SvaExpr) -> u32 {
1535 match expr {
1536 SvaExpr::Delay { min, max, body } => {
1537 let delay_span = max.unwrap_or(self.bound);
1539 delay_span + self.sequence_span(body)
1540 }
1541 SvaExpr::Repetition { min, max, body } => {
1542 let rep_count = max.unwrap_or((*min).max(1) + self.bound);
1544 rep_count * self.sequence_span(body).max(1)
1545 }
1546 SvaExpr::And(l, r) | SvaExpr::Or(l, r) => {
1547 self.sequence_span(l).max(self.sequence_span(r))
1548 }
1549 SvaExpr::Implication { antecedent, consequent, overlapping } => {
1550 let ante_span = self.sequence_span(antecedent);
1551 let cons_span = self.sequence_span(consequent);
1552 ante_span + cons_span + if *overlapping { 0 } else { 1 }
1553 }
1554 SvaExpr::GotoRepetition { count, body } => {
1555 *count * self.sequence_span(body).max(1)
1556 }
1557 SvaExpr::NonConsecRepetition { min, body, .. } => {
1558 *min * self.sequence_span(body).max(1)
1559 }
1560 SvaExpr::AcceptOn { body, .. } | SvaExpr::RejectOn { body, .. } => {
1561 self.sequence_span(body)
1562 }
1563 _ => 1, }
1565 }
1566
1567 pub fn translate_property(&mut self, expr: &SvaExpr) -> TranslateResult {
1570 let mut result: Option<BoundedExpr> = None;
1571 for t in 0..self.bound {
1572 let step = self.translate(expr, t);
1573 result = Some(match result {
1574 None => step,
1575 Some(acc) => BoundedExpr::And(Box::new(acc), Box::new(step)),
1576 });
1577 }
1578 let expr = result.unwrap_or(BoundedExpr::Bool(true));
1579 let declarations: Vec<String> = self.declarations.iter().cloned().collect();
1580 TranslateResult {
1581 expr,
1582 declarations,
1583 }
1584 }
1585
1586 pub fn translate_directive(&mut self, directive: &super::sva_model::SvaDirective) -> DirectiveResult {
1591 use super::sva_model::SvaDirectiveKind;
1592
1593 let effective_property = if let Some(ref disable_cond) = directive.disable_iff {
1595 SvaExpr::DisableIff {
1596 condition: Box::new(disable_cond.clone()),
1597 body: Box::new(directive.property.clone()),
1598 }
1599 } else {
1600 directive.property.clone()
1601 };
1602
1603 let translated = self.translate_property(&effective_property);
1604
1605 match directive.kind {
1606 SvaDirectiveKind::Assert => DirectiveResult {
1607 expr: translated.expr,
1608 declarations: translated.declarations,
1609 role: DirectiveRole::Check,
1610 },
1611 SvaDirectiveKind::Assume | SvaDirectiveKind::Restrict => DirectiveResult {
1612 expr: translated.expr,
1613 declarations: translated.declarations,
1614 role: DirectiveRole::Constraint,
1615 },
1616 SvaDirectiveKind::Cover => DirectiveResult {
1617 expr: translated.expr,
1618 declarations: translated.declarations,
1619 role: DirectiveRole::Reachability,
1620 },
1621 SvaDirectiveKind::CoverSequence => DirectiveResult {
1622 expr: translated.expr,
1623 declarations: translated.declarations,
1624 role: DirectiveRole::ReachabilityMultiple,
1625 },
1626 }
1627 }
1628}
1629
1630#[derive(Debug, Clone, PartialEq)]
1632pub enum DirectiveRole {
1633 Check,
1635 Constraint,
1637 Reachability,
1639 ReachabilityMultiple,
1641}
1642
1643fn combinations(n: u32, k: u32) -> Vec<Vec<u32>> {
1646 if k == 0 {
1647 return vec![vec![]];
1648 }
1649 if k > n {
1650 return vec![];
1651 }
1652 let mut result = Vec::new();
1653 fn helper(start: u32, n: u32, k: u32, current: &mut Vec<u32>, result: &mut Vec<Vec<u32>>) {
1654 if current.len() == k as usize {
1655 result.push(current.clone());
1656 return;
1657 }
1658 for i in start..=n {
1659 current.push(i);
1660 helper(i + 1, n, k, current, result);
1661 current.pop();
1662 }
1663 }
1664 helper(1, n, k, &mut Vec::new(), &mut result);
1665 result
1666}
1667
1668pub fn count_or_leaves(e: &BoundedExpr) -> usize {
1670 match e {
1671 BoundedExpr::Or(left, right) => count_or_leaves(left) + count_or_leaves(right),
1672 _ => 1,
1673 }
1674}
1675
1676pub fn count_and_leaves(e: &BoundedExpr) -> usize {
1678 match e {
1679 BoundedExpr::And(left, right) => count_and_leaves(left) + count_and_leaves(right),
1680 _ => 1,
1681 }
1682}
1683
1684fn collect_vars_from_bounded(expr: &BoundedExpr, vars: &mut std::collections::HashSet<String>) {
1686 match expr {
1687 BoundedExpr::Var(name) => { vars.insert(name.clone()); }
1688 BoundedExpr::And(l, r) | BoundedExpr::Or(l, r)
1689 | BoundedExpr::Implies(l, r) | BoundedExpr::Eq(l, r)
1690 | BoundedExpr::Lt(l, r) | BoundedExpr::Gt(l, r)
1691 | BoundedExpr::Lte(l, r) | BoundedExpr::Gte(l, r)
1692 | BoundedExpr::BitVecConcat(l, r) => {
1693 collect_vars_from_bounded(l, vars);
1694 collect_vars_from_bounded(r, vars);
1695 }
1696 BoundedExpr::Not(inner) => collect_vars_from_bounded(inner, vars),
1697 BoundedExpr::BitVecVar(name, _) => { vars.insert(name.clone()); }
1698 BoundedExpr::BitVecBinary { left, right, .. }
1699 | BoundedExpr::IntBinary { left, right, .. }
1700 | BoundedExpr::Comparison { left, right, .. } => {
1701 collect_vars_from_bounded(left, vars);
1702 collect_vars_from_bounded(right, vars);
1703 }
1704 BoundedExpr::BitVecExtract { operand, .. } => collect_vars_from_bounded(operand, vars),
1705 BoundedExpr::ArraySelect { array, index } => {
1706 collect_vars_from_bounded(array, vars);
1707 collect_vars_from_bounded(index, vars);
1708 }
1709 BoundedExpr::ArrayStore { array, index, value } => {
1710 collect_vars_from_bounded(array, vars);
1711 collect_vars_from_bounded(index, vars);
1712 collect_vars_from_bounded(value, vars);
1713 }
1714 BoundedExpr::ForAll { body, .. } | BoundedExpr::Exists { body, .. } => {
1715 collect_vars_from_bounded(body, vars);
1716 }
1717 BoundedExpr::Apply { args, .. } => {
1718 for arg in args {
1719 collect_vars_from_bounded(arg, vars);
1720 }
1721 }
1722 BoundedExpr::Bool(_) | BoundedExpr::Int(_)
1723 | BoundedExpr::BitVecConst { .. } | BoundedExpr::Unsupported(_) => {}
1724 }
1725}
1726
1727pub fn collect_vars_from_bounded_pub(expr: &BoundedExpr, vars: &mut std::collections::HashSet<String>) {
1729 collect_vars_from_bounded(expr, vars);
1730}
1731
1732#[cfg(feature = "verification")]
1738pub fn bounded_to_verify(expr: &BoundedExpr) -> logicaffeine_verify::VerifyExpr {
1739 use logicaffeine_verify::{VerifyExpr, VerifyOp};
1740 match expr {
1741 BoundedExpr::Var(name) => VerifyExpr::Var(name.clone()),
1742 BoundedExpr::Bool(b) => VerifyExpr::Bool(*b),
1743 BoundedExpr::Int(i) => VerifyExpr::Int(*i),
1744 BoundedExpr::And(l, r) => VerifyExpr::binary(
1745 VerifyOp::And,
1746 bounded_to_verify(l),
1747 bounded_to_verify(r),
1748 ),
1749 BoundedExpr::Or(l, r) => VerifyExpr::binary(
1750 VerifyOp::Or,
1751 bounded_to_verify(l),
1752 bounded_to_verify(r),
1753 ),
1754 BoundedExpr::Not(e) => VerifyExpr::not(bounded_to_verify(e)),
1755 BoundedExpr::Implies(l, r) => VerifyExpr::binary(
1756 VerifyOp::Implies,
1757 bounded_to_verify(l),
1758 bounded_to_verify(r),
1759 ),
1760 BoundedExpr::Eq(l, r) => VerifyExpr::binary(
1761 VerifyOp::Eq,
1762 bounded_to_verify(l),
1763 bounded_to_verify(r),
1764 ),
1765 BoundedExpr::Lt(l, r) => VerifyExpr::binary(
1766 VerifyOp::Lt,
1767 bounded_to_verify(l),
1768 bounded_to_verify(r),
1769 ),
1770 BoundedExpr::Gt(l, r) => VerifyExpr::binary(
1771 VerifyOp::Gt,
1772 bounded_to_verify(l),
1773 bounded_to_verify(r),
1774 ),
1775 BoundedExpr::Lte(l, r) => VerifyExpr::binary(
1776 VerifyOp::Lte,
1777 bounded_to_verify(l),
1778 bounded_to_verify(r),
1779 ),
1780 BoundedExpr::Gte(l, r) => VerifyExpr::binary(
1781 VerifyOp::Gte,
1782 bounded_to_verify(l),
1783 bounded_to_verify(r),
1784 ),
1785 BoundedExpr::Unsupported(_) => VerifyExpr::Bool(false),
1786
1787 BoundedExpr::BitVecConst { width, value } => VerifyExpr::bv_const(*width, *value),
1789 BoundedExpr::BitVecVar(name, width) => VerifyExpr::Var(format!("{name}_bv{width}")),
1794 BoundedExpr::BitVecBinary { op, left, right } => {
1795 use logicaffeine_verify::BitVecOp;
1796 let vop = match op {
1797 BitVecBoundedOp::And => BitVecOp::And,
1798 BitVecBoundedOp::Or => BitVecOp::Or,
1799 BitVecBoundedOp::Xor => BitVecOp::Xor,
1800 BitVecBoundedOp::Not => BitVecOp::Not,
1801 BitVecBoundedOp::Shl => BitVecOp::Shl,
1802 BitVecBoundedOp::Shr => BitVecOp::Shr,
1803 BitVecBoundedOp::AShr => BitVecOp::AShr,
1804 BitVecBoundedOp::Add => BitVecOp::Add,
1805 BitVecBoundedOp::Sub => BitVecOp::Sub,
1806 BitVecBoundedOp::Mul => BitVecOp::Mul,
1807 BitVecBoundedOp::ULt => BitVecOp::ULt,
1808 BitVecBoundedOp::SLt => BitVecOp::SLt,
1809 BitVecBoundedOp::Eq => BitVecOp::Eq,
1810 };
1811 VerifyExpr::bv_binary(vop, bounded_to_verify(left), bounded_to_verify(right))
1812 }
1813 BoundedExpr::BitVecExtract { high, low, operand } => VerifyExpr::BitVecExtract {
1814 high: *high,
1815 low: *low,
1816 operand: Box::new(bounded_to_verify(operand)),
1817 },
1818 BoundedExpr::BitVecConcat(l, r) => VerifyExpr::BitVecConcat(
1819 Box::new(bounded_to_verify(l)),
1820 Box::new(bounded_to_verify(r)),
1821 ),
1822 BoundedExpr::ArraySelect { array, index } => VerifyExpr::Select {
1823 array: Box::new(bounded_to_verify(array)),
1824 index: Box::new(bounded_to_verify(index)),
1825 },
1826 BoundedExpr::ArrayStore { array, index, value } => VerifyExpr::Store {
1827 array: Box::new(bounded_to_verify(array)),
1828 index: Box::new(bounded_to_verify(index)),
1829 value: Box::new(bounded_to_verify(value)),
1830 },
1831 BoundedExpr::IntBinary { op, left, right } => {
1832 let vop = match op {
1833 ArithBoundedOp::Add => VerifyOp::Add,
1834 ArithBoundedOp::Sub => VerifyOp::Sub,
1835 ArithBoundedOp::Mul => VerifyOp::Mul,
1836 ArithBoundedOp::Div => VerifyOp::Div,
1837 };
1838 VerifyExpr::binary(vop, bounded_to_verify(left), bounded_to_verify(right))
1839 }
1840 BoundedExpr::Comparison { op, left, right } => {
1841 let vop = match op {
1842 CmpBoundedOp::Gt => VerifyOp::Gt,
1843 CmpBoundedOp::Lt => VerifyOp::Lt,
1844 CmpBoundedOp::Gte => VerifyOp::Gte,
1845 CmpBoundedOp::Lte => VerifyOp::Lte,
1846 };
1847 VerifyExpr::binary(vop, bounded_to_verify(left), bounded_to_verify(right))
1848 }
1849 BoundedExpr::ForAll { var, sort, body } => {
1850 use logicaffeine_verify::VerifyType;
1851 let ty = match sort {
1852 BoundedSort::Bool => VerifyType::Bool,
1853 BoundedSort::Int => VerifyType::Int,
1854 BoundedSort::BitVec(w) => VerifyType::BitVector(*w),
1855 BoundedSort::Real => VerifyType::Real,
1856 };
1857 VerifyExpr::forall(vec![(var.clone(), ty)], bounded_to_verify(body))
1858 }
1859 BoundedExpr::Exists { var, sort, body } => {
1860 use logicaffeine_verify::VerifyType;
1861 let ty = match sort {
1862 BoundedSort::Bool => VerifyType::Bool,
1863 BoundedSort::Int => VerifyType::Int,
1864 BoundedSort::BitVec(w) => VerifyType::BitVector(*w),
1865 BoundedSort::Real => VerifyType::Real,
1866 };
1867 VerifyExpr::exists(vec![(var.clone(), ty)], bounded_to_verify(body))
1868 }
1869 BoundedExpr::Apply { name, args } => {
1870 VerifyExpr::Apply {
1871 name: name.clone(),
1872 args: args.iter().map(bounded_to_verify).collect(),
1873 }
1874 }
1875 }
1876}
1877
1878pub fn extract_signal_names(result: &TranslateResult) -> Vec<String> {
1881 let mut signals: HashSet<String> = HashSet::new();
1882 for decl in &result.declarations {
1883 if let Some(at_pos) = decl.find('@') {
1884 signals.insert(decl[..at_pos].to_string());
1885 } else {
1886 signals.insert(decl.clone());
1887 }
1888 }
1889 signals.into_iter().collect()
1890}