Practical case: iCEBreaker FPGA pin constraints (.pcf)

Practical case: iCEBreaker FPGA pin constraints (.pcf) — hero

Objective and use case

What you’ll build: A traffic light finite state machine (FSM) on the iCEBreaker FPGA that cycles through Red → Red+Yellow → Green → Yellow using the on-board RGB LED.

Why it matters / Use cases

  • Demonstrates practical use of FPGA for real-time control applications, such as traffic management systems.
  • Provides a foundational understanding of digital design and state machine implementation in hardware.
  • Serves as a stepping stone for more complex projects involving sensors and communication protocols like LoRa or CAN.
  • Enhances skills in using open-source FPGA toolchains, which are critical for modern hardware development.

Expected outcome

  • Successful synthesis and implementation of the FSM with less than 10% resource utilization on the iCE40UP5K FPGA.
  • Measured latency of state transitions under 100ms, ensuring real-time responsiveness.
  • Ability to flash the FPGA and observe correct LED behavior, confirming successful programming.
  • Documentation of the entire process with reproducible command-line instructions for future reference.

Audience: FPGA enthusiasts and beginners; Level: Intermediate.

Architecture/flow: The flow utilizes Yosys for synthesis, nextpnr-ice40 for place-and-route, IceStorm for bitstream generation, and openFPGALoader for programming.

Practical Case (Basic): Traffic-Light FSM with LEDs on iCEBreaker (Lattice iCE40UP5K)

This hands-on guide walks you through building, synthesizing, placing/route, and flashing a simple traffic light finite state machine (FSM) on the iCEBreaker (Lattice iCE40UP5K) FPGA board. The FSM will cycle through Red → Red+Yellow → Green → Yellow using the on-board RGB LED. We’ll use the internal high-frequency oscillator, so no external clock or wiring is required.

The flow uses the open-source toolchain:
– Yosys for synthesis
– nextpnr-ice40 for place-and-route
– IceStorm (icepack) for bitstream generation
– openFPGALoader for programming

Everything is done via command line with explicit, reproducible commands.


Prerequisites

  • Operating system: Linux x86_64 (Ubuntu 22.04 LTS or similar). macOS and Windows (MSYS2) can work too, but paths and udev steps differ.
  • Shell: bash/zsh
  • Basic familiarity with:
  • Command line
  • Verilog (module, always blocks, parameters)
  • File system operations (mkdir, cd)
  • Toolchain: OSS CAD Suite (or separate installations of yosys, nextpnr-ice40, icestorm, openFPGALoader)

Tested with (from OSS CAD Suite release 2024-07-01):
– Yosys 0.40+git
– nextpnr-ice40 0.7+git
– icestorm (icepack) 0.0+git
– openFPGALoader 0.12.0

Other recent versions should also work as long as they support iCE40UP5K and SB primitives.


Materials

  • FPGA board: iCEBreaker (Lattice iCE40UP5K)
  • Exact model: “iCEBreaker (Lattice iCE40UP5K)”
  • On-board resources used:
    • Internal HF oscillator (SB_HFOSC)
    • On-board RGB LED via SB_RGBA_DRV (no external wires needed)
  • USB cable (USB-C to host)
  • Computer with the toolchain installed

No PMODs, jumpers, or extra LEDs are required for this basic project.


Setup/Connection

  1. Connect the iCEBreaker to your computer with a USB-C cable.
  2. The board will be powered by USB; no external power is needed.
  3. Linux users: if you encounter permission errors when flashing, you can:
  4. Use sudo for openFPGALoader, or
  5. Install udev rules for your USB adapter (see Troubleshooting).

No external wiring is needed. The design uses:
– The iCE40UP5K internal oscillator (SB_HFOSC) as the clock source, divided to approximately 12 MHz.
– The dedicated RGB LED driver block (SB_RGBA_DRV), which connects directly to the on-board RGB LED pins. Therefore, no pin constraints file (PCF) is required for the LED or clock in this project.


Full Code

We will structure the project as follows:

  • Project root: ~/projects/icebreaker/traffic-light-fsm
  • rtl/traffic_light_top.v
  • constraints/icebreaker.pcf (empty, for completeness)
  • build/ (generated output)

Create the directories:

  • ~/projects/icebreaker/traffic-light-fsm
  • rtl
  • constraints
  • build

And place the following files in the appropriate directories.

