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
195 lines
6.3 KiB
Python
195 lines
6.3 KiB
Python
#!/usr/bin/env python3
|
|
"""A/B test: compare BCM4500 register state with and without firmware download.
|
|
|
|
Uses wIndex boot flags on BOOT_8PSK (0x89):
|
|
bit 0 (0x01): skip EEPROM firmware download
|
|
bit 1 (0x02): add 200ms DSP startup delay after download
|
|
bit 2 (0x04): skip register init blocks
|
|
|
|
Test matrix:
|
|
A) wIndex=0x01 — skip FW download, init blocks only
|
|
B) wIndex=0x00 — full boot (FW download + init blocks)
|
|
C) wIndex=0x02 — full boot + 200ms DSP delay
|
|
D) wIndex=0x04 — FW download only, skip init blocks
|
|
"""
|
|
import sys
|
|
import time
|
|
sys.path.insert(0, 'tools')
|
|
from skywalker_lib import SkyWalker1
|
|
|
|
CMD_RAW_DEMOD_READ = 0xB1
|
|
CMD_I2C_RAW_READ = 0xB5
|
|
CMD_GET_PLL_DIAG = 0xBF
|
|
BCM4500_ADDR = 0x08
|
|
|
|
# Key direct registers
|
|
KEY_REGS = [
|
|
(0xA0, 'CFG_MODE'),
|
|
(0xA2, 'STATUS'),
|
|
(0xA4, 'LOCK'),
|
|
(0xA6, 'PAGE'),
|
|
(0xA7, 'DATA'),
|
|
(0xA8, 'CMD'),
|
|
(0xA9, 'PLL_A9'),
|
|
(0xAA, 'PLL_AA'),
|
|
(0xAB, 'PLL_AB'),
|
|
]
|
|
|
|
|
|
def read_key_regs(sw, label):
|
|
"""Read key BCM4500 registers via raw I2C (ground truth)."""
|
|
print(f' --- {label} ---')
|
|
results = {}
|
|
for reg, name in KEY_REGS:
|
|
try:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
val = data[0]
|
|
results[reg] = val
|
|
marker = ''
|
|
if val == 0x00:
|
|
marker = ' (zero)'
|
|
elif val == 0x02:
|
|
marker = ' << non-zero!'
|
|
elif val != 0x00:
|
|
marker = ' << non-zero!'
|
|
print(f' 0x{reg:02X} ({name:8s}): 0x{val:02X}{marker}')
|
|
except Exception as e:
|
|
results[reg] = None
|
|
print(f' 0x{reg:02X} ({name:8s}): FAILED ({e})')
|
|
nonzero = sum(1 for v in results.values() if v and v != 0)
|
|
print(f' Non-zero registers: {nonzero}/{len(results)}')
|
|
return results
|
|
|
|
|
|
def boot_with_flags(sw, wval=1, flags=0, label=''):
|
|
"""Send BOOT_8PSK with wValue and wIndex (boot flags)."""
|
|
print(f'\n{"="*60}')
|
|
print(f'BOOT: wValue={wval}, wIndex=0x{flags:02X} — {label}')
|
|
print(f'{"="*60}')
|
|
|
|
# Send boot command: wValue=boot mode, wIndex=flags
|
|
try:
|
|
result = sw._vendor_in(0x89, value=wval, index=flags, length=3)
|
|
cfg = result[0]
|
|
stage = result[1]
|
|
bits = []
|
|
if cfg & 0x01: bits.append('Started')
|
|
if cfg & 0x02: bits.append('FW_Loaded')
|
|
if cfg & 0x04: bits.append('Intersil')
|
|
if cfg & 0x08: bits.append('DVBmode')
|
|
print(f' Config: 0x{cfg:02X} ({" | ".join(bits) if bits else "none"})')
|
|
print(f' Boot stage: 0x{stage:02X}' +
|
|
(' (COMPLETE)' if stage == 0xFF else f' (stopped at {stage})'))
|
|
except Exception as e:
|
|
print(f' Boot command failed: {e}')
|
|
return None
|
|
|
|
# PLL diagnostics
|
|
try:
|
|
pd = sw._vendor_in(CMD_GET_PLL_DIAG, value=0, index=0, length=10)
|
|
print(f' PLL diag: eeprom={"Y" if pd[0] else "N"} '
|
|
f'blocks={pd[2]} '
|
|
f'exit={"OK" if pd[6]==1 else "FAIL" if pd[6]==0 else "skip"} '
|
|
f'result={"OK" if pd[7]==1 else "FAIL"}')
|
|
except Exception:
|
|
pass
|
|
|
|
return read_key_regs(sw, 'Registers immediately after boot')
|
|
|
|
|
|
def main():
|
|
sw = SkyWalker1()
|
|
sw.open()
|
|
print('=== BCM4500 Boot A/B Test ===')
|
|
print(f'Firmware: {sw.get_fw_version()}')
|
|
|
|
# Pre-boot register state (BCM4500 may still be in stock-firmware state)
|
|
print('\n' + '='*60)
|
|
print('PRE-BOOT: Register state before any boot command')
|
|
print('='*60)
|
|
pre = read_key_regs(sw, 'Before boot')
|
|
|
|
# Shutdown first to establish baseline
|
|
print('\n--- Shutting down BCM4500 ---')
|
|
sw._vendor_in(0x89, value=0, index=0, length=3)
|
|
time.sleep(0.5)
|
|
|
|
# ===== TEST A: Init blocks only (no firmware download) =====
|
|
regs_a = boot_with_flags(sw, wval=1, flags=0x01,
|
|
label='Init blocks ONLY (skip FW download)')
|
|
|
|
# Read again after 200ms
|
|
time.sleep(0.2)
|
|
regs_a2 = read_key_regs(sw, 'After 200ms settle (test A)')
|
|
|
|
# Shutdown and wait
|
|
print('\n--- Shutting down BCM4500 ---')
|
|
sw._vendor_in(0x89, value=0, index=0, length=3)
|
|
time.sleep(0.5)
|
|
|
|
# ===== TEST B: Full boot (firmware download + init blocks) =====
|
|
regs_b = boot_with_flags(sw, wval=1, flags=0x00,
|
|
label='Full boot (FW download + init blocks)')
|
|
|
|
# Read again after 200ms
|
|
time.sleep(0.2)
|
|
regs_b2 = read_key_regs(sw, 'After 200ms settle (test B)')
|
|
|
|
# Shutdown and wait
|
|
print('\n--- Shutting down BCM4500 ---')
|
|
sw._vendor_in(0x89, value=0, index=0, length=3)
|
|
time.sleep(0.5)
|
|
|
|
# ===== TEST C: Full boot + 200ms DSP startup delay =====
|
|
regs_c = boot_with_flags(sw, wval=1, flags=0x02,
|
|
label='Full boot + 200ms DSP delay')
|
|
|
|
regs_c2 = read_key_regs(sw, 'Registers (test C)')
|
|
|
|
# Shutdown and wait
|
|
print('\n--- Shutting down BCM4500 ---')
|
|
sw._vendor_in(0x89, value=0, index=0, length=3)
|
|
time.sleep(0.5)
|
|
|
|
# ===== TEST D: Firmware download only (no init blocks) =====
|
|
regs_d = boot_with_flags(sw, wval=1, flags=0x04,
|
|
label='FW download ONLY (skip init blocks)')
|
|
|
|
time.sleep(0.2)
|
|
regs_d2 = read_key_regs(sw, 'After 200ms settle (test D)')
|
|
|
|
# ===== SUMMARY =====
|
|
print('\n' + '='*60)
|
|
print('SUMMARY: Register 0xA2 (STATUS) across tests')
|
|
print('='*60)
|
|
|
|
def val_str(regs, reg=0xA2):
|
|
if regs is None:
|
|
return 'FAIL'
|
|
v = regs.get(reg)
|
|
if v is None:
|
|
return 'FAIL'
|
|
return f'0x{v:02X}'
|
|
|
|
print(f' Pre-boot (stock FW state): {val_str(pre)}')
|
|
print(f' A) Init only, immediate: {val_str(regs_a)}')
|
|
print(f' A) Init only, +200ms: {val_str(regs_a2)}')
|
|
print(f' B) Full boot, immediate: {val_str(regs_b)}')
|
|
print(f' B) Full boot, +200ms: {val_str(regs_b2)}')
|
|
print(f' C) Full boot + DSP delay: {val_str(regs_c)}')
|
|
print(f' D) FW only, +200ms: {val_str(regs_d2)}')
|
|
|
|
print()
|
|
print('Expected: A should show 0x02 (previous working state)')
|
|
print(' B should show 0x00 (current broken state)')
|
|
print(' C tests if DSP just needs more startup time')
|
|
print(' D isolates firmware download effect')
|
|
|
|
sw.close()
|
|
print('\n=== Done ===')
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|