fpga4fun.comwhere FPGAs are fun

7-segments LED displays

Now that we know how to drive an individual LED, let's try a 7-segment display.
The 7-segments display

The 7-segments display consists of 8 LEDs (let's not forget the dot) aggregated as shown below.

Each segment is created using a separate LED, typically named "A" to "G", plus DP for the dot. So a 7-segments display consists of 8 LEDs.

Now, the LEDs are not wired separately. To save a few pins in the 7-segments display, either the anodes or the cathodes are tied together, so that only 9 pins are required out of the display. Here's a typical "common-cathode" display:

Such a display requires at least 9 pins. It is often made in 10 pins packages (and the common signal is available on 2 pins).
Now, to drive such a display from an FPGA, the straightforward solution is to use 8 IOs.

Using such arrangement, here's how to display the digit '2'.
module LED_7seg(segA, segB, segC, segD, segE, segF, segG, segDP);
output segA, segB, segC, segD, segE, segF, segG, segDP;

assign {segA, segB, segC, segD, segE, segF, segG, segDP} = 8'b11011010;   // light the leds to display '2'
endmodule

Now that wasn't too hard.
Decimal counter
Let's try a decimal counter.
module LED_7seg(
    input clk,
    output segA, segB, segC, segD, segE, segF, segG, segDP
);

// cnt is used as a prescaler
reg [23:0] cnt;
always @(posedge clk) cnt <= cnt+24'h1;
wire cntovf = &cnt;

// BCD is a counter that counts from 0 to 9
reg [3:0] BCD;
always @(posedge clk) if(cntovf) BCD <= (BCD==4'h9 ? 4'h0 : BCD+4'h1);

reg [7:0] SevenSeg;
always @(*)
case(BCD)
    4'h0: SevenSeg = 8'b11111100;
    4'h1: SevenSeg = 8'b01100000;
    4'h2: SevenSeg = 8'b11011010;
    4'h3: SevenSeg = 8'b11110010;
    4'h4: SevenSeg = 8'b01100110;
    4'h5: SevenSeg = 8'b10110110;
    4'h6: SevenSeg = 8'b10111110;
    4'h7: SevenSeg = 8'b11100000;
    4'h8: SevenSeg = 8'b11111110;
    4'h9: SevenSeg = 8'b11110110;
    default: SevenSeg = 8'b00000000;
endcase

assign {segA, segB, segC, segD, segE, segF, segG, segDP} = SevenSeg;
endmodule
Smooth counter
Finally let's try a "smooth" counter (fades each digit into then next).
module LED_7seg(
    input clk,
    output segA, segB, segC, segD, segE, segF, segG, segDP
);

reg [23:0] cnt;
always @(posedge clk) cnt <= cnt+24'h1;
wire cntovf = &cnt;

reg [3:0] BCD_new, BCD_old;
always @(posedge clk) if(cntovf) BCD_new <= (BCD_new==4'h9 ? 4'h0 : BCD_new+4'h1);
always @(posedge clk) if(cntovf) BCD_old <= BCD_new;

reg [4:0] PWM;
wire [3:0] PWM_input = cnt[22:19];
always @(posedge clk) PWM <= PWM[3:0]+PWM_input;
wire [3:0] BCD = (cnt[23] | PWM[4]) ? BCD_new : BCD_old;

reg [7:0] SevenSeg;
always @(*)
case(BCD)
    4'h0: SevenSeg = 8'b11111100;
    4'h1: SevenSeg = 8'b01100000;
    4'h2: SevenSeg = 8'b11011010;
    4'h3: SevenSeg = 8'b11110010;
    4'h4: SevenSeg = 8'b01100110;
    4'h5: SevenSeg = 8'b10110110;
    4'h6: SevenSeg = 8'b10111110;
    4'h7: SevenSeg = 8'b11100000;
    4'h8: SevenSeg = 8'b11111110;
    4'h9: SevenSeg = 8'b11110110;
    default: SevenSeg = 8'b00000000;
endcase

assign {segA, segB, segC, segD, segE, segF, segG, segDP} = SevenSeg;
endmodule

Nice, isn't it?
Sorry you have to build it if you want to see it.