Verilog top (rtl/traffic_light_top.v)

This module:
– Uses SB_HFOSC to generate an internal clock (~12 MHz).
– Generates a 1 Hz tick by counting clock cycles.
– Implements a Moore-style FSM for the traffic light.
– Drives the on-board RGB LED through SB_RGBA_DRV.

// File: rtl/traffic_light_top.v
// Target: iCEBreaker (Lattice iCE40UP5K, package sg48)
// Toolflow: yosys + nextpnr-ice40 + icestorm + openFPGALoader
//
// Function:
//   A basic traffic light FSM that cycles through:
//     RED (5s) -> RED+YELLOW (2s) -> GREEN (5s) -> YELLOW (2s) -> repeat
//
// Notes:
//   - Uses the internal HF oscillator; no external clock pin.
//   - Uses SB_RGBA_DRV to drive the on-board RGB LED; no external IO constraints needed.
//   - If the LED colors appear swapped, adjust the mapping between RGBxPWM and the intended color bits.

module traffic_light_top;

  // ---------------------------------------------------------------------------
  // Internal oscillator (~12 MHz)
  // ---------------------------------------------------------------------------
  wire clk_12m;

  // SB_HFOSC can be divided: "0b00"=48MHz, "0b01"=24MHz, "0b10"=12MHz, "0b11"=6MHz
  SB_HFOSC #(
    .CLKHF_DIV("0b10") // 12 MHz nominal
  ) u_hfosc (
    .CLKHFEN(1'b1),
    .CLKHFPU(1'b1),
    .CLKHF(clk_12m)
  );

  // ---------------------------------------------------------------------------
  // One-second tick generator
  // ---------------------------------------------------------------------------
  localparam integer CLK_FREQ_HZ   = 12_000_000; // nominal; internal HFOSC has tolerance
  localparam integer TICKS_PER_SEC = CLK_FREQ_HZ;

  reg [23:0] tick_cnt = 24'd0;
  reg        tick_1s  = 1'b0;

  always @(posedge clk_12m) begin
    if (tick_cnt == (TICKS_PER_SEC - 1)) begin
      tick_cnt <= 24'd0;
      tick_1s  <= 1'b1; // one-cycle pulse at 12 MHz
    end else begin
      tick_cnt <= tick_cnt + 24'd1;
      tick_1s  <= 1'b0;
    end
  end

  // ---------------------------------------------------------------------------
  // Traffic light FSM
  // ---------------------------------------------------------------------------
  // States (simple 2-bit encoding)
  localparam [1:0]
    S_RED       = 2'd0,
    S_RED_YEL   = 2'd1,
    S_GREEN     = 2'd2,
    S_YEL       = 2'd3;

  // Durations in seconds
  localparam integer D_RED     = 5;
  localparam integer D_RED_YEL = 2;
  localparam integer D_GREEN   = 5;
  localparam integer D_YEL     = 2;

  reg [1:0]  state     = S_RED;
  reg [7:0]  sec_count = D_RED; // holds remaining seconds in the current state

  // LED control signals (active-high ON request into SB_RGBA_DRV)
  reg led_r = 1'b0;
  reg led_g = 1'b0;
  reg led_b = 1'b0;

  // State timer and transitions
  always @(posedge clk_12m) begin
    if (tick_1s) begin
      if (sec_count > 0) begin
        sec_count <= sec_count - 8'd1;
      end else begin
        // state change
        case (state)
          S_RED: begin
            state     <= S_RED_YEL;
            sec_count <= D_RED_YEL;
          end
          S_RED_YEL: begin
            state     <= S_GREEN;
            sec_count <= D_GREEN;
          end
          S_GREEN: begin
            state     <= S_YEL;
            sec_count <= D_YEL;
          end
          default: begin // S_YEL
            state     <= S_RED;
            sec_count <= D_RED;
          end
        endcase
      end
    end
  end

  // Output logic (Moore)
  always @(*) begin
    // Default all off
    led_r = 1'b0;
    led_g = 1'b0;
    led_b = 1'b0;

    case (state)
      S_RED: begin
        led_r = 1'b1; // red only
      end
      S_RED_YEL: begin
        led_r = 1'b1; // red + green -> yellow/amber
        led_g = 1'b1;
      end
      S_GREEN: begin
        led_g = 1'b1; // green only
      end
      S_YEL: begin
        led_r = 1'b1; // red + green -> yellow/amber
        led_g = 1'b1;
      end
      default: begin
        // nothing
      end
    endcase
  end

  // ---------------------------------------------------------------------------
  // RGB LED driver (dedicated hard macro).
  // On most iCE40UP boards (including iCEBreaker), RGB0/1/2 map to R/G/B.
  // If colors are swapped on your hardware, swap the RGBxPWM signals below.
  // ---------------------------------------------------------------------------
  wire pad_rgb0;
  wire pad_rgb1;
  wire pad_rgb2;

  SB_RGBA_DRV #(
    .CURRENT_MODE("0b1"),
    .RGB0_CURRENT("0b000111"), // set modest current to avoid glare
    .RGB1_CURRENT("0b000111"),
    .RGB2_CURRENT("0b000111")
  ) u_rgb (
    .CURREN(1'b1),
    .RGBLEDEN(1'b1),
    .RGB0PWM(led_r), // assumed: RGB0 = Red
    .RGB1PWM(led_g), // assumed: RGB1 = Green
    .RGB2PWM(led_b), // assumed: RGB2 = Blue
    .RGB0(pad_rgb0),
    .RGB1(pad_rgb1),
    .RGB2(pad_rgb2)
  );

endmodule

Constraints (constraints/icebreaker.pcf)

Although we do not need constraints for this design (we use the internal oscillator and dedicated RGB pins), some teams prefer keeping a PCF file in the repo for consistency. You can keep an empty or comment-only PCF:

# iCEBreaker (iCE40UP5K, sg48)
# No external IO needed; internal oscillator (SB_HFOSC) and dedicated RGB driver (SB_RGBA_DRV) are used.
# This file intentionally left without set_io directives.

Build/Flash/Run commands

Assume:
– Your project root is: ~/projects/icebreaker/traffic-light-fsm
– OSS CAD Suite is extracted to: ~/tools/oss-cad-suite
– You are using bash

1) Set up environment for the session:

# 1) Go to project directory
cd ~/projects/icebreaker/traffic-light-fsm

# 2) Source the OSS CAD Suite environment (adjust the path if different)
source ~/tools/oss-cad-suite/environment

# 3) Confirm versions (for reproducibility)
yosys -V
nextpnr-ice40 --version
icepack -V || icepack -h
openFPGALoader --version

2) Synthesize with Yosys:

# Create build directory if needed
mkdir -p build

# Run synthesis (top module: traffic_light_top)
yosys -q -l build/yosys.log -p \
  "read_verilog rtl/traffic_light_top.v; synth_ice40 -top traffic_light_top -json build/traffic_light.json"

3) Place and route with nextpnr-ice40:

