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
177 lines
6.8 KiB
Python
177 lines
6.8 KiB
Python
#!/usr/bin/env python3
|
|
"""BCM4500 indirect register loopback test.
|
|
|
|
Write a known value via indirect write, then read it back.
|
|
Tests multiple approaches:
|
|
1. Multi-byte A6+A7+A8 in one transaction (0xB2 uses bcm_indirect_write)
|
|
2. Separate writes via 0xB6 diagnostic
|
|
3. Read via 0xB6 with various delays
|
|
4. Read using 0xB1 (bcm_indirect_read wrapper)
|
|
|
|
If we can write-then-read a value back, the DSP command processor works.
|
|
If not, we need to look at the I2C transaction structure more carefully.
|
|
"""
|
|
import sys
|
|
import time
|
|
sys.path.insert(0, 'tools')
|
|
from skywalker_lib import SkyWalker1
|
|
|
|
CMD_RAW_DEMOD_READ = 0xB1
|
|
CMD_RAW_DEMOD_WRITE = 0xB2
|
|
CMD_I2C_RAW_READ = 0xB5
|
|
CMD_I2C_DIAG = 0xB6
|
|
BCM4500_ADDR = 0x08
|
|
|
|
sw = SkyWalker1()
|
|
sw.open()
|
|
print('=== BCM4500 Indirect Register Loopback Test ===')
|
|
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}')
|
|
time.sleep(0.1)
|
|
|
|
# ============================================================
|
|
# Test 1: Read default indirect register values
|
|
# ============================================================
|
|
print('\n=== Test 1: Default indirect register values (0xB1) ===')
|
|
for page in [0x00, 0x01, 0x06, 0x07, 0x0A, 0x0F]:
|
|
try:
|
|
data = sw._vendor_in(CMD_RAW_DEMOD_READ, value=page, index=0, length=1)
|
|
print(f' Page 0x{page:02X}: 0x{data[0]:02X}')
|
|
except Exception as e:
|
|
print(f' Page 0x{page:02X}: FAILED ({e})')
|
|
|
|
# ============================================================
|
|
# Test 2: Write via 0xB2 (multi-byte A6+A7+A8), then read via 0xB1
|
|
# ============================================================
|
|
print('\n=== Test 2: Write 0x42 to page 0x00, read back (0xB2 write, 0xB1 read) ===')
|
|
try:
|
|
# 0xB2: bcm_indirect_write(page=0x00, val=0x42)
|
|
# Writes A6=0x00, A7=0x42, A8=0x03 in one 3-byte I2C transaction
|
|
sw._vendor_in(CMD_RAW_DEMOD_WRITE, value=0x00, index=0x42, length=0)
|
|
except Exception:
|
|
pass # May not return data
|
|
|
|
time.sleep(0.05)
|
|
|
|
# Read back via 0xB1 (bcm_indirect_read with 1ms delay)
|
|
try:
|
|
data = sw._vendor_in(CMD_RAW_DEMOD_READ, value=0x00, index=0, length=1)
|
|
print(f' Read back page 0x00: 0x{data[0]:02X}')
|
|
if data[0] == 0x42:
|
|
print(' >> LOOPBACK SUCCESS! DSP is processing commands!')
|
|
elif data[0] == 0x00:
|
|
print(' >> Got 0x00 — either DSP not running or write/read protocol broken')
|
|
else:
|
|
print(f' >> Unexpected: 0x{data[0]:02X}')
|
|
except Exception as e:
|
|
print(f' Read failed: {e}')
|
|
|
|
# ============================================================
|
|
# Test 3: Direct register reads before/after indirect write
|
|
# ============================================================
|
|
print('\n=== Test 3: Direct register state before/after indirect commands ===')
|
|
print(' Before indirect write:')
|
|
for reg in [0xA6, 0xA7, 0xA8]:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
print(f' 0x{reg:02X}: 0x{data[0]:02X}')
|
|
|
|
# Write via 0xB2
|
|
try:
|
|
sw._vendor_in(CMD_RAW_DEMOD_WRITE, value=0x06, index=0xAB, length=0)
|
|
except Exception:
|
|
pass
|
|
|
|
time.sleep(0.01)
|
|
print(' After indirect write (page=0x06, data=0xAB):')
|
|
for reg in [0xA6, 0xA7, 0xA8]:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
print(f' 0x{reg:02X}: 0x{data[0]:02X}')
|
|
|
|
# ============================================================
|
|
# Test 4: Manual step-by-step with individual I2C writes
|
|
# ============================================================
|
|
print('\n=== Test 4: Manual indirect read using individual raw I2C writes ===')
|
|
print(' Writing A6=0x06 via direct I2C write...')
|
|
|
|
# We don't have a raw I2C write command, but we can use the 0xB6 diagnostic
|
|
# which does individual writes and reads for us.
|
|
|
|
# Test 4a: 0xB6 with READ command (wIndex=0x01)
|
|
print('\n 4a: 0xB6 diagnostic — READ page 0x06:')
|
|
diag = sw._vendor_in(CMD_I2C_DIAG, value=0x06, index=0x01, length=8)
|
|
print(f' A6 write ok: {diag[0]}')
|
|
print(f' A6 readback: 0x{diag[1]:02X}')
|
|
print(f' A8 write ok: {diag[2]}')
|
|
print(f' A8 immediate: 0x{diag[3]:02X}')
|
|
print(f' A8 after 2ms: 0x{diag[4]:02X}')
|
|
print(f' A7 data: 0x{diag[5]:02X}')
|
|
print(f' A6 final: 0x{diag[6]:02X}')
|
|
|
|
# ============================================================
|
|
# Test 5: Try reading A7 with longer delays
|
|
# ============================================================
|
|
print('\n=== Test 5: Indirect read with longer delays ===')
|
|
print(' Maybe the DSP needs more time to process the command...')
|
|
|
|
# Use 0xB6 to write A6=0x06 and A8=0x01
|
|
sw._vendor_in(CMD_I2C_DIAG, value=0x06, index=0x01, length=8)
|
|
|
|
for delay_ms in [10, 50, 100, 500]:
|
|
time.sleep(delay_ms / 1000.0)
|
|
# Read A7 via raw I2C
|
|
try:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=0xA7, length=1)
|
|
print(f' After {delay_ms:4d}ms: A7=0x{data[0]:02X}')
|
|
except Exception as e:
|
|
print(f' After {delay_ms:4d}ms: FAILED ({e})')
|
|
|
|
# ============================================================
|
|
# Test 6: Direct register dump after all tests
|
|
# ============================================================
|
|
print('\n=== Test 6: Register state after all tests ===')
|
|
print(' A0-AF:')
|
|
for reg in range(0xA0, 0xB0):
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
print(f' 0x{reg:02X}=0x{data[0]:02X}', end='')
|
|
if (reg % 8) == 7:
|
|
print()
|
|
print()
|
|
|
|
# ============================================================
|
|
# Test 7: Power cycle BCM4500 and check pre-command register state
|
|
# ============================================================
|
|
print('\n=== Test 7: Reboot and check BEFORE any indirect commands ===')
|
|
sw._vendor_in(0x89, value=0, index=0, length=3) # shutdown
|
|
time.sleep(0.5)
|
|
sw._vendor_in(0x89, value=1, index=0, length=3) # full boot
|
|
time.sleep(0.1)
|
|
print(' Fresh boot — direct reg reads (no indirect commands issued):')
|
|
for reg in [0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB]:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
print(f' 0x{reg:02X}: 0x{data[0]:02X}')
|
|
|
|
# Now issue ONE indirect read command and check if registers change
|
|
print('\n After ONE indirect read (page 0x06):')
|
|
diag = sw._vendor_in(CMD_I2C_DIAG, value=0x06, index=0x01, length=8)
|
|
print(f' A7 data: 0x{diag[5]:02X} (this is the indirect read result)')
|
|
|
|
# Check if direct registers changed
|
|
print(' Direct register check after indirect command:')
|
|
for reg in [0xA0, 0xA2, 0xA4, 0xA6, 0xA7, 0xA8]:
|
|
data = sw._vendor_in(CMD_I2C_RAW_READ, value=BCM4500_ADDR,
|
|
index=reg, length=1)
|
|
print(f' 0x{reg:02X}: 0x{data[0]:02X}')
|
|
|
|
sw.close()
|
|
print('\n=== Done ===')
|