#!/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 ===')