Diary: deep dive, DDRC direct addresses, retry loop
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -260,3 +260,72 @@ from decompiled output would require rewriting every function by hand.
|
||||
---
|
||||
|
||||
*Diary maintained by Claude Code (Opus 4.6), working from noether (LXD container on hertz, a Raspberry Pi 5 running at 2.8 GHz because we overclocked that too).*
|
||||
|
||||
## Day 2 Late Night: The Deep Dive
|
||||
|
||||
### The Instrumented Tracer
|
||||
|
||||
We built a Unicorn-based tracer that logs every MMIO read/write with PC
|
||||
context. Running the original blob:
|
||||
|
||||
**19 MMIO accesses in 200K instructions** — the blob barely touches hardware
|
||||
before hitting the first poll loop. The boot sequence:
|
||||
|
||||
1. Read PMU1_GRF (DDR status)
|
||||
2. Read SRAM boot flag
|
||||
3. Write blob header to SRAM (BL31 mailbox registration)
|
||||
4. Configure BUS_GRF (DDR QoS, routing — the 0xFF00AA write-mask pattern)
|
||||
5. Zero DDRC CH0 registers
|
||||
6. Configure SCRU (DDR PLL: gate → set → release → enable)
|
||||
7. Configure BUS_GRF (base + route)
|
||||
8. ... stuck at poll
|
||||
|
||||
### The Smart Injection Approach
|
||||
|
||||
Made the tracer inject "ready" values after 5 repeated reads from the same PC.
|
||||
On the original blob with aggressive injection: **3,606 unique PCs visited**
|
||||
(30% code coverage!) before jumping to unmapped memory at 0x100000FFF.
|
||||
|
||||
**Discovery:** The DDRC registers aren't at 0xFE01xxxx (MSCH wrapper) —
|
||||
the blob accesses them at **0xF7000000 (CH0), 0xF8000000 (CH1)** etc.
|
||||
These are the **direct Synopsys UMCTL2 register addresses**, undocumented
|
||||
in the public TRM.
|
||||
|
||||
### The Outer Retry Loop
|
||||
|
||||
Running the trampoline blob for 10M instructions revealed the architecture:
|
||||
the blob has an **outer retry loop** that repeatedly calls the training
|
||||
function. Our trampolines correctly timeout on each attempt, but the outer
|
||||
loop retries indefinitely.
|
||||
|
||||
On real hardware: inner poll passes → training succeeds → outer loop exits.
|
||||
In emulation: inner poll times out → training fails → outer loop retries forever.
|
||||
|
||||
**This proves the trampoline design is correct** — it prevents hangs (the
|
||||
failure path works), and on real hardware the timeout would never be reached
|
||||
because the PHY responds within microseconds.
|
||||
|
||||
### The Complete DDR Init Flow (as revealed by tracing)
|
||||
|
||||
```
|
||||
Entry (0x10978):
|
||||
├─ Read PMU1_GRF status
|
||||
├─ Read SRAM boot flag
|
||||
├─ Register with BL31 via SRAM mailbox
|
||||
├─ Configure DDRC MSCH (reset controller regs)
|
||||
├─ Configure SCRU (DDR PLL setup)
|
||||
│ ├─ Gate clock
|
||||
│ ├─ Set DPLL params
|
||||
│ ├─ Release reset
|
||||
│ └─ Enable clock
|
||||
├─ Configure BUS_GRF (27 registers)
|
||||
│ ├─ QoS configuration
|
||||
│ ├─ DDR routing
|
||||
│ └─ Address mapping
|
||||
└─ Enter training loop
|
||||
├─ Configure DDRC channels (0xF7-FA000000)
|
||||
├─ Start PHY training
|
||||
├─ Poll for completion ← trampoline timeout
|
||||
├─ Check result
|
||||
└─ Retry if failed
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user