From 32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5 Mon Sep 17 00:00:00 2001 From: Arthur Hanson Rasmusson Date: Sat, 3 Oct 2026 14:05:38 -0400 Subject: vgsp: host CUDA bridge + sema release recovery; greeneye simulator wave - kernel: gsp_cuda_bridge (QMD launch capture, debugfs job/out/done, param+output resolution cached from inline writes), sema drain fixes (never hold/starve a channel), GR ring autopin + counter autoreg, per-page push reads, counter-derive gate; module builds clean - tools/cuda-bridge: CUDA driver-API daemon executing mail/vadd kernels on the physical GPU, CUBB2/CBD2 v2 protocol, layout check + self-tests - tools/guest-diag + tools/cuda-ab: scanner/pin fixes, victory push-only mode - debug/risc-v-gsp-rm-vm: Phase S simulator wave (GSP device model, boot ladder, triage/re-demo harnesses, docs 30-41), GPGPU-sim scaffolding - STATE-CURRENT/docs updates --- debug/risc-v-gsp-rm-vm/sim/cycles.py | 247 +++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) create mode 100644 debug/risc-v-gsp-rm-vm/sim/cycles.py (limited to 'debug/risc-v-gsp-rm-vm/sim/cycles.py') diff --git a/debug/risc-v-gsp-rm-vm/sim/cycles.py b/debug/risc-v-gsp-rm-vm/sim/cycles.py new file mode 100644 index 0000000..b841c6c --- /dev/null +++ b/debug/risc-v-gsp-rm-vm/sim/cycles.py @@ -0,0 +1,247 @@ +"""Modeled RV64IMAC instruction-class cycle accounting for the native core. + +**Every number in this module is a modeled default - NOT a measurement.** +The tables encode deliberately simple, public assumptions about an in-order +single-issue RISC-V core: one cycle for plain ALU work, a multi-cycle cost for +the M extension, a fixed modeled latency added when a memory access completes, +and a larger one for MMIO. They are *not* derived from the GSP core, not from +silicon, and not from vendor data. + +The simulator's fidelity statement is unchanged (docs/06-ACCURACY.md): +instruction semantics (L0) and memory behaviour (L1) are modelled; L2 +(time/concurrency) is **not**. A cycle count from this module is a +deterministic accounting artifact under an explicit model - never a timing, +latency, throughput or silicon claim. + +The model is deterministic by construction: cost depends only on the decoded +instruction class (``sim.decode`` kind/sub) and whether a completed access was +RAM or MMIO. No host clock, no entropy, no caches, no pipeline hazards. +""" +from __future__ import annotations + +from .decode import ( + A_DIV, + A_DIVU, + A_MUL, + A_MULH, + A_MULHSU, + A_MULHU, + A_REM, + A_REMU, + K_AMO, + K_AUIPC, + K_BRANCH, + K_CSR, + K_EBREAK, + K_ECALL, + K_FENCE, + K_ILLEGAL, + K_JAL, + K_JALR, + K_LOAD, + K_LUI, + K_MRET, + K_OP, + K_OP32, + K_OPI, + K_OPI32, + K_STORE, +) + +#: Model identifier written into session metadata and reports. +CYCLE_MODEL_NAME = "rv64imac-modeled/1" + +#: Fidelity label that must travel with every cycle number from this module. +CYCLE_FIDELITY = "L2-not-modelled; cycles are modeled defaults, not measured" + +#: One-line provenance/label, repeated wherever the model is described. +NOTE = ( + "modeled defaults sourced from public in-order RISC-V pipeline assumptions; " + "not measured, not a silicon timing claim" +) + +# Instruction classes used for reporting and per-class reasoning. +CLASS_ALU = "alu" +CLASS_BRANCH = "branch" +CLASS_JUMP = "jump" +CLASS_MUL = "mul" +CLASS_DIV = "div" +CLASS_LOAD = "load" +CLASS_STORE = "store" +CLASS_AMO = "amo" +CLASS_CSR = "csr" +CLASS_TRAP = "trap" +CLASS_FENCE = "fence" +CLASS_ILLEGAL = "illegal" + +_CLASS_BY_KIND = { + K_LUI: CLASS_ALU, + K_AUIPC: CLASS_ALU, + K_JAL: CLASS_JUMP, + K_JALR: CLASS_JUMP, + K_BRANCH: CLASS_BRANCH, + K_LOAD: CLASS_LOAD, + K_STORE: CLASS_STORE, + K_OPI: CLASS_ALU, + K_OPI32: CLASS_ALU, + K_FENCE: CLASS_FENCE, + K_CSR: CLASS_CSR, + K_ECALL: CLASS_TRAP, + K_EBREAK: CLASS_TRAP, + K_MRET: CLASS_TRAP, + K_AMO: CLASS_AMO, + K_ILLEGAL: CLASS_ILLEGAL, +} + +#: Sentinel in ``base_costs``: the cost lives in ``op_costs`` (M extension). +_SENTINEL = -1 + +_M_EXT_KINDS = (K_OP, K_OP32) + + +class CycleModel: + """Table-driven, deterministic instruction-class cycle costs. + + All keyword values are **modeled defaults** (see the module docstring); + they are parameters so a future calibration can replace them without + touching the execution loop. ``memory_latency``/``mmio_latency`` are the + extra cycles added when a load/store/AMO access completes (RAM vs + modelled-MMIO respectively); they are *not* cache models. + """ + + name = CYCLE_MODEL_NAME + + def __init__( + self, + *, + alu: int = 1, # plain integer ALU / shift / lui / auipc / op-imm + branch: int = 1, # conditional branch (taken penalty deliberately not modelled) + jump: int = 1, # jal / jalr + mul: int = 3, # mul / mulh / mulhsu / mulhu (conservative) + div: int = 20, # div / divu / rem / remu (conservative, data-independent) + load: int = 1, # load base; + memory_latency/mmio_latency on completion + store: int = 1, # store base; + memory_latency/mmio_latency on completion + amo: int = 2, # amoswap/lr/sc base; + one memory latency on completion + csr: int = 2, # CSR read/modify/write + trap: int = 3, # ecall / ebreak / mret + fence: int = 1, # fence / fence.i (no ordering model; nominal) + illegal: int = 1, # decode failure (execution stops immediately) + memory_latency: int = 3, # extra cycles for a completed RAM access + mmio_latency: int = 12, # extra cycles for a completed unmodelled-MMIO access + ): + for label, value in ( + ("alu", alu), + ("branch", branch), + ("jump", jump), + ("mul", mul), + ("div", div), + ("load", load), + ("store", store), + ("amo", amo), + ("csr", csr), + ("trap", trap), + ("fence", fence), + ("illegal", illegal), + ("memory_latency", memory_latency), + ("mmio_latency", mmio_latency), + ): + if not isinstance(value, int) or isinstance(value, bool) or value < 0: + raise ValueError(f"cycle model: {label} must be a non-negative int, got {value!r}") + if alu < 1 or branch < 1 or jump < 1: + raise ValueError("cycle model: alu/branch/jump must be >= 1 for a clean per-instruction model") + self.alu = alu + self.branch = branch + self.jump = jump + self.mul = mul + self.div = div + self.load = load + self.store = store + self.amo = amo + self.csr = csr + self.trap = trap + self.fence = fence + self.illegal = illegal + self.memory_latency = memory_latency + self.mmio_latency = mmio_latency + + base = [0] * 18 + base[K_LUI] = alu + base[K_AUIPC] = alu + base[K_JAL] = jump + base[K_JALR] = jump + base[K_BRANCH] = branch + base[K_LOAD] = load + base[K_STORE] = store + base[K_OPI] = alu + base[K_OP] = _SENTINEL + base[K_OPI32] = alu + base[K_OP32] = _SENTINEL + base[K_FENCE] = fence + base[K_CSR] = csr + base[K_ECALL] = trap + base[K_EBREAK] = trap + base[K_MRET] = trap + base[K_AMO] = amo + base[K_ILLEGAL] = illegal + + op = [alu] * 18 + for sub in (A_MUL, A_MULH, A_MULHSU, A_MULHU): + op[sub] = mul + for sub in (A_DIV, A_DIVU, A_REM, A_REMU): + op[sub] = div + # Immutable fast-path tables consumed by Machine.run's hot loop. + self.base_costs = tuple(base) + self.op_costs = tuple(op) + + # -- queries ----------------------------------------------------------- + + def instruction_cost(self, kind: int, sub: int = 0) -> int: + """Modeled cycles for one decoded instruction (memory latency excluded).""" + cost = self.base_costs[kind] + if cost == _SENTINEL: + return self.op_costs[sub] + return cost + + def memory_cost(self, is_mmio: bool = False) -> int: + """Modeled extra cycles when a load/store/AMO access completes.""" + return self.mmio_latency if is_mmio else self.memory_latency + + @staticmethod + def classify(kind: int, sub: int = 0) -> str: + """Human-readable class for a decoded instruction (reporting only).""" + if kind in _M_EXT_KINDS: + if sub >= A_DIV: + return CLASS_DIV + if sub >= A_MUL: + return CLASS_MUL + return CLASS_ALU + return _CLASS_BY_KIND.get(kind, "other") + + def describe(self) -> dict: + """The full table, for session metadata and reports.""" + return { + "name": self.name, + "note": NOTE, + "fidelity": CYCLE_FIDELITY, + "classes": { + CLASS_ALU: self.alu, + CLASS_BRANCH: self.branch, + CLASS_JUMP: self.jump, + CLASS_MUL: self.mul, + CLASS_DIV: self.div, + CLASS_LOAD: self.load, + CLASS_STORE: self.store, + CLASS_AMO: self.amo, + CLASS_CSR: self.csr, + CLASS_TRAP: self.trap, + CLASS_FENCE: self.fence, + CLASS_ILLEGAL: self.illegal, + }, + "memory_latency": self.memory_latency, + "mmio_latency": self.mmio_latency, + } + + +#: Shared default model. Machine() uses it unless told otherwise; it is +#: immutable in practice, so one instance is safe for every target. +DEFAULT_MODEL = CycleModel() -- cgit v1.2.3