Firmware: Rewrite skywalker1.c for EEPROM boot experiment — tests whether I2C hardware controller works after FX2 boot ROM completes EEPROM load (bypassing the CPUCS restart that triggers BERR). Tools: - fw_load.py: Add I2C cleanup stub, pre-halt register flush, improved error handling and segment loading - eeprom_write.py: Add IHX→C2 EEPROM image converter (16KB format with length-prefixed segments, checksum) - eeprom_dump.py: Refactor for cleaner output, better hex display - skywalker_lib.py: Minor I2C register constant updates Docs: - EEPROM-RECOVERY.md: Four recovery options for soft-bricked device (SOIC clip, SDA pull-up, desolder, wait-for-timeout) - Master reference: Updated with EEPROM boot findings Status: EEPROM flash blocked — stock firmware I2C proxy returns pipe errors, host-side 0xA0 writes proven unable to drive peripheral bus. Device boot ROM intermittently hangs on EEPROM I2C read (~3-6% success).
158 lines
5.4 KiB
Python
158 lines
5.4 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Genpix SkyWalker-1 EEPROM exploration tool.
|
|
|
|
Reads the calibration EEPROM (AT24Cxxx at I2C addr 0x51) via the custom
|
|
firmware's EEPROM_READ (0xC0) vendor command. This uses 16-bit addressing
|
|
directly, bypassing the stock firmware's single-byte I2C_READ protocol.
|
|
|
|
Primary purpose: Find where PLL configuration data is stored so the
|
|
bcm4500_load_pll_config() function reads from the correct address.
|
|
"""
|
|
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):
|
|
"""Read bytes from EEPROM at 16-bit address.
|
|
wValue = address, wIndex = length."""
|
|
return sw._vendor_in(CMD_EEPROM_READ, value=addr, index=length, length=length)
|
|
|
|
|
|
def hex_dump(addr, data):
|
|
"""Print hex dump with ASCII sidebar."""
|
|
for i in range(0, len(data), 16):
|
|
chunk = data[i:i + 16]
|
|
hex_str = ' '.join(f'{b:02X}' for b in chunk)
|
|
ascii_str = ''.join(chr(b) if 32 <= b < 127 else '.' for b in chunk)
|
|
print(f' {addr + i:04X}: {hex_str:<48s} |{ascii_str}|')
|
|
|
|
|
|
print('=== EEPROM Exploration ===')
|
|
print()
|
|
|
|
# Step 1: Determine EEPROM size by aliasing detection
|
|
print('--- Size Detection ---')
|
|
data_0000 = eeprom_read(0x0000, 16)
|
|
data_4000 = eeprom_read(0x4000, 16)
|
|
data_8000 = eeprom_read(0x8000, 16)
|
|
print(f' 0x0000: {data_0000.hex(" ")}')
|
|
print(f' 0x4000: {data_4000.hex(" ")}')
|
|
print(f' 0x8000: {data_8000.hex(" ")}')
|
|
if data_0000 == data_4000:
|
|
print(' Result: 0x4000 ALIASES to 0x0000 → AT24C128 (16KB)')
|
|
eeprom_size = 16384
|
|
elif data_0000 == data_8000:
|
|
print(' Result: 0x8000 aliases to 0x0000 → AT24C256 (32KB)')
|
|
eeprom_size = 32768
|
|
else:
|
|
print(' Result: All different → AT24C512+ (64KB+)')
|
|
eeprom_size = 65536
|
|
print()
|
|
|
|
# Step 2: Dump first 512 bytes (FX2 boot firmware header + data)
|
|
print('--- EEPROM 0x0000-0x01FF (C2 boot header region) ---')
|
|
for addr in range(0x0000, 0x0200, 64):
|
|
data = eeprom_read(addr, 64)
|
|
hex_dump(addr, data)
|
|
print()
|
|
|
|
# Step 3: Scan for PLL-like 20-byte blocks
|
|
# Format: [count(1-16), A9_val, AA_val, unused_byte, AB_data[count], padding...]
|
|
# Sentinel: count=0
|
|
print('--- Scanning for PLL config blocks ---')
|
|
print(' Format: [count, A9, AA, unused, AB_data[count]]')
|
|
print(' Sentinel: count=0')
|
|
print()
|
|
|
|
# Scan the entire EEPROM in 20-byte strides
|
|
pll_candidates = []
|
|
for addr in range(0, min(eeprom_size, 0x4000), 20):
|
|
data = eeprom_read(addr, 20)
|
|
count = data[0]
|
|
# Look for potential sentinel (count=0) preceded by valid blocks
|
|
if count == 0 and addr > 0:
|
|
# Check if previous 20 bytes looked like PLL data
|
|
prev = eeprom_read(addr - 20, 20)
|
|
if 1 <= prev[0] <= 16:
|
|
pll_candidates.append({
|
|
'sentinel_addr': addr,
|
|
'last_block_addr': addr - 20,
|
|
'last_count': prev[0],
|
|
'last_a9': prev[1],
|
|
'last_aa': prev[2],
|
|
})
|
|
|
|
if pll_candidates:
|
|
print(' Found sentinel(s):')
|
|
for c in pll_candidates:
|
|
print(f' Sentinel at 0x{c["sentinel_addr"]:04X}')
|
|
print(f' Last block at 0x{c["last_block_addr"]:04X}: '
|
|
f'count={c["last_count"]} A9=0x{c["last_a9"]:02X} AA=0x{c["last_aa"]:02X}')
|
|
# Walk backwards to find start of PLL data
|
|
start = c['last_block_addr']
|
|
while start >= 20:
|
|
prev = eeprom_read(start - 20, 20)
|
|
if 1 <= prev[0] <= 16:
|
|
start -= 20
|
|
else:
|
|
break
|
|
print(f' PLL data likely starts at: 0x{start:04X}')
|
|
# Dump the PLL blocks
|
|
print(f' PLL block dump:')
|
|
for baddr in range(start, c['sentinel_addr'] + 20, 20):
|
|
block = eeprom_read(baddr, 20)
|
|
cnt = block[0]
|
|
if cnt == 0:
|
|
print(f' 0x{baddr:04X}: [sentinel count=0]')
|
|
break
|
|
ab = block[4:4 + cnt]
|
|
print(f' 0x{baddr:04X}: count={cnt} A9=0x{block[1]:02X} '
|
|
f'AA=0x{block[2]:02X} unused=0x{block[3]:02X} '
|
|
f'AB=[{ab.hex(" ")}]')
|
|
else:
|
|
print(' No PLL sentinel found in first 16KB!')
|
|
print(' Dumping any 20-byte-aligned blocks with count 1-16:')
|
|
for addr in range(0, min(eeprom_size, 0x1000), 20):
|
|
data = eeprom_read(addr, 20)
|
|
count = data[0]
|
|
if 1 <= count <= 16:
|
|
ab = data[4:4 + count]
|
|
print(f' 0x{addr:04X}: count={count} A9=0x{data[1]:02X} '
|
|
f'AA=0x{data[2]:02X} unused=0x{data[3]:02X} '
|
|
f'AB=[{ab.hex(" ")}]')
|
|
print()
|
|
|
|
# Step 4: Dump around the 16KB boundary (where our code expects PLL data)
|
|
if eeprom_size > 16384:
|
|
print('--- EEPROM 0x3FE0-0x4060 (16KB boundary) ---')
|
|
for addr in range(0x3FE0, 0x4060, 64):
|
|
data = eeprom_read(addr, 64)
|
|
hex_dump(addr, data)
|
|
print()
|
|
|
|
# Step 5: Check for 0xFF regions (empty/erased)
|
|
print('--- Empty region scan ---')
|
|
last_was_ff = False
|
|
for addr in range(0, min(eeprom_size, 0x4000), 64):
|
|
data = eeprom_read(addr, 64)
|
|
is_ff = all(b == 0xFF for b in data)
|
|
if is_ff and not last_was_ff:
|
|
print(f' 0xFF starts at 0x{addr:04X}')
|
|
last_was_ff = True
|
|
elif not is_ff and last_was_ff:
|
|
print(f' Data resumes at 0x{addr:04X}')
|
|
last_was_ff = False
|
|
if last_was_ff:
|
|
print(f' 0xFF continues to end of scanned region')
|
|
|
|
sw.close()
|
|
print()
|
|
print('=== Done ===')
|