--- 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:** ```bash 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. ```bash # 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): ```bash 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: ```bash 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.