use logicaffeine_verify::equivalence::{Trace, CycleState, SignalValue};
use logicaffeine_verify::kinduction::SignalDecl;
pub fn trace_to_testbench(trace: &Trace, module_name: &str) -> String {
let mut tb = String::new();
let tb_name = format!("tb_{}", module_name);
tb.push_str(&format!("module {};\n\n", tb_name));
tb.push_str(" reg clk;\n");
tb.push_str(" initial clk = 0;\n");
tb.push_str(" always #5 clk = ~clk;\n\n");
let mut all_signals: Vec<String> = Vec::new();
for cycle in &trace.cycles {
for name in cycle.signals.keys() {
if !all_signals.contains(name) {
all_signals.push(name.clone());
}
}
}
all_signals.sort();
for sig in &all_signals {
let width = trace.cycles.iter()
.find_map(|c| c.signals.get(sig))
.map(|v| match v {
SignalValue::BitVec { width, .. } => *width,
_ => 1,
})
.unwrap_or(1);
if width > 1 {
tb.push_str(&format!(" reg [{:}:0] {};\n", width - 1, sig));
} else {
tb.push_str(&format!(" reg {};\n", sig));
}
}
tb.push('\n');
tb.push_str(&format!(" {} dut(.*);\n\n", module_name));
tb.push_str(" initial begin\n");
if trace.cycles.is_empty() {
tb.push_str(" // No stimulus (empty trace)\n");
} else {
for cycle in &trace.cycles {
tb.push_str(&format!(" // Cycle {}\n", cycle.cycle));
tb.push_str(" @(posedge clk);\n");
for sig in &all_signals {
if let Some(val) = cycle.signals.get(sig) {
let formatted = format_signal_value(sig, val);
tb.push_str(&format!(" {} = {};\n", sig, formatted));
}
}
}
if let Some(last) = trace.cycles.last() {
tb.push_str(&format!("\n $display(\"Violation at cycle {}\");\n", last.cycle));
}
}
tb.push_str(" $finish;\n");
tb.push_str(" end\n\n");
tb.push_str("endmodule\n");
tb
}
pub fn trace_to_stimulus(trace: &Trace, signals: &[SignalDecl]) -> String {
let mut stim = String::new();
for cycle in &trace.cycles {
stim.push_str(&format!("// Cycle {}\n", cycle.cycle));
stim.push_str("#10;\n");
for sig_decl in signals {
if let Some(val) = cycle.signals.get(&sig_decl.name) {
let formatted = format_signal_value(&sig_decl.name, val);
stim.push_str(&format!("{} = {};\n", sig_decl.name, formatted));
}
}
}
stim
}
fn format_signal_value(_name: &str, val: &SignalValue) -> String {
match val {
SignalValue::Bool(true) => "1'b1".into(),
SignalValue::Bool(false) => "1'b0".into(),
SignalValue::Int(n) => n.to_string(),
SignalValue::BitVec { width, value } => {
let hex_digits = ((*width + 3) / 4) as usize;
format!("{}'h{:0>width$X}", width, value, width = hex_digits)
}
SignalValue::Unknown => "1'bx".into(),
}
}