Put Verilog On An FPGA

July 23, 2026

FPGA + Verilog · Chapter 7 of 7

Simulation asks whether your model behaves. Implementation asks whether a particular FPGA can be configured to realize it.

This chapter follows both paths without collapsing them: Verilog plus a testbench goes through Icarus, checks, and a VCD; the selected hardware top goes through Yosys, nextpnr, IceStorm’s icepack, a binary bitstream, and a board loader.

The Alchitry Cu is a useful iCE40 target, but “Cu” now spans an original legacy board and a Cu V2. Select and record the exact revision before choosing a package, constraints file, reset polarity, LED polarity, or loader target.

See the complete seven-chapter course

Two Verilog paths: simulation through Icarus Verilog, VCD, GTKWave, and checks; implementation through Yosys, a JSON netlist, nextpnr, icepack, a binary bitstream, a loader, and an FPGA
Original Greenforest I/O diagram. One source language leads to two forms of evidence. This chapter follows the lower implementation path one artifact at a time.

Historical Starting Point And Acknowledgement

The handwritten curriculum’s Alchitry section began with Cody Snider’s “Getting Started with Alchitry CU”. The manuscript labels it “Written on February 27th, 2022”; the published URL is dated February 28. Cody described the difficult path from unboxing through a binary LED counter on Debian 11 and Linux Mint 20, using open-source tools built from source.

That post is the historical trail behind this chapter, and Cody remains visibly credited. His prose is not reproduced here. Tool releases, board support, Alchitry Labs, installation practice, and the Cu product line have changed, so the main walkthrough below is independently written for the current course. The complete 2022 command sequence is preserved later as a dated appendix, not presented as today’s default.

First Identify The Board You Actually Have

Alchitry’s current site places the original Cu under legacy boards. It documents a Lattice iCE40 HX FPGA, a 100 MHz oscillator, eight general-purpose LEDs, a reset button, and USB-C configuration. The separate Cu V2 product page specifies an iCE40HX8K-CB132, the same visible number of LEDs and oscillator frequency, plus its own schematic and control-header details.

Similar feature lists do not make board files interchangeable. Before building, record:

Do not fix a pin mismatch by trying random PCF files until one appears to blink. The constraint file is part of the design’s physical contract.

The Current Short Path: Alchitry Labs V2

Alchitry currently recommends Alchitry Labs V2 across Windows, Linux, and macOS. For a Cu, its settings expose Cu Toolchain → Yosys (Open Source), and the installation includes the Alchitry Loader for sending a third-party .bin file to the board.

  1. Install a named stable Alchitry Labs V2 release and record that release.
  2. Connect the board with a known data-capable USB cable and verify that the loader identifies it before debugging HDL.
  3. Create the smallest board project for the exact Cu revision.
  4. Select the Yosys open-source Cu toolchain.
  5. Build and load a one-LED or divided-counter smoke test.
  6. Only after board detection, pin mapping, build, and loading work should you substitute the course counter.

Alchitry’s current starter curriculum is centered on Lucid. This remains a Verilog course. Use Alchitry’s current project flow to prove the board and loader, but verify the Verilog design independently with Icarus or Verilator. Do not assume the IDE’s interactive simulator provides the same Verilog support as its builder or language-interoperability path.

The current first-party references are Getting Started, Your First FPGA Project, the original Cu page, and the Cu V2 page.

The Inspectable Path: Pin An Open Toolchain

When you want to see every artifact instead of delegating the build to an IDE, use a named OSS CAD Suite release or another pinned package of Yosys, nextpnr, and Project IceStorm. Yosys’s installation guidance identifies OSS CAD Suite as the easiest binary route.

Do not write “latest” in the lab record. Save the release name and version outputs:

yosys -V
nextpnr-ice40 --version
icepack -h 2>&1 | head
iverilog -V

The point of pinning is not ceremony. A course should be able to explain which parser, device database, packer, and placer produced a result.

Wrap The Verified Counter For Human-Speed LEDs

A 100 MHz board clock changes far too quickly for a person to see every four-bit update. Reuse the parameterized counter as a wider divider, then expose a slow slice to the LEDs:

module board_top (
    input  wire       clock_100mhz,
    input  wire       reset_n,
    output wire [7:0] led
);
    wire [26:0] divider;

    counter #(.WIDTH(27)) counter_for_humans (
        .clk   (clock_100mhz),
        .reset (~reset_n),
        .count (divider)
    );

    assign led = divider[26:19];
endmodule

This wrapper is a template, not a claim about polarity. If the matching schematic says the LEDs are active low, invert the assignment. If the reset button is active high, remove the inversion. Keep the reusable counter unchanged; put board-specific facts at the board boundary.

The expected observation is an eight-bit binary pattern advancing slowly enough to see. A photograph of lit LEDs proves power and one state. A short video or timed observation showing the binary sequence proves more. A build log plus exact source, PCF, tool versions, and bitstream hash makes the result reproducible.

Yosys: RTL To An iCE40 Technology Netlist

Yosys reads the selected Verilog sources, elaborates the hierarchy, checks the chosen top, synthesizes the logic, and maps it toward iCE40 resources. In this example it writes a JSON netlist for nextpnr:

mkdir -p build
yosys -p '
  read_verilog counter.v board_top.v
  synth_ice40 -top board_top -json build/board_top.json
'

JSON is an interchange artifact in this flow, not the universal meaning of “Yosys output.” Inspect the synthesis log for the selected top, inferred registers, resource counts, and warnings about undriven or optimized-away ports. The official synth_ice40 documentation defines the device-oriented synthesis step.

nextpnr: Place, Route, And Check The Target Frequency

nextpnr consumes the netlist, device and package selection, and board constraints. It chooses physical cells and programmable routes, then reports whether the resulting timing meets the requested frequency.

