xlsynth-driver 0.61.0

Binary that integrates XLS capabilities into a driver program
Documentation
// SPDX-License-Identifier: Apache-2.0

use std::io::Write;
use std::path::Path;

use xlsynth_g8r::aig::get_summary_stats::AigStats;
use xlsynth_g8r::aig::get_summary_stats::get_aig_stats;
use xlsynth_g8r::aig_serdes::emit_aiger::emit_aiger;
use xlsynth_g8r::aig_serdes::emit_aiger_binary::emit_aiger_binary;
use xlsynth_g8r::netlist::gv2aig::{Gv2AigOptions, convert_gv2aig_paths_with_optional_liberty};

fn format_fanout_histogram(stats: &AigStats) -> String {
    let mut s = String::new();
    s.push('{');
    for (i, (fanout, count)) in stats.fanout_histogram.iter().enumerate() {
        if i != 0 {
            s.push(',');
        }
        s.push_str(&format!("{}:{}", fanout, count));
    }
    s.push('}');
    s
}

pub fn handle_gv2aig(matches: &clap::ArgMatches) {
    let netlist_path = matches.get_one::<String>("netlist").unwrap();
    let liberty_proto_path = matches.get_one::<String>("liberty_proto");
    let aiger_out = matches.get_one::<String>("aiger_out").unwrap();

    let module_name: Option<String> = matches.get_one::<String>("module_name").cloned();

    let collapse_sequential = matches
        .get_one::<bool>("collapse_sequential")
        .copied()
        .unwrap_or(true);

    let opts = Gv2AigOptions {
        module_name,
        collapse_sequential,
    };

    let gate_fn = match convert_gv2aig_paths_with_optional_liberty(
        Path::new(netlist_path),
        liberty_proto_path.map(|path| Path::new(path.as_str())),
        &opts,
    ) {
        Ok(g) => g,
        Err(e) => {
            eprintln!("Failed to convert netlist to AIG: {:#}", e);
            std::process::exit(1);
        }
    };

    let stats = get_aig_stats(&gate_fn);

    let is_binary_aig = Path::new(aiger_out)
        .extension()
        .and_then(|s| s.to_str())
        .is_some_and(|s| s.eq_ignore_ascii_case("aig"));

    if is_binary_aig {
        let bytes = match emit_aiger_binary(&gate_fn, true) {
            Ok(bytes) => bytes,
            Err(e) => {
                eprintln!("Failed to emit binary AIGER: {}", e);
                std::process::exit(1);
            }
        };
        let mut f = match std::fs::File::create(aiger_out) {
            Ok(f) => f,
            Err(e) => {
                eprintln!("Failed to create aiger-out file '{}': {}", aiger_out, e);
                std::process::exit(1);
            }
        };
        if let Err(e) = f.write_all(&bytes) {
            eprintln!("Failed to write aiger-out file '{}': {}", aiger_out, e);
            std::process::exit(1);
        }
    } else {
        let aiger = match emit_aiger(&gate_fn, true) {
            Ok(aiger) => aiger,
            Err(e) => {
                eprintln!("Failed to emit ASCII AIGER: {}", e);
                std::process::exit(1);
            }
        };
        let mut f = match std::fs::File::create(aiger_out) {
            Ok(f) => f,
            Err(e) => {
                eprintln!("Failed to create aiger-out file '{}': {}", aiger_out, e);
                std::process::exit(1);
            }
        };
        if let Err(e) = f.write_all(aiger.as_bytes()) {
            eprintln!("Failed to write aiger-out file '{}': {}", aiger_out, e);
            std::process::exit(1);
        }
    }

    println!(
        "aig stats: and_nodes={} depth={} fanout_hist={}",
        stats.and_nodes,
        stats.max_depth,
        format_fanout_histogram(&stats)
    );
}