summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/sim/devices/__init__.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
commitcc79b478843f0230fe6aa8ecd822de7ba71c891e (patch)
tree5de760ddec4e45cc23d39ce7502635f5c0711217 /debug/risc-v-gsp-rm-vm/sim/devices/__init__.py
parent40351167dd209012ab485e6164ce94f0b8965422 (diff)
downloadopen-iov-cc79b478843f0230fe6aa8ecd822de7ba71c891e.tar.gz
open-iov-cc79b478843f0230fe6aa8ecd822de7ba71c891e.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/__init__.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/sim/devices/__init__.py163
1 files changed, 163 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/sim/devices/__init__.py b/debug/risc-v-gsp-rm-vm/sim/devices/__init__.py
new file mode 100644
index 0000000..caf22b3
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/sim/devices/__init__.py
@@ -0,0 +1,163 @@
+"""GSP device model: registry + per-block modules.
+
+Public surface:
+
+* :class:`~sim.devices.base.DeviceRegistry` -- address-range lookup, access
+ log and serialisable state.
+* :func:`registry_from_profile` -- build a registry from a versioned
+ ``sim/profiles/*.json`` document.
+* per-block devices: :mod:`sim.devices.pmc`, :mod:`sim.devices.falcon`,
+ :mod:`sim.devices.priscv`, :mod:`sim.devices.timer`, :mod:`sim.devices.csb`,
+ :mod:`sim.devices.fwsec`, :mod:`sim.devices.pmu`, plus the
+ :mod:`sim.devices.dma` translation seam and :mod:`sim.devices.irq` router.
+
+Evidence and limits for every modelled register are documented in the module
+docstrings and in ``docs/36-GSP-DEVICE-MODEL.md``; unmodelled offsets always
+stay unhandled so the machine's unknown-MMIO log remains the source of truth
+for what is still missing.
+"""
+from __future__ import annotations
+
+import json
+
+from .base import Device, DeviceRegistry, SNAPSHOT_SCHEMA, Window
+from .csb import CsbWindow
+from .dma import DmaTranslator
+from .falcon import FalconBlock
+from .fwsec import Wpr2Block
+from .irq import IrqRouter
+from .pmc import PmcBlock
+from .pmu import PmuStub
+from .priscv import PriscvBlock
+from .timer import PtimerBlock
+
+PROFILE_SCHEMA = "greeneye-sim-profile/1"
+
+__all__ = [
+ "Device",
+ "DeviceRegistry",
+ "Window",
+ "SNAPSHOT_SCHEMA",
+ "PROFILE_SCHEMA",
+ "PmcBlock",
+ "FalconBlock",
+ "PriscvBlock",
+ "PtimerBlock",
+ "CsbWindow",
+ "Wpr2Block",
+ "PmuStub",
+ "DmaTranslator",
+ "IrqRouter",
+ "load_profile",
+ "registry_from_profile",
+]
+
+
+def load_profile(path) -> dict:
+ """Load and minimally validate a ``greeneye-sim-profile/1`` document."""
+ with open(path, "r", encoding="utf-8") as fh:
+ data = json.load(fh)
+ if data.get("schema") != PROFILE_SCHEMA:
+ raise ValueError(f"{path}: expected schema {PROFILE_SCHEMA!r}")
+ if "devices" not in data or not isinstance(data["devices"], dict):
+ raise ValueError(f"{path}: missing 'devices' object")
+ return data
+
+
+def _int(value, default=None):
+ """Profile numbers may be JSON integers or ``0x``/decimal strings."""
+ if value is None:
+ return default
+ if isinstance(value, str):
+ return int(value, 0)
+ return int(value)
+
+
+def _windows(section: dict, default_size: int = 0x1000):
+ out = []
+ for win in section.get("windows", ()):
+ out.append((_int(win["base"]), _int(win.get("size", default_size))))
+ return out
+
+
+def registry_from_profile(profile: dict, *, machine=None,
+ max_access_log: int = 100_000) -> DeviceRegistry:
+ """Build a :class:`DeviceRegistry` from a profile document.
+
+ ``machine`` (optional) supplies RAM to the DMA translation seam; without
+ it DMA transfers are still modelled structurally but refuse to move data
+ (recorded, never faked).
+ """
+ if profile.get("schema") != PROFILE_SCHEMA:
+ raise ValueError(f"expected schema {PROFILE_SCHEMA!r}")
+ sections = profile.get("devices", {})
+ reg = DeviceRegistry(max_access_log=max_access_log)
+
+ dma_section = sections.get("dma", {})
+ dma_windows = [
+ {"pa": _int(w["pa"]), "sim": _int(w["sim"]), "size": _int(w["size"])}
+ for w in dma_section.get("windows", ())
+ ]
+ if machine is not None:
+ dma = DmaTranslator(
+ ram=machine.ram,
+ ram_base=machine.ram_base,
+ ram_size=machine.ram_size,
+ windows=dma_windows,
+ )
+ else:
+ dma = DmaTranslator(ram=bytearray(), ram_base=0, ram_size=0,
+ windows=())
+
+ falcon = None
+ priscv = None
+
+ for name, section in sections.items():
+ if not isinstance(section, dict):
+ continue
+ if name == "dma":
+ continue
+ if name == "pmc":
+ dev = PmcBlock(
+ chip_id=_int(section.get("chip_id", 0x164)),
+ major=_int(section.get("major", 0)),
+ minor=_int(section.get("minor", 0)),
+ minor_ext=_int(section.get("minor_ext", 0)),
+ boot_42=(_int(section["boot_42"]) if "boot_42" in section else None),
+ )
+ elif name == "falcon":
+ shims = section.get("shims", {})
+ dev = FalconBlock(
+ dma=dma,
+ debuginfo_host_consume=bool(shims.get("debuginfo_host_consume", False)),
+ debuginfo_stage_mask=_int(section.get("debuginfo_stage_mask", 0x700000)),
+ debuginfo_stage_shift=_int(section.get("debuginfo_stage_shift", 20)),
+ )
+ falcon = dev
+ elif name == "priscv":
+ dev = PriscvBlock()
+ priscv = dev
+ elif name == "timer":
+ if not section.get("enabled", True):
+ continue
+ dev = PtimerBlock(
+ ns_per_insn=float(section.get("ns_per_insn", 1.0)),
+ clock=(machine.insn_count if machine is not None else None),
+ )
+ elif name == "csb":
+ dev = CsbWindow(registers=section.get("registers", {}))
+ elif name == "fwsec":
+ dev = Wpr2Block(
+ wpr2_start=(_int(section["wpr2_start"]) if "wpr2_start" in section else None),
+ wpr2_end=(_int(section["wpr2_end"]) if "wpr2_end" in section else None),
+ )
+ elif name == "pmu":
+ dev = PmuStub()
+ else:
+ raise ValueError(f"unknown device section {name!r}")
+ for base, size in _windows(section):
+ reg.add(dev, base, size)
+
+ reg.add_service(IrqRouter(falcon=falcon, priscv=priscv))
+ reg.dma = dma
+ return reg