skywalker-1/tools/eeprom_deep_scan.py
Ryan Malloy 0d6facb321 Add experimental I2C debugging and EEPROM analysis tools
One-off diagnostic scripts from experiments 0xD7-0xDB investigating
the I2C BERR deadlock. Documents the systematic elimination of
software-only recovery approaches:

- i2c_host_test.py: Proved 0xA0 register writes cannot drive I2C bus
- i2c_register_test.py: Tested I2C register writability from host
- i2c_recovery_boot.py: Attempted I2C state machine recovery via boot
- eeprom_flash_a0.py: Host-side EEPROM flash attempt (failed)
- boot_ab_test.py / boot_test.py: EEPROM boot reliability testing
- a8_autoclear_test.py: BCM4500 command register auto-clear behavior
- addr_gateway_test.py: BCM3440 gateway address routing analysis
- stock_fw_compare.py / stock_fw_test.py: Stock vs custom fw analysis
2026-02-20 10:57:10 -07:00

113 lines
3.3 KiB
Python

#!/usr/bin/env python3
"""Deep EEPROM scan — find the real PLL data location."""
import sys
sys.path.insert(0, 'tools')
from skywalker_lib import SkyWalker1
CMD_EEPROM_READ = 0xC0
sw = SkyWalker1()
sw.open()
def ee(addr, length):
return sw._vendor_in(CMD_EEPROM_READ, value=addr, index=length, length=length)
def hex_line(addr, data):
hex_str = ' '.join(f'{b:02X}' for b in data)
return f'{addr:04X}: {hex_str}'
# Dump 0x3F00-0x4100 (area around the boundary — zero gap between FX2 and BCM firmware?)
print('=== Region around FX2 firmware end (0x2530) ===')
for addr in range(0x2530, 0x2600, 16):
d = ee(addr, 16)
print(f' {hex_line(addr, d)}')
print()
print('=== Region 0x3F00-0x4080 (end of first 16KB, start of second) ===')
for addr in range(0x3F00, 0x4080, 16):
d = ee(addr, 16)
print(f' {hex_line(addr, d)}')
# Check the END of the AT24C256 (0x7F00-0x7FFF)
print()
print('=== End of EEPROM 0x7F00-0x7FFF ===')
for addr in range(0x7F00, 0x8000, 16):
d = ee(addr, 16)
print(f' {hex_line(addr, d)}')
# Parse the second image at 0x4000 to find its end
print()
print('=== Parsing 0x4000 as C2-like records ===')
offset = 0x4000
# First check if it's a C2 header
header = ee(0x4000, 4)
print(f' Header bytes: {header.hex(" ")}')
# Could be: [len_hi, len_lo, addr_hi, addr_lo]
rec_len = (header[0] << 8) | header[1]
rec_addr = (header[2] << 8) | header[3]
print(f' As record: len={rec_len} (0x{rec_len:04X}), addr=0x{rec_addr:04X}')
print()
# Try parsing as load records (same format as C2 but without the 8-byte header)
offset = 0x4000
print(' Attempting record parse:')
total_size = 0
while offset < 0x7000:
hdr = ee(offset, 4)
rlen = (hdr[0] << 8) | hdr[1]
raddr = (hdr[2] << 8) | hdr[3]
if rlen == 0x8001:
print(f' [{total_size}] END MARKER at 0x{offset:04X} → entry=0x{raddr:04X}')
offset += 4
break
elif rlen == 0 or rlen > 0x4000:
print(f' [{total_size}] STOP at 0x{offset:04X}: len=0x{rlen:04X} addr=0x{raddr:04X}')
# Dump surrounding bytes
d = ee(offset, 32)
print(f' Bytes: {d.hex(" ")}')
offset += 4
break
end = raddr + rlen - 1
print(f' [{total_size}] {rlen:5d} bytes at EEPROM 0x{offset:04X} → 0x{raddr:04X}-0x{end:04X}')
offset += 4 + rlen
total_size += rlen
if total_size > 30000:
print(' (aborting, too much data)')
break
print(f' Second image ends at: 0x{offset:04X}')
print()
# Check immediately after second image for PLL data
print(f'=== After second image: 0x{offset:04X}-0x{offset+256:04X} ===')
for addr in range(offset, offset + 256, 16):
d = ee(addr, 16)
print(f' {hex_line(addr, d)}')
# Also check for PLL blocks at this offset
print()
print(f'=== PLL block scan starting at 0x{offset:04X} ===')
for addr in range(offset, offset + 400, 20):
block = ee(addr, 20)
count = block[0]
if count == 0:
print(f' 0x{addr:04X}: [sentinel count=0]')
break
elif 1 <= count <= 16:
ab = block[4:4 + count]
print(f' 0x{addr:04X}: count={count} A9=0x{block[1]:02X} AA=0x{block[2]:02X} '
f'unused=0x{block[3]:02X} AB=[{ab.hex(" ")}]')
else:
print(f' 0x{addr:04X}: NOT PLL (first byte=0x{count:02X})')
# But continue scanning
pass
sw.close()
print()
print('=== Done ===')