use std::collections::HashMap;
use hw_regmap::generator;
use hw_regmap::regmap;
use regex::Regex;
use tera::Tera;
use clap::Parser;
#[derive(clap::Parser, Debug, Clone)]
#[clap(long_about = "Generate RTL register map")]
pub struct Args {
#[clap(long, value_parser)]
toml_file: Vec<String>,
#[clap(long, value_parser, default_value = "output")]
output_path: String,
#[clap(long, value_parser, default_value = "regmap")]
basename: String,
#[clap(long, value_parser)]
verbose: bool,
}
fn post_process(raw: &str) -> String {
let regex = Regex::new(r"\s*\n\s*\n+").unwrap();
let post_rendered = regex.replace_all(raw, "\n");
post_rendered.to_string()
}
pub fn as_hex(args: &HashMap<String, tera::Value>) -> tera::Result<tera::Value> {
let width = if let Some(width) = args.get("width") {
if let tera::Value::Number(num) = width {
Ok(num.as_u64().unwrap() as usize)
} else {
Err(tera::Error::msg("Width is not an integer"))
}
} else {
Ok(0)
}?;
if let Some(value) = args.get("val") {
if let tera::Value::Number(num) = value {
let hex_str = format!("0x{:0>width$x}", num.as_u64().unwrap(), width = width);
Ok(tera::Value::String(hex_str))
} else {
Err(tera::Error::msg("Value is not an integer"))
}
} else {
Err(tera::Error::msg(
"Function `as_hex` didn't receive a `val` argument",
))
}
}
pub fn as_sv_hex(args: &HashMap<String, tera::Value>) -> tera::Result<tera::Value> {
let width = if let Some(width) = args.get("width") {
if let tera::Value::Number(num) = width {
Ok(Some(num.as_u64().unwrap() as usize))
} else {
Err(tera::Error::msg("Width is not an integer"))
}
} else {
Ok(None)
}?;
if let Some(value) = args.get("val") {
if let tera::Value::Number(num) = value {
let hex_str = format!(
"{}'h{:x}",
if let Some(w) = width {
format!("{w}")
} else {
"".to_string()
},
num.as_u64().unwrap(),
);
Ok(tera::Value::String(hex_str))
} else {
Err(tera::Error::msg("Value is not an integer"))
}
} else {
Err(tera::Error::msg(
"Function `as_sv_hex` didn't receive a `val` argument",
))
}
}
fn generate_sv(regmap: ®map::Regmap, output_path: &str, engine: &Tera) {
let rtl_module = format!("{}/{}.sv", output_path, regmap.module_name());
let mut regs_sv = Vec::new();
let mut used_params = Vec::new();
regmap.section().iter().for_each(|sec| {
sec.register().iter().for_each(|reg| {
regs_sv.push(generator::SvRegister::from_register(
sec.name(),
reg,
&mut used_params,
engine,
));
})
});
let mut context = tera::Context::new();
let git_version = option_env!("GIT_VERSION").unwrap_or("unknown");
context.insert("tool_version", git_version);
context.insert("module_name", ®map.module_name());
context.insert("word_size_b", ®map.word_size_b());
context.insert("offset", ®map.offset());
context.insert("ext_pkg", ®map.ext_pkg());
context.insert("range", ®map.range());
context.insert("regs_sv", ®s_sv);
let module_rendered = engine.render("module.sv", &context).unwrap();
let module_post_rendered = post_process(&module_rendered);
std::fs::write(&rtl_module, module_post_rendered)
.unwrap_or_else(|_| panic!("Unable to write file {rtl_module}"));
let rtl_pkg = format!("{}/{}_pkg.sv", output_path, regmap.module_name());
let mut regs_pkg_sv = Vec::new();
regmap.section().iter().for_each(|sec| {
sec.register().iter().for_each(|reg| {
regs_pkg_sv.push(generator::SvRegisterPkg::from_register(
sec.name(),
regmap.word_size_b(),
reg,
engine,
));
})
});
let mut context = tera::Context::new();
let git_version = option_env!("GIT_VERSION").unwrap_or("unknown");
context.insert("tool_version", git_version);
context.insert("module_name", ®map.module_name());
context.insert("word_size_b", ®map.word_size_b());
context.insert("regs_pkg_sv", ®s_pkg_sv);
let pkg_rendered = engine.render("pkg.sv", &context).unwrap();
let pkg_post_rendered = post_process(&pkg_rendered);
std::fs::write(&rtl_pkg, pkg_post_rendered)
.unwrap_or_else(|_| panic!("Unable to write file {rtl_pkg}"));
}
fn generate_doc(regmap: ®map::Regmap, output_path: &str, engine: &Tera) -> std::io::Result<()> {
let doc_json = format!("{}/{}_doc.json", output_path, regmap.module_name());
let doc_json_f = std::fs::File::create(&doc_json)?;
serde_json::to_writer_pretty(doc_json_f, ®map)?;
let doc_md = format!("{}/{}_doc.md", output_path, regmap.module_name());
let mut context = tera::Context::new();
let git_version = option_env!("GIT_VERSION").unwrap_or("unknown");
context.insert("tool_version", git_version);
context.insert("regmap", ®map);
let md_rendered = engine.render("docs/fmt_as.md", &context).unwrap();
std::fs::write(&doc_md, md_rendered)
.unwrap_or_else(|_| panic!("Unable to write file {doc_md}"));
Ok(())
}
fn main() -> std::io::Result<()> {
let args = Args::parse();
println!("User Options: {args:?}");
let mut tera_sv = Tera::new("templates/**/*.sv").unwrap();
tera_sv.register_function("as_sv_hex", as_sv_hex);
let mut tera_doc = Tera::new("templates/**/fmt_as.*").unwrap();
tera_doc.register_function("as_hex", as_hex);
std::fs::create_dir_all(&args.output_path).unwrap();
let mut regmap_list = args
.toml_file
.iter()
.map(|toml| regmap::parser::RegmapOpt::read_from(toml))
.collect::<Vec<_>>();
let mut fused_regmap = regmap::Regmap::from_opt(&mut regmap_list).unwrap();
if args.verbose {
println!("{fused_regmap}");
}
*fused_regmap.module_name_mut() = args.basename.clone();
generate_doc(&fused_regmap, &args.output_path, &tera_doc)?;
args.toml_file.iter().for_each(|toml| {
let regmap_opt = regmap::parser::RegmapOpt::read_from(toml);
let regmap = regmap::Regmap::from_opt(&mut [regmap_opt]).unwrap();
generate_sv(®map, &args.output_path, &tera_sv);
});
Ok(())
}