summaryrefslogtreecommitdiffstats
path: root/debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py
diff options
context:
space:
mode:
authorArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 10:58:21 -0400
committerArthur Hanson Rasmusson <arthur@vgpu.io>2026-10-02 10:58:21 -0400
commitf909a5f822c5c4a4e9526b861d4171f8925f5113 (patch)
treeed362021c83569f49b48079a774f05775e84b0bd /debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py
parent09f241601423b1ae98e97846ee451768ff8a0a9e (diff)
downloadopen-iov-f909a5f822c5c4a4e9526b861d4171f8925f5113.tar.gz
open-iov-f909a5f822c5c4a4e9526b861d4171f8925f5113.zip
debug: green-eye wave 3 - RM harness, local simulator, device map, MCP plugin, value-validation demos, CI pipeline, toolchain docs; add host RM tools rmtool/rmtop
Diffstat (limited to 'debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py')
-rw-r--r--debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py126
1 files changed, 126 insertions, 0 deletions
diff --git a/debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py b/debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py
new file mode 100644
index 0000000..690f60f
--- /dev/null
+++ b/debug/risc-v-gsp-rm-vm/harness/value_demos/s02_counter_publication.py
@@ -0,0 +1,126 @@
+#!/usr/bin/env python3
+"""s02 - counter publication: which of N slots advanced, and when?
+
+Real scenario (Handoff-vGPU-IO.md PART 46-64, 2026-09-24..2026-09-29):
+``cuCtxCreate`` waits on 20 GR per-channel completion counters. The host saw
+the client's releases but could not translate the counter page; for many boots
+only 4 of 20 client-visible slots ever moved (PART 49). The exact slot layout
+(page VA ``0x20400f000``, slots at ``+0xec0``, 16-byte stride) was only proven
+by a raw live page dump (PART 64, 2026-09-29 ~16:55), and the host raced a
+~2-3 s pin path against the client's ~2 s GR-phase deadline. Static reading
+could not tell which slots were published: the page's GPA changed every boot
+and stale translations pointed at the previous client's page (PART 51).
+
+Analog: post 20 sequential commands and watch the demo's published result
+field; prove every slot was observed, with the publish PC recorded per slot.
+"""
+from __future__ import annotations
+
+import os
+
+try: # package import (tests) or direct script execution (run_all)
+ from . import _common as C
+except ImportError:
+ import sys as _sys
+
+ _sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
+ import _common as C
+
+SLOTS = 20
+
+
+def run() -> dict:
+ import greeneye as ge
+
+ backend = C.choose_backend("native")
+ out = C.artifact_dir("s02_counter_publication")
+ commands = [
+ 'ge.launch(kernel=DEMO_ELF, backend="%s")' % backend,
+ "s.watch(gsp_ctrl+0x40, size=8, kind='w', callback=record pc) # publication",
+ "for slot in 1..20: write mailbox cmd=5 (STATE); run(duration=0.05)",
+ "read gsp_ctrl.result after every slot; compare against the expected stage value",
+ ]
+ scenario = C.scenario(
+ ids=[
+ "Handoff-vGPU-IO.md PART 49/50 (2026-09-29): only 4/20 counters moved; all 20 reached 1 after the queue fix",
+ "PART 64 (2026-09-29 ~16:55): raw page dump proves 20 slots at 16-byte stride",
+ "PART 46/48 (2026-09-29): rescue/sweep latency and tracker waits",
+ ],
+ baseline=(
+ "PARTs 46->64: ~5 days of sessions; PART 49 'only 4 of 20 counters "
+ "ever moved'; each boot re-derived the slot layout because the "
+ "counter page GPA changed (PART 51/53)"
+ ),
+ projection=(
+ "on real silicon the 20 slots are per-channel memory pages; the "
+ "analog publishes 20 logical slots through the same watchpoint "
+ "shape, one store each"
+ ),
+ blocked=True,
+ device="H2.2 device model + real GSP-RM counter pages",
+ )
+ C.render_banner(
+ "s02_counter_publication",
+ "Of the N client-visible slots, which ones advanced, and where was each published?",
+ "20 GR per-channel completion counters: 4/20 published for days until a live page dump",
+ scenario["static_baseline"],
+ commands,
+ )
+
+ session = ge.launch(kernel=C.DEMO_ELF, backend=backend, name="vd-s02")
+ slots = []
+ try:
+ ctrl = session.symbols.lookup("gsp_ctrl")
+ publishes = []
+ session.watch(ctrl + C.CTRL_RESULT, size=8, kind="w", callback=lambda hit: publishes.append(hit.pc))
+ session.run(duration=0.25)
+ before = session.memory.read_u64(ctrl + C.CTRL_RPC_COUNT)
+ for slot in range(SLOTS):
+ session.memory.write_u64(ctrl + C.CTRL_MAILBOX, 5) # STATE
+ session.run(duration=0.05)
+ value = session.memory.read_u64(ctrl + C.CTRL_RESULT)
+ slots.append(
+ {
+ "slot": slot + 1,
+ "result": value,
+ "result_hex": f"{value:#x}",
+ "published": value in (0x10, 0x20, 0x30, 0x40),
+ }
+ )
+ after = session.memory.read_u64(ctrl + C.CTRL_RPC_COUNT)
+ published = [s for s in slots if s["published"]]
+ payload = {
+ "success": len(published) == SLOTS and (after - before) == SLOTS,
+ "backend": backend,
+ "slots_requested": SLOTS,
+ "slots_published": len(published),
+ "rpc_count_delta": after - before,
+ "watch_stores": len(publishes),
+ "distinct_values": sorted({s["result_hex"] for s in published}),
+ "per_slot": slots,
+ "publish_pcs": sorted(set(publishes)),
+ "question": f"did all {SLOTS} slots publish, and can each publication be attributed?",
+ "answer": (
+ f"{len(published)}/{SLOTS} slots published ({after - before} dispatcher "
+ f"completions, {len(publishes)} stores to gsp_ctrl.result, values "
+ f"{sorted({s['result_hex'] for s in published})}); every store PC is captured, "
+ "exactly the evidence class that took a live 512-byte page dump on the real path"
+ ),
+ "success_criteria": {
+ "pass": f"{SLOTS}/{SLOTS} slots observed with rpc_count advancing once per slot",
+ "fail": "any slot does not publish or the count delta disagrees",
+ },
+ "real_case": scenario,
+ }
+ C.write_json(os.path.join(out, "counters.json"), payload)
+ finally:
+ session.close()
+ return payload
+
+
+def main() -> int:
+ return 0 if C.timed_demo(run, C.artifact_dir("s02_counter_publication"))["success"] else 1
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())