diff --git a/.claude/skills/flash-re-bba-rb/SKILL.md b/.claude/skills/flash-re-bba-rb/SKILL.md index 8d557ef..69ac0ac 100644 --- a/.claude/skills/flash-re-bba-rb/SKILL.md +++ b/.claude/skills/flash-re-bba-rb/SKILL.md @@ -1,6 +1,6 @@ --- name: flash-re-bba-rb -description: Use when flashing the re-bba-rb board (GC BBA FPGA replacement) — programming the iCE40UP5K bitstream with iceprog and/or the FT2232H EEPROM product string ("re-BBA-rb") with pyftdi, from inside the WSL2 devcontainer. Covers USB passthrough prerequisites, best-seed bitstream selection, sudo/libusb access, and backup-first EEPROM programming. +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 @@ -62,34 +62,52 @@ image. `ICEPROG=/path/to/iceprog` overrides the binary if needed; the container's is on `PATH`. -## Step 2 — Program the FT2232H EEPROM (optional, cosmetic) +## Step 2 — FT2232H EEPROM product string — ⚠️ DO NOT DO THIS ON V1 -Only after Step 1 verifies. This changes **descriptor strings only** — VID/PID -`0403:6010` and the dual-channel config are preserved, so `iceprog` and the -channel-B UART keep working. Values live in `hardware/re-bba-rb/flash_ftdi_eeprom.py` -(and mirror `hardware/re-bba-rb/ftdi_eeprom.conf`): manufacturer `hashru`, -product `re-BBA-rb`, serial `RBBARB001`. +**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. -```bash -# Dry-run first: dumps a backup (eeprom-backup.bin) + prints the diff, no write. -sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py +`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. -# If the diff looks right, commit: -sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py --commit -``` -The script always writes `hardware/re-bba-rb/eeprom-backup.bin` before staging -changes. After `--commit`, re-plug / re-attach USB so the host re-reads -descriptors; the unit then shows as **re-BBA-rb**. +### Recovery — if U7 ever gets a bad config written (soft-brick) -To restore the backup later, use `pyftdi`/`ftdi_eeprom` to write -`eeprom-backup.bin` back (raw image). +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`** → not attached to WSL2. Run - `.devcontainer/attach-icebreaker.ps1` (admin) on Windows; check - `usbipd list` shows the FT2232H `Attached`. If the WSL2 kernel lacks usbfs, - `/dev/bus/usb` won't populate at all — update WSL (`wsl --update`). +- **`/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 @@ -106,11 +124,12 @@ To restore the backup later, use `pyftdi`/`ftdi_eeprom` to write ## Guardrails -- **Bitstream before EEPROM** — the EEPROM string is cosmetic and never worth - risking before the board is functionally proven. +- **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). -- Keep the EEPROM backup (`eeprom-backup.bin`) until the string change is - confirmed good on the host. +- A **blank** EEPROM is the correct, working state on V1; only a *partial* write + is dangerous. diff --git a/hardware/re-bba-rb/REVIEW.md b/hardware/re-bba-rb/REVIEW.md index a6c51a2..a6631f9 100644 --- a/hardware/re-bba-rb/REVIEW.md +++ b/hardware/re-bba-rb/REVIEW.md @@ -85,12 +85,23 @@ datasheet extraction, WebSearch/WebFetch for parts and reference designs. 128-byte chip mirrors into the upper half (`has_mirroring=True`); a config write mirror-clobbers word 0x80→0x00 and **fails verify at word 0x02**; and `set_property` indexes past byte 128. **Consequence:** the "re-BBA-rb" product - string CANNOT be stored on V1. **Not a functional defect** — with U7 left - blank the FT2232H uses ROM defaults ("Dual RS232-HS", 0403:6010), and iceprog - (bitstream flash — verified OK on this unit) + the channel-B UART work - normally. **Fix for a future rev:** fit a 93LC56B at U7 (same SOIC-8, fixed - 16-bit org, pin-compatible). Tooling: `hardware/re-bba-rb/flash_ftdi_eeprom.py` - detects the mirroring and refuses the write; `--erase` blanks U7. + string CANNOT be stored on V1 — and worse, **attempting the write SOFT-BRICKS + the FT2232H** (confirmed 2026-08-22): the partial/mirrored write leaves a + checksum-valid-but-garbage config, and the FT2232H then **fails USB enumeration + entirely** (dead silent — no `dmesg` attach at all). It does NOT cleanly fall + back to ROM defaults; only a BLANK or checksum-INVALID EEPROM does that. (An + earlier note here wrongly said a bad EEPROM always falls back and still + enumerates — that is FALSE for a partial write; corrected.) + **RECOVERY (done on this unit, works):** short U7's **CLK to GND while powering + on** so the FT2232H can't read a valid EEPROM → forces ROM defaults → + enumerates → then **erase U7** (`flash_ftdi_eeprom.py --erase --commit`, or + FT_PROG). Classic FTDI EEPROM recovery. With U7 blank the board runs on ROM + defaults ("Dual RS232-HS", 0403:6010); iceprog (bitstream flash — VERIFY OK on + this unit) + the channel-B UART are unaffected. + **DO NOT run the string write on this board.** `flash_ftdi_eeprom.py` now + detects the mirroring and refuses (use --force only to deliberately re-soft- + brick for testing; `--erase` blanks). **Fix for a future rev:** fit a 93LC56B + at U7 (same SOIC-8, fixed 16-bit org, pin-compatible). - **EE_CS pull-down (R43, 10 kΩ) — ADDED 2026-07-31.** FT2232H DS Table 3.4 states EECS/EECLK/EEDATA are all **"Tri-State during device reset"**, so CS floats while the FT2232H is in reset. The Microchip 93LC46B DS is explicit: diff --git a/hardware/re-bba-rb/TODO.md b/hardware/re-bba-rb/TODO.md index b722d00..16363db 100644 --- a/hardware/re-bba-rb/TODO.md +++ b/hardware/re-bba-rb/TODO.md @@ -361,12 +361,17 @@ Major work this session: - [ ] **U7 EEPROM undersized — swap 93LC46B → 93LC56B for V2 (found at bring-up 2026-08-22).** The 93LC46B is 128 B; the FT2232H needs ≥256 B (93LC56B / 93LC66B) to hold its config, so the "re-BBA-rb" USB product string can't be - programmed on V1 (write mirrors + fails verify — see REVIEW.md "U7 PART IS - UNDERSIZED"). **Not a functional blocker:** V1 ships with U7 blank → FT2232H - ROM defaults ("Dual RS232-HS", 0403:6010); iceprog bitstream flash (VERIFY - OK on the repaired unit) and the channel-B UART are unaffected. 93LC56B is - the same SOIC-8 / fixed-16-bit-org, pin-compatible drop-in. Pick a JLC part - (e.g. 93LC56BT-I/SN) when respinning. + programmed on V1. **⚠️ Worse than cosmetic: ATTEMPTING the write SOFT-BRICKS + the FT2232H** — the partial/mirrored write leaves a checksum-valid-garbage + config and the chip then fails USB enumeration entirely (dead silent, no + dmesg). It does NOT fall back to ROM defaults; only a blank/invalid EEPROM + does. **Recovery (done, works):** power on with U7 CLK shorted to GND → + forces ROM defaults → enumerates → then erase U7. See REVIEW.md "U7 PART IS + UNDERSIZED". **Do NOT run the string write on V1** (`flash_ftdi_eeprom.py` + refuses via mirroring detection). Blank U7 is the correct state: iceprog + bitstream flash (VERIFY OK on the repaired unit) + the channel-B UART work + normally. 93LC56B is the same SOIC-8 / fixed-16-bit-org, pin-compatible + drop-in (e.g. 93LC56BT-I/SN) for the respin. - [x] RSET_BG 12.4 k — ACCEPTED 2026-07-18 (closed, no change): datasheet says 12.3 kΩ, but that is an E192 value with no JLC Basic part — WIZnet's own reference synthesizes it as 12 k + 300 Ω. The 0.8 % diff --git a/hardware/re-bba-rb/flash_ftdi_eeprom.py b/hardware/re-bba-rb/flash_ftdi_eeprom.py index 04c54fb..4aa0f25 100644 --- a/hardware/re-bba-rb/flash_ftdi_eeprom.py +++ b/hardware/re-bba-rb/flash_ftdi_eeprom.py @@ -5,32 +5,35 @@ Uses pyftdi (libusb) — the in-container tool, since debian's libftdi1-2 ships only the runtime lib, not the `ftdi_eeprom` binary. The intended descriptor values mirror hardware/re-bba-rb/ftdi_eeprom.conf. -SAFE BY DESIGN: - * Keeps the stock VID/PID 0403:6010 and the dual-channel config, so iceprog - (MPSSE on channel A) and the channel-B UART keep working unchanged — only - the descriptor STRINGS change. - * DRY-RUN BY DEFAULT: prints the diff and writes a raw backup, but does NOT - touch the EEPROM unless you pass --commit. - * Always dumps the current EEPROM to a backup file first. +DRY-RUN BY DEFAULT: prints the diff and writes a raw backup, but does NOT touch +the EEPROM unless you pass --commit. Keeps the stock VID/PID 0403:6010. -Usage (inside the rebuilt devcontainer, FT2232H usbipd-attached): +Usage (inside the devcontainer, FT2232H attached): sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py # dry-run + backup - sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py --commit # write strings sudo /opt/venv/bin/python hardware/re-bba-rb/flash_ftdi_eeprom.py --erase --commit # blank U7 -⚠️ re-bba-rb V1 HARDWARE LIMITATION: U7 is a 93LC46B (128-byte / 1 Kbit) EEPROM, -which is TOO SMALL for the FT2232H — the H-series needs a 93LC56B (256 B) or -93LC66B (the 93LC46 is for the FT232R / FT2232D). The FT2232H config mirrors in -the 128-byte chip and the string write fails verify, so the "re-BBA-rb" product -string CANNOT be programmed on V1. This script therefore REFUSES the write when -it detects mirroring (use --force to override, --erase to blank U7). 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. Fix in a future rev -by fitting a 93LC56B at U7. +⚠️⚠️ re-bba-rb V1: DO NOT PROGRAM THE STRINGS — IT SOFT-BRICKS THE FT2232H. ⚠️⚠️ +U7 is a 93LC46B (128-byte / 1 Kbit) EEPROM, TOO SMALL for the FT2232H — the +H-series needs a 93LC56B (256 B) or 93LC66B (the 93LC46 is for the FT232R / +FT2232D). The FT2232H config mirrors in the 128-byte chip, so a string write +lands a PARTIAL, checksum-valid-but-garbage config. The FT2232H then 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 that). This was confirmed on hardware 2026-08-22. The script therefore +REFUSES the write when it detects mirroring; --force exists only to reproduce the +soft-brick deliberately. -Flash the FPGA bitstream FIRST and confirm iceprog works — the string is purely -cosmetic. Do NOT rely on reset_device(): it re-enumerates the FT2232H and drops -the usbipd attachment on WSL2 — re-attach with usbipd instead. +RECOVERY if U7 ever gets a bad config written (done on the V1 unit, works): + 1. Power the board on with U7's CLK pin shorted to GND — the FT2232H then can't + read a valid EEPROM and enumerates on ROM defaults ("Dual RS232-HS", + 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 + (Classic FTDI EEPROM recovery.) With U7 blank the board is fully functional; + iceprog + the channel-B UART work normally. Fix for a future rev: 93LC56B at U7. + +reset_device() is intentionally NOT called — it re-enumerates the FT2232H and can +drop a usbip attachment; re-plug / re-attach manually instead. """ import argparse import sys @@ -55,7 +58,8 @@ def main() -> int: help="blank U7 to 0xFF (FT2232H falls back to ROM defaults)") ap.add_argument("--force", action="store_true", help="attempt the string write even if U7 reports mirroring " - "(undersized 93LC46B) — will likely fail/corrupt") + "(undersized 93LC46B) — WILL SOFT-BRICK the FT2232H on " + "V1 (fails USB enumeration; recover via CLK-to-GND + erase)") ap.add_argument("--url", default=URL, help=f"pyftdi device URL (default {URL})") args = ap.parse_args() @@ -94,17 +98,19 @@ def main() -> int: # 128 bytes: pyftdi sees the 128-byte chip mirror into the upper half # (has_mirroring=True), the config write mirror-clobbers word 0x80->0x00, # and verify fails. FTDI specs a 93LC56B (256 B) / 93LC66B for the H-series - # (the 93LC46 is for the FT232R / FT2232D). So the product string CANNOT be - # stored on V1 — the board still works fine on ROM defaults ("Dual RS232-HS", - # 0403:6010; iceprog + the UART are unaffected). Refuse rather than - # half-write (which corrupts U7 and needs an --erase recovery). + # (the 93LC46 is for the FT232R / FT2232D). Writing the strings leaves a + # PARTIAL checksum-valid-but-garbage config that SOFT-BRICKS the FT2232H: it + # then fails USB enumeration entirely (dead silent — does NOT fall back to + # ROM defaults). Recovery: power on with U7 CLK shorted to GND, then --erase. + # So REFUSE the write — do not half-brick the chip. # ------------------------------------------------------------------ if eeprom.has_mirroring and not args.force: print("\nREFUSING: U7 reports EEPROM mirroring — it is a 128-byte chip " "(93LC46B), too small for the FT2232H's 256-byte config.\n" - "The 're-BBA-rb' product string cannot be stored on this board " - "(see the header comment / REVIEW.md). The board works fine on ROM " - "defaults. Use --erase to (re)blank U7; --force to attempt anyway.", + "Writing the 're-BBA-rb' strings SOFT-BRICKS the FT2232H on this " + "board (partial config -> fails USB enumeration; recover by shorting " + "U7 CLK to GND at boot then --erase; see header / REVIEW.md).\n" + "Use --erase to blank U7; --force ONLY to reproduce the soft-brick.", file=sys.stderr) eeprom.close() return 3