2
votes

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?

2
Can you provide LCD part number? Probably you should update each LCD's 50 times per second or more.alexander
@alexander LCD part number...you want to see the constraints (xdc file)?archity

2 Answers

4
votes

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
0
votes

The problem I'm facing is that both the 7-segment dispays on the board are changing digits at the same time

  1. The code lacks the timing constraints that control the switching mechanism between both digits on the 8 digit display. You would have to consider a refresh rate; this is what enables you to update the display continuously. By convention, the refresh rate can be anywhere between 1 kHz and 60 Hz. If we choose frequency values that are too low, the display will start to flicker, which means that the switching is not fast enough.

Let's say you choose the refresh period to be 1 ms. This means that each digit remains active for 1/8th of the cycle (for an 8 digit display). In other words, the digit period is 1/8 ms. This cycle occurs continuously thereby giving the appearance of independent and constantly illuminated digits, which is one of the objectives for producing a 2 digit BCD counter.

We can convert the existing FPGA clock into a counter that increments to a value corresponding to a refresh period between 1 and 16ms. If we divide the internal clock frequency by the refresh frequency, this would give us a counter value for the entire refresh cycle.

  • For example, a 1 ms refresh period using a 100MHz clock, would generate a counter value of 10 * 10^4. Thus, in order for a register to hold the counter value, it must be at least 17 bits wide.

Next, we have to decide how we can trigger the correct digit while counting from 0 to 99 using the digit period. In the code below, we take advantage of the width of the register "refresh_counter" for the first case structure

  • We only have two digits to control, so the refresh period gets divided by two. Assuming there is a refresh period of 1 ms, we then have a 0.5 ms digit period. Based on this, we can find which bit position, in the register "refresh_counter", will toggle after 0.5 ms.

BCD Counter Code

module PartD(
             input MHZ,
             output reg DP = 1'b1,
             output reg [6:0] A_TO_G,
             output reg [7:0] AN
             );

localparam divisor = 50000000;
reg [25:0] counter = 0;
reg clock = 0;

always@(posedge MHZ) begin
if (counter < divisor) begin
    counter <= counter + 1;
    clock <= clock; 
end

else begin
    counter <= 0;
    clock <= ~clock; end
 end

reg [16:0] refresh_counter = 0; //17 bits wide
always@(posedge MHZ) begin
    refresh_counter <= refresh_counter + 1;
end

reg [3:0] num_one, num_two;
always@(posedge clock) begin

        if(num_one == 4'd9) begin
               num_one <= 0;
        
               if (num_two == 4'd9)
                      num_two <= 0;
             
               else
                      num_two <= num_two + 1; 
         end
                    
         else 
            num_one <= num_one + 1; 
 end                          

 reg [3:0] NUMBER;
 always@(refresh_counter or num_one or num_two) begin

 // digit_per = 0.5 ms ---> digit_freq = 2000 Hz

 // mhz_freq = 100*10^6 Hz

 // bit = log(mhz_freq/digit_freq)/log(2)

 // bit_position = bit - 1

 // refresh_counter[bit_position]

 case(refresh_counter[15]) 
    1'b0: begin
         AN = 8'b11111110;
         NUMBER  = num_one;
    end
    1'b1: begin
         AN = 8'b11111101;
         NUMBER = num_two;
    end
endcase   
end

always@(NUMBER) begin
case(NUMBER)
    0: A_TO_G = 7'b0000001;
    1: A_TO_G = 7'b1001111;
    2: A_TO_G = 7'b0010010;
    3: A_TO_G = 7'b0000110;
    4: A_TO_G = 7'b1001100;
    5: A_TO_G = 7'b0100100;
    6: A_TO_G = 7'b0100000;
    7: A_TO_G = 7'b0001111;
    8: A_TO_G = 7'b0000000;
    9: A_TO_G = 7'b0000100;
    default: A_TO_G = 7'b0000001;
endcase
end


endmodule