Table of Contents

Seven Segment Controller

In the seven segment decoder laboratory assignment, you created a circuit that can display a hexadecimal value on a single digit.

On this page, you are provided with a Seven-Segment Controller module that can display a unique value on each digit of the eight-digit display. Displaying multiple digits on the display is accomplished by multiplexing the cathode signals and “driving” or displaying one digit at a time. To make it appear to the human eye that more than one digit is displayed, we rapidly turn on each digit and sequence through the digits fast enough so the eye and brain cannot see this sequencing. Even though only one digit is actively displayed at a time, our persistence of vision and slow brain response cannot perceive this single digit display strategy. Instead, it will appear as if all eight digits are being displayed at the same time. If you were to take a high-speed video of the display, you would see this sequential digit display approach in action.

The example shown below demonstrates how the value “0123” is displayed on the first four digits of the display using time multiplexing. In this example, only one digit is being driven on the display at a given time – digit “0” is displayed on digit 3 (the left most digit) during the first time period, digit “1” is displayed on digit 2 during the second time period, digit “2” is displayed on digit 1 during the third time period, and digit “3” is displayed on digit 0 (the right most digit) during the fourth time period. This process of displaying one digit at a time repeats continuously to make it appear that all four digits are being displayed at the same time.

This example demonstrates the signals that must be asserted during each time period to produce the desired display. During the first time period, AN3 is asserted (i.e. is low) and the other annode signals are deasserted. The following cathode signals are asserted (driven low) to display the character “0” on digit 3: CA, CB, CC, CD, CE, and CF. During the next digit period, AN2 is asserted (while AN2 is deasserted) and the following cathode signals are asserted to display the character “1”: CB and CC. Next, AN1 is asserted and the following cathode signals are asserted to display the character “2”: CA, CB, CC, CD, and CE. Finally, AN0 is asserted and the following cathode signals are asserted to display the character “3”: CA, CB, CC, CD, and CG. This process repeats continuously to keep all four values actively highlighted on the four-digit display.

The challenge we have is deciding how long to drive each anode signal. If the “on” time of the anode signals is too long, the eye and brain will detect that only one digit is being displayed at a time and we will see the digits turn on and then off in sequential order. If the “on” time is just right, human brain will perceive all four characters as being displayed at the same time through persistence of vision.

The easiest way to sequence through the eight different digits is to create a “free running” counter. A free running counter is a counter that increments continuously at each clock cycle (See Figure 16.6). When the free running counter reaches all 1's, it will then roll over to zero on the next clock cycle. The top three bits of this free running counter will cycle through the digits, while the rest of the bits of the counter will determine the amount of time to display each digit. For example, suppose you used an 8-bit counter. Then, the top three bits of the counter would determine which digit to display and the bottom 5 bits would be used to count the duration that each digit is displayed. With 5 bits, each digit will be displayed for 25 = 32 clock cycles.

The table below summarizes how the top three bits of this free running counter are used to select the appropriate digit in the display:

Top 3 Bits Function
000 Display digit 0 (right most) - Assert AN0
001 Display digit 1 - Assert AN1
010 Display digit 2 - Assert AN2
011 Display digit 3 - Assert AN3
100 Display digit 4 - Assert AN4
101 Display digit 5 - Assert AN5
110 Display digit 6 - Assert AN6
111 Display digit 7 (left most) - Assert AN7

The clock we have on the NEXYS 4 board runs at 100 MHz with a clock period of 10 ns. If you use an eight-bit counter to sequence the digits as described above, the top three bits would be used to sequence through each digit and the bottom five bits would be used to count the number of clock cycles to display each digit. Thus, each digit would be displayed for 25 x 10 ns = 320 ns. (The module provided below does not use an 8-bit counter, this is just described here as an example).

The actual module given to you does not use an 8-bit counter, but instead the counter width is parameterizable. Look at the SystemVerilog below to see the default width of the counter.

SystemVerilog Module

Here is the SystemVerilog module. To use this in your Vivado project, 1) click the link to download the file directly, 2) move the file to your project directory, and 3) add the file as a source in your Vivado project.

SevenSegmentControl.sv
//////////////////////////////////////////////////////////////////////////////////
//
//  Filename: SevenSegmentControl.sv
//
//  Author: Mike Wirthlin, Jeff Goeders
//  
//  Description:
//
//     
//////////////////////////////////////////////////////////////////////////////////
 
