You dont have javascript enabled! Please enable it!

Practical case: PWM LED Dimming on DE10-Lite MAX 10 FPGA

Practical case: PWM LED Dimming on DE10-Lite MAX 10 FPGA — hero

Objective and use case

What you’ll build: This tutorial guides you through creating a PWM LED brightness controller on the DE10-Lite FPGA board, utilizing Verilog for implementation.

Why it matters / Use cases

  • Control LED brightness in embedded systems for visual feedback in user interfaces.
  • Implement PWM dimming in IoT devices to save power while maintaining visibility.
  • Utilize PWM techniques in educational projects to teach students about digital signal processing.
  • Enhance lighting systems in smart homes by allowing dynamic brightness adjustments based on ambient light.

Expected outcome

  • Achieve smooth brightness transitions with a PWM frequency of 1 kHz.
  • Measure LED brightness levels with a variance of less than 5% across different PWM duty cycles.
  • Validate responsiveness to user input via on-board switches with less than 100 ms latency.
  • Demonstrate successful programming of the DE10-Lite board with a 100% success rate in compilation and upload.

Audience: Electronics enthusiasts; Level: Intermediate

Architecture/flow: Verilog design -> Compilation with Intel Quartus Prime -> JTAG programming -> LED brightness validation

Basic Hands‑On Practical: LED Brightness Control with PWM on DE10‑Lite (Intel MAX 10)

This tutorial walks you through building a pulse‑width‑modulation (PWM) LED brightness controller on the DE10‑Lite (Intel MAX 10) FPGA board. You will write a small Verilog design, compile it with Intel Quartus Prime Lite, program the board via JTAG, and validate the brightness levels using on‑board switches and LEDs. The focus is practical and reproducible: exact commands, file paths, and a lean Verilog implementation.

Target device family and model: FPGA — DE10‑Lite (Intel MAX 10), device 10M50DAF484C7G, using Quartus Prime Lite Edition.

Objective: led‑brightness‑control‑pwm


Prerequisites

  • Skills
  • Basic understanding of Verilog (always blocks, parameters, modules).
  • Ability to use a terminal/command prompt for running build/programming tools.

  • Operating system

  • Windows 10/11 (64‑bit) or Ubuntu 22.04 LTS (64‑bit).

  • Tools (versions known to support DE10‑Lite/MAX 10)

  • Intel Quartus Prime Lite Edition 22.1std (Build 922 or later).
    • Windows default install path: C:\intelFPGA_lite\22.1std\quartus
    • Linux default install path: /opt/intelFPGA_lite/22.1std/quartus
  • USB‑Blaster II driver (installed via Quartus Programmer).
  • JTAG utilities included with Quartus:

    • quartus_sh (scripts and project automation)
    • quartus_map, quartus_fit, quartus_asm (invoked by quartus_sh –flow compile)
    • quartus_pgm (JTAG programming)
    • jtagconfig (enumerate JTAG chain)
    • quartus_cpf (SOF→POF conversion for flash programming, optional)
  • Board support files

  • DE10‑Lite official pin assignment TCL/QSF from the Terasic DE10‑Lite Golden Reference Design.
  • Place the vendor pin TCL somewhere in your project (for example, board/DE10_Lite_pins.tcl). We will source it during project creation so you don’t have to hand‑type device pin locations.

  • Verify tool availability

  • On Windows (PowerShell):
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_sh.exe" --version
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\jtagconfig.exe"
  • On Linux (bash):
    /opt/intelFPGA_lite/22.1std/quartus/bin/quartus_sh --version
    /opt/intelFPGA_lite/22.1std/quartus/bin/jtagconfig

Materials (exact model)

  • DE10‑Lite development kit (Intel MAX 10 10M50DAF484C7G)
  • Micro‑USB cable for the on‑board USB‑Blaster II
  • Computer with Quartus Prime Lite 22.1std installed
  • Optional for validation:
  • A multimeter or oscilloscope (if you later export the PWM to a header pin; not mandatory for this tutorial)
  • Smartphone with slow‑motion video to visually compare brightness steps