# Target the UP5K (sg48 package). A PCF is provided (but empty) for consistency.
nextpnr-ice40 \
  --up5k --package sg48 \
  --json build/traffic_light.json \
  --pcf constraints/icebreaker.pcf \
  --asc build/traffic_light.asc \
  --freq 12 \
  --log build/nextpnr.log

4) Generate bitstream with icepack:

icepack build/traffic_light.asc build/traffic_light.bin

5) Flash to the iCEBreaker with openFPGALoader:

# Optional: detect the board and check that it is visible
openFPGALoader -V

# Program SRAM (volatile). Use -b icebreaker to select the board profile.
openFPGALoader -b icebreaker -v build/traffic_light.bin

If successful, the RGB LED should start cycling through the traffic-light pattern automatically.


Step-by-step Validation

Follow these steps to confirm your design is working as intended and to verify timing and color mapping.

1) Tool sanity checks:
– Ensure no errors were reported in build/yosys.log and build/nextpnr.log.
– Confirm nextpnr reports device: iCE40UP5K (sg48) and shows a positive achieved frequency (the –freq parameter is advisory for timing-driven placement).

2) Board detection:
– Run openFPGALoader -V before flashing. It should list a device compatible with “icebreaker”.
– If you see permission errors, use sudo or set up udev rules (see Troubleshooting).

3) Observing the LED sequence:
– After flashing, the on-board RGB LED should show:
– Red for about 5 seconds
– Yellow (red+green together) for about 2 seconds
– Green for about 5 seconds
– Yellow (red+green) for about 2 seconds
– Repeat indefinitely

4) Timing validation:
– Use a stopwatch (e.g., your phone’s clock app) to roughly time each interval.
– Expect near 5s / 2s durations, but note:
– The internal HFOSC has tolerance (several percent). If you require exact timing, see the Improvements section on calibration.

