xlsynth-driver 0.45.0

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

use std::path::Path;

use xlsynth_g8r::aig::GateFn;
use xlsynth_g8r::aig_serdes::emit_netlist::{
    emit_netlist, emit_netlist_with_version_and_port_style, NetlistPortStyle,
};
use xlsynth_g8r::aig_serdes::gate2ir::{
    repack_gate_fn_interface_with_schema, GateFnInterfacePort, GateFnInterfaceSchema,
};
use xlsynth_g8r::aig_serdes::load_aiger_auto::load_aiger_auto_from_path;
use xlsynth_g8r::gate_builder::GateBuilderOptions;
use xlsynth_g8r::verilog_version::VerilogVersion;
use xlsynth_pir::ir;

use crate::fn_type_arg::parse_function_type_text;

fn load_aig_gate_fn(path: &Path) -> Result<GateFn, String> {
    load_aiger_auto_from_path(path, GateBuilderOptions::no_opt())
        .map(|res| res.gate_fn)
        .map_err(|e| format!("failed to load {}: {}", path.display(), e))
}

fn require_nonzero_bits_type(kind: &str, ty: &ir::Type) -> Result<(), String> {
    match ty {
        ir::Type::Bits(0) => Err(format!(
            "aig2v --fn-type {kind} has zero width; packed Verilog ports require bits[N] with N > 0"
        )),
        ir::Type::Bits(_) => Ok(()),
        _ => Err(format!(
            "aig2v --fn-type currently supports only top-level bits[N] parameters and a bits[M] or top-level tuple of bits[M] return; {kind} has type {ty}"
        )),
    }
}

fn output_ports_from_supported_return_type(
    return_type: &ir::Type,
) -> Result<Vec<GateFnInterfacePort>, String> {
    match return_type {
        ir::Type::Bits(_) => {
            require_nonzero_bits_type("return value", return_type)?;
            Ok(vec![GateFnInterfacePort {
                name: "output_value".to_string(),
                ty: return_type.clone(),
            }])
        }
        ir::Type::Tuple(elements) => {
            if elements.is_empty() {
                return Err(
                    "aig2v --fn-type return value has zero width; packed Verilog ports require bits[N] with N > 0"
                        .to_string(),
                );
            }

            let mut output_ports = Vec::with_capacity(elements.len());
            for (index, element_type) in elements.iter().enumerate().rev() {
                require_nonzero_bits_type(&format!("return tuple element {index}"), element_type)?;
                output_ports.push(GateFnInterfacePort {
                    name: format!("output_value_{index}"),
                    ty: (**element_type).clone(),
                });
            }
            Ok(output_ports)
        }
        _ => {
            require_nonzero_bits_type("return value", return_type)?;
            unreachable!("non-bits return types are rejected above")
        }
    }
}

fn schema_from_supported_function_type(
    function_type: &ir::FunctionType,
) -> Result<GateFnInterfaceSchema, String> {
    for (index, param_type) in function_type.param_types.iter().enumerate() {
        require_nonzero_bits_type(&format!("parameter {index}"), param_type)?;
    }
    let output_ports = output_ports_from_supported_return_type(&function_type.return_type)?;

    Ok(GateFnInterfaceSchema {
        input_ports: function_type
            .param_types
            .iter()
            .enumerate()
            .map(|(index, ty)| GateFnInterfacePort {
                name: format!("arg{index}"),
                ty: ty.clone(),
            })
            .collect::<Vec<GateFnInterfacePort>>(),
        output_ports,
        return_type: function_type.return_type.clone(),
    })
}

pub fn handle_aig2v(matches: &clap::ArgMatches) -> Result<(), String> {
    let aig_input_file = matches.get_one::<String>("aig_input_file").unwrap();
    let module_name = matches.get_one::<String>("module-name").unwrap();
    let add_clk_port = matches.get_one::<String>("add-clk-port").cloned();
    let flop_inputs = matches.get_flag("flop-inputs");
    let flop_outputs = matches.get_flag("flop-outputs");
    let use_system_verilog = matches.get_flag("use-system-verilog");
    let fn_type = matches
        .get_one::<String>("fn_type")
        .map(|text| parse_function_type_text(text))
        .transpose()?;

    if (flop_inputs || flop_outputs) && add_clk_port.is_none() {
        return Err(
            "--add-clk-port <NAME> is required when --flop-inputs or --flop-outputs is used."
                .to_string(),
        );
    }

    let gate_fn = load_aig_gate_fn(Path::new(aig_input_file))?;
    let netlist_str = if let Some(function_type) = fn_type {
        let schema = schema_from_supported_function_type(&function_type)?;
        let gate_fn = repack_gate_fn_interface_with_schema(gate_fn, &schema)?;
        let version = if use_system_verilog {
            VerilogVersion::SystemVerilog
        } else {
            VerilogVersion::Verilog
        };
        emit_netlist_with_version_and_port_style(
            module_name,
            &gate_fn,
            flop_inputs,
            flop_outputs,
            version,
            add_clk_port,
            NetlistPortStyle::PackedBits,
        )?
    } else {
        emit_netlist(
            module_name,
            &gate_fn,
            flop_inputs,
            flop_outputs,
            use_system_verilog,
            add_clk_port,
        )?
    };

    println!("{}", netlist_str);
    Ok(())
}