Setup / Connection

  • Board power and JTAG connection
  • Ensure the power switch on the DE10‑Lite is OFF.
  • Connect the micro‑USB cable from the PC to the on‑board USB‑Blaster II connector (labeled “USB‑BLASTER”).
  • Turn the DE10‑Lite power switch ON. The power LED should light.
  • Verify JTAG connectivity:

    • Windows (PowerShell):
      & "C:\intelFPGA_lite\22.1std\quartus\bin64\jtagconfig.exe"
    • Linux (bash):
      /opt/intelFPGA_lite/22.1std/quartus/bin/jtagconfig
    • Expected: a line showing “10M50” or “Intel MAX 10” and the USB‑Blaster II cable.
  • No external wiring required

  • We will use on‑board resources only:
    • 50 MHz oscillator input (CLOCK_50)
    • Slide switches SW[7:0] to set the PWM duty cycle
    • User LEDs LEDR[9:0], primarily LEDR[0] for PWM output

What We Will Build

We will implement a simple PWM generator using a free‑running counter and comparator:

  • PWM frequency = fclk / 2^N. With fclk = 50 MHz and N = 12, PWM ≈ 12.207 kHz (well above flicker).
  • Duty cycle is 12‑bit (0..4095). We map SW[7:0] to the 12‑bit duty by left‑shifting 4 bits (i.e., duty = SW × 16).
  • LEDR[0] shows the PWM brightness.
  • LEDR[8:1] mirror the eight switches so you can confirm the input value.
  • LEDR[9] is a slow “heartbeat” to confirm the design is running.

Interface Mapping (Logical)

This table describes the top‑level ports and how they map to the DE10‑Lite board resources we will use. The physical pin locations will be assigned by importing the official DE10‑Lite pin‑assignment TCL/QSF file in the project script.

Top‑Level Port Board Resource Direction Purpose
CLOCK_50 50 MHz oscillator Input System clock
SW[7:0] Slide switches (SW[7:0]) Input PWM duty control (0..255 mapped to 0..4095)
KEY[1:0] Pushbuttons Input Reserved for future expansion (not required)
LEDR[0] User LED 0 Output PWM output (brightness control)
LEDR[8:1] User LEDs 1..8 Output Display raw switch inputs (debug)
LEDR[9] User LED 9 Output Heartbeat (slow blink)

Note: DE10‑Lite LEDs are active‑high; a logic 1 turns the LED on.


Full Code

Create a workspace folder with the following structure:

  • de10lite_pwm/
  • rtl/
    • pwm.v
    • de10lite_pwm_top.v
  • constraints/
    • de10lite_pwm.sdc
  • board/
    • DE10_Lite_pins.tcl (from the DE10‑Lite Golden Reference; see Setup)
  • scripts/
    • de10lite_pwm_project.tcl

rtl/pwm.v

A compact, parameterized PWM generator. The PWM frequency is fclk / 2^WIDTH.

// File: rtl/pwm.v
// Simple parameterized PWM: free-running counter compared to duty.
// PWM frequency = fclk / (2^WIDTH)

`timescale 1ns/1ps

module pwm #(
    parameter integer WIDTH = 12  // PWM resolution bits
)(
    input  wire                  clk,   // System clock
    input  wire [WIDTH-1:0]      duty,  // Duty cycle (0..2^WIDTH-1)
    output reg                   pwm_out
);
    reg [WIDTH-1:0] ctr = {WIDTH{1'b0}};

    always @(posedge clk) begin
        ctr <= ctr + 1'b1;
        pwm_out <= (ctr < duty);
    end
endmodule

rtl/de10lite_pwm_top.v

Top‑level for DE10‑Lite. Maps switches to duty, drives LEDs.

// File: rtl/de10lite_pwm_top.v
// Target board: DE10-Lite (Intel MAX 10), device 10M50DAF484C7G
// Resources used: CLOCK_50, SW[7:0], LEDR[9:0], KEY[1:0] (reserved)

`timescale 1ns/1ps

module de10lite_pwm_top (
    input  wire        CLOCK_50,
    input  wire [7:0]  SW,
    input  wire [1:0]  KEY,       // active-low buttons; not used here
    output wire [9:0]  LEDR
);
    // Map 8-bit switches to 12-bit duty by left shift of 4 (x16)
    // 0x00 -> 0 (off), 0xFF -> 0xFF0 (~100% minus 15/4096)
    wire [11:0] duty12 = {SW, 4'b0000};

    // Instantiate PWM (12-bit -> ~12.2 kHz at 50 MHz)
    wire pwm_led;
    pwm #(.WIDTH(12)) u_pwm (
        .clk(CLOCK_50),
        .duty(duty12),
        .pwm_out(pwm_led)
    );

    // Heartbeat: divide clock down to ~1 Hz blink on LEDR[9]
    reg [25:0] hb_cnt = 26'd0;  // 2^26 / 50e6 ~ 1.34 s
    always @(posedge CLOCK_50) begin
        hb_cnt <= hb_cnt + 1'b1;
    end
    wire heartbeat = hb_cnt[25]; // toggle every ~0.67 s (visible blink)

    // Drive LEDs
    assign LEDR[0]   = pwm_led;   // PWM brightness
    assign LEDR[8:1] = SW;        // shows the input value
    assign LEDR[9]   = heartbeat; // heartbeat indicator

    // KEYs are unused but kept in port list for completeness
    // They can be used later for step-up/step-down control with debouncing.