For a Cu V2 whose matching documentation confirms iCE40HX8K-CB132, the command shape is:

nextpnr-ice40 \
  --hx8k \
  --package cb132 \
  --json build/board_top.json \
  --pcf path/to/the-matching-cu-v2.pcf \
  --asc build/board_top.asc \
  --freq 100

Replace the PCF path only with a file verified against that board revision. For the original Cu, re-confirm device, package, and pin map instead of inheriting the Cu V2 line. The --freq 100 requirement describes the 100 MHz input domain; do not delete it merely to turn a timing failure into a green build.

The output .asc is an IceStorm ASCII configuration representation. It is not yet the binary file the loader expects.

IceStorm: Pack The Configuration Bitstream

Project IceStorm documents iCE40 configuration and provides the chip databases and tools used by the open flow. icepack converts nextpnr’s ASCII configuration into the binary bitstream:

icepack build/board_top.asc build/board_top.bin
sha256sum build/board_top.bin

This is the missing step when a shorthand description says “nextpnr converts JSON into the FPGA format.” The complete chain has separate synthesis, place-and-route, and packing artifacts. Board pin assignments come from the PCF; IceStorm is not a generic catalog of board properties.

The official references are the nextpnr iCE40 example flow and the Project IceStorm overview.

Load The Binary And Observe A Contract

Open the Alchitry Loader included with Alchitry Labs, select the detected board, and load build/board_top.bin. The exact user-interface label and command-line entry point are release-specific; record the loader version and board identity instead of copying a stale invocation.

The board check is not merely “upload returned zero.” Confirm:

  1. The loader identifies the expected board over USB.
  2. The build log identifies the expected FPGA and package.
  3. The PCF binds the clock, reset, and each used LED to documented pins.
  4. The counter leaves reset and shows an ordered binary progression.
  5. A second load of a deliberately different LED mapping changes the observed hardware, demonstrating that the intended bitstream reached the device.

Troubleshoot By Artifact Boundary

No board in loader
Check a data-capable USB cable, power, exact board support, OS device visibility, and Linux udev or group permissions before touching Verilog.
Yosys removes the design
Confirm the selected top, top-level outputs, reset polarity, and that the LED path depends on live state.
nextpnr rejects pins
Confirm FPGA, package, PCF revision, exact top-level port spelling, bus-index syntax, and whether a pin is legal for the requested I/O use.
Timing fails
Read the critical-path report. Fix the path, pipeline it, or state a justified lower requirement; do not confuse bitstream generation with timing closure.
Upload succeeds, LEDs do not move
Check reset and LED polarity, selected divider bits, oscillator pin, and whether wrapper ports match the PCF.
Another machine behaves differently
Compare recorded tool releases, PATH, source commit, PCF hash, build commands, and bitstream hash.

The 2022 Build-From-Source Route, Preserved As History

Cody Snider’s original route addressed a real 2022 problem: open-source Alchitry Cu instructions were scattered, and Debian 11 or Linux Mint 20 users often compiled Yosys, IceStorm, and nextpnr themselves. The manuscript retained that dependency list and command sequence. It remains below so none of the technical trail disappears.

Do not use these unpinned commands as the default current installation. They clone moving branches, install into the host, and were written for a dated OS context. A reproducible source build must add tested commit hashes, a named OS image, and current upstream build instructions.

Historical dependency set

sudo apt-get install build-essential clang bison flex libreadline-dev \
gawk tcl-dev libffi-dev git mercurial graphviz \
xdot pkg-config python python3 libftdi-dev gperf \
libboost-program-options-dev autoconf libgmp-dev \
cmake python3-dev libboost-all-dev libeigen3-dev

Historical Yosys source build

git clone https://github.com/YosysHQ/yosys.git yosys
cd yosys
make -j$(nproc)
sudo make install

Its role was and remains synthesis: convert the selected Verilog hierarchy into a netlist suitable for the following iCE40 implementation tools. JSON was the chosen interchange in this tutorial, not the definition of Yosys.

Historical Project IceStorm source build

git clone https://github.com/YosysHQ/icestorm.git icestorm
cd icestorm
make -j$(nproc)
sudo make install

IceStorm supplies iCE40 device configuration knowledge and tools including icepack. Board-specific pins still come from matching constraints.

Historical nextpnr source build

git clone https://github.com/YosysHQ/nextpnr nextpnr
cd nextpnr
cmake -DARCH=ice40 -DCMAKE_INSTALL_PREFIX=/usr/local .
make -j$(nproc)
sudo make install

nextpnr performs placement and routing—the P and R in its name—and converts the synthesized logical graph into a legal physical iCE40 configuration representation.

Historical LED-counter tutorial and upload

Cody’s 2022 sequence then cloned his Alchitry Cu tutorial repository, built the on-board LED counter without requiring the I/O expansion board, noted that the expansion could remain attached without interfering, connected the Cu, and uploaded it:

git clone https://github.com/codysnider/alchitry-cu-tutorial.git alchitry_tutorial
cd alchitry_tutorial
make clean && make all
make upload

The manuscript used the SSH clone form git@github.com:codysnider/alchitry-cu-tutorial.git, which assumes a configured GitHub SSH key. The HTTPS form above is the lower-friction historical equivalent for a beginner. Neither command makes the repository current for Cu V2; inspect its board files and targets before attempting to use it.

Exit Check

The hardware chapter is complete when the simulation still passes, Yosys reports the intended top and resource mapping, nextpnr uses the exact device, package, PCF, and stated clock requirement, icepack produces a hashed binary, the loader identifies the exact board, and the LEDs demonstrate the expected sequence. “The command finished” is not the acceptance criterion; the artifact chain and physical observation are.