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
155 lines
4.9 KiB
Python
155 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Deep verification: full register dump + indirect register test.
|
|
|
|
Checks whether:
|
|
1. Direct registers (0xA0-0xBF) are truly all 0x02 or vary
|
|
2. Indirect registers respond (proves DSP core is running)
|
|
3. Signal monitoring works after boot
|
|
4. Boot with vs without FW download produces different indirect reg values
|
|
"""
|
|
import sys
|
|
import time
|
|
sys.path.insert(0, 'tools')
|
|
from skywalker_lib import SkyWalker1
|
|
|
|
CMD_RAW_DEMOD_READ = 0xB1
|
|
CMD_I2C_RAW_READ = 0xB5
|
|
BCM4500_ADDR = 0x08
|
|
|
|
|
|
def full_direct_dump(sw, label):
|
|
"""Read all direct registers 0xA0-0xBF via raw I2C."""
|
|
print(f'\n --- {label}: Direct Register Dump 0xA0-0xBF ---')
|
|
values = {}
|
|
for reg in range(0xA0, 0xC0):
|
|
try:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
values[reg] = data[0]
|
|
except Exception:
|
|
values[reg] = None
|
|
|
|
# Print in 2 rows of 16
|
|
for base in [0xA0, 0xB0]:
|
|
row = []
|
|
for reg in range(base, base + 16):
|
|
v = values.get(reg)
|
|
row.append(f'{v:02X}' if v is not None else '??')
|
|
print(f' {base:02X}: {" ".join(row)}')
|
|
|
|
unique = set(v for v in values.values() if v is not None)
|
|
print(f' Unique values: {sorted(f"0x{v:02X}" for v in unique)}')
|
|
return values
|
|
|
|
|
|
def indirect_reg_test(sw, label):
|
|
"""Read indirect registers to test DSP core responsiveness.
|
|
Uses 0xB1 with wIndex=0 (indirect mode): wValue=page."""
|
|
print(f'\n --- {label}: Indirect Register Test ---')
|
|
# Key DSP pages: 0x00 (config), 0x06 (acq), 0x07 (AGC),
|
|
# 0x0A (Viterbi), 0x0F (transport)
|
|
pages = [0x00, 0x01, 0x06, 0x07, 0x0A, 0x0F, 0x10, 0x20]
|
|
results = {}
|
|
for page in pages:
|
|
try:
|
|
data = sw._vendor_in(CMD_RAW_DEMOD_READ, value=page,
|
|
index=0, length=1)
|
|
val = data[0]
|
|
results[page] = val
|
|
marker = ' << DSP alive!' if val != 0 else ''
|
|
print(f' Page 0x{page:02X}: 0x{val:02X}{marker}')
|
|
except Exception as e:
|
|
results[page] = None
|
|
print(f' Page 0x{page:02X}: FAILED ({e})')
|
|
|
|
nonzero = sum(1 for v in results.values() if v and v != 0)
|
|
print(f' Non-zero pages: {nonzero}/{len(pages)}')
|
|
return results
|
|
|
|
|
|
def boot_and_test(sw, flags, label):
|
|
"""Boot with given flags and run full diagnostics."""
|
|
print(f'\n{"="*60}')
|
|
print(f'TEST: {label} (wIndex=0x{flags:02X})')
|
|
print(f'{"="*60}')
|
|
|
|
# Shutdown + wait
|
|
sw._vendor_in(0x89, value=0, index=0, length=3)
|
|
time.sleep(0.5)
|
|
|
|
# Boot
|
|
result = sw._vendor_in(0x89, value=1, index=flags, length=3)
|
|
cfg, stage = result[0], result[1]
|
|
bits = []
|
|
if cfg & 0x01: bits.append('Started')
|
|
if cfg & 0x02: bits.append('FW_Loaded')
|
|
print(f' Config: 0x{cfg:02X} ({" | ".join(bits) if bits else "none"})')
|
|
print(f' Stage: 0x{stage:02X}' +
|
|
(' (COMPLETE)' if stage == 0xFF else f' (at {stage})'))
|
|
|
|
# Full register dumps
|
|
direct = full_direct_dump(sw, label)
|
|
indirect = indirect_reg_test(sw, label)
|
|
|
|
# Wait and re-test indirect (DSP may need startup time)
|
|
time.sleep(0.5)
|
|
indirect2 = indirect_reg_test(sw, f'{label} +500ms')
|
|
|
|
# Signal monitor
|
|
print(f'\n --- Signal Monitor ---')
|
|
try:
|
|
sig = sw.signal_monitor()
|
|
print(f' {sig}')
|
|
except Exception as e:
|
|
print(f' Failed: {e}')
|
|
|
|
return direct, indirect, indirect2
|
|
|
|
|
|
def main():
|
|
sw = SkyWalker1()
|
|
sw.open()
|
|
print('=== BCM4500 Deep Boot Verification ===')
|
|
print(f'Firmware: {sw.get_fw_version()}')
|
|
|
|
# Test 1: Init blocks only (no FW download)
|
|
d1, i1, i1b = boot_and_test(sw, 0x01,
|
|
'Init blocks only (skip FW download)')
|
|
|
|
# Test 2: Full boot (FW download + init blocks)
|
|
d2, i2, i2b = boot_and_test(sw, 0x00,
|
|
'Full boot (FW download + init blocks)')
|
|
|
|
# Compare indirect registers between the two modes
|
|
print(f'\n{"="*60}')
|
|
print('COMPARISON: Indirect Registers (init-only vs full boot)')
|
|
print('='*60)
|
|
pages = [0x00, 0x01, 0x06, 0x07, 0x0A, 0x0F, 0x10, 0x20]
|
|
for page in pages:
|
|
v1 = i1b.get(page)
|
|
v2 = i2b.get(page)
|
|
v1s = f'0x{v1:02X}' if v1 is not None else '??'
|
|
v2s = f'0x{v2:02X}' if v2 is not None else '??'
|
|
diff = ' << DIFFERENT!' if v1 != v2 else ''
|
|
print(f' Page 0x{page:02X}: init-only={v1s} full={v2s}{diff}')
|
|
|
|
# Compare direct registers
|
|
print(f'\nDirect register comparison (0xA0-0xBF):')
|
|
diffs = []
|
|
for reg in range(0xA0, 0xC0):
|
|
v1 = d1.get(reg)
|
|
v2 = d2.get(reg)
|
|
if v1 != v2:
|
|
diffs.append(f' 0x{reg:02X}: init-only=0x{v1:02X} full=0x{v2:02X}')
|
|
if diffs:
|
|
print('\n'.join(diffs))
|
|
else:
|
|
print(' No differences (all registers identical)')
|
|
|
|
sw.close()
|
|
print('\n=== Done ===')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|