use crate::atpg::{
expose_dff, generate_comb_test_patterns, generate_random_seq_patterns,
report_comb_test_patterns,
};
use crate::equiv::check_equivalence_bounded;
use crate::io::{read_network_file, read_pattern_file, write_network_file, write_pattern_file};
use crate::optim;
use crate::sim::simulate;
use clap::{Args, Parser, Subcommand};
use std::path::PathBuf;
#[derive(Parser)]
#[command(author, version, about, long_about = None)]
#[command(propagate_version = true)]
pub struct Cli {
#[command(subcommand)]
pub command: Commands,
}
#[derive(Subcommand)]
pub enum Commands {
#[clap()]
Show(ShowArgs),
#[clap(visible_alias = "opt")]
Optimize(OptArgs),
#[clap(visible_alias = "sim")]
Simulate(SimulateArgs),
#[clap()]
Atpg(AtpgArgs),
#[clap(hide = true)]
AtpgReport(AtpgReportArgs),
#[clap(visible_alias = "equiv")]
CheckEquivalence(EquivArgs),
#[clap()]
Convert(ConvertArgs),
}
#[derive(Args)]
pub struct EquivArgs {
file1: PathBuf,
file2: PathBuf,
#[arg(short = 'c', long, default_value_t = 1)]
num_cycles: usize,
#[arg(long)]
sat_only: bool,
}
impl EquivArgs {
pub fn run(&self) {
let aig1 = read_network_file(&self.file1);
let aig2 = read_network_file(&self.file2);
if aig1.nb_inputs() != aig2.nb_inputs() {
println!(
"Different number of inputs: {} vs {}. Networks are not equivalent",
aig1.nb_inputs(),
aig2.nb_inputs()
);
std::process::exit(1);
}
if aig1.nb_outputs() != aig2.nb_outputs() {
println!(
"Different number of outputs: {} vs {}. Networks are not equivalent",
aig1.nb_outputs(),
aig2.nb_outputs()
);
std::process::exit(1);
}
let res = check_equivalence_bounded(&aig1, &aig2, self.num_cycles, !self.sat_only);
let is_comb = aig1.is_comb() && aig2.is_comb();
match res {
Err(err) => {
println!("Networks are not equivalent");
println!("Test pattern:");
for v in err {
print!("\t");
for b in v {
print!("{}", if b { "0" } else { "1" });
}
println!();
}
std::process::exit(1);
}
Ok(()) => {
if is_comb {
println!("Networks are equivalent");
} else {
println!("Networks are equivalent up to {} cycles", self.num_cycles);
}
std::process::exit(0);
}
}
}
}
#[derive(Args)]
pub struct OptArgs {
file: PathBuf,
#[arg(short = 'o', long)]
output: PathBuf,
#[arg(long, default_value_t = 1)]
effort: u64,
#[arg(long)]
seed: Option<u64>,
}
impl OptArgs {
pub fn run(&self) {
let mut aig = read_network_file(&self.file);
if let Some(s) = self.seed {
aig.shuffle(s);
}
aig.cleanup();
aig.make_canonical();
optim::share_logic(&mut aig, 64);
for _ in 0..self.effort {
optim::infer_xor_mux(&mut aig);
optim::infer_dffe(&mut aig);
optim::share_logic(&mut aig, 64);
}
write_network_file(&self.output, &aig);
}
}
#[derive(Args)]
pub struct ShowArgs {
file: PathBuf,
}
impl ShowArgs {
pub fn run(&self) {
use crate::network::stats::stats;
let aig = read_network_file(&self.file);
println!("Network stats:\n{}\n\n", stats(&aig));
}
}
#[derive(Args)]
pub struct ConvertArgs {
file: PathBuf,
destination: PathBuf,
}
impl ConvertArgs {
pub fn run(&self) {
let aig = read_network_file(&self.file);
write_network_file(&self.destination, &aig);
}
}
#[derive(Args)]
pub struct SimulateArgs {
network: PathBuf,
#[arg(short = 'i', long)]
input: PathBuf,
#[arg(short = 'o', long)]
output: PathBuf,
#[arg(long)]
expose_ff: bool,
}
impl SimulateArgs {
pub fn run(&self) {
let mut aig = read_network_file(&self.network);
if self.expose_ff {
aig = expose_dff(&aig);
}
let input_values = read_pattern_file(&self.input);
let mut output_values = Vec::new();
for pattern in &input_values {
output_values.push(simulate(&aig, pattern));
}
write_pattern_file(&self.output, &output_values);
}
}
#[derive(Args)]
pub struct AtpgArgs {
network: PathBuf,
#[arg(short = 'o', long)]
output: PathBuf,
#[arg(long, default_value_t = 1)]
seed: u64,
#[arg(short = 'c', long)]
num_cycles: Option<usize>,
#[arg(short = 'r', long)]
num_random: Option<usize>,
#[arg(long, default_value_t = false)]
with_redundant_faults: bool,
}
impl AtpgArgs {
pub fn run(&self) {
let mut aig = read_network_file(&self.network);
if self.num_cycles.is_none() && self.num_random.is_none() {
if !aig.is_comb() {
println!("Exposing flip-flops for a sequential network");
aig = expose_dff(&aig);
}
let patterns = generate_comb_test_patterns(&aig, self.seed, self.with_redundant_faults);
let seq_patterns = patterns.iter().map(|p| vec![p.clone()]).collect();
write_pattern_file(&self.output, &seq_patterns);
} else {
println!("Generating only random patterns for multiple cycles");
let nb_timesteps = self.num_cycles.unwrap_or(1);
let nb_patterns = self.num_random.unwrap_or(4 * (aig.nb_inputs() + 1));
let seq_patterns =
generate_random_seq_patterns(aig.nb_inputs(), nb_timesteps, nb_patterns, self.seed);
write_pattern_file(&self.output, &seq_patterns);
}
}
}
#[derive(Args)]
pub struct AtpgReportArgs {
network: PathBuf,
patterns: PathBuf,
#[arg(long, default_value_t = false)]
with_redundant_faults: bool,
}
impl AtpgReportArgs {
pub fn run(&self) {
let mut aig = read_network_file(&self.network);
if !aig.is_comb() {
println!("Exposing flip-flops for a sequential network");
aig = expose_dff(&aig);
}
let seq_patterns = read_pattern_file(&self.patterns);
let patterns = seq_patterns.iter().map(|p| p[0].clone()).collect();
report_comb_test_patterns(&aig, patterns, self.with_redundant_faults);
}
}