1use crate::ir::{VerifyExpr, VerifyOp};
10use crate::automata::{ltl_to_buchi, BuchiAutomaton};
11use crate::ic3::check_sat;
12
13#[derive(Debug, Clone)]
15pub struct SignalDecl {
16 pub name: String,
17 pub width: Option<u32>,
18}
19
20#[derive(Debug)]
22pub enum SynthesisResult {
23 Realizable { controller: Circuit },
25 Unrealizable { reason: String },
27 Unknown,
29}
30
31#[derive(Debug, Clone)]
33pub struct Circuit {
34 pub inputs: Vec<SignalDecl>,
35 pub outputs: Vec<SignalDecl>,
36 pub states: Vec<String>,
37 pub init: String,
38 pub transitions: Vec<CircuitTransition>,
39}
40
41#[derive(Debug, Clone)]
43pub struct CircuitTransition {
44 pub from_state: String,
45 pub guard: VerifyExpr,
46 pub to_state: String,
47 pub outputs: Vec<(String, VerifyExpr)>,
48}
49
50pub fn synthesize_from_ltl(
56 spec: &VerifyExpr,
57 inputs: &[SignalDecl],
58 outputs: &[SignalDecl],
59) -> SynthesisResult {
60 if is_contradictory(spec) {
62 return SynthesisResult::Unrealizable {
63 reason: "Specification is contradictory".into(),
64 };
65 }
66
67 if is_tautology(spec) || outputs.is_empty() {
69 return SynthesisResult::Realizable {
70 controller: trivial_controller(inputs, outputs),
71 };
72 }
73
74 let safety_result = synthesize_safety(spec, inputs, outputs);
76 if matches!(safety_result, SynthesisResult::Realizable { .. }) {
77 return safety_result;
78 }
79
80 let buchi = ltl_to_buchi(spec);
82 synthesize_bounded(spec, inputs, outputs, &buchi)
83}
84
85fn synthesize_safety(
87 spec: &VerifyExpr,
88 inputs: &[SignalDecl],
89 outputs: &[SignalDecl],
90) -> SynthesisResult {
91 let mut transitions = Vec::new();
96 let output_assignments: Vec<(String, VerifyExpr)> = outputs.iter()
97 .map(|o| (o.name.clone(), VerifyExpr::bool(true)))
98 .collect();
99
100 let strategy_check = substitute_outputs(spec, &output_assignments);
102 if is_tautology(&strategy_check) {
103 transitions.push(CircuitTransition {
104 from_state: "s0".into(),
105 guard: VerifyExpr::bool(true),
106 to_state: "s0".into(),
107 outputs: output_assignments,
108 });
109
110 return SynthesisResult::Realizable {
111 controller: Circuit {
112 inputs: inputs.to_vec(),
113 outputs: outputs.to_vec(),
114 states: vec!["s0".into()],
115 init: "s0".into(),
116 transitions,
117 },
118 };
119 }
120
121 if inputs.len() == outputs.len() {
123 let mirror_assignments: Vec<(String, VerifyExpr)> = outputs.iter()
124 .zip(inputs.iter())
125 .map(|(o, i)| (o.name.clone(), VerifyExpr::var(&i.name)))
126 .collect();
127
128 let mirror_check = substitute_outputs(spec, &mirror_assignments);
129 if is_tautology(&mirror_check) {
130 transitions.push(CircuitTransition {
131 from_state: "s0".into(),
132 guard: VerifyExpr::bool(true),
133 to_state: "s0".into(),
134 outputs: mirror_assignments,
135 });
136
137 return SynthesisResult::Realizable {
138 controller: Circuit {
139 inputs: inputs.to_vec(),
140 outputs: outputs.to_vec(),
141 states: vec!["s0".into()],
142 init: "s0".into(),
143 transitions,
144 },
145 };
146 }
147 }
148
149 let false_assignments: Vec<(String, VerifyExpr)> = outputs.iter()
151 .map(|o| (o.name.clone(), VerifyExpr::bool(false)))
152 .collect();
153 let false_check = substitute_outputs(spec, &false_assignments);
154 if is_tautology(&false_check) {
155 transitions.push(CircuitTransition {
156 from_state: "s0".into(),
157 guard: VerifyExpr::bool(true),
158 to_state: "s0".into(),
159 outputs: false_assignments,
160 });
161 return SynthesisResult::Realizable {
162 controller: Circuit {
163 inputs: inputs.to_vec(),
164 outputs: outputs.to_vec(),
165 states: vec!["s0".into()],
166 init: "s0".into(),
167 transitions,
168 },
169 };
170 }
171
172 if !inputs.is_empty() && !outputs.is_empty() {
174 let resp_assignments: Vec<(String, VerifyExpr)> = outputs.iter()
175 .enumerate()
176 .map(|(i, o)| {
177 let value = if i < inputs.len() {
178 VerifyExpr::var(&inputs[i].name)
179 } else {
180 VerifyExpr::bool(true)
181 };
182 (o.name.clone(), value)
183 })
184 .collect();
185
186 let resp_check = substitute_outputs(spec, &resp_assignments);
187 if is_tautology(&resp_check) {
188 transitions.push(CircuitTransition {
189 from_state: "s0".into(),
190 guard: VerifyExpr::bool(true),
191 to_state: "s0".into(),
192 outputs: resp_assignments,
193 });
194 return SynthesisResult::Realizable {
195 controller: Circuit {
196 inputs: inputs.to_vec(),
197 outputs: outputs.to_vec(),
198 states: vec!["s0".into()],
199 init: "s0".into(),
200 transitions,
201 },
202 };
203 }
204 }
205
206 if !inputs.is_empty() && !outputs.is_empty() {
208 let neg_assignments: Vec<(String, VerifyExpr)> = outputs.iter()
209 .enumerate()
210 .map(|(i, o)| {
211 let value = if i < inputs.len() {
212 VerifyExpr::not(VerifyExpr::var(&inputs[i].name))
213 } else {
214 VerifyExpr::bool(false)
215 };
216 (o.name.clone(), value)
217 })
218 .collect();
219
220 let neg_check = substitute_outputs(spec, &neg_assignments);
221 if is_tautology(&neg_check) {
222 transitions.push(CircuitTransition {
223 from_state: "s0".into(),
224 guard: VerifyExpr::bool(true),
225 to_state: "s0".into(),
226 outputs: neg_assignments,
227 });
228 return SynthesisResult::Realizable {
229 controller: Circuit {
230 inputs: inputs.to_vec(),
231 outputs: outputs.to_vec(),
232 states: vec!["s0".into()],
233 init: "s0".into(),
234 transitions,
235 },
236 };
237 }
238 }
239
240 SynthesisResult::Unknown
241}
242
243fn synthesize_bounded(
245 spec: &VerifyExpr,
246 inputs: &[SignalDecl],
247 outputs: &[SignalDecl],
248 buchi: &BuchiAutomaton,
249) -> SynthesisResult {
250 let states: Vec<String> = buchi.states.iter()
251 .map(|s| format!("s{}", s.id))
252 .collect();
253
254 let mut transitions = Vec::new();
255 let mut all_valid = true;
256
257 let candidate_strategies: Vec<Vec<(String, VerifyExpr)>> = vec![
259 outputs.iter().map(|o| (o.name.clone(), VerifyExpr::bool(true))).collect(),
260 outputs.iter().map(|o| (o.name.clone(), VerifyExpr::bool(false))).collect(),
261 ];
262
263 for trans in &buchi.transitions {
264 let mut found_valid = false;
265 for strategy in &candidate_strategies {
266 let substituted = substitute_outputs(spec, strategy);
267 let check = VerifyExpr::and(trans.guard.clone(), VerifyExpr::not(substituted));
270 if !check_sat(&check) {
271 transitions.push(CircuitTransition {
272 from_state: format!("s{}", trans.from),
273 guard: trans.guard.clone(),
274 to_state: format!("s{}", trans.to),
275 outputs: strategy.clone(),
276 });
277 found_valid = true;
278 break;
279 }
280 }
281 if !found_valid {
282 all_valid = false;
283 break;
284 }
285 }
286
287 if all_valid {
288 SynthesisResult::Realizable {
289 controller: Circuit {
290 inputs: inputs.to_vec(),
291 outputs: outputs.to_vec(),
292 states,
293 init: format!("s{}", buchi.initial),
294 transitions,
295 },
296 }
297 } else {
298 SynthesisResult::Unknown
299 }
300}
301
302pub fn circuit_to_sva(circuit: &Circuit) -> String {
304 let mut sva = String::new();
305
306 for (i, trans) in circuit.transitions.iter().enumerate() {
307 sva.push_str(&format!(
308 "property p_trans_{};\n @(posedge clk) (state == {} && {}) |-> ##1 (state == {});\nendproperty\n",
309 i, trans.from_state, expr_to_sva(&trans.guard), trans.to_state
310 ));
311 }
312
313 sva
314}
315
316pub fn circuit_to_verilog(circuit: &Circuit) -> String {
318 let mut v = String::new();
319
320 let input_ports: Vec<String> = circuit.inputs.iter().map(|i| format!("input {}", i.name)).collect();
322 let output_ports: Vec<String> = circuit.outputs.iter().map(|o| format!("output reg {}", o.name)).collect();
323 let all_ports: Vec<String> = vec!["input clk".into(), "input rst".into()]
324 .into_iter().chain(input_ports).chain(output_ports).collect();
325
326 v.push_str(&format!("module controller(\n {}\n);\n\n", all_ports.join(",\n ")));
327
328 let state_bits = (circuit.states.len() as f64).log2().ceil() as u32;
330 let state_bits = state_bits.max(1);
331 v.push_str(&format!(" reg [{}:0] state;\n\n", state_bits - 1));
332
333 for (i, state) in circuit.states.iter().enumerate() {
335 v.push_str(&format!(" localparam {} = {};\n", state, i));
336 }
337 v.push('\n');
338
339 v.push_str(" always @(posedge clk or posedge rst) begin\n");
341 v.push_str(" if (rst) begin\n");
342 v.push_str(&format!(" state <= {};\n", circuit.init));
343 v.push_str(" end else begin\n");
344 v.push_str(" case (state)\n");
345
346 for state in &circuit.states {
347 v.push_str(&format!(" {}: begin\n", state));
348 let state_transitions: Vec<&CircuitTransition> = circuit.transitions.iter()
349 .filter(|t| t.from_state == *state)
350 .collect();
351 for trans in state_transitions {
352 v.push_str(&format!(" state <= {};\n", trans.to_state));
353 for (output, value) in &trans.outputs {
354 v.push_str(&format!(" {} <= {};\n", output, expr_to_verilog(value)));
355 }
356 }
357 v.push_str(" end\n");
358 }
359
360 v.push_str(" endcase\n");
361 v.push_str(" end\n");
362 v.push_str(" end\n\n");
363 v.push_str("endmodule\n");
364
365 v
366}
367
368fn trivial_controller(inputs: &[SignalDecl], outputs: &[SignalDecl]) -> Circuit {
369 Circuit {
370 inputs: inputs.to_vec(),
371 outputs: outputs.to_vec(),
372 states: vec!["s0".into()],
373 init: "s0".into(),
374 transitions: vec![CircuitTransition {
375 from_state: "s0".into(),
376 guard: VerifyExpr::bool(true),
377 to_state: "s0".into(),
378 outputs: outputs.iter().map(|o| (o.name.clone(), VerifyExpr::bool(true))).collect(),
379 }],
380 }
381}
382
383fn is_contradictory(spec: &VerifyExpr) -> bool {
384 !check_sat(spec)
385}
386
387fn is_tautology(expr: &VerifyExpr) -> bool {
388 !check_sat(&VerifyExpr::not(expr.clone()))
389}
390
391fn substitute_outputs(spec: &VerifyExpr, assignments: &[(String, VerifyExpr)]) -> VerifyExpr {
392 match spec {
393 VerifyExpr::Var(name) => {
394 for (out_name, value) in assignments {
395 if name == out_name {
396 return value.clone();
397 }
398 }
399 spec.clone()
400 }
401 VerifyExpr::Binary { op, left, right } => VerifyExpr::binary(
402 *op,
403 substitute_outputs(left, assignments),
404 substitute_outputs(right, assignments),
405 ),
406 VerifyExpr::Not(inner) => VerifyExpr::not(substitute_outputs(inner, assignments)),
407 _ => spec.clone(),
408 }
409}
410
411fn expr_to_sva(expr: &VerifyExpr) -> String {
412 match expr {
413 VerifyExpr::Bool(true) => "1".into(),
414 VerifyExpr::Bool(false) => "0".into(),
415 VerifyExpr::Var(name) => name.clone(),
416 VerifyExpr::Not(inner) => format!("!({})", expr_to_sva(inner)),
417 VerifyExpr::Binary { op: VerifyOp::And, left, right } => {
418 format!("({} && {})", expr_to_sva(left), expr_to_sva(right))
419 }
420 VerifyExpr::Binary { op: VerifyOp::Or, left, right } => {
421 format!("({} || {})", expr_to_sva(left), expr_to_sva(right))
422 }
423 VerifyExpr::Binary { op: VerifyOp::Implies, left, right } => {
424 format!("({} |-> {})", expr_to_sva(left), expr_to_sva(right))
425 }
426 _ => format!("{:?}", expr),
427 }
428}
429
430fn expr_to_verilog(expr: &VerifyExpr) -> String {
431 match expr {
432 VerifyExpr::Bool(true) => "1'b1".into(),
433 VerifyExpr::Bool(false) => "1'b0".into(),
434 VerifyExpr::Var(name) => name.clone(),
435 _ => "1'b0".into(),
436 }
437}