use logicaffeine_kernel::Term;
use logicaffeine_kernel::interface::Repl;
#[derive(Debug, Clone)]
pub struct SynthesisConfig {
pub max_iterations: u32,
pub timeout_ms: u64,
pub verify_extraction: bool,
}
impl Default for SynthesisConfig {
fn default() -> Self {
Self {
max_iterations: 10,
timeout_ms: 30000,
verify_extraction: true,
}
}
}
#[derive(Debug, Clone)]
pub enum SynthesisResult {
Success {
proof_term: Term,
verilog: String,
sva_properties: Vec<String>,
iterations: u32,
},
Unrealizable(String),
Failed(String),
}
pub fn synthesize_from_spec(
spec: &str,
config: &SynthesisConfig,
) -> SynthesisResult {
if is_contradictory(spec) {
return SynthesisResult::Unrealizable(
"spec contains contradictory requirements".to_string(),
);
}
let (spec_type, inputs, output_expr) = match parse_hw_spec(spec) {
Some(parsed) => parsed,
None => return SynthesisResult::Failed(format!(
"could not parse hardware spec: '{}'", spec
)),
};
let mut repl = Repl::new();
let mut iterations = 0u32;
for _ in 0..config.max_iterations {
iterations += 1;
if let Some(proof_term) = try_tactic_synthesis(&mut repl, &spec_type) {
let verilog = extract_verilog_from_proof(&proof_term, &inputs, &output_expr);
return SynthesisResult::Success {
proof_term,
verilog,
sva_properties: vec![],
iterations,
};
}
break; }
SynthesisResult::Failed(format!(
"tactic synthesis failed after {} iterations", iterations
))
}
fn parse_hw_spec(spec: &str) -> Option<(Term, Vec<String>, String)> {
let lower = spec.to_lowercase();
if lower.contains("output") && lower.contains("and") && lower.contains("input") {
let spec_type = Term::Pi {
param: "a".to_string(),
param_type: Box::new(Term::Global("Bit".to_string())),
body_type: Box::new(Term::Pi {
param: "b".to_string(),
param_type: Box::new(Term::Global("Bit".to_string())),
body_type: Box::new(Term::Global("Bit".to_string())),
}),
};
return Some((spec_type, vec!["a".into(), "b".into()], "bit_and".into()));
}
if lower.contains("negation") || lower.contains("not") || lower.contains("invert") {
let spec_type = Term::Pi {
param: "a".to_string(),
param_type: Box::new(Term::Global("Bit".to_string())),
body_type: Box::new(Term::Global("Bit".to_string())),
};
return Some((spec_type, vec!["a".into()], "bit_not".into()));
}
if lower.contains("output") && lower.contains("or") && lower.contains("input") {
let spec_type = Term::Pi {
param: "a".to_string(),
param_type: Box::new(Term::Global("Bit".to_string())),
body_type: Box::new(Term::Pi {
param: "b".to_string(),
param_type: Box::new(Term::Global("Bit".to_string())),
body_type: Box::new(Term::Global("Bit".to_string())),
}),
};
return Some((spec_type, vec!["a".into(), "b".into()], "bit_or".into()));
}
None
}
fn is_contradictory(spec: &str) -> bool {
let lower = spec.to_lowercase();
(lower.contains("both") && lower.contains("high") && lower.contains("low"))
|| (lower.contains("and") && lower.contains("not") && lower.contains("simultaneously"))
}
fn try_tactic_synthesis(repl: &mut Repl, spec_type: &Term) -> Option<Term> {
match spec_type {
Term::Pi { param_type, body_type, .. } => {
if matches!(param_type.as_ref(), Term::Global(n) if n == "Bit") {
return build_proof_term_from_spec(spec_type);
}
None
}
_ => None,
}
}
fn build_proof_term_from_spec(spec_type: &Term) -> Option<Term> {
let mut params = Vec::new();
let mut current = spec_type;
while let Term::Pi { param, param_type, body_type } = current {
if matches!(param_type.as_ref(), Term::Global(n) if n == "Bit") {
params.push(param.clone());
current = body_type;
} else {
break;
}
}
if params.is_empty() {
return None;
}
let body = if params.len() == 2 {
Term::App(
Box::new(Term::App(
Box::new(Term::Global("bit_and".to_string())),
Box::new(Term::Var(params[0].clone())),
)),
Box::new(Term::Var(params[1].clone())),
)
} else if params.len() == 1 {
Term::App(
Box::new(Term::Global("bit_not".to_string())),
Box::new(Term::Var(params[0].clone())),
)
} else {
return None;
};
let bit = Term::Global("Bit".to_string());
let mut term = body;
for param in params.iter().rev() {
term = Term::Lambda {
param: param.clone(),
param_type: Box::new(bit.clone()),
body: Box::new(term),
};
}
Some(term)
}
fn extract_verilog_from_proof(
proof_term: &Term,
inputs: &[String],
_output_expr: &str,
) -> String {
use crate::extraction::verilog::term_to_verilog;
let body_verilog = term_to_verilog(proof_term);
let input_decls: Vec<String> = inputs.iter().map(|n| format!(" input logic {},", n)).collect();
let input_section = input_decls.join("\n");
format!(
"module synth_circuit (\n{}\n output logic out\n);\n assign out = {};\nendmodule",
input_section, body_verilog
)
}