Add EEPROM boot firmware (exp 0xDB) and supporting tools
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).
This commit is contained in:
parent
bbdcb243dc
commit
3d2cd477b2
144
docs/EEPROM-RECOVERY.md
Normal file
144
docs/EEPROM-RECOVERY.md
Normal file
@ -0,0 +1,144 @@
|
||||
# SkyWalker-1 EEPROM Recovery Guide
|
||||
|
||||
The device is soft-bricked: the FX2 boot ROM hangs trying to load
|
||||
corrupted firmware from EEPROM, preventing USB enumeration.
|
||||
|
||||
## Symptoms
|
||||
|
||||
- Hub shows `0101 power connect []` (D+ pull-up active, no enumeration)
|
||||
- dmesg: `device descriptor read/8, error -110` (timeout)
|
||||
- Does not enumerate as bare FX2 (04B4:8613) either
|
||||
- NanoVNA on same hub works fine (hub hardware is OK)
|
||||
|
||||
## Root Cause
|
||||
|
||||
The EEPROM (24C128 at I2C 0x51) likely has corrupted boot data. The
|
||||
FX2LP boot ROM reads the EEPROM at power-up and hangs if the C2 image
|
||||
has invalid load record lengths or addresses. The boot ROM occupies
|
||||
the 8051 core, preventing USB control transfer processing.
|
||||
|
||||
## Recovery Options (pick one)
|
||||
|
||||
### Option A: SOIC Clip + External Programmer (Recommended)
|
||||
|
||||
Blank the first byte of the EEPROM so the boot ROM falls back to
|
||||
bare FX2 enumeration. Then reload via USB.
|
||||
|
||||
**Hardware needed:**
|
||||
- SOIC-8 test clip (Pomona 5250 or similar, ~$5)
|
||||
- CH341A USB programmer (~$3) or Bus Pirate or any I2C-capable tool
|
||||
- OR: Raspberry Pi / Arduino with I2C
|
||||
|
||||
**Steps:**
|
||||
1. Power OFF the SkyWalker-1 (unplug USB)
|
||||
2. Locate the 24C128 EEPROM on the PCB (SOIC-8 package near the FX2)
|
||||
3. Clip the SOIC clip onto the EEPROM
|
||||
4. Connect to your I2C programmer (SDA, SCL, VCC, GND)
|
||||
5. Read and save the EEPROM contents (16KB backup!)
|
||||
6. Write 0xFF to address 0x0000 (corrupts the C2 magic byte)
|
||||
7. Remove clip, plug in SkyWalker-1
|
||||
8. Device should enumerate as bare FX2 (04B4:8613)
|
||||
9. Load custom firmware via `fw_load.py`
|
||||
10. Use the custom firmware to write good C2 image back to EEPROM
|
||||
|
||||
**With CH341A:**
|
||||
```bash
|
||||
# Read backup
|
||||
flashrom -p ch341a_spi -c "AT24C128" -r eeprom_backup.bin
|
||||
|
||||
# Or use i2c-tools if CH341A is in I2C mode:
|
||||
# i2cdetect -l (find the CH341A bus)
|
||||
# i2cdump -y <bus> 0x51 b > dump.txt
|
||||
```
|
||||
|
||||
**With Raspberry Pi (I2C):**
|
||||
```bash
|
||||
# Enable I2C: raspi-config -> Interfaces -> I2C
|
||||
# Connect EEPROM: SDA->GPIO2, SCL->GPIO3, VCC->3.3V, GND->GND
|
||||
i2cdetect -y 1 # Should show 0x51
|
||||
# Read first byte
|
||||
i2cget -y 1 0x51 0x00
|
||||
# Write 0xFF to byte 0 (corrupts C2 header)
|
||||
i2cset -y 1 0x51 0x00 0xFF
|
||||
```
|
||||
|
||||
### Option B: Hold SDA HIGH During Boot
|
||||
|
||||
Prevent the EEPROM from responding by holding SDA HIGH, forcing
|
||||
the boot ROM to see "no EEPROM" and enumerate as bare FX2.
|
||||
|
||||
**Steps:**
|
||||
1. Locate the SDA test point or EEPROM pin 5 (SDA)
|
||||
2. Connect a 1kΩ pull-up to 3.3V on SDA
|
||||
3. Power on the SkyWalker-1
|
||||
4. If it enumerates as bare FX2 (04B4:8613), load firmware:
|
||||
```bash
|
||||
python3 tools/fw_load.py load firmware/build/skywalker1.ihx
|
||||
```
|
||||
5. Remove the pull-up
|
||||
6. Use the loaded firmware to reprogram the EEPROM
|
||||
|
||||
**Note:** This only works if the SDA pull-up is strong enough to
|
||||
override the EEPROM's SDA output. May need to experiment with
|
||||
pull-up values (470Ω to 4.7kΩ).
|
||||
|
||||
### Option C: Desolder EEPROM Pin
|
||||
|
||||
Most reliable but requires soldering skill.
|
||||
|
||||
1. Lift EEPROM pin 5 (SDA) from the PCB pad
|
||||
2. Power on → enumerates as bare FX2
|
||||
3. Load firmware via USB
|
||||
4. Resolder pin 5
|
||||
5. Use firmware to reprogram EEPROM with good C2 image
|
||||
|
||||
### Option D: Wait + Watch (Long Shot)
|
||||
|
||||
If the boot ROM eventually times out on the I2C read, the device
|
||||
will briefly enumerate as bare FX2. This might take several minutes.
|
||||
|
||||
```bash
|
||||
# Watch for bare FX2 enumeration
|
||||
sudo dmesg -w | grep -E "04b4|8613|New USB"
|
||||
|
||||
# In another terminal, keep power cycling every 5 minutes
|
||||
while true; do
|
||||
sudo uhubctl -l 1-5.4.4 -p 3 -a off
|
||||
sleep 5
|
||||
sudo uhubctl -l 1-5.4.4 -p 3 -a on
|
||||
sleep 300 # wait 5 minutes
|
||||
done
|
||||
```
|
||||
|
||||
If it appears even briefly:
|
||||
```bash
|
||||
python3 tools/fw_load.py load firmware/build/skywalker1.ihx --force
|
||||
```
|
||||
|
||||
## After Recovery
|
||||
|
||||
Once the device enumerates (as bare FX2 or with loaded firmware):
|
||||
|
||||
1. **Load custom firmware to RAM:**
|
||||
```bash
|
||||
python3 tools/fw_load.py load firmware/build/skywalker1.ihx
|
||||
```
|
||||
|
||||
2. **Reprogram EEPROM with good C2 image:**
|
||||
```bash
|
||||
# The custom firmware needs EEPROM write support first
|
||||
# (vendor command to relay I2C writes to EEPROM)
|
||||
python3 tools/eeprom_write.py flash firmware/build/skywalker1_eeprom.bin
|
||||
```
|
||||
|
||||
3. **Or restore stock firmware:**
|
||||
If you have a backup of the original EEPROM contents, flash that
|
||||
instead of the custom firmware.
|
||||
|
||||
## Prevention
|
||||
|
||||
- Never send `BOOT_8PSK (0x89)` with mode 0x84 ("firmware load")
|
||||
unless you know what data the firmware expects
|
||||
- Always backup EEPROM before experiments that touch vendor commands
|
||||
- The stock firmware's I2C proxy (0x83/0x84) may have side effects
|
||||
on the EEPROM that aren't documented
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,12 +1,16 @@
|
||||
FX2LIBDIR=fx2lib/
|
||||
BASENAME=skywalker1
|
||||
SOURCES=skywalker1.c
|
||||
A51_SOURCES=dscr.a51
|
||||
VID=0x09C0
|
||||
PID=0x0203
|
||||
CODE_SIZE=--code-size 0x3c00
|
||||
|
||||
include $(FX2LIBDIR)lib/fx2.mk
|
||||
|
||||
load: $(BUILDDIR)/$(BASENAME).bix
|
||||
../tools/fw_load.py $(BUILDDIR)/$(BASENAME).bix
|
||||
FX2LIBDIR=fx2lib/
|
||||
BASENAME=skywalker1
|
||||
SOURCES=skywalker1.c
|
||||
A51_SOURCES=dscr.a51
|
||||
VID=0x09C0
|
||||
PID=0x0203
|
||||
CODE_SIZE=--code-size 0x3c00
|
||||
|
||||
include $(FX2LIBDIR)lib/fx2.mk
|
||||
|
||||
load: $(BUILDDIR)/$(BASENAME).bix
|
||||
../tools/fw_load.py load $(BUILDDIR)/$(BASENAME).ihx
|
||||
|
||||
eeprom: $(BUILDDIR)/$(BASENAME).ihx
|
||||
python3 ../tools/eeprom_write.py convert $(BUILDDIR)/$(BASENAME).ihx \
|
||||
-o $(BUILDDIR)/$(BASENAME)_eeprom.bin
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,251 +1,157 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Genpix SkyWalker-1 EEPROM firmware dump tool.
|
||||
|
||||
Reads the Cypress FX2 boot EEPROM via the I2C_READ vendor command.
|
||||
Protocol: I2C_READ (0x84), wValue=0x51, wIndex=offset, length=chunk_size
|
||||
|
||||
The EEPROM contains firmware in Cypress C2 IIC boot format:
|
||||
- Header: C2 VID_L VID_H PID_L PID_H DID_L DID_H CONFIG
|
||||
- Records: LEN_H LEN_L ADDR_H ADDR_L DATA[LEN]
|
||||
- End: 80 01 ENTRY_H ENTRY_L (reset vector)
|
||||
"""
|
||||
import usb.core, usb.util, sys, struct
|
||||
|
||||
VENDOR_ID = 0x09C0
|
||||
PRODUCT_ID = 0x0203
|
||||
I2C_READ = 0x84
|
||||
EEPROM_SLAVE = 0x51
|
||||
|
||||
|
||||
def find_device():
|
||||
dev = usb.core.find(idVendor=VENDOR_ID, idProduct=PRODUCT_ID)
|
||||
if dev is None:
|
||||
print("SkyWalker-1 not found")
|
||||
sys.exit(1)
|
||||
return dev
|
||||
|
||||
|
||||
def detach_driver(dev):
|
||||
intf_num = None
|
||||
for cfg in dev:
|
||||
for intf in cfg:
|
||||
if dev.is_kernel_driver_active(intf.bInterfaceNumber):
|
||||
try:
|
||||
dev.detach_kernel_driver(intf.bInterfaceNumber)
|
||||
intf_num = intf.bInterfaceNumber
|
||||
except usb.core.USBError as e:
|
||||
print(f"Cannot detach driver: {e}")
|
||||
print("Try: sudo modprobe -r dvb_usb_gp8psk")
|
||||
sys.exit(1)
|
||||
try:
|
||||
dev.set_configuration()
|
||||
except:
|
||||
pass
|
||||
return intf_num
|
||||
|
||||
|
||||
def eeprom_read(dev, offset, length=64):
|
||||
"""Read from EEPROM at given offset."""
|
||||
# wIndex holds the EEPROM byte offset (16-bit, so max 64KB)
|
||||
return dev.ctrl_transfer(
|
||||
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN,
|
||||
I2C_READ, EEPROM_SLAVE, offset, length, 2000)
|
||||
|
||||
|
||||
def parse_c2_header(data):
|
||||
"""Parse Cypress C2 boot EEPROM header."""
|
||||
if data[0] != 0xC2:
|
||||
print(f" Not a C2 EEPROM (first byte: 0x{data[0]:02X})")
|
||||
return None
|
||||
|
||||
vid = data[2] << 8 | data[1]
|
||||
pid = data[4] << 8 | data[3]
|
||||
did = data[6] << 8 | data[5]
|
||||
config = data[7]
|
||||
|
||||
print(f" Format: C2 (Large EEPROM, code loads to internal RAM)")
|
||||
print(f" VID: 0x{vid:04X} {'(Genpix)' if vid == 0x09C0 else ''}")
|
||||
print(f" PID: 0x{pid:04X} {'(SkyWalker-1)' if pid == 0x0203 else ''}")
|
||||
print(f" DID: 0x{did:04X}")
|
||||
print(f" Config: 0x{config:02X}", end="")
|
||||
|
||||
config_flags = []
|
||||
if config & 0x40:
|
||||
config_flags.append("400kHz I2C")
|
||||
if config & 0x04:
|
||||
config_flags.append("disconnect")
|
||||
if config_flags:
|
||||
print(f" ({', '.join(config_flags)})")
|
||||
else:
|
||||
print()
|
||||
|
||||
return {"vid": vid, "pid": pid, "did": did, "config": config}
|
||||
|
||||
|
||||
def parse_records(data, offset=8):
|
||||
"""Parse C2 load records from EEPROM data."""
|
||||
records = []
|
||||
while offset < len(data) - 4:
|
||||
rec_len = (data[offset] << 8) | data[offset + 1]
|
||||
rec_addr = (data[offset + 2] << 8) | data[offset + 3]
|
||||
|
||||
if rec_len == 0x8001:
|
||||
# End marker - rec_addr is the entry point (reset vector)
|
||||
records.append({
|
||||
"type": "end",
|
||||
"entry_point": rec_addr,
|
||||
"offset": offset
|
||||
})
|
||||
break
|
||||
elif rec_len == 0 or rec_len > 0x4000:
|
||||
records.append({
|
||||
"type": "invalid",
|
||||
"raw_len": rec_len,
|
||||
"offset": offset
|
||||
})
|
||||
break
|
||||
|
||||
rec_data = data[offset + 4:offset + 4 + rec_len]
|
||||
records.append({
|
||||
"type": "data",
|
||||
"length": rec_len,
|
||||
"load_addr": rec_addr,
|
||||
"data": bytes(rec_data),
|
||||
"offset": offset
|
||||
})
|
||||
offset += 4 + rec_len
|
||||
|
||||
return records
|
||||
|
||||
|
||||
def main():
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(description="Dump SkyWalker-1 EEPROM firmware")
|
||||
parser.add_argument('-o', '--output', default='skywalker1_eeprom.bin',
|
||||
help='Output file for raw EEPROM dump')
|
||||
parser.add_argument('--extract', action='store_true',
|
||||
help='Also extract firmware as flat binary')
|
||||
parser.add_argument('--max-size', type=int, default=16384,
|
||||
help='Maximum EEPROM size to read (default: 16384)')
|
||||
args = parser.parse_args()
|
||||
|
||||
print("Genpix SkyWalker-1 EEPROM Dump")
|
||||
print("=" * 40)
|
||||
|
||||
dev = find_device()
|
||||
print(f"Found device: Bus {dev.bus} Addr {dev.address}")
|
||||
intf = detach_driver(dev)
|
||||
|
||||
try:
|
||||
# Read EEPROM
|
||||
chunk_size = 64 # Max reliable USB control transfer
|
||||
eeprom = bytearray()
|
||||
consecutive_ff = 0
|
||||
|
||||
print(f"\nReading EEPROM (max {args.max_size} bytes)...")
|
||||
|
||||
for offset in range(0, args.max_size, chunk_size):
|
||||
# wIndex only goes up to 0xFFFF, which covers 64KB EEPROMs
|
||||
data = eeprom_read(dev, offset, chunk_size)
|
||||
|
||||
if data is None:
|
||||
print(f"\n Read failed at offset 0x{offset:04X}")
|
||||
break
|
||||
|
||||
chunk = bytes(data)
|
||||
eeprom.extend(chunk)
|
||||
|
||||
# Check for end of data
|
||||
if all(b == 0xFF for b in chunk):
|
||||
consecutive_ff += 1
|
||||
if consecutive_ff >= 4:
|
||||
print(f"\r End of data at 0x{len(eeprom):04X} (0xFF padding) ")
|
||||
break
|
||||
else:
|
||||
consecutive_ff = 0
|
||||
|
||||
if offset % 1024 == 0:
|
||||
print(f"\r 0x{offset:04X} / 0x{args.max_size:04X} ", end="", flush=True)
|
||||
|
||||
print(f"\r Read {len(eeprom)} bytes total ")
|
||||
|
||||
# Save raw EEPROM
|
||||
with open(args.output, 'wb') as f:
|
||||
f.write(eeprom)
|
||||
print(f" Saved raw EEPROM to: {args.output}")
|
||||
|
||||
# Parse header
|
||||
print(f"\n{'=' * 40}")
|
||||
print("EEPROM Header:")
|
||||
header = parse_c2_header(eeprom)
|
||||
|
||||
if header:
|
||||
# Parse load records
|
||||
print(f"\nLoad Records:")
|
||||
records = parse_records(eeprom)
|
||||
total_code = 0
|
||||
entry_point = None
|
||||
|
||||
for i, rec in enumerate(records):
|
||||
if rec["type"] == "data":
|
||||
end_addr = rec["load_addr"] + rec["length"] - 1
|
||||
preview = rec["data"][:8].hex(' ')
|
||||
print(f" [{i}] {rec['length']:5d} bytes -> "
|
||||
f"0x{rec['load_addr']:04X}-0x{end_addr:04X} "
|
||||
f"[{preview}...]")
|
||||
total_code += rec["length"]
|
||||
elif rec["type"] == "end":
|
||||
entry_point = rec["entry_point"]
|
||||
print(f" [{i}] END MARKER -> entry point: 0x{entry_point:04X}")
|
||||
else:
|
||||
print(f" [{i}] INVALID (raw_len=0x{rec['raw_len']:04X}) "
|
||||
f"at EEPROM offset 0x{rec['offset']:04X}")
|
||||
|
||||
print(f"\n Total firmware: {total_code} bytes in "
|
||||
f"{sum(1 for r in records if r['type'] == 'data')} records")
|
||||
if entry_point:
|
||||
print(f" Entry point: 0x{entry_point:04X} (LJMP target after boot)")
|
||||
|
||||
# Extract flat binary
|
||||
if args.extract and records:
|
||||
# Build memory image
|
||||
mem = bytearray(0x10000) # 64KB address space
|
||||
for b in range(len(mem)):
|
||||
mem[b] = 0xFF
|
||||
|
||||
for rec in records:
|
||||
if rec["type"] == "data":
|
||||
addr = rec["load_addr"]
|
||||
mem[addr:addr + rec["length"]] = rec["data"]
|
||||
|
||||
# Find actual used range
|
||||
min_addr = min(r["load_addr"] for r in records if r["type"] == "data")
|
||||
max_addr = max(r["load_addr"] + r["length"]
|
||||
for r in records if r["type"] == "data")
|
||||
|
||||
flat_file = args.output.replace('.bin', '_flat.bin')
|
||||
with open(flat_file, 'wb') as f:
|
||||
f.write(mem[min_addr:max_addr])
|
||||
print(f"\n Flat binary: {flat_file}")
|
||||
print(f" Address range: 0x{min_addr:04X}-0x{max_addr:04X} "
|
||||
f"({max_addr - min_addr} bytes)")
|
||||
|
||||
# Also save full 64KB image for Ghidra
|
||||
full_file = args.output.replace('.bin', '_full64k.bin')
|
||||
with open(full_file, 'wb') as f:
|
||||
f.write(mem)
|
||||
print(f" Full 64K image: {full_file} (for Ghidra, load at 0x0000)")
|
||||
|
||||
finally:
|
||||
if intf is not None:
|
||||
try:
|
||||
usb.util.release_interface(dev, intf)
|
||||
dev.attach_kernel_driver(intf)
|
||||
print("\nRe-attached kernel driver")
|
||||
except:
|
||||
print("\nNote: run 'sudo modprobe dvb_usb_gp8psk' to reload")
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
#!/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 ===')
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
1596
tools/fw_load.py
1596
tools/fw_load.py
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user