summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py
diff options
context:
space:
mode:
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py75
1 files changed, 75 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py b/debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py
new file mode 100644
index 0000000..fa805ad
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/examples/scripts/sim_bench.py
@@ -0,0 +1,75 @@
+"""Measure native (M1) simulator throughput on the bundled demo firmware.
+
+Run standalone (from anywhere):
+
+ python3 examples/scripts/sim_bench.py # untraced baseline
+ python3 examples/scripts/sim_bench.py --trace # baseline + R4 trace overhead
+
+Reports MIPS for a fixed instruction budget -- the budget
+``SimClient.continue_(timeout=1.0)`` derives from SIM_IPS = 1e6. With
+``--trace`` a second run enables the exec/access trace and reports the
+per-instruction slowdown, so the non-trace path can be checked for regressions.
+"""
+from __future__ import annotations
+
+import argparse
+import sys
+import time
+from pathlib import Path
+
+ROOT = Path(__file__).resolve().parents[2]
+if str(ROOT) not in sys.path:
+ sys.path.insert(0, str(ROOT))
+
+import greeneye as ge
+from greeneye.errors import RspTimeout
+from sim.trace import Trace
+
+DEMO = ROOT / "firmware" / "uproc_demo" / "build" / "uproc_demo.elf"
+
+
+def measure(budget: int, *, trace: bool = False) -> tuple[int, float]:
+ """Run a fixed instruction budget; return (instructions, seconds)."""
+ session = ge.launch(kernel=str(DEMO), backend="native", name="sim-bench")
+ machine = session.client.machine
+ if trace:
+ session.client.trace = Trace() # recorder stays null: nothing is written
+ try:
+ machine.insns = 0
+ started = time.perf_counter()
+ try:
+ session.client.continue_(timeout=budget / 1_000_000.0)
+ except RspTimeout:
+ pass # budget exhausted: the benchmark workload completed
+ elapsed = time.perf_counter() - started
+ banner = session.target.read_uart().splitlines()
+ print(f"uart: {banner[0] if banner else '<empty>'!r}")
+ return machine.insns, elapsed
+ finally:
+ session.close()
+
+
+def main(argv: list[str] | None = None) -> int:
+ parser = argparse.ArgumentParser(description=__doc__.splitlines()[0])
+ parser.add_argument("--budget", type=int, default=1_000_000)
+ parser.add_argument("--trace", action="store_true", help="also measure traced throughput")
+ parser.add_argument("--trace-budget", type=int, default=200_000)
+ args = parser.parse_args(argv)
+
+ insns, elapsed = measure(args.budget)
+ mips = insns / elapsed / 1e6 if elapsed else float("inf")
+ print(f"native M1: {insns} instructions in {elapsed:.3f}s = {mips:.2f} MIPS (untraced)")
+
+ if args.trace:
+ t_insns, t_elapsed = measure(args.trace_budget, trace=True)
+ t_mips = t_insns / t_elapsed / 1e6 if t_elapsed else float("inf")
+ overhead = (mips / t_mips - 1.0) if t_mips else float("inf")
+ print(
+ f"native M1 trace: {t_insns} instructions in {t_elapsed:.3f}s = {t_mips:.2f} MIPS "
+ f"({overhead * 100:+.1f}% per-instruction vs untraced)"
+ )
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())