use clap::{arg, Parser, ValueEnum};
use libpatron::ir::*;
use libpatron::mc::Simulator;
use libpatron::sim::interpreter::{InitKind, Interpreter};
use libpatron::*;
use num_bigint::BigUint;
use num_traits::Num;
use std::collections::HashMap;
use std::io::BufRead;
#[derive(Parser, Debug)]
#[command(name = "sim")]
#[command(author = "Kevin Laeufer <laeufer@berkeley.edu>")]
#[command(version)]
#[command(about = "Simulates a transitions system from a btor2 file.", long_about = None)]
struct Args {
#[arg(short, long)]
verbose: bool,
#[arg(long, help = "print out signal values for each step")]
trace_values: bool,
#[arg(long, help = "prints out simulator instruction execution")]
trace_instructions: bool,
#[arg(long, help = "prints out interpreter instructions before execution")]
show_programs: bool,
#[arg(
long,
value_enum,
default_value = "random",
help = "initialization strategy"
)]
init: Init,
#[arg(long, help = "Filename of a testbench.")]
testbench: Option<String>,
#[arg(value_name = "BTOR2", index = 1)]
filename: String,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)]
pub enum Init {
Zero,
Random,
}
fn main() {
let args = Args::parse();
let (ctx, sys) = btor2::parse_file(&args.filename).expect("Failed to load btor2 file!");
if args.verbose {
println!("Loaded: {}", sys.name);
println!("{}", sys.serialize_to_str(&ctx));
println!();
println!();
}
let start_load = std::time::Instant::now();
let mut sim = if args.trace_instructions {
Interpreter::new_with_trace(&ctx, &sys)
} else {
Interpreter::new(&ctx, &sys)
};
if args.show_programs {
sim.print_programs();
}
match args.init {
Init::Zero => {
sim.init(InitKind::Zero);
}
Init::Random => {
sim.init(InitKind::Random(0));
}
}
let delta_load = std::time::Instant::now() - start_load;
println!("Loaded the design into the interpreter in {:?}", delta_load);
let testbench_file = match args.testbench {
None => {
println!("No testbench provided. Exiting...");
return;
}
Some(tb) => tb,
};
let mut tb = std::io::BufReader::new(
std::fs::File::open(testbench_file).expect("Failed to load testbench file"),
);
let name_to_ref = sys.generate_name_to_ref(&ctx);
let (inputs, outputs) =
read_header(&mut tb, &name_to_ref, &sys, &ctx).expect("Failed to read testbench header");
if args.verbose {
println!("Inputs: {inputs:?}");
println!("Outputs: {outputs:?}");
}
let mut signals_to_print: Vec<(String, ExprRef)> = Vec::new();
if args.trace_values {
for (name, expr) in name_to_ref.iter() {
if expr.get_type(&ctx).is_bit_vector() {
signals_to_print.push((name.clone(), expr.clone()));
}
}
signals_to_print.sort_by_key(|(name, _)| name.clone());
}
let start_exec = std::time::Instant::now();
for (step_id, line) in tb.lines().flatten().enumerate() {
do_step(
step_id,
&mut sim,
&line,
&inputs,
&outputs,
&signals_to_print,
);
}
let delta_exec = std::time::Instant::now() - start_exec;
println!("Executed {} steps in {:?}", sim.step_count(), delta_exec);
}
type IOInfo = Vec<(usize, ExprRef, String, WidthInt)>;
fn read_header(
input: &mut impl BufRead,
name_to_ref: &HashMap<String, ExprRef>,
sys: &TransitionSystem,
ctx: &Context,
) -> std::io::Result<(IOInfo, IOInfo)> {
let mut line = String::new();
input.read_line(&mut line)?;
let mut inputs = Vec::new();
let mut outputs = Vec::new();
for (cell_id, cell) in line.split(",").enumerate() {
let name = cell.trim();
if let Some(signal_ref) = name_to_ref.get(name) {
let width = signal_ref.get_bv_type(ctx).unwrap();
let signal = sys.get_signal(*signal_ref).unwrap();
if signal.is_input() {
inputs.push((cell_id, *signal_ref, name.to_string(), width));
}
if signal.is_output() {
outputs.push((cell_id, *signal_ref, name.to_string(), width));
}
}
}
Ok((inputs, outputs))
}
fn do_step(
step_id: usize,
sim: &mut impl Simulator,
line: &str,
inputs: &[(usize, ExprRef, String, WidthInt)],
outputs: &[(usize, ExprRef, String, WidthInt)],
signal_to_print: &[(String, ExprRef)],
) {
let mut input_iter = inputs.iter();
if let Some(mut input) = input_iter.next() {
for (cell_id, cell) in line.split(",").enumerate() {
if cell_id == input.0 {
let trimmed = cell.trim();
if trimmed.to_ascii_lowercase() != "x" {
let value = u64::from_str_radix(trimmed, 10).unwrap();
let width = input.3;
sim.set(input.1, (&Value::from_u64(value, width)).into());
}
if let Some(next_input) = input_iter.next() {
input = next_input;
} else {
break;
}
}
}
}
sim.update();
if !signal_to_print.is_empty() {
println!();
for (name, expr) in signal_to_print.iter() {
if let Some(value_ref) = sim.get(*expr) {
let value = value_ref.to_bit_string();
println!("{name}@{step_id} = {value}")
}
}
}
let mut output_iter = outputs.iter();
if let Some(mut output) = output_iter.next() {
for (cell_id, cell) in line.split(",").enumerate() {
if cell_id == output.0 {
let trimmed = cell.trim();
if trimmed.to_ascii_lowercase() != "x" {
let expected_big = BigUint::from_str_radix(trimmed, 10).unwrap();
let width = output.3;
let expected = Value::from_big_uint(&expected_big, width);
let actual = sim.get(output.1).unwrap();
assert_eq!(expected, actual, "{}@{step_id}", output.2);
}
if let Some(next_output) = output_iter.next() {
output = next_output;
} else {
break;
}
}
}
}
sim.step();
}