Search code examples
verilogfpgalcd

2 digit BCD counter on FPGA


I'm trying to make a 2 digit BCD counter which would count from 0 to 99. The problem I'm facing is that both the 7-segment dispays on the board are changing digits at the same time. The scenairio is like this - 00, 11, 22...99.

Here's the main logic code:

module PartD(
output reg[0:6] lcd, //for one particular 7 segment LCD.
input clock,
output reg[0:7] sel // for selecting which LCD to be used
);
integer count=0;
//integer i=0, j=0;

reg[3:0] i, j;  //4 bit reg for counting

always@(posedge clock)
begin
    if(count==100000000)  //(100MHz) count till 100M cycles...(1 sec delay)
    begin
        count = 0;

        sel=00000001;  //selecting the most significant digit
        case (i)
              0: lcd = 7'b0000001;
              1: lcd = 7'b1001111;
              2: lcd = 7'b0010010;
              3: lcd = 7'b0000110;
              4: lcd = 7'b1001100;
              5: lcd = 7'b0100100;
              6: lcd = 7'b0100000;
              7: lcd = 7'b0001111;
              8: lcd = 7'b0000000;
              9: lcd = 7'b0000100;
        endcase


        sel=00000010; //selecting the least significant digit
        case (j)
              0: lcd = 7'b0000001;
              1: lcd = 7'b1001111;
              2: lcd = 7'b0010010;
              3: lcd = 7'b0000110;
              4: lcd = 7'b1001100;
              5: lcd = 7'b0100100;
              6: lcd = 7'b0100000;
              7: lcd = 7'b0001111;
              8: lcd = 7'b0000000;
              9: lcd = 7'b0000100;
        endcase
        j = j+1;
        if(j>9)
        begin
            j=0;
            i = i+1;    //increment i only when j overflows.
            if(i>9)
            i = 0;
        end  
    end
    else count = count + 1;
end
endmodule

Since both displays are changing at once, there seems to be some logical error. What could it be. Am I making the mistake of not thinking in terms of hardware?


Solution

  • The major problem your code has is that you are trying to active both LCD segments within the same cycle when your counter is 100000000. Logically you are using a blocking assignment '=', and the 2nd assignment effectively overrides the 1st one.

    You need to decide on when to turn on each segment.

    Given your requirement, I would find a way to drive each segment 50% of the time. Given that the largest 2^n divisor of 10^8 is 256, I chose to use count[7] to decide between segment 1 and 2, so that each segment is refreshed for 128 cycles before moving to the other one.

    In addition, you had a syntax mistake, and forgot to use a binary prefix on the sel assignments (0000010 instead of 'b000010).

    This is the bare minimum to get it working.

    In practice I would have changed this further and break it into several 'always' blocks:
    One for the counter
    One for the display
    One for incrementing i and j.

    I also chose to replace the counter from an integer type to a register type. Keep integer for non-hardware constructs (for loop counters, etc.)

    Added a default value to the case statement, driving x's to 'lcd'. This helps both optimizing the synthesis for the unexpected i,j values (10..15) as well as propagating x's for simulation.

    I also added a reset input and moved the clock & reset signals to be the first signals on the module interface. Without a reset, your count,i,j starts with random values. In your case they eventually get legal, but it makes simulation harder because they start with 'x' values.

    Also, keep in mind the difference between blocking ('=') and non-blocking ('<=') assignments. Try avoiding using blocking assignments for clocked always block as you did.

    module PartD(
        input clock,
        input reset,
        output reg[0:6] lcd, //for one particular 7 segment LCD.
        output reg[0:7] sel // for selecting which LCD to be used
    );
    reg [26:0]count;
    reg[3:0] i, j;  //4 bit reg for counting
    
    always@(posedge clock)
    begin
        count <= reset ? 'd0 : (count == 100000000) ? 'd0 : (count + 1);
    end
    
    always@(posedge clock)
    begin
        j <= reset ? 'b0 : (j < 9) ? j+1 : 'b0;
        i <= reset ? 'b0 : (j < 9) ? i : (i < 9) ? (i + 1) : 'b0;
    end
    
    always@(posedge clock)
    begin
        if (count & 128)
        begin
            sel <= 'b00000001;  //selecting the most significant digit
            case (i)
                  0: lcd <= 7'b0000001;
                  1: lcd <= 7'b1001111;
                  2: lcd <= 7'b0010010;
                  3: lcd <= 7'b0000110;
                  4: lcd <= 7'b1001100;
                  5: lcd <= 7'b0100100;
                  6: lcd <= 7'b0100000;
                  7: lcd <= 7'b0001111;
                  8: lcd <= 7'b0000000;
                  9: lcd <= 7'b0000100;
                  default:
                     lcd <= 'bx;
            endcase
        end
        else
        begin
            sel <= 'b00000010; //selecting the least significant digit
            case (j)
                  0: lcd <= 7'b0000001;
                  1: lcd <= 7'b1001111;
                  2: lcd <= 7'b0010010;
                  3: lcd <= 7'b0000110;
                  4: lcd <= 7'b1001100;
                  5: lcd <= 7'b0100100;
                  6: lcd <= 7'b0100000;
                  7: lcd <= 7'b0001111;
                  8: lcd <= 7'b0000000;
                  9: lcd <= 7'b0000100;
                  default:
                     lcd <= 'bx;
            endcase
        end
    end
    
    endmodule