diff options
| author | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-03 14:05:38 -0400 |
|---|---|---|
| committer | Arthur Hanson Rasmusson <arthur@vgpu.io> | 2026-10-03 14:05:38 -0400 |
| commit | cc79b478843f0230fe6aa8ecd822de7ba71c891e (patch) | |
| tree | 5de760ddec4e45cc23d39ce7502635f5c0711217 /debug/risc-v-gsp-rm-vm/sim/devices/__init__.py | |
| parent | 40351167dd209012ab485e6164ce94f0b8965422 (diff) | |
| download | open-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__.py | 163 |
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 |
