bitloom_sim/
formal_equiv.rs1use bitloom_hir::{FrozenHir, PortValues};
15
16use crate::{
17 AbstractionView, EquivStatus, check_functional_equiv, check_functional_equiv_generated,
18 check_generated_bridge_with,
19};
20
21#[derive(Debug, Clone)]
27pub struct FormalEquivProduct {
28 pub seed: u64,
30 pub random_cycles: usize,
32 boolean_ports: Vec<String>,
34 exhaustive_depth: usize,
36}
37
38impl FormalEquivProduct {
39 pub fn new(seed: u64, random_cycles: usize) -> Self {
42 Self {
43 seed,
44 random_cycles,
45 boolean_ports: Vec::new(),
46 exhaustive_depth: 0,
47 }
48 }
49
50 pub fn with_boolean_ports(mut self, ports: &[&str]) -> Self {
52 self.boolean_ports = ports.iter().map(|p| (*p).to_string()).collect();
53 self
54 }
55
56 pub fn with_exhaustive_depth(mut self, depth: usize) -> Self {
58 self.exhaustive_depth = depth;
59 self
60 }
61
62 pub fn alphabet(&self) -> Vec<PortValues> {
65 if self.boolean_ports.is_empty() {
66 return vec![PortValues::default()];
67 }
68 let n = self.boolean_ports.len();
69 assert!(
70 n <= 12,
71 "FormalEquivProduct alphabet supports at most 12 boolean ports (got {n}); F4 MVP scale"
72 );
73 let combinations = 1usize << n;
74 let mut out = Vec::with_capacity(combinations);
75 for mask in 0..combinations {
76 let mut pv = PortValues::default();
77 for (i, name) in self.boolean_ports.iter().enumerate() {
78 let bit = ((mask >> i) & 1) as u64;
79 pv.set(name, bit);
80 }
81 out.push(pv);
82 }
83 out
84 }
85
86 pub fn random_stimuli(&self) -> Vec<PortValues> {
88 let alphabet = self.alphabet();
89 let mut state = self.seed;
90 let mut out = Vec::with_capacity(self.random_cycles.max(1));
91 let n = self.random_cycles.max(1);
92 for _ in 0..n {
93 state = state.wrapping_mul(1664525).wrapping_add(1013904223);
95 let idx = (state as usize) % alphabet.len();
96 out.push(alphabet[idx].clone());
97 }
98 out
99 }
100
101 pub fn check_random_compare(&self, hir: FrozenHir) -> EquivStatus {
105 check_functional_equiv_generated(hir, self.random_stimuli())
106 }
107
108 pub fn check_random_compare_with<A: AbstractionView>(
111 &self,
112 hir: FrozenHir,
113 abs: &mut A,
114 ) -> EquivStatus {
115 check_generated_bridge_with(hir, abs, self.random_stimuli())
116 }
117
118 pub fn check_bounded_exhaustive(&self, hir: FrozenHir) -> EquivStatus {
124 let sequences = self.exhaustive_sequences();
125 let mut total_cycles = 0usize;
126 for seq in sequences {
127 match check_functional_equiv_generated(hir.clone(), seq) {
128 EquivStatus::Pass { cycles } => total_cycles = total_cycles.saturating_add(cycles),
129 fail @ EquivStatus::Fail { .. } => return fail,
130 }
131 }
132 EquivStatus::Pass {
133 cycles: total_cycles,
134 }
135 }
136
137 pub fn check_bounded_exhaustive_with<A: AbstractionView>(
139 &self,
140 hir: FrozenHir,
141 abs: &mut A,
142 ) -> EquivStatus {
143 let sequences = self.exhaustive_sequences();
144 let mut total_cycles = 0usize;
145 for seq in sequences {
146 match check_functional_equiv(hir.clone(), abs, seq) {
147 EquivStatus::Pass { cycles } => total_cycles = total_cycles.saturating_add(cycles),
148 fail @ EquivStatus::Fail { .. } => return fail,
149 }
150 }
151 EquivStatus::Pass {
152 cycles: total_cycles,
153 }
154 }
155
156 pub fn exhaustive_sequences(&self) -> Vec<Vec<PortValues>> {
158 let alphabet = self.alphabet();
159 let depth = self.exhaustive_depth;
160 if depth == 0 {
161 return vec![Vec::new()];
162 }
163 let mut sequences: Vec<Vec<PortValues>> = vec![Vec::new()];
164 for _ in 0..depth {
165 let mut next = Vec::with_capacity(sequences.len() * alphabet.len());
166 for prefix in &sequences {
167 for frame in &alphabet {
168 let mut seq = prefix.clone();
169 seq.push(frame.clone());
170 next.push(seq);
171 }
172 }
173 sequences = next;
174 }
175 sequences
176 }
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn alphabet_two_ports_has_four_frames() {
185 let p = FormalEquivProduct::new(0, 0).with_boolean_ports(&["rst", "en"]);
186 assert_eq!(p.alphabet().len(), 4);
187 }
188
189 #[test]
190 fn exhaustive_depth_two_bool_has_four_sequences() {
191 let p = FormalEquivProduct::new(0, 0)
192 .with_boolean_ports(&["rst"])
193 .with_exhaustive_depth(2);
194 assert_eq!(p.exhaustive_sequences().len(), 4);
195 }
196
197 #[test]
198 fn random_stimuli_reproducible() {
199 let p = FormalEquivProduct::new(42, 5).with_boolean_ports(&["rst"]);
200 assert_eq!(p.random_stimuli(), p.random_stimuli());
201 }
202}