endmodule

constraints/de10lite_pwm.sdc

Timing constraint for the 50 MHz clock.

create_clock -name CLOCK_50 -period 20.000 [get_ports {CLOCK_50}]

Build/Flash/Run Commands (CLI)

We will create a Quartus project from a TCL script, import DE10‑Lite pin assignments, build the design, and program the board. The flow is the same on Windows and Linux; only the tool paths differ.

1) Project creation script

Place the official DE10‑Lite pin assignment script in:
– board/DE10_Lite_pins.tcl
This file is provided by Terasic in the DE10‑Lite Golden Reference Design package. It contains set_location_assignment calls for CLOCK_50, SW, KEY, LEDR, etc. If you prefer the GUI, you can import it via Assignments → Import Assignments; here we use CLI for reproducibility.

Create scripts/de10lite_pwm_project.tcl:

# File: scripts/de10lite_pwm_project.tcl
# Creates the Quartus project, sets device, adds files, imports pin assignments, and sets constraints.

set proj_name "de10lite_pwm"
set top_name  "de10lite_pwm_top"
set device    "10M50DAF484C7G"   ;# DE10-Lite device

project_new $proj_name -overwrite

set_global_assignment -name FAMILY "MAX 10"
set_global_assignment -name DEVICE $device
set_global_assignment -name TOP_LEVEL_ENTITY $top_name

# Add RTL sources
set_global_assignment -name VERILOG_FILE "rtl/pwm.v"
set_global_assignment -name VERILOG_FILE "rtl/de10lite_pwm_top.v"

# Add SDC timing constraints
set_global_assignment -name SDC_FILE "constraints/de10lite_pwm.sdc"

# Import DE10-Lite official pin assignments (from Terasic Golden Reference)
# Adjust the path below if you placed the file elsewhere.
source board/DE10_Lite_pins.tcl

# Optional: set optimization effort and other compile switches
set_global_assignment -name OPTIMIZATION_MODE "AGGRESSIVE PERFORMANCE"

# Save and exit
project_close

2) Run the project script and compile

  • Windows (PowerShell):
Set-Location C:\work\fpga\de10lite_pwm
& "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_sh.exe" -t scripts\de10lite_pwm_project.tcl
& "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_sh.exe" --flow compile de10lite_pwm
  • Linux (bash):
cd ~/work/fpga/de10lite_pwm
/opt/intelFPGA_lite/22.1std/quartus/bin/quartus_sh -t scripts/de10lite_pwm_project.tcl
/opt/intelFPGA_lite/22.1std/quartus/bin/quartus_sh --flow compile de10lite_pwm

Expected outputs:
– Database and output files under ./output_files/
– A .sof file at ./output_files/de10lite_pwm.sof (for JTAG SRAM programming)
– Fitter/timing reports showing one clock domain at 50 MHz

3) Program the FPGA (volatile, .sof)

  • Verify JTAG cable:

  • Windows:
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\jtagconfig.exe"

  • Linux:
    /opt/intelFPGA_lite/22.1std/quartus/bin/jtagconfig

  • Program:

  • Windows:
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_pgm.exe" -m jtag -o "p;output_files\de10lite_pwm.sof"

  • Linux:
    /opt/intelFPGA_lite/22.1std/quartus/bin/quartus_pgm -m jtag -o "p;output_files/de10lite_pwm.sof"

The LED configuration will be loaded into SRAM; it is lost on power‑cycle.

4) Optional: Program on‑chip configuration flash (non‑volatile, .pof)

