Skip to content
Development
Skill

/fpga-bringup

Use when loading a bitstream onto a physical FPGA and driving or observing it over JTAG or GPIO, especially bit-banged JTAG from a host like a Raspberry Pi, or when configuration silently fails

From plugin
claude-for-hardware
2114 skills3 agents3 commands1 hook
Install
$ npx -y skills add Midstall/claude-for-hardware --skill fpga-bringup --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/fpga-bringup

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use when loading a bitstream onto a physical FPGA and driving or observing it over JTAG or GPIO, especially bit-banged JTAG from a host like a Raspberry Pi, or when configuration silently fails

SKILL.md

fpga-bringup.SKILL.md
name: fpga-bringup
description: Use when loading a bitstream onto a physical FPGA and driving or observing it over JTAG or GPIO, especially bit-banged JTAG from a host like a Raspberry Pi, or when configuration silently fails

FPGA Bring-Up

Overview

Bringing up an FPGA on the bench means three things: get the bitstream in over a real transport, drive the design's inputs, and observe its outputs. Most early failures are transport and pin-mapping problems, not logic problems.

**Core principle:** Bring the transport up first and prove it independently, before you trust anything the design does. A bitstream that "loaded" but didn't is the most expensive hour on the bench.

When to Use

  • Loading a bitstream onto a board over JTAG, SPI, or a custom config chain
  • Bit-banging JTAG from GPIO (Pi-as-host, no FTDI/FT2232)
  • Driving test vectors into pins and reading results back
  • Configuration "succeeds" but the design doesn't run

Bring Up The Transport First

Before any design-level work, prove the link end to end:

1. **Read the IDCODE.** Shift the JTAG IDCODE instruction and confirm the value matches the part. If IDCODE is wrong or all-ones/all-zeros, you have a wiring, voltage, or clock problem. Stop here and fix it. Nothing downstream matters yet. 2. **Confirm the IR width.** The instruction register width is part-specific and must match the design's TAP. A wrong IR width shifts every instruction into garbage and configuration silently no-ops. Make the IR width a parameter, not a magic constant baked in one place. 3. **Confirm clock and levels.** TCK speed, signal voltage, pull directions. Bit-banged GPIO has no buffering; mind the levels and keep TCK slow until the link is proven.

Load The Bitstream

  • Use the part's documented configuration instruction (for example a JTAG `CONFIG` opcode) to enter config mode, then shift the bitstream.
  • After load, read back a status or DONE indication. Do not assume success from "the shift completed." A clocked-but-ignored shift looks identical to a real one.
  • If load fails intermittently, suspect TCK too fast, marginal levels, or a shared bus contending during config.

Generate The Bitstream At A Safe Clock

Before you blame the bench, confirm the bitstream's configured clock is at or below the design's real Fmax. The PLL output, UART baud divisor, and timer timebase all derive from it. A bitstream configured for 48 MHz on logic that only times at 29 MHz fails with setup violations AND a wrong baud rate, which looks exactly like a wiring or transport fault. Regenerate at a clean integer PLL divide below Fmax. See `fpga-synthesis-fit`.

The PLL Must Actually Lock

A dead-silent board with a clean configuration load is very often a PLL that never locked, not a logic or wiring fault. When the design gates reset on `~LOCK` (the common `sysReset = porReset | ~LOCK`), a PLL that never locks holds the core in reset forever and every output stays dead.

Check the VCO math from the emitted PLL parameters BEFORE you flash:

  • The ECP5 VCO must land in the legal band (roughly 400 to 800 MHz). `fVCO = fIN / CLKI_DIV * CLKFB_DIV`. A hardcoded `CLKI_DIV = 1` that cannot divide a 48 MHz oscillator down to a 24 MHz system clock drives `fVCO` to 1584 MHz, out of band, no lock. Realize the ratio as a reduced fraction (`CLKFB_DIV : CLKI_DIV` by gcd) so the divider is honest.
  • The constraint file's input-frequency line (`FREQUENCY PORT "clk"` in the `.lpf`) must carry the OSCILLATOR frequency on the pin, not the system frequency. Constrain it to the system freq and the tool derives the VCO from the wrong input (24/2*25 = 300 MHz, out of band) and writes analog and loop settings the silicon never runs, even though the gateware is correct.
  • Loop-filter attributes (ICP_CURRENT, LPF_RESISTOR, MFG_*) are a red herring here. They are not even valid yosys EHXPLLL parameters; chasing them wastes hours. The fix is the divider math and the input-frequency constraint.

A second PLL whose LOCK is unconnected (a DDR PHY PLL that does not gate core reset) can carry the same divider bug silently until you bring that block up.

Partition The Design In Sim Before Blaming The Bench

Before bench guessing, partition logic from analog in simulation. Generate the design with NO target (sim-passthrough clock, external reset, flop memories, no PLL or block-RAM blackboxes), dump the SystemVerilog, and run it under a fast compiled simulator (Verilator). If the design streams its output here, the LOGIC is correct and the suspect is the PLL or a primitive the flop sim does not model. An interpreted RTL sim is usually too slow for a full boot (tens of thousands of cycles to first output); a compiled sim does it in a fraction of a second after a one-time compile. Lower the clock or UART divisor to shrink the run.

To sim the REAL netlist instead of the flop stand-in, supply functional models for the vendor blackboxes. The yosys `ecp5/cells_sim.v` DP16KD is a pure stub: it declares the INITVAL params but has no read or write logic, so a real-netlist sim reads every block-RAM ROM as zero and the core hangs at instruction zero. That hang is a SIM ARTIFACT, not a hardware bug. Write a functional primitive model (unpack INITVAL per `fpga-synthesis-fit`'s `dp16kd-initval-packing.md`, clocked read and write) plus a behavioral PLL and config stub, and the real netlist runs, isolating the remaining failure to the analog PLL the stub cannot model.

When Sim Says OK But The Chip Is Silent: Probe One Layer At A Time

When every simulation passes but the assembled design is dead on the board and you cannot observe internals, do NOT keep theorizing. Build a ladder of tiny bitstreams, each bit-banging ONE diagnostic byte out the UART pin, each isolating a single layer, from the rawest signal upward:

1. Raw-oscillator streamer (a fixed byte clocked straight off the input pin): proves the oscillator, FPGA configuration, the pin, and the host adapter. 2. PLL-lock probe (emit

Read more
Ships withclaude-for-hardware

Claude Code skills for hardware design, validation, and bring-up. A plugin of focused skills that teach Claude how to do real hardware work: designing reusable HDL, integrating an SoC, bringing up FPGAs and bare-metal targets, building firmware boot chains,

Get the whole plugin

Other skills on claude-for-hardware.