aboutsummaryrefslogtreecommitdiffstats
path: root/hw/tinygrad-psp-ring-wrap.patch
blob: 7b94069d2a57c848c6d9f5ccf13ca51ac5bceb08 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
--- tinygrad/runtime/support/am/ip.py.orig	2026-10-05 14:28:31.791136814 +0200
+++ tinygrad/runtime/support/am/ip.py	2026-10-05 14:29:52.707496539 +0200
@@ -692,7 +692,19 @@
     wait_cond(lambda: self.adev.reg(f"{self.reg_pref}_64").read() & 0x8000FFFF, value=0x80000000, msg="sOS ring not created")
 
   def _ring_submit(self, cmd:am.struct_psp_gfx_cmd_resp) -> am.struct_psp_gfx_cmd_resp:
-    msg = am.struct_psp_gfx_rb_frame(fence_value=(prev_wptr:=self.adev.reg(f"{self.reg_pref}_67").read()) + 1,
+    # The PSP KM ring is circular (ring_size = 0x10000, frame = 64 B => 1024 frames) but the wptr
+    # was advanced without ever wrapping, so after ~1024 submissions the frame was written past the
+    # end of the ring and the fence stopped advancing ("sOS ring not responding", wptr ~16353 of
+    # 16384 dwords). Wrap the wptr, and use an independent monotonic fence so the wrapped value
+    # cannot collide with a stale fence already in memory. Wrap one frame EARLY (>=, not >):
+    # the final slot is unusable because advancing into it makes wptr == ring_size == 0 (mod
+    # size) == rptr, which the PSP reads as an EMPTY ring, so that frame is never consumed.
+    # Measured: the stall always happened at wptr 16368 of 16384 dwords, i.e. the last slot.
+    _frame_dw = ctypes.sizeof(am.struct_psp_gfx_rb_frame) // 4
+    prev_wptr = self.adev.reg(f"{self.reg_pref}_67").read()
+    if (prev_wptr + _frame_dw) * 4 >= self.ring_size: prev_wptr = 0
+    self._fence_ctr = getattr(self, "_fence_ctr", 0) + 1
+    msg = am.struct_psp_gfx_rb_frame(fence_value=self._fence_ctr,
       cmd_buf_addr_lo=lo32(self.adev.paddr2mc(self.cmd_paddr)), cmd_buf_addr_hi=hi32(self.adev.paddr2mc(self.cmd_paddr)),
       fence_addr_lo=lo32(self.adev.paddr2mc(self.fence_paddr)), fence_addr_hi=hi32(self.adev.paddr2mc(self.fence_paddr)))
 
@@ -700,7 +712,7 @@
     self.adev.vram.view(self.ring_paddr + prev_wptr * 4, ctypes.sizeof(msg))[:] = memoryview(msg).cast('B')
 
     # Move the wptr
-    self.adev.reg(f"{self.reg_pref}_67").write(prev_wptr + ctypes.sizeof(am.struct_psp_gfx_rb_frame) // 4)
+    self.adev.reg(f"{self.reg_pref}_67").write(prev_wptr + _frame_dw)
 
     wait_cond(lambda: self.adev.vram.view(self.fence_paddr, 4, 'I')[0], value=msg.fence_value, msg="sOS ring not responding")