xlsynth-driver 0.61.0

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

use prost::Message;
use std::fs::File;
use std::io::Write;
use std::path::Path;
use xlsynth_g8r::liberty::CellFilterPolicy;
use xlsynth_g8r::liberty::descriptor::liberty_proto_bytes_to_pretty_textproto;
use xlsynth_g8r::liberty::model::library_to_proto;
use xlsynth_g8r::liberty::parser::{
    LibertyPayloadOptions, ThresholdVoltageGroupRule,
    parse_liberty_files_with_vt_rules_and_payload_options,
};

fn parse_vt_group_rule(raw: &str) -> Result<ThresholdVoltageGroupRule, String> {
    let mut parts = raw.splitn(3, ':');
    let Some(name) = parts.next() else {
        return Err(format!(
            "invalid --vt-group '{}'; expected NAME:CLASS_INDEX:REGEX",
            raw
        ));
    };
    let Some(class_index) = parts.next() else {
        return Err(format!(
            "invalid --vt-group '{}'; expected NAME:CLASS_INDEX:REGEX",
            raw
        ));
    };
    let Some(cell_name_regex) = parts.next() else {
        return Err(format!(
            "invalid --vt-group '{}'; expected NAME:CLASS_INDEX:REGEX",
            raw
        ));
    };
    if name.is_empty() {
        return Err(format!(
            "invalid --vt-group '{}'; VT group name must be non-empty",
            raw
        ));
    }
    if cell_name_regex.is_empty() {
        return Err(format!(
            "invalid --vt-group '{}'; regex must be non-empty",
            raw
        ));
    }
    let class_index = class_index.parse::<i32>().map_err(|e| {
        format!(
            "invalid --vt-group '{}'; VT class index must be a signed integer: {}",
            raw, e
        )
    })?;
    Ok(ThresholdVoltageGroupRule {
        name: name.to_string(),
        class_index,
        cell_name_regex: cell_name_regex.to_string(),
    })
}

pub fn handle_lib2proto(matches: &clap::ArgMatches) {
    let liberty_files: Vec<_> = matches
        .get_many::<String>("liberty_files")
        .unwrap()
        .map(|s| s.as_str())
        .collect();
    let output = matches.get_one::<String>("output").unwrap();
    let vt_group_rules = matches
        .get_many::<String>("vt_group")
        .map(|values| {
            values
                .map(|value| parse_vt_group_rule(value))
                .collect::<Result<Vec<_>, _>>()
        })
        .transpose()
        .unwrap_or_else(|e| panic!("{e}"))
        .unwrap_or_default();

    let payload_options = LibertyPayloadOptions {
        include_timing: matches.get_flag("include_timing"),
        include_power: matches.get_flag("include_power"),
    };
    let mut library = parse_liberty_files_with_vt_rules_and_payload_options(
        &liberty_files,
        &vt_group_rules,
        payload_options,
    )
    .expect("Failed to parse Liberty files");
    library.provenance = matches
        .get_one::<String>("provenance")
        .cloned()
        .unwrap_or_default();
    library.source_files = liberty_files
        .iter()
        .map(|path| {
            Path::new(path)
                .file_name()
                .and_then(|name| name.to_str())
                .unwrap_or(path)
                .to_string()
        })
        .collect();
    if let Some(policy_path) = matches.get_one::<String>("cell_filter_policy") {
        let policy = CellFilterPolicy::from_path(Path::new(policy_path))
            .unwrap_or_else(|e| panic!("Failed to load cell-filter policy: {e:#}"));
        let stats = policy.apply(&mut library);
        log::info!(
            "Applied {} cell-filter rules: input={} native_dont_use={} retained={} removed={}",
            policy.rules().len(),
            stats.input_cells,
            stats.native_dont_use_cells,
            stats.retained_cells,
            stats.removed_cells
        );
    }
    let proto = library_to_proto(library).expect("Failed to encode Liberty LUT data");

    if output.ends_with(".proto") {
        let mut f = File::create(output).expect("Failed to create output file");
        log::info!("Encoding proto...");
        let bytes = proto.encode_to_vec();
        log::info!("Writing proto...");
        f.write_all(&bytes).expect("Failed to write proto");
    } else if output.ends_with(".textproto") {
        log::info!("Encoding proto...");
        let encoded = proto.encode_to_vec();
        let text = liberty_proto_bytes_to_pretty_textproto(&encoded)
            .expect("Failed to format Liberty textproto");
        log::info!("Writing textproto...");
        let mut f = File::create(output).expect("Failed to create output file");
        f.write_all(text.as_bytes())
            .expect("Failed to write textproto");
    } else {
        panic!(
            "Output must end with .proto or .textproto; got {:?}",
            output
        );
    }
}