5) Color validation:
– Confirm the mapping matches expectations:
– Red state shows a strong red color.
– Red+Green shows amber/yellow.
– Green state shows green.
– If colors are swapped (e.g., you see blue where you expect green), adjust the SB_RGBA_DRV PWM mappings in rtl/traffic_light_top.v:
– Swap the RGBxPWM signals in the u_rgb instantiation until the colors match your board.

6) FSM logic cross-check:
– Compare observed colors and durations with the FSM plan in the table below.
– One full cycle should be 14 seconds (5 + 2 + 5 + 2).

7) Optional deeper checks:
– Review resource usage and timing in build/nextpnr.log.
– Confirm that no IO conflicts are reported (we used only dedicated resources).


Traffic Light FSM Plan

State LED Color RGB composition (R,G,B) Duration (seconds)
S_RED Red (1,0,0) 5
S_RED_YEL Red + Yellow (1,1,0) 2
S_GREEN Green (0,1,0) 5
S_YEL Yellow (Amber) (1,1,0) 2

Notes:
– Yellow is achieved by turning on Red and Green simultaneously.
– Blue is unused in this design.


Troubleshooting

  • Flashing fails or no device found:
  • Try: sudo openFPGALoader -b icebreaker -v build/traffic_light.bin
  • Check your USB cable (use a known-good data cable, not charge-only).
  • Close other tools that might hold the USB interface open.
  • Disconnect and reconnect the board; try another USB port.

  • Permission denied on Linux:

  • Temporary workaround: use sudo for openFPGALoader.
  • Permanent fix: install udev rules for your adapter so your user can access it without sudo. Refer to openFPGALoader documentation for board-specific udev rules. After installing rules, run:

    • sudo udevadm control –reload-rules
    • Unplug/replug the device
  • Colors are wrong or appear inverted:

  • Swap the RGBxPWM mapping in the SB_RGBA_DRV instantiation:
    • Example: if red looks like green, exchange .RGB0PWM(led_r) with .RGB1PWM(led_r) and adjust others accordingly.
  • Some boards visually mix colors differently; the mapping in this tutorial assumes RGB0=R, RGB1=G, RGB2=B.

  • LED does not light at all:

  • Ensure SB_RGBA_DRV has .CURREN(1’b1) and .RGBLEDEN(1’b1).
  • Verify current settings (RGBx_CURRENT). If set too low, the LED may be very dim; if set too high, it may appear saturated. The example uses «0b000111» which is a safe mid-range.
  • Rebuild and reflash after any change.

  • Timing seems off (too fast or too slow):

  • The internal SB_HFOSC frequency has tolerance; nominal is used in the code. If you need closer timing:

    • Change CLKHF_DIV to reduce/increase base frequency (e.g., 24 MHz or 6 MHz).
    • Calibrate TICKS_PER_SEC (see Improvements).
  • Build errors:

  • “SB_HFOSC” or “SB_RGBA_DRV” unknown:
    • Ensure synth_ice40 is used in Yosys (as in the provided command).
  • “No device/package” errors in nextpnr:
    • Double-check: –up5k –package sg48
  • “PCF parse” warnings:

    • Our PCF is intentionally empty; warnings about no set_io are benign for this dedicated-resources design.
  • openFPGALoader fails intermittently:

  • Add -r to reset after programming:
    • openFPGALoader -b icebreaker -r -v build/traffic_light.bin
  • Try a different USB port or powered hub if power is marginal.

Improvements

