use std::process;
use clap::ArgMatches;
use crate::common::{
CodegenFlags, DEFAULT_WARNINGS_AS_ERRORS, PipelineSpec, enforce_extern_verilog_codegen_policy,
extract_codegen_flags, extract_pipeline_spec, parse_bool_flag, parse_bool_flag_or,
pipeline_codegen_flags_proto, resolve_type_inference_v2, scheduling_options_proto,
};
use crate::report_cli_error::report_cli_error_and_exit;
use crate::toolchain_config::ToolchainConfig;
use crate::tools::{run_codegen_pipeline, run_ir_converter_main, run_opt_main};
fn dslx2pipeline(
input_file: &std::path::Path,
dslx_top: &str,
pipeline_spec: &PipelineSpec,
codegen_flags: &CodegenFlags,
delay_model: &str,
keep_temps: &Option<bool>,
type_inference_v2: Option<bool>,
allow_extern_verilog: bool,
output_unopt_ir: &Option<&std::path::Path>,
output_opt_ir: &Option<&std::path::Path>,
config: &Option<ToolchainConfig>,
) {
log::info!("dslx2pipeline; config: {:?}", config);
let module_name = xlsynth::dslx_path_to_module_name(input_file).unwrap();
let ir_top = xlsynth::mangle_dslx_name(module_name, dslx_top).unwrap();
log::info!("dslx2pipeline; dslx_top: {}; ir_top: {}", dslx_top, ir_top);
if let Some(tool_path) = config.as_ref().and_then(|c| c.tool_path.as_deref()) {
log::info!("dslx2pipeline using tool path: {}", tool_path);
let mut temp_dir = tempfile::Builder::new()
.prefix("dslx2pipeline.")
.tempdir()
.unwrap();
let dslx_stdlib_path = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.dslx_stdlib_path.as_deref());
let dslx_path_slice = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.dslx_path.as_deref());
let dslx_path = dslx_path_slice.map(|s| s.join(":"));
let dslx_path_ref = dslx_path.as_ref().map(|s| s.as_str());
let enable_warnings = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.enable_warnings.as_deref());
let disable_warnings = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.disable_warnings.as_deref());
let unopt_ir = run_ir_converter_main(
input_file,
Some(dslx_top),
dslx_stdlib_path,
dslx_path_ref,
tool_path,
enable_warnings,
disable_warnings,
type_inference_v2,
false,
);
let unopt_ir_path = temp_dir.path().join("unopt.ir");
std::fs::write(&unopt_ir_path, unopt_ir).unwrap();
let opt_ir = run_opt_main(&unopt_ir_path, Some(&ir_top), tool_path);
enforce_extern_verilog_codegen_policy(&opt_ir, allow_extern_verilog)
.unwrap_or_else(|err| report_cli_error_and_exit(&err, Some("dslx2pipeline"), vec![]));
let opt_ir_path = temp_dir.path().join("opt.ir");
std::fs::write(&opt_ir_path, opt_ir.clone()).unwrap();
if let Some(path) = output_unopt_ir {
std::fs::write(path, &std::fs::read_to_string(&unopt_ir_path).unwrap())
.expect("write output_unopt_ir");
}
if let Some(path) = output_opt_ir {
std::fs::write(path, &std::fs::read_to_string(&opt_ir_path).unwrap())
.expect("write output_opt_ir");
}
let sv = run_codegen_pipeline(
&opt_ir_path,
delay_model,
pipeline_spec,
codegen_flags,
tool_path,
);
let sv_path = temp_dir.path().join("output.sv");
std::fs::write(&sv_path, &sv).unwrap();
if let Some(_) = keep_temps {
temp_dir.disable_cleanup(true);
eprintln!(
"Pipeline generation successful. Output written to: {}",
temp_dir.path().to_str().unwrap()
);
}
println!("{}", sv);
} else {
if type_inference_v2 == Some(true) {
eprintln!(
"error: --type_inference_v2 is only supported when using --toolchain (external tool path)"
);
std::process::exit(1);
}
log::info!("dslx2pipeline using runtime APIs");
let dslx = std::fs::read_to_string(input_file).unwrap();
let dslx_path = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.dslx_path.as_deref());
let dslx_path_vec = match dslx_path {
Some(entries) => entries
.iter()
.map(|p| std::path::Path::new(p))
.collect::<Vec<_>>(),
None => vec![],
};
let dslx_stdlib_path = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.dslx_stdlib_path.as_deref());
let enable_warnings = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.enable_warnings.as_deref());
let disable_warnings = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.disable_warnings.as_deref());
let warnings_as_errors = config
.as_ref()
.and_then(|c| c.dslx.as_ref()?.warnings_as_errors)
.unwrap_or(DEFAULT_WARNINGS_AS_ERRORS);
let convert_options = xlsynth::DslxConvertOptions {
dslx_stdlib_path: dslx_stdlib_path.map(|p| std::path::Path::new(p)),
additional_search_paths: dslx_path_vec,
enable_warnings: enable_warnings,
disable_warnings: disable_warnings,
force_implicit_token_calling_convention: false,
};
let convert_result: xlsynth::DslxToIrPackageResult =
xlsynth::convert_dslx_to_ir(&dslx, input_file, &convert_options)
.expect("DSLX to IR conversion failed");
if warnings_as_errors && !convert_result.warnings.is_empty() {
for warning in convert_result.warnings {
eprintln!(
"DSLX warning for {}: {}",
input_file.to_str().unwrap(),
warning
);
}
eprintln!("DSLX warnings found with warnings-as-errors enabled; exiting.");
process::exit(1);
}
for warning in convert_result.warnings {
log::warn!(
"DSLX warning for {}: {}",
input_file.to_str().unwrap(),
warning
);
}
let opt_ir = xlsynth::optimize_ir(&convert_result.ir, &ir_top).unwrap();
let opt_ir_text = opt_ir.to_string();
enforce_extern_verilog_codegen_policy(&opt_ir_text, allow_extern_verilog)
.unwrap_or_else(|err| report_cli_error_and_exit(&err, Some("dslx2pipeline"), vec![]));
if let Some(path) = output_unopt_ir {
std::fs::write(path, &convert_result.ir.to_string()).expect("write output_unopt_ir");
}
if let Some(path) = output_opt_ir {
std::fs::write(path, &opt_ir_text).expect("write output_opt_ir");
}
let scheduling_options_flags_proto = scheduling_options_proto(delay_model, pipeline_spec);
let codegen_flags_proto = pipeline_codegen_flags_proto(codegen_flags);
let codegen_result = xlsynth::schedule_and_codegen(
&opt_ir,
&scheduling_options_flags_proto,
&codegen_flags_proto,
)
.unwrap();
let sv = codegen_result.get_verilog_text().unwrap();
println!("{}", sv);
}
}
pub fn handle_dslx2pipeline(matches: &ArgMatches, config: &Option<ToolchainConfig>) {
log::info!("handle_dslx2pipeline");
let input_file = matches.get_one::<String>("dslx_input_file").unwrap();
let input_path = std::path::Path::new(input_file);
let top = matches.get_one::<String>("dslx_top").unwrap();
let pipeline_spec = extract_pipeline_spec(matches);
let delay_model = matches.get_one::<String>("DELAY_MODEL").unwrap();
let keep_temps = parse_bool_flag(matches, "keep_temps");
let allow_extern_verilog = parse_bool_flag_or(matches, "allow_extern_verilog", true);
let codegen_flags = extract_codegen_flags(matches, config.as_ref());
let output_unopt_ir: Option<std::path::PathBuf> = matches
.get_one::<String>("output_unopt_ir")
.map(|s| std::path::PathBuf::from(s));
let output_opt_ir: Option<std::path::PathBuf> = matches
.get_one::<String>("output_opt_ir")
.map(|s| std::path::PathBuf::from(s));
let type_inference_v2 = resolve_type_inference_v2(matches, config);
dslx2pipeline(
input_path,
top,
&pipeline_spec,
&codegen_flags,
delay_model,
&keep_temps,
type_inference_v2,
allow_extern_verilog,
&output_unopt_ir.as_deref(),
&output_opt_ir.as_deref(),
config,
);
}