MAX 10 devices include on‑chip configuration flash. Convert SOF to POF and program it to retain the design after power‑cycle.

  • Convert SOF→POF:

  • Windows:
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_cpf.exe" -c output_files\de10lite_pwm.sof output_files\de10lite_pwm.pof

  • Linux:
    /opt/intelFPGA_lite/22.1std/quartus/bin/quartus_cpf -c output_files/de10lite_pwm.sof output_files/de10lite_pwm.pof

  • Program POF to flash:

  • Windows:
    & "C:\intelFPGA_lite\22.1std\quartus\bin64\quartus_pgm.exe" -m jtag -o "p;output_files\de10lite_pwm.pof"

  • Linux:
    /opt/intelFPGA_lite/22.1std/quartus/bin/quartus_pgm -m jtag -o "p;output_files/de10lite_pwm.pof"

Note: If your JTAG chain has multiple devices, you may need to specify the index with @N (e.g., p;file.sof@2). For a single DE10‑Lite in the chain, the above typically works.


Step‑by‑Step Validation

Follow these steps immediately after programming with the .sof or .pof.

1) Basic function check
– LEDR[9] (heartbeat) should blink slowly (toggle roughly once per ~0.67 s).
– LEDR[8:1] should mirror SW[7:0] exactly: toggling any switch drives the corresponding LED.

2) PWM brightness behavior on LEDR[0]
– Set all switches SW[7:0] = 0x00. LEDR[0] should be off (duty = 0%).
– Set SW[7:0] = 0xFF. LEDR[0] should be fully on (almost 100% duty; with 0xFF0/4096 ≈ 99.6%).
– Try mid‑scale:
– SW = 0x80 (128 dec). LEDR[0] should be about “half brightness.”
– SW = 0x40 (64 dec). LEDR[0] should look dimmer than the previous step.
– SW = 0xC0 (192 dec). LEDR[0] should be brighter than half.
– Observe that perceived brightness is not perfectly linear with duty (human vision is logarithmic). This is expected; see Improvements for gamma correction.

3) Verify PWM frequency is not causing flicker
– The PWM frequency is ~12.2 kHz; the LED should not visibly flicker at any duty setting.
– Optional: record a slow‑motion video at 240 fps on a smartphone while pointing at LEDR[0] at low duty (e.g., SW=0x10) and high duty (e.g., SW=0xF0). You should not see rolling or banding artifacts because 12 kHz >> 240 fps.

4) Stability over time
– Leave the board on for a few minutes and change switches. LEDR[0] should respond instantaneously and consistently with no bounces (switch bounces don’t matter here because they change a static value; we’re not edge‑detecting).

5) Optional quantitative check (if you have a DMM/oscilloscope and use headers)
– If later you export pwm_led to a GPIO header pin (by adding a port and pin assignment), you can:
– Use a DMM with duty measurement to confirm the duty ratio at several SW values (e.g., 25%, 50%, 75%).
– Use an oscilloscope to confirm frequency ≈ 50e6/4096 ≈ 12.207 kHz and duty tracks SW×(1/256).

6) Edge cases
– SW = 0x00 → duty12 = 0x000 → LED off.
– SW = 0x01 → duty12 = 0x010 → barely on (about 0.39% duty).
– SW = 0xFE → duty12 = 0xFE0 → ~99.0% duty.
– SW = 0xFF → duty12 = 0xFF0 → ~99.6% duty; not exactly 100% because of the shift scheme (avoids corner case saturation and keeps comparator logic simple).


Troubleshooting

  • quartus_pgm or jtagconfig shows no cable/device
  • Symptom:
    1) No JTAG hardware available
  • Fix:

    • Ensure the DE10‑Lite power switch is ON and the USB cable is connected to the USB‑BLASTER port (not the UART).
    • Install the USB‑Blaster II driver (Windows: run Quartus Programmer as admin once; Device Manager → Update Driver).
    • Try another USB port/cable. On Linux, add your user to the plugdev group or configure udev rules (Intel provides a script in /drivers/usb-blaster).
  • Device mismatch or assignment errors

  • Symptom:
    Error (18933): Device 10M50DAF484C7G not found in project
  • Fix:

    • Ensure the project TCL sets DEVICE «10M50DAF484C7G».
    • Re‑run the project creation script:
      quartus_sh -t scripts/de10lite_pwm_project.tcl
    • Delete (or move) the db/ and incremental_db/ folders before re‑compiling if the device was changed.
  • Pin assignment conflicts or missing locations

  • Symptom:
    Error (169171): Can't place node LEDR[0] -- no legal location
  • Fix:

    • Confirm the DE10_Lite_pins.tcl is sourced in the project TCL and points to the correct file path.
    • Use Assignments → Pin Planner to verify that CLOCK_50, SW[7:0], LEDR[9:0] have valid locations.
    • Do not mix pin assignments from other MAX 10 boards; use the DE10‑Lite official file.
  • Timing violation messages

  • At 50 MHz with such a small design, timing violations should not occur.
  • If you see negative slack:

    • Double‑check that your SDC has the 20 ns clock on CLOCK_50 and no stray clocks.
    • Try a clean rebuild or set OPTIMIZATION_MODE to “Balanced”.
  • LED polarity inverted (LED appears off when expected on)

  • DE10‑Lite user LEDs are active‑high. If you used a different pin file with inverted polarity or added external wiring, adjust your logic accordingly.
  • For on‑board LEDR, the provided mapping is active‑high.

  • SOF programming succeeds but the design vanishes on reboot

  • Expected: .sof goes to SRAM (volatile).
  • If you want it to persist, convert to .pof and program the on‑chip flash as shown above.

