Files
Roflin 6ba3447e58 Correct EEPROM soft-brick record: partial write on undersized U7 kills enumeration
Hardware bring-up correction (Dennis, 2026-08-22): writing the FT2232H product
string to the undersized 93LC46B (U7) does NOT harmlessly fail — the partial,
mirrored write lands a checksum-valid-but-garbage config, and the FT2232H then
FAILS USB enumeration entirely (dead silent, no dmesg attach). It does NOT fall
back to ROM defaults; only a blank/checksum-invalid EEPROM does. The earlier
claim that "a bad EEPROM always falls back and still enumerates" was WRONG.

Recovery (confirmed working): power on with U7 CLK shorted to GND so the FT2232H
can't read a valid config -> forces ROM defaults -> enumerates -> then erase U7.

Updated REVIEW.md, TODO.md, the flash-re-bba-rb skill (do-not-program warning +
recovery, native-Linux troubleshooting), and flash_ftdi_eeprom.py comments/help
to state the soft-brick reality. The script already refuses the write on
mirroring detection; --force now documented as "reproduce the soft-brick".

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-22 18:57:52 +00:00

7.1 KiB


name: flash-re-bba-rb description: Use when flashing the re-bba-rb board (GC BBA FPGA replacement) — programming the iCE40UP5K bitstream with iceprog from inside the devcontainer (native-Linux or WSL2 host USB passthrough). Covers USB prerequisites, best-seed bitstream selection, sudo/libusb access. WARNING: on V1 the FT2232H EEPROM (U7 93LC46B) is undersized and writing the product string SOFT-BRICKS enumeration — the skill documents why not to, and the CLK-to-GND recovery.

Flashing the re-bba-rb board

The board is FT2232H (USB) → iCE40UP5K (FPGA) + W5100S (ethernet). Two flashable targets, done in this order:

  1. FPGA bitstream via iceprog (MPSSE on FT2232H channel A) — the functional flash. Do this first; it works with a blank EEPROM.
  2. FT2232H EEPROM product string via pyftdi — cosmetic ("re-BBA-rb" instead of "Dual RS232-HS"). Optional, do it only after the bitstream is proven.

Both need USB access to the FT2232H (VID:PID 0403:6010) from inside the container. The vscode user reaches the root-owned /dev/bus/usb nodes via passwordless sudo.

