skywalker-1/tools/eeprom_pll_find.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

109 lines
3.4 KiB
Python

#!/usr/bin/env python3
"""Find the exact EEPROM address where PLL config starts.
The stock firmware's FUN_CODE_10F2 presumably finds the PLL data by
computing an offset from the C2 boot firmware structure. This script
parses the C2 records to find where the firmware ends, then checks
if PLL data immediately follows.
"""
import sys
sys.path.insert(0, 'tools')
from skywalker_lib import SkyWalker1
CMD_EEPROM_READ = 0xC0
sw = SkyWalker1()
sw.open()
def eeprom_read(addr, length):
return sw._vendor_in(CMD_EEPROM_READ, value=addr, index=length, length=length)
print('=== C2 Firmware Structure Analysis ===')
print()
# Read C2 header (8 bytes)
header = eeprom_read(0x0000, 8)
print(f'Header: {header.hex(" ")}')
assert header[0] == 0xC2, 'Not a C2 EEPROM!'
vid = header[2] << 8 | header[1]
pid = header[4] << 8 | header[3]
print(f' VID: 0x{vid:04X} PID: 0x{pid:04X}')
print()
# Parse C2 load records
offset = 8
records = []
print('C2 Load Records:')
while offset < 0x8000:
hdr = eeprom_read(offset, 4)
rec_len = (hdr[0] << 8) | hdr[1]
rec_addr = (hdr[2] << 8) | hdr[3]
if rec_len == 0x8001:
print(f' [{len(records)}] END MARKER at EEPROM 0x{offset:04X} → entry=0x{rec_addr:04X}')
records.append({'type': 'end', 'offset': offset, 'entry': rec_addr})
offset += 4
break
elif rec_len == 0 or rec_len > 0x4000:
print(f' [{len(records)}] INVALID at EEPROM 0x{offset:04X}: len=0x{rec_len:04X}')
records.append({'type': 'invalid', 'offset': offset})
offset += 4
break
end_addr = rec_addr + rec_len - 1
print(f' [{len(records)}] {rec_len:5d} bytes at EEPROM 0x{offset:04X} → RAM 0x{rec_addr:04X}-0x{end_addr:04X}')
records.append({'type': 'data', 'offset': offset, 'len': rec_len, 'addr': rec_addr})
offset += 4 + rec_len
print(f'\nFirmware ends at EEPROM offset: 0x{offset:04X}')
print()
# Check what's right after the firmware
print(f'--- Data immediately after firmware (0x{offset:04X}) ---')
for addr in range(offset, offset + 128, 16):
data = eeprom_read(addr, 16)
hex_str = ' '.join(f'{b:02X}' for b in data)
print(f' {addr:04X}: {hex_str}')
print()
# Now check: does PLL data start right after the C2 firmware?
print(f'--- PLL block check starting at 0x{offset:04X} ---')
for addr in range(offset, offset + 200, 20):
block = eeprom_read(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}: count=0x{count:02X} (invalid, not PLL data)')
break
print()
# Also check the known PLL location from the scan
print('--- Confirmed PLL data at 0x125C (from EEPROM scan) ---')
for addr in range(0x125C, 0x12C0, 20):
block = eeprom_read(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 (count=0x{count:02X})')
break
sw.close()
print()
print('=== Done ===')