summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/devices/timer.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-03 14:05:38 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-03 14:05:38 -0400
commit32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5 (patch)
treed1cab6bcc35e461610cd5dfcf3f51ba679a576fc /debug/risc-v-gsp-rm-vm/sim/devices/timer.py
parentfa71f1b53b524e5f6c6c1fbff1088d610b9f3236 (diff)
downloadopen-iov-32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5.tar.gz
open-iov-32b551d43781dc7b8ac4587ca2ba1554b0cbd5e5.zip
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
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/sim/devices/timer.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/sim/devices/timer.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/devices/timer.py b/debug/risc-v-gsp-rm-vm/sim/devices/timer.py
new file mode 100644
index 0000000..c57ee06
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/sim/devices/timer.py
@@ -0,0 +1,86 @@
+"""PTIMER block (modeled virtual time; not a silicon timing claim).
+
+Evidence kind: **hardware register/field header** (offsets and field layout)
+plus a *modeled* time source. The firmware has not yet been observed reading
+this block in the 570.144 boot runs (the observed time-like reads were on
+falcon MAILBOX1); the device is therefore opt-in: it is registered only when
+a profile declares a ``timer`` section, and a section with
+``"enabled": false`` is skipped. Its values are explicitly labelled
+*modeled*.
+
+Header facts used:
+
+* ``NV_PTIMER`` block base ``0x9000`` in the register aperture.
+* ``NV_PTIMER_TIME_0`` at aperture offset ``0x9400``: nanoseconds bits
+ ``31:5``.
+* ``NV_PTIMER_TIME_1`` at aperture offset ``0x9410``: nanoseconds bits
+ ``28:0``.
+* Reads of TIME_0 latch TIME_1 (paired read), as on the hardware register
+ pair; the simulator derives nanoseconds from the machine's instruction
+ count times the profile's ``ns_per_insn`` (default 1.0, i.e. 1 ns/insn --
+ a placeholder, not calibrated).
+
+Register offsets below are **relative to the block window base** the profile
+declares (``PRIV + 0x9000``), i.e. aperture offset minus ``PTIMER_BASE``:
+``0x9400 - 0x9000 = 0x400``. Using the aperture-absolute numbers here would
+place the registers outside the declared window and serve nothing.
+"""
+from __future__ import annotations
+
+from .base import Device
+
+PTIMER_BASE = 0x9000
+#: Block-relative offsets of NV_PTIMER_TIME_0/1 (aperture 0x9400/0x9410).
+TIME_0 = 0x400
+TIME_1 = 0x410
+
+
+class PtimerBlock(Device):
+ """Two-register nanosecond timer with a modeled tick source."""
+
+ name = "timer"
+
+ def __init__(self, *, base: int = 0, size: int = 0x1000,
+ ns_per_insn: float = 1.0, clock=None):
+ super().__init__(base=base, size=size)
+ self.ns_per_insn = float(ns_per_insn)
+ self.clock = clock # callable returning virtual instruction count
+ self._latch = 0
+ self.reads = 0
+
+ def _now_ns(self) -> int:
+ insns = self.clock() if self.clock is not None else 0
+ return int(insns * self.ns_per_insn) & 0xFFFFFFFFFFFFFFFF
+
+ def read(self, offset: int, size: int) -> int | None:
+ if size != 4:
+ return None
+ if offset == TIME_0:
+ self._latch = self._now_ns()
+ self.reads += 1
+ return (self._latch >> 5) & 0xFFFFFFFF # NSEC field 31:5
+ if offset == TIME_1:
+ self.reads += 1
+ return self._latch & 0x1FFFFFFF # NSEC field 28:0
+ return None
+
+ def peek(self, offset: int, size: int) -> int | None:
+ if size != 4:
+ return None
+ if offset == TIME_0:
+ return (self._now_ns() >> 5) & 0xFFFFFFFF
+ if offset == TIME_1:
+ return self._now_ns() & 0x1FFFFFFF
+ return None
+
+ def write(self, offset: int, size: int, value: int) -> bool:
+ return False
+
+ def state(self) -> dict:
+ return {"ns_per_insn": self.ns_per_insn, "latch": self._latch,
+ "reads": self.reads}
+
+ def load_state(self, data: dict) -> None:
+ self.ns_per_insn = float(data.get("ns_per_insn", self.ns_per_insn))
+ self._latch = int(data.get("latch", 0))
+ self.reads = int(data.get("reads", 0))