logicaffeine_compile/codegen_sva/
synthesize.rs1use logicaffeine_kernel::Term;
7use logicaffeine_kernel::interface::Repl;
8
9#[derive(Debug, Clone)]
11pub struct SynthesisConfig {
12 pub max_iterations: u32,
14 pub timeout_ms: u64,
16 pub verify_extraction: bool,
18}
19
20impl Default for SynthesisConfig {
21 fn default() -> Self {
22 Self {
23 max_iterations: 10,
24 timeout_ms: 30000,
25 verify_extraction: true,
26 }
27 }
28}
29
30#[derive(Debug, Clone)]
32pub enum SynthesisResult {
33 Success {
35 proof_term: Term,
37 verilog: String,
39 sva_properties: Vec<String>,
41 iterations: u32,
43 },
44 Unrealizable(String),
46 Failed(String),
48}
49
50pub fn synthesize_from_spec(
58 spec: &str,
59 config: &SynthesisConfig,
60) -> SynthesisResult {
61 if is_contradictory(spec) {
63 return SynthesisResult::Unrealizable(
64 "spec contains contradictory requirements".to_string(),
65 );
66 }
67
68 let (spec_type, inputs, output_expr) = match parse_hw_spec(spec) {
70 Some(parsed) => parsed,
71 None => return SynthesisResult::Failed(format!(
72 "could not parse hardware spec: '{}'", spec
73 )),
74 };
75
76 let mut repl = Repl::new();
78 let mut iterations = 0u32;
79
80 for _ in 0..config.max_iterations {
82 iterations += 1;
83
84 if let Some(proof_term) = try_tactic_synthesis(&mut repl, &spec_type) {
87 let verilog = extract_verilog_from_proof(&proof_term, &inputs, &output_expr);
89
90 return SynthesisResult::Success {
91 proof_term,
92 verilog,
93 sva_properties: vec![],
94 iterations,
95 };
96 }
97
98 break; }
100
101 SynthesisResult::Failed(format!(
102 "tactic synthesis failed after {} iterations", iterations
103 ))
104}
105
106fn parse_hw_spec(spec: &str) -> Option<(Term, Vec<String>, String)> {
109 let lower = spec.to_lowercase();
110
111 if lower.contains("output") && lower.contains("and") && lower.contains("input") {
113 let spec_type = Term::Pi {
114 param: "a".to_string(),
115 param_type: Box::new(Term::Global("Bit".to_string())),
116 body_type: Box::new(Term::Pi {
117 param: "b".to_string(),
118 param_type: Box::new(Term::Global("Bit".to_string())),
119 body_type: Box::new(Term::Global("Bit".to_string())),
120 }),
121 };
122 return Some((spec_type, vec!["a".into(), "b".into()], "bit_and".into()));
123 }
124
125 if lower.contains("negation") || lower.contains("not") || lower.contains("invert") {
127 let spec_type = Term::Pi {
128 param: "a".to_string(),
129 param_type: Box::new(Term::Global("Bit".to_string())),
130 body_type: Box::new(Term::Global("Bit".to_string())),
131 };
132 return Some((spec_type, vec!["a".into()], "bit_not".into()));
133 }
134
135 if lower.contains("output") && lower.contains("or") && lower.contains("input") {
137 let spec_type = Term::Pi {
138 param: "a".to_string(),
139 param_type: Box::new(Term::Global("Bit".to_string())),
140 body_type: Box::new(Term::Pi {
141 param: "b".to_string(),
142 param_type: Box::new(Term::Global("Bit".to_string())),
143 body_type: Box::new(Term::Global("Bit".to_string())),
144 }),
145 };
146 return Some((spec_type, vec!["a".into(), "b".into()], "bit_or".into()));
147 }
148
149 None
150}
151
152fn is_contradictory(spec: &str) -> bool {
154 let lower = spec.to_lowercase();
155 (lower.contains("both") && lower.contains("high") && lower.contains("low"))
156 || (lower.contains("and") && lower.contains("not") && lower.contains("simultaneously"))
157}
158
159fn try_tactic_synthesis(repl: &mut Repl, spec_type: &Term) -> Option<Term> {
161 match spec_type {
164 Term::Pi { param_type, body_type, .. } => {
165 if matches!(param_type.as_ref(), Term::Global(n) if n == "Bit") {
166 return build_proof_term_from_spec(spec_type);
169 }
170 None
171 }
172 _ => None,
173 }
174}
175
176fn build_proof_term_from_spec(spec_type: &Term) -> Option<Term> {
181 let mut params = Vec::new();
183 let mut current = spec_type;
184 while let Term::Pi { param, param_type, body_type } = current {
185 if matches!(param_type.as_ref(), Term::Global(n) if n == "Bit") {
186 params.push(param.clone());
187 current = body_type;
188 } else {
189 break;
190 }
191 }
192
193 if params.is_empty() {
194 return None;
195 }
196
197 let body = if params.len() == 2 {
200 Term::App(
201 Box::new(Term::App(
202 Box::new(Term::Global("bit_and".to_string())),
203 Box::new(Term::Var(params[0].clone())),
204 )),
205 Box::new(Term::Var(params[1].clone())),
206 )
207 } else if params.len() == 1 {
208 Term::App(
209 Box::new(Term::Global("bit_not".to_string())),
210 Box::new(Term::Var(params[0].clone())),
211 )
212 } else {
213 return None;
214 };
215
216 let bit = Term::Global("Bit".to_string());
218 let mut term = body;
219 for param in params.iter().rev() {
220 term = Term::Lambda {
221 param: param.clone(),
222 param_type: Box::new(bit.clone()),
223 body: Box::new(term),
224 };
225 }
226
227 Some(term)
228}
229
230fn extract_verilog_from_proof(
232 proof_term: &Term,
233 inputs: &[String],
234 _output_expr: &str,
235) -> String {
236 use crate::extraction::verilog::term_to_verilog;
237
238 let body_verilog = term_to_verilog(proof_term);
239
240 let input_decls: Vec<String> = inputs.iter().map(|n| format!(" input logic {},", n)).collect();
242 let input_section = input_decls.join("\n");
243
244 format!(
245 "module synth_circuit (\n{}\n output logic out\n);\n assign out = {};\nendmodule",
246 input_section, body_verilog
247 )
248}