Prerequisites (once per session)

  1. Container built with USB access. .devcontainer/ must have --privileged, the /dev/bus/usb bind mount, sudo, libusb-1.0-0, libftdi1-2, and pyftdi. If .devcontainer/ was just edited, the user must rebuild the container — those changes are baked at build/create time.
  2. Device attached to WSL2. On the Windows host, as Administrator: .devcontainer/attach-icebreaker.ps1 (wraps usbipd attach). Attach before or after container start — the bind mount is live, so a later attach still appears.
  3. Confirm the device is visible from inside the container:
    ls /dev/bus/usb/*/*                      # nodes must exist
    sudo /opt/venv/bin/python -c "import usb.core,usb.backend.libusb1 as b; \
      print(usb.core.find(idVendor=0x0403, idProduct=0x6010, backend=b.get_backend()))"
    
    A non-None device object means libusb can see it. If /dev/bus/usb is empty, the device is not attached (fix on the Windows side) — see Troubleshooting.

Step 1 — Flash the FPGA bitstream

The design's capture domain (54 MHz) closes on only a minority of seeds — always sweep and flash the best passing seed, never the default seed-1 build.

# Build all 8 seeds (from workspace root). Prints the best passing seed + path.
python -m exi_bba.synth --seeds 8

Note the reported best seed, e.g. Best seed: 4 ... build/seed4/top.bin. (As of the last build: seed 4, build/seed4/top.bin, capture 58.36 MHz, 4/8 pass. build/ is on the host bind mount, so it survives container rebuilds.)

Flash it (SRAM-less part → iceprog writes the SPI flash; the FPGA boots from it):

sudo iceprog build/seed4/top.bin        # substitute the reported best-seed path

Expect iceprog to detect the FTDI, erase, write, and verify OK. If it says "Can't find iCE FTDI USB device", the USB passthrough is not working — see Troubleshooting. After a successful flash, power-cycle / re-plug to boot the new image.

ICEPROG=/path/to/iceprog overrides the binary if needed; the container's is on PATH.

Step 2 — FT2232H EEPROM product string — ⚠️ DO NOT DO THIS ON V1

On the re-bba-rb V1 board, writing the EEPROM product string SOFT-BRICKS the FT2232H. U7 is a 93LC46B (128 B), too small for the FT2232H (which needs a 93LC56B / 256 B). A string write lands a partial, checksum-valid-but-garbage config; the FT2232H reads it at power-up and fails USB enumeration entirely — dead silent, no dmesg attach. It does NOT fall back to ROM defaults (only a BLANK / checksum-invalid EEPROM does). Confirmed on hardware 2026-08-22.

flash_ftdi_eeprom.py detects the mirrored/undersized chip and refuses the write — leave it that way. There is no way to program the string on V1; it needs a 93LC56B at U7 (a future rev). The board is fully functional without it: it enumerates on ROM defaults (Dual RS232-HS, 0403:6010) and iceprog + the channel-B UART work normally.

Recovery — if U7 ever gets a bad config written (soft-brick)

Symptom: the FT2232H disappears from USB completely (no dmesg on plug) even though it's powered (3.3 V on VCC/VPHY/RESET#/DP all present). Fix — the classic FTDI EEPROM recovery, done on the V1 unit and confirmed working:

  1. Power the board on with U7's CLK pin shorted to GND. This corrupts the EEPROM read so the FT2232H can't load the bad config → it enumerates on ROM defaults (0403:6010).
  2. Release the short and immediately blank U7:
    sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py --erase --commit
    
    (On the mirrored chip pyftdi's 256-byte verify trips even though the 0xFF writes land — the script loops and checks actual content, so ignore the per-attempt verify warnings; it reports "fully blank" when done.)

To restore a saved backup instead, write eeprom-backup.bin back as a raw image.

Troubleshooting

  • /dev/bus/usb empty / device object is None → the FT2232H isn't on the host bus (the container's /dev/bus/usb mirrors the host live). On a native Linux host (e.g. Arch — Docker directly): lsusb | grep 0403 on the host; if missing, re-plug the cable. On a Windows/WSL2 host: run .devcontainer/attach-icebreaker.ps1 (admin) and check usbipd list shows it Attached.
  • Powered but totally silent on plug (no dmesg attach at all) → most likely a soft-bricked FT2232H EEPROM (see Step 2 recovery) if the EEPROM was ever written; otherwise a severed USB power/data via from the board repair. A blank EEPROM does NOT cause this — a partial write does.
  • Permission denied / LIBUSB_ERROR_ACCESS → run the command under sudo (the nodes are root-owned). Both iceprog and the pyftdi script need it.
  • iceprog can't find the device but pyftdi sees it (or vice-versa) → a kernel driver (ftdi_sio) may hold an interface. iceprog/pyftdi detach it automatically under sudo; if stuck, sudo modprobe -r ftdi_sio (or re-attach the device) and retry.
  • iceprog verify fails / flakey → the drilled-out-via repair on this V1 unit may have damaged a flash/CDONE/CRESET trace; check continuity of the SPI flash lines and CRESET_B/CDONE to the FPGA before assuming gateware fault.
  • FPGA boots but the GC won't enumerate the BBA → this is a timing seed issue, not a flash issue. Re-flash the reported best passing seed (the capture domain fails on ~half of seeds). See CLAUDE.md "Current Implementation State".

Guardrails

  • Do NOT program the EEPROM string on V1 — it soft-bricks the FT2232H (U7 is an undersized 93LC46B; see Step 2). The script refuses by design; do not --force it. Recovery is CLK-to-GND at boot + --erase.
  • Never change VID/PID in the EEPROM — iceprog and D2XX look for 0403:6010. The script/conf keep it; don't override.
  • Always flash the swept best-seed .bin, never a no---seeds seed-1 build (capture timing fails on it).
  • A blank EEPROM is the correct, working state on V1; only a partial write is dangerous.