diff --git a/src/cmd/script.rs b/src/cmd/script.rs index 231503cc..4adedfc5 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,76 @@ 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 -%}