u280_hbm_source_sim/
main.rs1use std::env;
4use std::ffi::OsString;
5use std::path::PathBuf;
6use std::process::ExitCode;
7
8use anyhow::{Context, Result, bail, ensure};
9use u280_hbm_sim::{
10 STANDARD_SCENARIOS, Scenario, SimulationCase, run_and_write_standard_suite, run_case,
11 write_case_artifacts,
12};
13
14const USAGE: &str = "Usage: u280-hbm-source-sim --output NEW_DIR --case all|CASE [--seed U64]";
15
16struct Options {
17 output: PathBuf,
18 case: Option<Scenario>,
19 seed: Option<u64>,
20}
21
22fn main() -> ExitCode {
23 match parse_options().and_then(execute) {
24 Ok(()) => ExitCode::SUCCESS,
25 Err(error) => {
26 eprintln!("u280-hbm-source-sim: {error:#}");
27 ExitCode::FAILURE
28 }
29 }
30}
31
32fn execute(options: Options) -> Result<()> {
33 match options.case {
34 None => {
35 ensure!(options.seed.is_none(), "--seed requires one named case");
36 let reports = run_and_write_standard_suite(&options.output)?;
37 println!(
38 "completed {} source-simulation cases in {}; hardware_completion_promotable=false",
39 reports.len(),
40 options.output.display()
41 );
42 }
43 Some(scenario) => {
44 let mut case = SimulationCase::standard(scenario);
45 if let Some(seed) = options.seed {
46 case.seed = seed;
47 }
48 let artifacts = run_case(case)?;
49 write_case_artifacts(&options.output, &artifacts)?;
50 println!(
51 "completed {} in {} modeled cycles at source-simulation tier; hardware_completion_promotable=false",
52 artifacts.report.case, artifacts.report.modeled_cycles
53 );
54 }
55 }
56 Ok(())
57}
58
59fn parse_options() -> Result<Options> {
60 let mut arguments = env::args_os();
61 let _program = arguments.next();
62 let mut output = None;
63 let mut case = None;
64 let mut seed = None;
65 while let Some(argument) = arguments.next() {
66 match argument.to_str() {
67 Some("--output") => {
68 ensure!(output.is_none(), "--output was repeated");
69 output = Some(PathBuf::from(required_value(&mut arguments, "--output")?));
70 }
71 Some("--case") => {
72 ensure!(case.is_none(), "--case was repeated");
73 let value = required_value(&mut arguments, "--case")?;
74 let value = value.to_str().context("--case must be valid UTF-8")?;
75 case = Some(if value == "all" {
76 None
77 } else {
78 Some(Scenario::from_name(value).with_context(|| {
79 let names = STANDARD_SCENARIOS
80 .iter()
81 .copied()
82 .map(Scenario::name)
83 .collect::<Vec<_>>()
84 .join(", ");
85 format!("unknown case {value}; expected all or one of: {names}")
86 })?)
87 });
88 }
89 Some("--seed") => {
90 ensure!(seed.is_none(), "--seed was repeated");
91 let value = required_value(&mut arguments, "--seed")?;
92 let text = value.to_str().context("--seed must be valid UTF-8")?;
93 seed = Some(parse_u64(text)?);
94 }
95 Some("--help" | "-h") => bail!(USAGE),
96 Some(value) => bail!("unknown option {value}; {USAGE}"),
97 None => bail!("option name is not valid UTF-8; {USAGE}"),
98 }
99 }
100 let selected = case.with_context(|| USAGE.to_owned())?;
101 Ok(Options {
102 output: output.with_context(|| USAGE.to_owned())?,
103 case: selected,
104 seed,
105 })
106}
107
108fn parse_u64(value: &str) -> Result<u64> {
109 if let Some(hex) = value.strip_prefix("0x") {
110 ensure!(!hex.is_empty(), "hexadecimal --seed is empty");
111 u64::from_str_radix(hex, 16).context("invalid hexadecimal --seed")
112 } else {
113 ensure!(
114 !value.is_empty() && value.bytes().all(|byte| byte.is_ascii_digit()),
115 "decimal --seed must contain only digits"
116 );
117 value.parse().context("invalid decimal --seed")
118 }
119}
120
121fn required_value(
122 arguments: &mut impl Iterator<Item = OsString>,
123 option: &str,
124) -> Result<OsString> {
125 arguments
126 .next()
127 .with_context(|| format!("{option} requires a value; {USAGE}"))
128}
129
130#[cfg(test)]
131mod tests {
132 use super::parse_u64;
133
134 #[test]
135 fn seed_parser_accepts_decimal_and_prefixed_hex() {
136 assert_eq!(parse_u64("17").unwrap(), 17);
137 assert_eq!(parse_u64("0x11").unwrap(), 17);
138 assert!(parse_u64("11x").is_err());
139 assert!(parse_u64("0x").is_err());
140 }
141}