skywalker-1/tools/boot_reg_probe.py
Ryan Malloy 0d6facb321 Add experimental I2C debugging and EEPROM analysis tools
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
2026-02-20 10:57:10 -07:00

190 lines
7.0 KiB
Python

#!/usr/bin/env python3
"""Probe BCM4500 register behavior after boot.
1. Multi-byte reads via 0xB5 (do adjacent registers differ?)
2. Step-by-step indirect read via 0xB6 diagnostic
3. Write a register and read back (is the BCM alive or echoing?)
4. Try tuning to see if the signal path works
"""
import sys
import time
sys.path.insert(0, 'tools')
from skywalker_lib import SkyWalker1
CMD_I2C_RAW_READ = 0xB5
CMD_I2C_DIAG = 0xB6
CMD_RAW_DEMOD_READ = 0xB1
CMD_RAW_DEMOD_WRITE = 0xB2
BCM4500_ADDR = 0x08
sw = SkyWalker1()
sw.open()
print('=== BCM4500 Register Probe ===')
print(f'Firmware: {sw.get_fw_version()}')
# Boot with full sequence
print('\n--- Booting BCM4500 (full boot) ---')
result = sw._vendor_in(0x89, value=1, index=0, length=3)
cfg, stage = result[0], result[1]
print(f' Config: 0x{cfg:02X}, Stage: 0x{stage:02X}')
# ============================================================
# TEST 1: Multi-byte read — read 16 bytes starting at 0xA0
# ============================================================
print('\n=== Test 1: Multi-byte read (16 bytes from 0xA0) ===')
print('If BCM4500 truly maps different registers, bytes should differ.')
try:
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
index=0xA0, length=16)
hex_str = ' '.join(f'{b:02X}' for b in data)
print(f' 0xA0-0xAF: {hex_str}')
unique = set(data)
print(f' Unique values: {sorted(f"0x{v:02X}" for v in unique)}')
if len(unique) == 1:
print(f' ALL SAME VALUE: 0x{data[0]:02X} — BCM4500 may be returning')
print(f' a fixed status byte, not true register contents.')
except Exception as e:
print(f' Failed: {e}')
# Read second block 0xB0-0xBF
try:
data2 = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
index=0xB0, length=16)
hex_str = ' '.join(f'{b:02X}' for b in data2)
print(f' 0xB0-0xBF: {hex_str}')
except Exception as e:
print(f' Failed: {e}')
# ============================================================
# TEST 2: Write-then-read — does the BCM4500 retain writes?
# ============================================================
print('\n=== Test 2: Write-then-read (register 0xA6 PAGE) ===')
print('Write 0x42 to 0xA6, read back. If we get 0x42, register works.')
print('If we get 0x02, the chip may be ignoring writes.')
# Read current value
try:
before = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
index=0xA6, length=1)
print(f' Before write: 0xA6 = 0x{before[0]:02X}')
except Exception as e:
print(f' Read failed: {e}')
# Write 0x42 to 0xA6 via 0xB1 direct write
try:
# 0xB2: RAW_DEMOD_WRITE — wValue=register, wIndex=data
sw._vendor_in(CMD_RAW_DEMOD_WRITE, value=0xA6, index=0x42, length=0)
except Exception:
# 0xB2 might not return data
pass
time.sleep(0.01)
# Read back
try:
after = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
index=0xA6, length=1)
print(f' After write 0x42: 0xA6 = 0x{after[0]:02X}')
if after[0] == 0x42:
print(f' >> REGISTER WORKS — BCM4500 is alive and accepting writes!')
elif after[0] == 0x02:
print(f' >> Still 0x02 — chip may be ignoring writes or returning status')
else:
print(f' >> Got 0x{after[0]:02X} — unexpected')
except Exception as e:
print(f' Read failed: {e}')
# Restore 0xA6 to 0x00
try:
sw._vendor_in(CMD_RAW_DEMOD_WRITE, value=0xA6, index=0x00, length=0)
except Exception:
pass
# ============================================================
# TEST 3: Step-by-step indirect read via 0xB6
# ============================================================
print('\n=== Test 3: Step-by-step indirect read (0xB6) ===')
print('Reading indirect register page 0x06 (acquisition config)')
try:
diag = sw._vendor_in(CMD_I2C_DIAG, value=0x06, index=0, length=8)
labels = [
'Write 0xA6 ok', # [0]
'Readback 0xA6', # [1]
'Write 0xA8 ok', # [2]
'Readback 0xA8', # [3]
'Readback 0xA7', # [4] — the indirect register data
'Direct read 0xA6', # [5]
'Direct read 0xA7', # [6]
'Direct read 0xA8', # [7]
]
for i, label in enumerate(labels):
ok_marker = ''
if i in [0, 2]:
ok_marker = ' (success)' if diag[i] == 0x01 else ' (FAILED!)' if diag[i] == 0x00 else ''
print(f' [{i}] {label:20s}: 0x{diag[i]:02X}{ok_marker}')
print()
if diag[0] == 0x01 and diag[2] == 0x01:
print(' Writes to A6/A8 succeeded.')
if diag[1] == 0x06:
print(f' A6 readback = 0x06 — page register WORKS.')
else:
print(f' A6 readback = 0x{diag[1]:02X} — expected 0x06!')
if diag[4] != 0x00:
print(f' A7 (indirect data) = 0x{diag[4]:02X} — DSP responding!')
else:
print(f' A7 (indirect data) = 0x00 — DSP may not be running.')
else:
print(' Write step FAILED — I2C issue.')
except Exception as e:
print(f' Diagnostic failed: {e}')
# Try a few more pages
for page in [0x00, 0x07, 0x0F]:
try:
diag = sw._vendor_in(CMD_I2C_DIAG, value=page, index=0, length=8)
a6_ok = 'ok' if diag[0] == 1 else 'FAIL'
a8_ok = 'ok' if diag[2] == 1 else 'FAIL'
print(f' Page 0x{page:02X}: A6={a6_ok} A6_rb=0x{diag[1]:02X} '
f'A8={a8_ok} A8_rb=0x{diag[3]:02X} '
f'A7_data=0x{diag[4]:02X} '
f'direct=[A6=0x{diag[5]:02X} A7=0x{diag[6]:02X} A8=0x{diag[7]:02X}]')
except Exception as e:
print(f' Page 0x{page:02X}: {e}')
# ============================================================
# TEST 4: Read registers 0x00-0x9F (below the A0 range)
# ============================================================
print('\n=== Test 4: Registers OUTSIDE 0xA0-0xBF range ===')
print('If BCM4500 returns 0x02 for everything, it might do so for ALL addresses.')
for reg in [0x00, 0x10, 0x50, 0x80, 0x90, 0x9F, 0xC0, 0xD0, 0xFF]:
try:
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
index=reg, length=1)
print(f' Reg 0x{reg:02X}: 0x{data[0]:02X}')
except Exception as e:
print(f' Reg 0x{reg:02X}: FAILED ({e})')
# ============================================================
# TEST 5: Quick tune test (no dish needed — just check AGC)
# ============================================================
print('\n=== Test 5: Tune to 1200 MHz / 27500 ksps (AGC check) ===')
try:
# Enable Intersil (LNB controller)
sw._vendor_in(0x8A, value=1, index=0, length=1)
time.sleep(0.1)
print(' Intersil enabled')
# Tune: 1200 MHz, 27500 ksps, QPSK
result = sw.tune_monitor(freq_mhz=1200, sr_ksps=27500, mod_index=0, dwell_ms=200)
print(f' Tune result: {result}')
if result.get('agc1', 0) > 0 or result.get('agc2', 0) > 0:
print(' >> AGC non-zero — tuner + demod signal path is alive!')
else:
print(' >> AGC=0 — signal path not working (or tuner not configured)')
except Exception as e:
print(f' Tune failed: {e}')
sw.close()
print('\n=== Done ===')