module SevenSegmentControl(
    input wire logic            clk, 
    input wire logic            reset, 
    input wire logic    [31:0]  dataIn, 
    input wire logic    [7:0]   digitDisplay, 
    input wire logic    [7:0]   digitPoint, 
    output logic        [7:0]   anode, 
    output logic        [7:0]   segment
    );
 
    parameter integer COUNT_BITS = 17;
 
    logic   [COUNT_BITS-1:0]    count_val;
    logic   [2:0]               anode_select;
    logic   [7:0]               cur_anode;
    logic   [3:0]               cur_data_in;
 
    // Create counter    
    always_ff @(posedge clk) begin
       if (reset)
           count_val <= 0;
       else
           count_val <= count_val + 1;
    end
 
    // Signal to indicate which anode we are driving
    assign anode_select = count_val[COUNT_BITS-1:COUNT_BITS-3];
 
    // current andoe
    assign cur_anode = 
                        (anode_select == 3'b000) ? 8'b11111110 :
                        (anode_select == 3'b001) ? 8'b11111101 :
                        (anode_select == 3'b010) ? 8'b11111011 :
                        (anode_select == 3'b011) ? 8'b11110111 :
                        (anode_select == 3'b100) ? 8'b11101111 :
                        (anode_select == 3'b101) ? 8'b11011111 :
                        (anode_select == 3'b110) ? 8'b10111111 :
                        8'b01111111;
 
    // Mask anode values that are not enabled with digit display
    //  (if a bit of digitDisplay is '0' (off), then it will be 
    //   inverted and "ored" with the annode making it '1' (no drive)
    assign anode = cur_anode | (~digitDisplay);
 
    // This is a statement to simulate a common student problem with the
    // anode is "stuck". This statement is used to make sure the testbench catches
    // this condition.
    //assign anode = 8'hff;              
 
    assign cur_data_in = 
                        (anode_select == 3'b000) ? dataIn[3:0] :
                        (anode_select == 3'b001) ? dataIn[7:4]  :
                        (anode_select == 3'b010) ? dataIn[11:8]  :
                        (anode_select == 3'b011) ? dataIn[15:12]  :
                        (anode_select == 3'b100) ? dataIn[19:16]  :
                        (anode_select == 3'b101) ? dataIn[23:20]  :
                        (anode_select == 3'b110) ? dataIn[27:24]  :
                        dataIn[31:28] ;
 
    // Digit point (drive segmetn with inverted version of input digit point)
    assign segment[7] = 
                        (anode_select == 3'b000) ? ~digitPoint[0] :
                        (anode_select == 3'b001) ? ~digitPoint[1]  :
                        (anode_select == 3'b010) ? ~digitPoint[2]  :
                        (anode_select == 3'b011) ? ~digitPoint[3]  :
                        (anode_select == 3'b100) ? ~digitPoint[4]  :
                        (anode_select == 3'b101) ? ~digitPoint[5]  :
                        (anode_select == 3'b110) ? ~digitPoint[6]  :
                        ~digitPoint[7] ;
 
    assign segment[6:0] =
        (cur_data_in == 0) ? 7'b1000000 :
        (cur_data_in == 1) ? 7'b1111001 :
        (cur_data_in == 2) ? 7'b0100100 :
        (cur_data_in == 3) ? 7'b0110000 :
        (cur_data_in == 4) ? 7'b0011001 :
        (cur_data_in == 5) ? 7'b0010010 :
        (cur_data_in == 6) ? 7'b0000010 :
        (cur_data_in == 7) ? 7'b1111000 :
        (cur_data_in == 8) ? 7'b0000000 :
        (cur_data_in == 9) ? 7'b0010000 :
        (cur_data_in == 10) ? 7'b0001000 :
        (cur_data_in == 11) ? 7'b0000011 :
        (cur_data_in == 12) ? 7'b1000110 :
        (cur_data_in == 13) ? 7'b0100001 :
        (cur_data_in == 14) ? 7'b0000110 :
        7'b0001110;
 
 
endmodule

Module Description

The controller has the following three important inputs:

Full list of ports for this module:

Module Name: SevenSegmentControl
Port Name Direction Width Function
clk Input 1 100 MHz System Clock
reset Input 1 Active high reset, which resets the counter
dataIn Input 32 Data value to display
digitDisplay Input 8 Indicates which digits to display
digitPoint Input 8 Indicates which digit points to turn on
anode Output 8 Anode signal for each of the 8 digits
segment Output 8 Segment signals ([7] is digit point, [6:0] are regular segments)