From 1c82bdf3d5f0a573db5e1f54fd9db08f54e6b405 Mon Sep 17 00:00:00 2001 From: Daniel Keller Date: Wed, 27 Aug 2025 13:31:33 +0200 Subject: [PATCH 1/2] Add support for Xcelium Signed-off-by: Daniel Keller --- src/cmd/script.rs | 103 +++++++++++++++++++++++++++++++++- src/script_fmt/xcelium_f.tera | 27 +++++++++ 2 files changed, 129 insertions(+), 1 deletion(-) create mode 100644 src/script_fmt/xcelium_f.tera diff --git a/src/cmd/script.rs b/src/cmd/script.rs index 231503cc..940a2f65 100644 --- a/src/cmd/script.rs +++ b/src/cmd/script.rs @@ -46,6 +46,7 @@ pub fn new() -> Command { .value_parser([ PossibleValue::new("flist"), PossibleValue::new("flist-plus"), + PossibleValue::new("xcelium"), PossibleValue::new("vsim"), PossibleValue::new("vcs"), PossibleValue::new("verilator"), @@ -92,6 +93,15 @@ pub fn new() -> Command { .action(ArgAction::Append) .value_parser(value_parser!(String)), ) + .arg( + Arg::new("incdir") + .short('I') + .long("incdir") + .help("Add an include directory (repeatable)") + .num_args(1..) + .action(ArgAction::Append) + .value_parser(value_parser!(String)), + ) .arg( Arg::new("only-defines") .long("only-defines") @@ -217,6 +227,7 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> { match format.as_str() { "flist" => vec!["flist"], "flist-plus" => vec!["flist"], + "xcelium" => vec!["xcelium", "simulation"], "vsim" => vec!["vsim", "simulation"], "vcs" => vec!["vcs", "simulation"], "verilator" => vec!["verilator", "synthesis"], @@ -342,6 +353,13 @@ pub fn run(sess: &Session, matches: &ArgMatches) -> Result<()> { targets, srcs, ), + "xcelium" => emit_template( + sess, + include_str!("../script_fmt/xcelium_f.tera"), + matches, + targets, + srcs, + ), "vsim" => emit_template( sess, include_str!("../script_fmt/vsim_tcl.tera"), @@ -496,6 +514,7 @@ fn emit_template( let mut tera_context = Context::new(); tera_context.insert("HEADER_AUTOGEN", HEADER_AUTOGEN); tera_context.insert("root", sess.root); + tera_context.insert("root_package", &sess.manifest.package.name); // tera_context.insert("srcs", &srcs); tera_context.insert("abort_on_error", &!matches.get_flag("no-abort-on-error")); @@ -537,11 +556,21 @@ fn emit_template( tera_context.insert("all_defines", &all_defines); all_incdirs.sort(); + // user-provided include dirs + let user_incdirs: IndexSet = if let Some(dirs) = matches.get_many::("incdir") { + dirs.map(|d| PathBuf::from(d)).collect() + } else { + IndexSet::new() + }; let all_incdirs: IndexSet = if (!matches.get_flag("only-defines") && !matches.get_flag("only-sources")) || matches.get_flag("only-includes") { - all_incdirs.into_iter().map(|p| p.to_path_buf()).collect() + all_incdirs + .into_iter() + .map(|p| p.to_path_buf()) + .chain(user_incdirs) + .collect() } else { IndexSet::new() }; @@ -562,6 +591,78 @@ fn emit_template( }; tera_context.insert("all_files", &all_files); + // Compute per-package files and root files, plus per-package incdirs/defines + #[derive(Debug, Serialize)] + struct TplLib { + name: String, + incdirs: IndexSet, + defines: IndexSet<(String, Option)>, + files: IndexSet, + } + + let mut libs: IndexMap = IndexMap::new(); + let mut root_files_map: IndexSet = IndexSet::new(); + for src in &srcs { + // Gather common per-group data + let incdirs: IndexSet = src + .clone() + .get_incdirs() + .into_iter() + .map(|p| p.to_path_buf()) + .collect(); + let mut defines: IndexSet<(String, Option)> = IndexMap::new() + .into_iter() + .collect(); + defines.extend( + src.defines + .iter() + .map(|(k, &v)| (k.to_string(), v.map(String::from))), + ); + defines.extend(target_defines.clone().into_iter().collect::>()); + // Add user-provided defines + { + let mut dmap = IndexMap::new(); + add_defines_from_matches(&mut dmap, matches); + defines.extend(dmap.into_iter().collect::>()); + } + + if let Some(pkg) = src.package { + if pkg == sess.manifest.package.name { + // Root package: collect files only + for file in &src.files { + if let SourceFile::File(p) = file { + root_files_map.insert(p.to_path_buf()); + } + } + } else { + let entry = libs.entry(pkg.to_string()).or_insert_with(|| TplLib { + name: pkg.to_string(), + incdirs: IndexSet::new(), + defines: IndexSet::new(), + files: IndexSet::new(), + }); + entry.incdirs.extend(incdirs); + entry.defines.extend(defines); + for file in &src.files { + if let SourceFile::File(p) = file { + entry.files.insert(p.to_path_buf()); + } + } + } + } else { + // No package: treat as root files + for file in &src.files { + if let SourceFile::File(p) = file { + root_files_map.insert(p.to_path_buf()); + } + } + } + } + // libs in stable order + let libs: Vec = libs.into_iter().map(|(_, v)| v).collect(); + tera_context.insert("libs", &libs); + tera_context.insert("root_files", &root_files_map); + let mut split_srcs = vec![]; for src in srcs { separate_files_in_group( diff --git a/src/script_fmt/xcelium_f.tera b/src/script_fmt/xcelium_f.tera new file mode 100644 index 00000000..2887b4f3 --- /dev/null +++ b/src/script_fmt/xcelium_f.tera @@ -0,0 +1,27 @@ +# {{ HEADER_AUTOGEN }} +{%- for incdir in all_incdirs %} ++incdir+{% if relativize_path and incdir is starting_with(root) %}{{ incdir | replace(from=root, to='$ROOT') }}{% else %}{{ incdir }}{% endif %} +{%- endfor %} +{%- for define in all_defines %} ++define+{{ define.0 }}{% if define.1 %}={{ define.1 }}{% endif %} +{%- endfor %} +{% for lib in libs %} +{#- Sanitize library name for Xcelium (avoid hyphens) -#} +{% set safe_lib_name = lib.name | replace(from="-", to="_") %} +# Library: {{ safe_lib_name }} +-makelib {{ safe_lib_name }} +{%- for incdir in lib.incdirs %} ++incdir+{% if relativize_path and incdir is starting_with(root) %}{{ incdir | replace(from=root, to='$ROOT') }}{% else %}{{ incdir }}{% endif %} +{%- endfor %} +{%- for define in lib.defines %} ++define+{{ define.0 }}{% if define.1 %}={{ define.1 }}{% endif %} +{%- endfor %} +{%- for file in lib.files %} +{% if relativize_path and file is starting_with(root) %}{{ file | replace(from=root, to='$ROOT') }}{% else %}{{ file }}{% endif %} +{%- endfor %} +-endlib +{% endfor %} + +{%- for file in root_files %} +{% if relativize_path and file is starting_with(root) %}{{ file | replace(from=root, to='$ROOT') }}{% else %}{{ file }}{% endif %} +{%- endfor -%} From 5d19a8aeadfff2aa50c534f5bb670164a12d53bb Mon Sep 17 00:00:00 2001 From: yvantor Date: Sun, 5 Oct 2025 09:09:33 +0200 Subject: [PATCH 2/2] Fix CI linter. --- src/cmd/script.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/cmd/script.rs b/src/cmd/script.rs index 940a2f65..4adedfc5 100644 --- a/src/cmd/script.rs +++ b/src/cmd/script.rs @@ -610,9 +610,7 @@ fn emit_template( .into_iter() .map(|p| p.to_path_buf()) .collect(); - let mut defines: IndexSet<(String, Option)> = IndexMap::new() - .into_iter() - .collect(); + let mut defines: IndexSet<(String, Option)> = IndexMap::new().into_iter().collect(); defines.extend( src.defines .iter()