skywalker-1/tools/test_boot_debug.py
Ryan Malloy bbdcb243dc Normalize line endings to LF across entire repository
Apply .gitattributes normalization to convert all CRLF line
endings inherited from Windows-origin source files to Unix LF.
175 files, zero content changes.
2026-02-20 10:55:50 -07:00

128 lines
3.7 KiB
Python

#!/usr/bin/env python3
"""Incremental BOOT_8PSK debug tester for SkyWalker-1.
Sends debug boot modes (wValue=0x80..0x83) one at a time to isolate
which stage of the BCM4500 boot sequence hangs the FX2 firmware.
Usage:
sudo python3 test_boot_debug.py # run all debug stages
sudo python3 test_boot_debug.py 0x82 # run only stage 0x82
"""
import usb.core
import usb.util
import sys
import time
BOOT_8PSK = 0x89
def find_device():
dev = usb.core.find(idVendor=0x09C0, idProduct=0x0203)
if not dev:
print("Device not found!")
sys.exit(1)
return dev
def setup_device(dev):
try:
if dev.is_kernel_driver_active(0):
dev.detach_kernel_driver(0)
except Exception:
pass
try:
dev.set_configuration()
except usb.core.USBError:
pass
def decode_stage(stage):
names = {
0x00: "NOT_STARTED",
0x01: "GPIO_SETUP",
0x02: "PWR_SETTLED",
0x03: "I2C_PROBE",
0x04: "INIT_BLK0",
0x05: "INIT_BLK1",
0x06: "INIT_BLK2",
0xA1: "DEBUG_GPIO_OK",
0xA2: "DEBUG_PROBE_OK",
0xA3: "DEBUG_BLK0_OK",
0xE3: "DEBUG_PROBE_FAIL",
0xE4: "DEBUG_BLK0_FAIL",
0xFF: "COMPLETE",
}
return names.get(stage, f"UNKNOWN(0x{stage:02X})")
def test_mode(dev, wval, label, timeout_ms=3000):
"""Send a debug boot mode and read 3-byte response."""
print(f"\n{'' * 50}")
print(f" Testing wValue=0x{wval:02X}: {label}")
print(f"{'' * 50}")
t0 = time.monotonic()
try:
ret = dev.ctrl_transfer(0xC0, BOOT_8PSK, wval, 0, 3, timeout=timeout_ms)
except usb.core.USBError as e:
elapsed = (time.monotonic() - t0) * 1000
print(f" FAILED after {elapsed:.0f}ms: {e}")
# Try to see if device is still alive
try:
dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6, timeout=1000)
print(" Device still responds to GET_FW_VERS")
except:
print(" Device is HUNG (no response to GET_FW_VERS)")
return None
elapsed = (time.monotonic() - t0) * 1000
status = ret[0]
stage = ret[1] if len(ret) > 1 else 0
probe = ret[2] if len(ret) > 2 else 0
print(f" Response in {elapsed:.0f}ms:")
print(f" config_status: 0x{status:02X}")
print(f" boot_stage: 0x{stage:02X} [{decode_stage(stage)}]")
print(f" probe_byte: 0x{probe:02X}")
return ret
def main():
dev = find_device()
setup_device(dev)
# Verify firmware is responding
try:
ret = dev.ctrl_transfer(0xC0, 0x92, 0, 0, 6, timeout=2000)
major, minor, patch = ret[2], ret[1], ret[0]
print(f"Firmware: v{major}.{minor:02d}.{patch}")
except usb.core.USBError as e:
print(f"GET_FW_VERS failed: {e}")
print("Device may be hung. Try reloading firmware with fw_load.py.")
sys.exit(1)
ret = dev.ctrl_transfer(0xC0, 0x80, 0, 0, 1)
print(f"Config: 0x{ret[0]:02X}")
# Parse optional argument for single-stage testing
single_stage = None
if len(sys.argv) > 1:
single_stage = int(sys.argv[1], 0)
stages = [
(0x80, "No-op: return current state only"),
(0x81, "GPIO setup + power + delays (no I2C)"),
(0x82, "GPIO + I2C bus reset + BCM4500 probe read"),
(0x83, "GPIO + I2C probe + write init block 0"),
]
for wval, label in stages:
if single_stage is not None and wval != single_stage:
continue
result = test_mode(dev, wval, label)
if result is None:
print("\n*** STOPPING: device not responding ***")
break
print(f"\n{'=' * 50}")
print("Debug complete.")
if __name__ == "__main__":
main()