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
190 lines
7.0 KiB
Python
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 ===')
|