HomeFPGA from ScratchDay 12
DAY 12 · REAL WORLD PROJECT

Seven-Segment Display Driver

By EcrioniX · Updated Jun 11, 2026

The seven-segment display is the most iconic FPGA project after blinking an LED. Building the driver correctly teaches you combinational decoders, active-low logic, and time-division multiplexing — all patterns you'll use constantly in real FPGA work. This lesson builds a single-digit decoder and then a full 4-digit multiplexed driver.

1. Segment layout

Seven-Segment Naming (a-g) a f b g e c d Active-low: seg[6:0] = {a,b,c,d,e,f,g} 0 = segment ON 1 = segment OFF
Standard 7-segment layout. Most FPGA boards use common-anode (active-low) displays.

2. seg7_decoder.v — hex digit to segments

PortDirWidthMeaning
digitinput4hex digit to display (0x0 – 0xF)
segoutput7segment pattern {a,b,c,d,e,f,g} — active-low
seg7_decoder.v — combinational 4-bit to 7-segment decoder
// 7-segment decoder: 4-bit hex input -> 7-segment output (active-low)
// seg[6:0] = {a, b, c, d, e, f, g}   0 = ON, 1 = OFF
module seg7_decoder (
    input  wire [3:0] digit,
    output reg  [6:0] seg   // active-low segments
);
    always @(*) begin
        case (digit)
        //          abcdefg
            4'h0: seg = 7'b000_0001;  // 0
            4'h1: seg = 7'b100_1111;  // 1
            4'h2: seg = 7'b001_0010;  // 2
            4'h3: seg = 7'b000_0110;  // 3
            4'h4: seg = 7'b100_1100;  // 4
            4'h5: seg = 7'b010_0100;  // 5
            4'h6: seg = 7'b010_0000;  // 6
            4'h7: seg = 7'b000_1111;  // 7
            4'h8: seg = 7'b000_0000;  // 8 (all on)
            4'h9: seg = 7'b000_0100;  // 9
            4'hA: seg = 7'b000_1000;  // A
            4'hB: seg = 7'b110_0000;  // b
            4'hC: seg = 7'b011_0001;  // C
            4'hD: seg = 7'b100_0010;  // d
            4'hE: seg = 7'b011_0000;  // E
            4'hF: seg = 7'b011_1000;  // F
            default: seg = 7'b111_1111; // blank
        endcase
    end
endmodule

3. Testbench for decoder

tb_seg7_decoder.v — testbench
`timescale 1ns/1ps
module tb_seg7_decoder;
    reg  [3:0] digit;
    wire [6:0] seg;
    seg7_decoder dut(.digit(digit),.seg(seg));
    integer errors=0;
    task chk(input [3:0]d, input [6:0]exp);
        digit=d; #1;
        if(seg!==exp)begin $display("FAIL digit=%0h seg=%b exp=%b",d,seg,exp);errors=errors+1;end
        else $display("ok   digit=%0h seg=%b",d,seg);
    endtask
    initial begin
        chk(4'h0,7'b0000001); // 0
        chk(4'h1,7'b1001111); // 1
        chk(4'h2,7'b0010010); // 2
        chk(4'h8,7'b0000000); // 8 all segments on
        chk(4'hF,7'b0111000); // F
        if(errors==0) $display("ALL TESTS PASSED"); else $display("%0d FAILED",errors);
        $finish;
    end
endmodule
expected output
ok   digit=0 seg=0000001
ok   digit=1 seg=1001111
ok   digit=2 seg=0010010
ok   digit=8 seg=0000000
ok   digit=f seg=0111000
ALL TESTS PASSED

4. 4-digit multiplexed display driver

Real boards have 4 digits sharing the same 7 segment pins. The FPGA cycles through each digit at ~1 kHz, enabling one at a time. Human persistence of vision makes it look like all four are on simultaneously.

display4.v — 4-digit multiplexed display driver
// 4-digit multiplexed 7-segment display driver
// Cycles through all 4 digits at ~1 kHz (100MHz / 100_000)
module display4 #(parameter REFRESH = 100_000) (
    input  wire        clk,
    input  wire        rst,
    input  wire [15:0] data,   // {digit3, digit2, digit1, digit0} each 4 bits
    output reg  [3:0]  an,     // anode enables — active-low (one LOW at a time)
    output wire [6:0]  seg     // shared segment outputs
);
    reg [16:0] cnt;
    reg [1:0]  sel;   // which digit is active
    reg [3:0]  cur_digit;

    // Refresh counter
    always @(posedge clk) begin
        if (rst) begin cnt<=0; sel<=0; end
        else if (cnt==REFRESH-1) begin cnt<=0; sel<=sel+1; end
        else cnt<=cnt+1;
    end

    // Mux digit data and anode
    always @(*) begin
        case (sel)
            2'd0: begin cur_digit=data[3:0];   an=4'b1110; end
            2'd1: begin cur_digit=data[7:4];   an=4'b1101; end
            2'd2: begin cur_digit=data[11:8];  an=4'b1011; end
            2'd3: begin cur_digit=data[15:12]; an=4'b0111; end
            default: begin cur_digit=4'h0; an=4'b1111; end
        endcase
    end

    seg7_decoder dec(.digit(cur_digit),.seg(seg));
endmodule

🎯 Day 12 takeaways

FAQ

How does a seven-segment display work?

Seven LEDs (a-g) arranged to form digits. Turning specific combinations on/off displays 0-9 and letters. Most FPGA boards use active-low: 0 turns a segment ON.

What is time-division multiplexing for displays?

All 4 digits share the same 7 segment pins. The FPGA rapidly enables one digit at a time (~1 kHz). Persistence of vision makes all 4 appear simultaneously lit.

Why active-low?

Most boards use common-anode displays (anode to VCC). Driving a pin LOW pulls the cathode down, completing the circuit to light the segment.

Previous
← Day 11: Finite State Machines

← Full roadmap