46155bbe91
Ship the new simulation & verification stack under simulation/:
- mmio_regions.py — address → region classifier (DDRCTL, DDRPHY,
OTP, SRAM, …). Shared by every other tool so trace output is
scannable without memorising the memory map.
- sim_tripwire.py — Bin-style per-access capture. Records
(seq, insn_tick, pc, addr, size, rw, val, region, fn_name) per
MMIO access. PCResolver bisects the vendor funs table parsed
from ddr_conservative_asm.s.
- tripwire_diff.py — PC-bucketed difflib.SequenceMatcher diff of
two tripwire CSVs. Buckets by fn_name so bitflip-induced control
flow divergence doesn't cascade noise.
- training_sim.py — DDR training simulator with --mode pass and
--mode bitflip (flip first N reads per training status, exercise
retry paths). BITFLIP_ONLY env var narrows to a single addr for
the sweep.
- bitflip_sweep.py — Flip each of 23 training-status addresses
one-at-a-time and tabulate retry convergence. Surfaces which
function(s) react to a transient fault by writing different
downstream register values.
Plus:
- mmio_diff.py updated: region-tagged divergence output,
--show-regions histogram, --tripwire-out-{vendor,rebuilt} CSV
capture, --capture-stack-writes for stack-allocated buffer diffs.
- debug_probes/tp_slot_{probe,writes}.py — ad-hoc Unicorn probes
for chasing a single-slot divergence in an SRAM buffer. Kept as
reference examples of how to extend the tripwire toolchain.
The stack found 6 silicon-hostile bugs in the rebuilt blob that
mmio_diff's write-sequence gate was structurally blind to, including
three ld-unresolved-symbol NULL derefs (case-mismatched externs,
missing DATA_SYMS) and one C-early-return-skips-shared-tail bug
where vendor's asm fell through to the tail via `b` after a `ret`.
122 lines
4.8 KiB
Python
122 lines
4.8 KiB
Python
#!/usr/bin/env python3
|
|
"""mmio_regions.py — address → region classifier for RK3588 DDR TPL.
|
|
|
|
Used by mmio_diff.py, blob_emu.py, call_trace.py, training_sim.py to
|
|
stamp each access with a short human-readable tag so trace output is
|
|
scannable without memorising hex ranges.
|
|
|
|
Categories (tag, short description):
|
|
DDRCTL uMCTL2 controller (per-channel ch+0x10000 window, or
|
|
global 0xFE010000)
|
|
DDRCTL:SW STAT/SWCTL/SWSTAT/PWRCTL subregion — frequently polled
|
|
DDRCTL:MR mode-register ops (MRCTRL0/MRSTAT subregion)
|
|
DDRPHY DDR PHY at 0xFE0C0000 (32 KB)
|
|
DDRPHY:TR training status subregion (+0x080/090/0B4/3CC/514/684/A24)
|
|
DDR_MEM actual DRAM content — 0x00000000..0x80000000 once trained
|
|
SRAM boot SRAM 0xFF000000..0xFF100000 (blob + globals)
|
|
CRU clock and reset 0xFD7C0000
|
|
DDR_CRU DDR PHY clock/reset (ch0..3) 0xFD800000..0xFD8C0000
|
|
SCRU secure clock/reset 0xFD8C0000
|
|
PMU_SRAM PMU SRAM 0xFF100000..0xFF110000
|
|
GRF general register file 0xFD580000
|
|
BUS_GRF bus-side GRF 0xFD5F0000
|
|
SGRF secure GRF 0xFE050000
|
|
PMU power-mgmt unit 0xFE400000
|
|
FW_DDR DDR firewall 0xFE030000
|
|
OTP OTP_NS — one-time-programmable controller 0xFECC0000
|
|
UART debug UART 0xFEB50000
|
|
OTHER unmapped / unclassified
|
|
"""
|
|
|
|
# Per-channel DDRCTL window repeats at these bases. Offset within
|
|
# a channel identifies the real sub-register.
|
|
DDRCTL_CHANNELS = (0xF7000000, 0xF8000000, 0xF9000000, 0xFA000000)
|
|
DDRCTL_GLOBAL = 0xFE010000
|
|
DDRCTL_WIN = 0x20000
|
|
DDRCTL_SUB = 0x10000 # ch+0x10000 lands inside ctrl space
|
|
|
|
# Training-status registers (the ones mmio_diff's REGION_OFF stubs).
|
|
DDRPHY_TRAINING_OFFSETS = {0x080, 0x090, 0x0B4, 0x3CC, 0x514, 0x684, 0xA24}
|
|
|
|
# DDRCTL sub-categories — offsets within the 0x10000 sub-window.
|
|
DDRCTL_SW_OFFSETS = {0x10014, 0x10180, 0x10C80, 0x10C84}
|
|
DDRCTL_MR_OFFSETS = {0x10080, 0x10090}
|
|
|
|
|
|
def classify(addr: int) -> str:
|
|
"""Return short region tag for an absolute address."""
|
|
# Emulator-only scratch stack (0x00400000..0x00500000) — not a real
|
|
# silicon region but tagged distinctly so tripwire can diff stack
|
|
# writes (e.g. param_2[] buffers fn_de40 fills).
|
|
if 0x00400000 <= addr < 0x00500000:
|
|
return "STACK"
|
|
# DDR memory (post-training)
|
|
if addr < 0x80000000:
|
|
return "DDR_MEM"
|
|
# Per-channel DDRCTL windows
|
|
for base in DDRCTL_CHANNELS:
|
|
if base <= addr < base + 0x40000:
|
|
off = addr - base
|
|
if off in DDRCTL_SW_OFFSETS:
|
|
return "DDRCTL:SW"
|
|
if off in DDRCTL_MR_OFFSETS:
|
|
return "DDRCTL:MR"
|
|
return "DDRCTL"
|
|
# Global DDRCTL (less common in TPL)
|
|
if DDRCTL_GLOBAL <= addr < DDRCTL_GLOBAL + DDRCTL_WIN:
|
|
return "DDRCTL"
|
|
# DDRPHY 0xFE0C0000..0xFE100000 (256 KB per 4 ports; training at base)
|
|
if 0xFE0C0000 <= addr < 0xFE100000:
|
|
off = addr & 0xFFF
|
|
if off in DDRPHY_TRAINING_OFFSETS:
|
|
return "DDRPHY:TR"
|
|
return "DDRPHY"
|
|
# Clock/reset
|
|
if 0xFD7C0000 <= addr < 0xFD800000: return "CRU"
|
|
if 0xFD800000 <= addr < 0xFD8C0000: return "DDR_CRU"
|
|
if 0xFD8C0000 <= addr < 0xFD8D0000: return "SCRU"
|
|
# Register files
|
|
if 0xFD580000 <= addr < 0xFD5A0000: return "GRF"
|
|
if 0xFD5F0000 <= addr < 0xFD600000: return "BUS_GRF"
|
|
if 0xFE050000 <= addr < 0xFE060000: return "SGRF"
|
|
# PMU / firewall / scrambler
|
|
if 0xFE400000 <= addr < 0xFE410000: return "PMU"
|
|
if 0xFE030000 <= addr < 0xFE040000: return "FW_DDR"
|
|
if 0xFECC0000 <= addr < 0xFECD0000: return "OTP"
|
|
# Debug UART
|
|
if 0xFEB50000 <= addr < 0xFEB60000: return "UART"
|
|
# Boot SRAM (blob + globals) and PMU SRAM
|
|
if 0xFF000000 <= addr < 0xFF100000: return "SRAM"
|
|
if 0xFF100000 <= addr < 0xFF110000: return "PMU_SRAM"
|
|
return "OTHER"
|
|
|
|
|
|
def classify_rw(addr: int, is_write: bool) -> str:
|
|
"""Direction-aware tag: 'DDRCTL:SW wr' vs 'DDRCTL:SW rd'."""
|
|
return f"{classify(addr):10s} {'wr' if is_write else 'rd'}"
|
|
|
|
|
|
if __name__ == "__main__":
|
|
import sys
|
|
# Smoke-test: classify a few known addresses
|
|
tests = [
|
|
(0xFE0C0A24, "DDRPHY:TR"), # DfiStatus
|
|
(0xFE0C0000, "DDRPHY"), # generic PHY reg
|
|
(0xF7010C80, "DDRCTL:SW"), # SWCTL ch0
|
|
(0xF7010080, "DDRCTL:MR"), # MRCTRL0 ch0
|
|
(0xF7010500, "DDRCTL"), # other DDRCTL
|
|
(0xFD7C0000, "CRU"),
|
|
(0xFD800000, "DDR_CRU"),
|
|
(0xFF001000, "SRAM"),
|
|
(0xFF100000, "PMU_SRAM"),
|
|
(0xFEB50000, "UART"),
|
|
(0x00100000, "DDR_MEM"),
|
|
]
|
|
fails = 0
|
|
for addr, want in tests:
|
|
got = classify(addr)
|
|
ok = "OK" if got == want else "FAIL"
|
|
if got != want: fails += 1
|
|
print(f" {ok} 0x{addr:08x} -> {got:12s} (want {want})")
|
|
sys.exit(1 if fails else 0)
|