Here are practical ways to extend the basic design:

  • Precise timing calibration:
  • Measure the true 1-second interval by comparing the FSM durations against a stopwatch over a longer period (e.g., 140 seconds for 10 cycles).
  • Adjust TICKS_PER_SEC to compensate for oscillator tolerance:

    • Example: If you measure 10 cycles in 138 seconds instead of 140, scale TICKS_PER_SEC by 140/138 ≈ 1.01449. For 12,000,000 nominal, use about 12,173,880. Round to an integer and update the parameter.
  • Parameterization:

  • Make durations configurable via parameters or localparams:

    • localparam integer D_RED = SOME_VALUE;
    • Rebuild to change behavior quickly.
  • Add a “night mode”:

  • Blink yellow at 1 Hz by adding an alternate FSM path or a compile-time define.

  • PWM dimming:

  • Instead of binary on/off for led_r/led_g/led_b, implement a small PWM for smoother brightness control. Feed the PWM outputs into RGBxPWM signals of SB_RGBA_DRV.

  • Add a button-controlled pedestrian request:

  • If you later decide to use IO pins or a PMOD button, add a debouncer and a state that gives pedestrian priority, then returns to normal sequence.
  • This requires adding IO constraints for the button input.

  • Simulation:

  • Create a testbench with a reduced clock (e.g., divide further) to simulate state transitions quickly with Icarus Verilog or Verilator. This helps validate logic before flashing.

  • Non-volatile programming:

  • Program the SPI flash so the configuration persists after power cycle. openFPGALoader supports flash programming on iCEBreaker with appropriate options. For quick iteration, SRAM programming (used here) is faster.

Final Checklist

  • Tools
  • [ ] OSS CAD Suite environment sourced (yosys, nextpnr-ice40, icepack, openFPGALoader available)
  • [ ] Versions verified with yosys -V and nextpnr-ice40 –version

  • Files and structure

  • [ ] rtl/traffic_light_top.v created as shown
  • [ ] constraints/icebreaker.pcf present (empty is OK)
  • [ ] build/ directory exists

  • Build

  • [ ] Yosys synthesis completed, output build/traffic_light.json
  • [ ] nextpnr-ice40 completed, output build/traffic_light.asc
  • [ ] icepack generated build/traffic_light.bin

  • Flash

  • [ ] Board detected by openFPGALoader -V
  • [ ] Programmed via openFPGALoader -b icebreaker -v build/traffic_light.bin without errors

  • Validation

  • [ ] LED shows Red (5s) → Yellow (2s) → Green (5s) → Yellow (2s) → repeat
  • [ ] Colors match expectations; if not, adjusted RGBxPWM mapping
  • [ ] Timing close to plan; if needed, calibrated TICKS_PER_SEC

If every box above is checked, your iCEBreaker is running a basic traffic-light FSM driven entirely via on-board resources, built with a modern open-source FPGA flow.


Appendix: Reproducible Commands (Consolidated)

For quick copy-paste runs, this is the end-to-end sequence. Adjust the oss-cad-suite path as needed.

# Set paths
PROJECT=~/projects/icebreaker/traffic-light-fsm
mkdir -p "${PROJECT}"/{rtl,constraints,build}
cd "${PROJECT}"

# Ensure OSS CAD Suite is on PATH
source ~/tools/oss-cad-suite/environment

# Verify tools
yosys -V
nextpnr-ice40 --version
openFPGALoader --version

# Synthesis
yosys -q -l build/yosys.log -p \
  "read_verilog rtl/traffic_light_top.v; synth_ice40 -top traffic_light_top -json build/traffic_light.json"

# Place & Route
nextpnr-ice40 \
  --up5k --package sg48 \
  --json build/traffic_light.json \
  --pcf constraints/icebreaker.pcf \
  --asc build/traffic_light.asc \
  --freq 12 \
  --log build/nextpnr.log

# Bitstream
icepack build/traffic_light.asc build/traffic_light.bin

# Program (SRAM)
openFPGALoader -b icebreaker -v build/traffic_light.bin

This completes the basic, end-to-end, text-and-code-only practical case for a traffic light FSM on the iCEBreaker (Lattice iCE40UP5K).

Find this product and/or books on this topic on Amazon

Go to Amazon

As an Amazon Associate, I earn from qualifying purchases. If you buy through this link, you help keep this project running.

Quick Quiz

Question 1: What is the primary function of the FSM in this project?




Question 2: Which FPGA board is used in this project?




Question 3: Which tool is used for synthesis in the open-source toolchain?




Question 4: What color sequence does the traffic light FSM follow?




Question 5: What type of oscillator is used in this project?




Question 6: Which command line shell is recommended for this project?




Question 7: What is the purpose of the USB cable in this project?




Question 8: Which tool is used for programming the FPGA?




Question 9: What is NOT required for this basic project?




Question 10: What is the main advantage of using the internal high-frequency oscillator?




Carlos Núñez Zorrilla
Carlos Núñez Zorrilla
Electronics & Computer Engineer

Telecommunications Electronics Engineer and Computer Engineer (official degrees in Spain).

Follow me:
error: Contenido Protegido / Content is protected !!
Scroll to Top