skywalker-1/tools/i2c_register_test.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

127 lines
3.8 KiB
Python

#!/usr/bin/env python3
"""Quick diagnostic: test if 0xA0 writes to I2C registers trigger hardware."""
import usb.core, usb.util, time
VID, PID = 0x09C0, 0x0203
A0 = 0xA0
I2CS = 0xE678; I2DAT = 0xE679; I2CTL = 0xE67A; CPUCS = 0xE600
dev = usb.core.find(idVendor=VID, idProduct=PID)
for cfg in dev:
for intf in cfg:
if dev.is_kernel_driver_active(intf.bInterfaceNumber):
dev.detach_kernel_driver(intf.bInterfaceNumber)
try:
dev.set_configuration()
except:
pass
def a0r(addr, n=1):
return bytes(dev.ctrl_transfer(
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN, A0, addr, 0, n, 2000))
def a0w(addr, val):
dev.ctrl_transfer(
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_OUT, A0, addr, 0, bytes([val]), 2000)
# Pre-halt flush
try:
lock = dev.ctrl_transfer(
usb.util.CTRL_TYPE_VENDOR | usb.util.CTRL_IN, 0x90, 0, 0, 1, 2000)
print(f"Pre-halt flush (0x90): 0x{lock[0]:02X}")
except Exception as e:
print(f"Flush failed: {e}")
# Halt
a0w(CPUCS, 0x01)
time.sleep(0.01)
print(f"CPUCS after halt: 0x{a0r(CPUCS)[0]:02X}")
print(f"\n=== Register State After Halt ===")
i2cs_orig = a0r(I2CS)[0]
i2ctl_orig = a0r(I2CTL)[0]
print(f"I2CS: 0x{i2cs_orig:02X}")
print(f"I2CTL: 0x{i2ctl_orig:02X}")
# Test 1: I2CTL writability
print(f"\n=== Test 1: I2CTL writability ===")
print(f"I2CTL before: 0x{a0r(I2CTL)[0]:02X}")
a0w(I2CTL, 0x00)
time.sleep(0.001)
v = a0r(I2CTL)[0]
print(f"I2CTL after 0x00 write: 0x{v:02X} {'CHANGED' if v == 0x00 else 'unchanged'}")
a0w(I2CTL, 0x01)
time.sleep(0.001)
v = a0r(I2CTL)[0]
print(f"I2CTL after 0x01 write: 0x{v:02X}")
# Test 2: Write START to I2CS, read back
print(f"\n=== Test 2: I2CS START ===")
print(f"I2CS before START: 0x{a0r(I2CS)[0]:02X}")
a0w(I2CS, 0x80) # bmSTART
for i in range(5):
val = a0r(I2CS)[0]
print(f" I2CS read {i}: 0x{val:02X}")
time.sleep(0.002)
# Test 3: Write to I2DAT
print(f"\n=== Test 3: I2DAT write (EEPROM addr) ===")
print(f"I2CS before I2DAT write: 0x{a0r(I2CS)[0]:02X}")
a0w(I2DAT, 0xA2) # EEPROM 0x51<<1
for i in range(5):
val = a0r(I2CS)[0]
print(f" I2CS read {i}: 0x{val:02X}")
time.sleep(0.002)
# Test 4: Read I2DAT
print(f"\n=== Test 4: I2DAT read ===")
dat = a0r(I2DAT)[0]
print(f"I2DAT read: 0x{dat:02X}")
print(f"I2CS after I2DAT read: 0x{a0r(I2CS)[0]:02X}")
# Test 5: Write STOP
print(f"\n=== Test 5: I2CS STOP ===")
a0w(I2CS, 0x40) # bmSTOP
for i in range(3):
val = a0r(I2CS)[0]
print(f" I2CS read {i}: 0x{val:02X}")
time.sleep(0.002)
# Test 6: Write arbitrary values to I2CS
print(f"\n=== Test 6: I2CS raw write/read ===")
for test_val in [0x00, 0xFF, 0x04, 0x80]:
a0w(I2CS, test_val)
time.sleep(0.001)
rb = a0r(I2CS)[0]
changed = "CHANGED" if rb != i2cs_orig else "unchanged"
print(f" Wrote 0x{test_val:02X}, read 0x{rb:02X} {changed}")
# Test 7: XDATA RAM write/read (control test)
print(f"\n=== Test 7: XDATA RAM control test ===")
TEST_ADDR = 0x3C00
orig = a0r(TEST_ADDR)[0]
a0w(TEST_ADDR, 0xBE)
time.sleep(0.001)
readback = a0r(TEST_ADDR)[0]
ok = "OK" if readback == 0xBE else "FAIL"
print(f"RAM[0x3C00]: wrote 0xBE, read 0x{readback:02X} {ok}")
# Test 8: Try writing 0x04 to I2CS (BERR clear bit)
print(f"\n=== Test 8: BERR clear bit ===")
a0w(I2CS, 0x04)
time.sleep(0.001)
v = a0r(I2CS)[0]
print(f"After writing 0x04: I2CS=0x{v:02X}")
# Summary
print(f"\n=== Summary ===")
final_i2cs = a0r(I2CS)[0]
print(f"Final I2CS: 0x{final_i2cs:02X} (started at 0x{i2cs_orig:02X})")
if final_i2cs == i2cs_orig:
print("I2CS NEVER CHANGED -- host-side I2C register writes ignored by hardware")
print("\nImplication: 0xA0 writes reach XDATA address space but the I2C")
print("controller only responds to 8051 MOVX instructions, not USB engine writes.")
print("The boot ROM's 0xA0 handler uses a different bus path for register access.")
else:
print("I2CS DID CHANGE -- host-side I2C register writes may work!")