Improvements (Beyond the Basic Objective)

  • Gamma correction (perceptual linearity)
  • Human brightness perception is roughly logarithmic. To achieve perceptually even steps, apply a gamma curve (e.g., gamma ≈ 2.2):

    • Create an 8‑bit to 12‑bit lookup table (LUT) mapping SW[7:0] to duty12 = floor((SW/255)^γ * 4095).
    • Store the LUT in a simple ROM in Verilog or initialize from a MIF.
  • Adjustable PWM frequency

  • Parameterize PWM WIDTH or add a prescaler so you can sweep frequency from a few hundred Hz to tens of kHz. For multiplexed LED displays you may want ~1–2 kHz; for motor control, tens of kHz may be desirable.

  • Pushbutton step control with debouncing

  • Use KEY[1:0] to increment/decrement an 8‑bit duty register with proper debouncing (two‑flip‑flop sync + counter‑based debounce).
  • This improves usability without touching slide switches.

  • Multiple channels

  • Instantiate several PWM modules to control multiple LEDs (e.g., LEDR[0..7]) independently, or implement a shared counter with multiple comparators for resource efficiency.

  • Advanced features

  • Add a “breathing” effect by modulating duty with a slow triangle/SIN LUT.
  • Export PWM to GPIO headers and drive an external high‑power LED (with a suitable current driver/transistor and resistor).

  • Safety and electrical considerations

  • On‑board LEDs have appropriate series resistors. For external LEDs or devices, ensure current limiting and do not exceed pin current limits of the MAX 10 device.

Final Checklist

  • Prerequisites
  • Quartus Prime Lite 22.1std installed and working
  • USB‑Blaster II recognized by jtagconfig
  • DE10‑Lite powered and connected via USB

  • Project files present

  • rtl/pwm.v
  • rtl/de10lite_pwm_top.v
  • constraints/de10lite_pwm.sdc
  • board/DE10_Lite_pins.tcl (official pin assignments)
  • scripts/de10lite_pwm_project.tcl

  • Build

  • quartus_sh -t scripts/de10lite_pwm_project.tcl completes without error
  • quartus_sh –flow compile de10lite_pwm generates output_files/de10lite_pwm.sof

  • Program

  • quartus_pgm -m jtag -o «p;output_files/de10lite_pwm.sof» succeeds
  • Optional: quartus_cpf and quartus_pgm used to create/program .pof for non‑volatile storage

  • Validation

  • LEDR[9] blinks (heartbeat)
  • LEDR[8:1] mirror SW[7:0]
  • LEDR[0] brightness changes with SW value; off at 0x00 and near‑full at 0xFF
  • No visible flicker across the range

  • Documentation

  • Notes kept on any deviations (e.g., custom pin file) and validated PWM frequency/duty if measured

With this, you have a complete, reproducible baseline for PWM‑based LED brightness control on the DE10‑Lite (Intel MAX 10) platform using Quartus Prime Lite and clean, minimal Verilog.

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 focus of the tutorial?




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




Question 3: What programming language is primarily used in the tutorial?




Question 4: What operating systems are supported for this project?




Question 5: What tool is used to compile the Verilog design?




Question 6: Which driver is necessary for programming the board?




Question 7: What command is used to program the board via JTAG?




Question 8: Which utility is used for project automation?




Question 9: What is the device model mentioned for the DE10-Lite?




Question 10: What is the default install path for Intel Quartus Prime Lite on Windows?




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

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

Follow me:
Scroll to Top