Counters

General purpose counting circuits

16209

A counter circuit receives a clock signal as input, and produces an n-bit binary number as an output, with the output binary number changing on every clock edge. Most counters cycle through all possible 2^n binary numbers before repeating, but some do not output all possible codes. Binary counters cycle through all 2^n numbers in a natural counting sequence. Gray code counters (and other related counters) count through all 2^n binary numbers, but not in a natural counting sequence (only 1 bit changes between successive count values). BCD counters (also called decimal counters) are 4-bit counters that count 0000-1001 and then repeat. Ring counters are built from shift registers, with an inverter that feeds the last bit back to the first bit through an inverter. Ring counters don’t use all the possible codes, and they don’t count in a natural counting sequence, but they can run very fast.

Synchronous Binary Counters

Synchronous binary counters are some of the simplest sequential circuit components. An n-bit counter uses an n-bit PIPO register and a combinational logic incrementer to add ‘1’ to the current count value. In operation, the counter’s output increments to the next binary number at each new clock edge, rolling over back to 0 on the clock edge after all bits are a 1.

Figure 1. Basic counter circuit and timing diagram
Figure 1. Basic counter circuit and timing diagram

As shown below, the incrementer structural circuit block can be designed using the truth table and K-map methods presented earlier.

Figure 2. Incrementer design
Figure 2. Incrementer design

By observing the timing diagram (and/or truth table) and recognizing a pattern, the use of K-maps could be avoided. Notice that each more significant bit toggles when all less significant bits are a 1. Articulating that pattern in Verilog, the equation for driving the flip-flop that holds bit N (BN) would be:

assign BN = BN ^ (BN-1 & BN-2 & … & B0)

A counter is a sequential circuit, and sequential circuits described in Verilog must use procedural assignment statements inside an “always” block. When constructing an always block, you must decide on several behaviors: which signals will trigger an update to output signals (these are the signals that go in the sensitivity list); how the outputs change in response to a trigger signal (like clock); and how the outputs react to a reset signal.

A behavioral Verilog description of a synchronous binary counter simply adds ‘1’ to the current counter value each time a clock rising edge occurs. The Verilog code below includes an asynchronous reset and a counter enable signal (ce) as well. When ce is de-asserted, the counter stops counting and holds its current count value - this is a common feature in many counter modules. Note in the code below, the output “counterout” is typecast to a reg in the module port statement. This must be done because “counterout” is assigned in a procedural assignment statement, and all procedural assignment targets must be reg type. The entire “counterout” bus is brought out to a module port, so the counter output can be used by other cirucits. It is also worth mentioning that module outputs can be type wire or reg, but inputs must be type wire.

module counter1(
    input clk, rst,
    output reg [7:0] counterout
    );
    
 always @ (posedge(clk), posedge(rst))
 begin
     if (rst) counterout <= 0;
     else if (ce) counterout <= counterout + 1;
end
endmodule

Binary counters are conceptually simple, but they suffer from increasingly large and slow combinational logic circuits to toggle their higher order bits. Consider a 32-bit counter. The most significant bit (B31) needs a 31-input AND gate to detect that all the lower-order bits are a ‘1’ before it can toggle. Such a large logic function will impose significant speed limits on the counter. In a Xilinx FPGA for example, a 31-input AND gate would most likely have a delay above 5ns, limiting the counter operating frequency to less than 200MHz.

Asynchronous Binary Counter

Another counter architecture, called an “asynchronous” counter, overcomes the limited operating frequency of a synchronous counter by eliminating the incrementer logic circuit all together. Each bit of an asynchronous counter toggles itself using a feedback inverter, and each self-toggling flip-flop serves as a clock source for its higher-order neighbor. Asynchronous counters can run very fast, but since each flip-flop’s clock comes from its neighbor (and not from a central, high-speed clock network as with the binary counter), the higher-order bits are delayed from the lower order bits due to the clock signal routing delays. This delay is called “skew”, and the bit-skew can be excessive for larger counters.

Figure 3. Asynchronous Counter
Figure 3. Asynchronous Counter

The Verilog code below includes one always block for each flip-flop, and each higher-order flip-flop uses it’s lower-order neighbor as a clock source. Note in an FPGA (like the ZYNQ chip on the Blackboard), clock signals into flip-flops can only come from the FPGA’s main clock input, or directly from the output of another flip-flop (a clock signal cannot come from any sort of logic gate or logic circuit).

module asynchcounter(
    input clk, rst,
    output reg [3:0] acount
    );
    
always @ (posedge(clk), posedge(rst))
begin
   if (rst) acount[0] <= 0;
   else acount[0] <= !acount[0];
end
    
always @ (posedge(acount[0]), posedge(rst))
begin
   if (rst) acount[1] <= 0;
   else acount[1] <= !acount[1];
end
       
always @ (posedge(acount[1]), posedge(rst))
begin
   if (rst) acount[2] <= 0;
   else acount[2] <= !acount[2];
end
    
always @ (posedge(acount[2]), posedge(rst))
begin
   if (rst) acount[3] <= 0;
   else acount[3] <= !acount[3];
end
endmodule
Decimal counters

A binary counter can easily be modified to count through a smaller range of numbers by adding a comparator to check for a “terminal count” number, and then resetting the counter when the terminal count is reached. In the figure below, the comparator is “hard wired” to check for a “9” and to assert the terminal count signal (TC) when a count value of nine is reached. The terminal count signal is fed back to a “synchronous reset” signal (SRST) that forces the incrementer circuit inside the counter to output a zero, so that the next counter value after nine is a zero.

Note that this circuit produces count values 0-9 in a repeating sequence. It’s called a decimal counter, and its output is called “Binary Coded Decimal”, or BCD. BCD is a 4-bit code, but bit patterns for 0xA – 0xF (i.e., 1010 – 1111) are not used.

Figure 4. Decimal Counter
Figure 4. Decimal Counter
module bcdcounter (
    input clk, rst,
    output reg [3:0] bcd_digit
    );

always @ (posedge(clk), posedge(rst))
begin
    if (rst) bcd_digit <= 0;
    else if (bcd_digit == 4'b1001) bcd_digit <= 0;
    else bcd_digit <= bcd_digit + 1;
end
endmodule
Ring Counter

Ring counters are shift registers with the last bit fed back to the first bit through an inverter. No logic is needed to form inputs to the flip-flops, so this counter can run at the highest possible frequency. And since all bits use the same clock, there is no bit skew. However, only 2 x N codes are formed from N flip-flops, so they are less efficient than binary counters.

Figure 5. Ring Counter
Figure 5. Ring Counter
module ringcounter (
    input clk, rst,
    output reg [3:0] ringcount
    );

always @ (posedge(clk), posedge(rst))
begin
    if (rst) ringcount <= 0;
    else ringcount <= {ringcount[2:0], !ringcount[3]};
end
endmodule