Chapter 24

Counters

Abstract

One of the most commonly used applications of flip-flops in practical systems is counters. They are used in microprocessors to count the program instructions (program counter or PC), for accessing sequential addresses in memory (such as ROM) or for checking progress of a test. Counters can start at any value, although most often they start at zero and they can increment or decrement. Counters may also change values by more than one at a time, or in different sequences (such as grey code, binary or binary coded decimal (BCD) counters).

Keywords

BCD

Counters

Grey code

Binary counters

24.1 Introduction

One of the most commonly used applications of flip-flops in practical systems is counters. They are used in microprocessors to count the program instructions (program counter or PC), for accessing sequential addresses in memory (such as ROM) or for checking progress of a test. Counters can start at any value, although most often they start at zero and they can increment or decrement. Counters may also change values by more than one at a time, or in different sequences, such as grey code, binary, or binary coded decimal (BCD) counters.

24.2 Basic Binary Counter using VHDL

The simplest counter to implement in many cases is the basic binary counter. The basic structure of a counter is a series of flip-flops (a register), that is controlled by a reset (to reset the counter to zero literally) and a clock signal used to increment the counter. The final signal is the counter output, the size of which is determined by the generic parameter n, which defines the size of the counter. The symbol for the counter is given in Figure 24.1. Notice that the reset is active low and the counter and reset inputs are given in a separate block of the symbol as defined in the IEEE standard format.

f24-01-9780080971292
Figure 24.1 Simple binary counter.

From an FPGA implementation point of view, the value of generic n also defines the number of D type flip-flops required (usually a single LUT) and hence the usage of the resources in the FPGA. A simple implementation of such a counter is given here:

1 library ieee;

2 use ieee.std_logic_1164.all;

3 use ieee.numeric_std.all;

5 entity counter is

6 generic (

7   n : integer := 4

8   );

9 port (

10   clk : in std_logic;

11   rst : in std_logic;

12   output : out std_logic_vector (( n −1) downto 0)

13 );

14 end;

15 

16 architecture simple of counter is

17 begin

18 process ( clk , rst )

19   variable count : unsigned (( n −1) downto 0);

20 begin

21   if rst = ’0’ then

22  count := ( others => ’0’);

23   elsif rising_edge ( clk ) then

24  count := count + 1;

25   end if;

26   output <= std_logic_vector ( count );

27 end process;

28 end;

The important aspect of this approach to the counter VHDL is that this is effectively a state machine; however, we do not have to explicitly define the next state logic, as this will be taken care of by the synthesis software. This counter can now be tested using a simple test bench that resets the counter and then clocks the state machine until the counter flips round to the next counter round. The test bench is given as follows:

1 library ieee;

2 use ieee.std_logic_1164.all;

3 use ieee.numeric_std.all;

5 entity CounterTest is

6 end CounterTest;

8 architecture stimulus of CounterTest is

9   signal rst : std_logic := ’0’;

10   signal clk : std_logic :=’0’;

11   signal count : std_logic_vector (3 downto 0);

12 

13   component counter

14   port (

15   clk : in std_logic;

16   rst : in std_logic;

17   output : out std_logic_vector (3 downto 0)

18   );

19   end component;

20   for all : counter use entity work.counter ;

21 

22 begin

23   DUT : counter port map ( clk => clk , rst => rst , output => count );

24   clk <= not clk after 1 us;

25   process

26   begin

27  rst <=’0’,’1’ after 2.5 us;

28  wait;

29   end process;

30 end;

Using this simple VHDL test bench, we reset the counter until 2.5 μs and then the counter will count on the rising edge of the clock after 2 μs (i.e., the counter is running at 500 kHz).

If we dissect this model, there are several interesting features to notice. The first is that we need to define an internal variable count rather than simply increment the output variable q. The output variable q has been defined as a standard logic vector (std_logic_vector) and with it being defined as an output we cannot use it as the input variable to an equation. Therefore we need to define a local variable (in this case, count) to store the current value of the counter.

The initial decision to make is whether to use a variable or a signal. In this case, we need an internal variable that we can effectively treat as a sequential signal, and also one that changes instantaneously, which immediately requires the use of a variable. If we chose a signal, then the changes would only take place when the cycle is resolved (i.e., the next time the process is activated).

The second decision is what type of unit to use for the count variable. The output variable is a std_logic_vector type, which has the advantage of being an array of std_logic signals, and so we don’t need to specify the individual bits on a word; this is done automatically. The major disadvantage, however, is that the std_logic_vector does not support simple arithmetic operations, such as addition, directly. In this example, we want the counter to have a simple definition in VHDL and so the best compromise type that has the bitwise definition and also the arithmetic functionality would be the unsigned or signed type. In this case, we wish to have a direct mapping between the std_logic_vector bits and the counter bits, so the unsigned type is appropriate. Thus the declaration of the internal counter variable count is as follows:

1 variable count : unsigned (( n −1) downto 0);

The final stage of the model is to assign the internal value of the count variable to the external std_logic_vector q. Luckily, the translation from unsigned to std_logic_vector is fairly direct, using the standard casting technique:

1 q <= std_logic_vector ( count );

As the basic types of both q and count are consistent, this can be done directly. The resulting model simply counts up the binary values on the clock edge as specified, as is shown in Figure 24.2.

f24-02-9780080971292
Figure 24.2 Simple binary counter simulation in VHDL.

24.3 Simple Binary Counter using Verilog

With the same basic specification as the VHDL counter, it is possible to implement a basic counter in Verilog using the same architecture of the model.

1 module counter (

2 clk ,        // clock input

3 rst ,        // reset ( active low )

4 counter_output // counter output

5  );

7 input clk;

8 input rst;

10 output [3:0] counter_output ;

11 

12 wire clk;

13 wire rst;

14 

15 reg [3:0] counter_output ;

16 

17 always @ (posedge clk)

18 begin : count

19  if (rst == 1’b0) begin

20  counter_output <= #1 4’b0000;

21  end

22  else begin

23  counter_output <= #1 counter_output + 1;

24  end

25 end

26 

27 endmodule

The model has the same connection points and operation, and will be treated in the same way for synthesis by the design software. The test bench is slightly different from the VHDL one in that it is much more explicit about the test function as well as the functionality of the test bench. For example, if we look at the top of the test bench Verilog has the $display and $monitor commands, which enable the time and variable values to be displayed in the monitor of the simulation as well as looking at the waveforms.

1 $display ( ” time t clk reset counter ” );

2 $monitor ( ” % g t % b %b %b”,

3  $time, clk, rst, counter_output);

Using this test bench and Verilog model the behavior of the simulation can also be verified as shown in Figure 24.3.

f24-03-9780080971292
Figure 24.3 Simple binary counter simulation in Verilog.

24.4 Synthesized Simple Binary Counter

At this point it is useful to consider what happens when we synthesize this VHDL, so to test this point the VHDL model of the simple binary counter was run through a typical RTL synthesis software package (Leonardo Spectrum) with the resultant synthesized VHDL model given here:

1 entity counter is

2   port (

3  clk : IN std_logic ;

4  rst : IN std_logic ;

5  output : OUT std_logic_vector (3 DOWNTO 0)) ;

6 end counter;

8 architecture simple of counter is

9   signal clk_int , rst_int , output_dup0_3 , output_dup0_2 , output_dup0_1 ,

10  output_dup0_0 , output_nx4 , output_nx7 , output_nx10 , NOT_rst ,

11  output_NOT_a_0 : std_logic ;

12 

13 begin

14   output_obuf_0 : OBUF port map ( O => output (0), I => output_dup0_0 );

15   output_obuf_1 : OBUF port map ( O => output (1), I => output_dup0_1 );

16   output_obuf_2 : OBUF port map ( O => output (2), I => output_dup0_2 );

17   output_obuf_3 : OBUF port map ( O => output (3), I => output_dup0_3 );

18   rst_ibuf : IBUF port map ( O => rst_int , I => rst );

19   output_3_EXMPLR_EXMPLR : FDC port map ( Q => output_dup0_3 , D => output_nx4 ,

20  C => clk_int , CLR => NOT_rst );

21   output_2_EXMPLR_EXMPLR : FDC port map ( Q => output_dup0_2 , D => output_nx7 ,

22  C => clk_int , CLR => NOT_rst );

23   output_1_EXMPLR_EXMPLR : FDC port map ( Q => output_dup0_1 , D => output_nx10 ,

24  C => clk_int , CLR => NOT_rst );

25   output_0_EXMPLR_EXMPLR : FDC port map ( Q => output_dup0_0 , D =>

26  output_NOT_a_0 , C => clk_int , CLR => NOT_rst );

27   clk_ibuf : BUFGP port map ( O => clk_int , I => clk );

28   output_nx4 <= ( not output_dup0_3 and output_dup0_2 and output_dup0_1 and

29   output_dup0_0 ) or ( output_dup0_3 and not output_dup0_0 ) or ( output_dup0_3

30   and not output_dup0_2 ) or ( output_dup0_3 and not output_dup0_1 ) ;

31   output_nx7 <= ( output_dup0_2 and not output_dup0_0 ) or ( not output_dup0_2

32   and output_dup0_1 and output_dup0_0 ) or ( output_dup0_2 and not

33   output_dup0_1 );

34   output_nx10 <= ( output_dup0_0 and not output_dup0_1 ) or ( not

35   output_dup0_0 and output_dup0_1 ) ;

36   NOT_rst <= ( not rst_int ) ;

37   output_NOT_a_0 <= ( not output_dup0_0 ) ;

38 end simple ;

The first obvious aspect of the model is that it is much longer than the simple RTL VHDL created originally. The next stage logic is now in evidence; as this is synthesized, the physical gates must be defined for the model. Finally the outputs are buffered, which leads to even more gates in the final model. If the optimization report is observed, the overall statistics of the resource usage of the FPGA can be examined (in this case, a Xilinx Virtex-II Pro device):

 Cell Library References Total Area

 ========================================================

 BUFGP xcv2p 1 x 1 1 BUFGP

 FDC xcv2p 4 x 1 4 Dffs or Latches

 IBUF xcv2p 1 x 1 1 IBUF

 LUT1 xcv2p 2 x 1 2 Function Generators

 LUT2 xcv2p 1 x 1 1 Function Generators

 LUT3 xcv2p 1 x 1 1 Function Generators

 LUT4 xcv2p 1 x 1 1 Function Generators

 OBUF xcv2p 4 x 1 4 OBUF

  Number of ports : 6

  Number of nets : 17

  Number of instances : 15

  Number of references to this view : 0

 Total accumulated area :

  Number of BUFGP : 1

  Number of Dffs or Latches : 4

  Number of Function Generators : 5

  Number of IBUF : 1

  Number of OBUF : 4

  Number of gates : 5

  Number of accumulated instances : 15

 Number of global buffers used: 1

 ***********************************************

 Device Utilization for 2VP2fg256

 ***********************************************

 Resource Used Avail Utilization

 -----------------------------------------------

 IOs 5 140 3.57%

 Global Buffers 1 16 6.25%

 Function Generators 5 2816 0.18%

 CLB Slices 3 1408 0.21%

 Dffs or Latches 4 3236 0.12%

 Block RAMs 0 12 0.00%

 Block Multipliers 0 12 0.00%

In this simple example, it can be seen that the overall utilization of the FPGA is minimal, with the relative resource allocation of IOs, buffers and functional blocks. This is an important aspect of FPGA design in that, even though the overall device may be underutilized, a particular resource (such as IO) might be used up. The output VHDL can then be used in a physical place and route software tool (such as the Xilinx Design Navigator) to produce the final bit file that will be downloaded to the device.

24.5 Shift Register

While a shift register is, strictly speaking, not a counter, it is useful to consider this in the context of other counters as it can be converted into a counter with very small changes. We will consider this element layer in this book, in more detail, but consider a simple case of a shift register that takes a single bit and stores in the least significant bit of a register and shifts each bit up one bit on the occurrence of a clock edge. If we consider an n-bit register and show the status before and after a clock edge, then the functionality of the shift register becomes clear, as shown in Figure 24.4.

f24-04-9780080971292
Figure 24.4 Simple shift register functionality: (a) Before the clock edge; (b) After the clock edge.

A basic shift register can be implemented in VHDL as shown here:

1 library ieee;

2 use ieee.std_logic_1164.all;

4 entity shift_register is

5 generic (

6   n : integer := 4;

7 port (

8   clk : in std_logic ;

9   rst : in std_logic ;

10   din : in std_logic;

11   q : out std_logic_vector (( n −1) downto 0)

12 );

13 end entity;

14 

15 architecture simple of shift_register is

16 begin

17 process ( clk , rst )

18   variable shift_reg : std_logic_vector (( n −1) downto 0);

19 begin

20   if rst = ’0’ then

21  shift_reg := ( others => ’0’);

22   elsif rising_edge ( clk ) then

23  shift_reg := shift_reg ( n −2 downto 0) & din;

24   end if;

25   q <= shift_reg;

26  end process;

27 end architecture simple;

The interesting parts of this model are very similar to the simple binary counter, but subtly different. As for the counter, we have defined an internal variable (shift_reg), but unlike the counter we do not need to carry out arithmetic functions, so we do not need to define this as an unsigned variable. Instead, we can define directly as a std_logic_vector, the same as the output q.

Notice that we have an asynchronous clock in this case. As we have discussed previously in this book, there are techniques for completely synchronous sets or resets, and these can be used if required.

The fundamental difference between the counter and the shift register is in how we move the bits around. In the counter we use arithmetic to add one to the internal counter variable (count). In this case, we just require shifting the register up by one bit, and to achieve this we simply assign the lowest (n − 1) bits of the internal register variable (shift_reg) to the upper (n − 1) bits and concatenate the input bit (din), effectively setting the lowest bit of the register to the input signal (din). This can be accomplished using the VHDL following:

1 shift_reg := shift_reg ( n −2 downto 0) & din;

The final stage of the model is similar to the basic counter in that we then assign the output signal to the value of the internal variable (shift_reg) using a standard signal assignment. In the shift register, we do not need to cast the type as both the internal and signal variable types are std_logic_vector:

1 q <= shift_reg;

We can also implement the shift register in Verilog, with the listing as shown here:

1 module shift_register (

2  clk ,        // clock input

3  rst ,        // reset ( active low )

4  din ,        // Digital Input

5  shiftreg    // shift register

6  );

8 input clk;

9 input rst;

10 input din;

11 

12 output [7:0] shiftreg ;

13 

14 wire clk;

15 wire rst;

16 wire din;

17 

18 reg [7:0] shiftreg ;

19 

20 always @ (posedge clk)

21 begin : count

22  if (rst == 1’b0) begin

23  shiftreg <= #1 4’b00000000;

24  end

25  else begin

26  shiftreg <= #1 {din, shiftreg[7:1]};

27  end

28 end

29 

30 endmodule

In both cases (VHDL and Verilog) we can test the behavior of the shift register by applying a data sequence and observing the shift register variable in the model, and in the case of the Verilog we can also add a $monitor command to display the transitions as they happen in the transcript of the simulator. The Verilog test bench code is given as:

2 module shift_register_tb ();

3 // declare the signals

4 reg clk;

5 reg rst;

6 reg din;

7 wire [7:0] shift_register_values;

9 // Set up the initial variables and reset

10 initial begin

11  $display ( ” time t clk reset counter ” );

12  $monitor ( ” % g t % b %b %b %h”,

13  $time, clk, rst, din, shift_register_values);

14  clk = 1; // initialize the clock to 1

15  rst = 1; // set the reset to 1 (not reset)

16  din = 0;    // Initialize the digital input

17  #5 rst = 0; // reset = 0 : resets the counter

18  #10 rst = 1; // reset back to 1 : counter can start

19  #4 din = 0; // test data sequence starting at cycle time 16

20  #10 din = 1; // din = 1 test data sequence

21  #10 din = 0; // din = 0 test data sequence

22  #10 din = 0; // din = 0 test data sequence

23  #10 din = 1; // din = 1 test data sequence

24  #10 din = 1; // din = 1 test data sequence

25  #10 din = 0; // din = 0 test data sequence

26  #10 din = 1; // din = 1 test data sequence

27  #10 din = 0; // din = 0 test data sequence

28  #10 din = 1; // din = 1 test data sequence

29  #1000 $finish; // Finish the simulation

30 end

31 

32 // Clock generator

33 always begin

34  #5 clk = ˜clk; // Clock every 5 time slots

35 end

36 

37 // Connect DUT to test bench

38 shift_register DUT (

39 clk,

40 rst,

41 din,

42 shift_register_values

43 );

44 

45 endmodule

The resulting simulation of the shift register can be seen in Figure 24.5.

f24-05-9780080971292
Figure 24.5 Simple shift register simulation.

24.6 The Johnson Counter

The Johnson counter is a counter that is a simple extension of the shift register. The only difference between the two is that the Johnson counter has its least significant bit inverted and fed back into the most significant bit of the register. In contrast to the classical binary counter with 2n states, the Johnson counter has 2n states. While this has some specific advantages, a disadvantage is that the Johnson counter has what is called a parasitic counter in the design. In other words, while the 2n counter is operating, there is another state machine that also operates concurrently with the Johnson counter using the unused states of the binary counter. A potential problem with this counter is that if, due to an error, noise or other glitch, the counter enters a state NOT in the standard Johnson counting sequence, it cannot return to the correct Johnson counter without a reset function. The normal Johnson counter sequence is shown in the following table:

CountQ(3:0)
00000
11000
21100
31110
41111
50111
60011
70001

The VHDL implementation of a simple Johnson counter can then be made by modifying the next stage logic of the internal shift_register function as shown in the following listing:

1 library ieee;

2 use ieee.std_logic_1164.all;

4 entity johnson_counter is

5 generic (

6   n : integer := 4;

7 port (

8   clk : in std_logic;

9   rst : in std_logic;

10   din : in std_logic;

11   q : out std_logic_vector (( n −1) downto 0)

12 );

13 end entity;

14 

15 architecture simple of Johnson_counter is

16 begin

17 process ( clk , rst )

18   variable j_state : std_logic_vector (( n −1) downto 0);

19 begin

20   if rst = ’0’ then

21  j_state := ( others => ’0’);

22   elsif rising_edge ( clk ) then

23  j_state := not j_state (0) & j_state ( n −1 downto 1);

24   end if;

25   q <= j_state;

26 end process;

27 end architecture simple;

Notice that the concatenation is now putting the inverse (NOT) of the least significant bit of the internal state variable (j_state(0)) onto the next state most significant bit, and then shifting the current state down by one bit.

It is also worth noting that the counter does not have any checking for the case of an incorrect state. It would be sensible in a practical design to perhaps include a check for an invalid state and then reset the counter in the event of that occurrence. The worst-case scenario would be that the counter would be incorrect for a further 7 clock cycles before correctly resuming the Johnson counter sequence.

In a similar manner we can implement a Johnson counter in Verilog using the code given here:

1 module johnson_counter (

2  clk ,        // clock input

3  rst ,        // reset ( active low )

4  johnsonreg    // shift register

5  );

7 input clk;

8 input rst;

10 output [7:0] johnsonreg;

11 

12 wire clk;

13 wire rst;

14 

15 reg [7:0] johnsonreg ;

16 

17 always @ (posedge clk)

18 begin : count

19  if (rst == 1’b0) begin

20  johnsonreg <= #1 4’b00000000;

21  end

22  else begin

23  johnsonreg <= #1 {!johnsonreg[0], johnsonreg[7:1]};

24  end

25 end

26 

27 endmodule

and test it using the same basic counter test bench created for the simple counter, giving the simulation results as shown in Figure 24.6. We can see that the counter variable “ripples” through till it gets to all 1s and then carries back on until it is all 0s.

f24-06-9780080971292
Figure 24.6 Johnson counter simulation.

24.7 BCD Counter

The BCD (Binary Coded Decimal) counter is simply a counter that resets when the decimal value 10 is reached instead of the normal 15 for a 4-bit binary counter. This counter is often used for decimal displays and other human interface hardware. The VHDL for a BCD counter is very similar to that of a basic binary counter except that the maximum value is 10 (hexadecimal A) instead of 15 (hexadecimal F). The VHDL for a simple BCD counter is given in the following listing. The only change is that the counter has an extra check to reset when the value of the count variable is greater than 9 (the counter range is 0 to 9).

1 library ieee;

2 use ieee.std_logic_1164.all;

3 use ieee.numeric_std.all;

5 entity counter is

6 generic (

7   n : integer := 4;

8 port (

9   clk : in std_logic;

10   rst : in std_logic;

11   output : out std_logic_vector (( n −1) downto 0)

12 );

13 end;

14 

15 architecture simple of counter is

16 begin

17 process ( clk , rst )

18   variable count : unsigned (( n −1) downto 0);

19 begin

20   if rst = ’0’ then

21  count := ( others => ’0’);

22   elsif rising_edge ( clk ) then

23  count := count + 1;

24   if count > 9 then

25   count := 0;

26   else if

27   end if;

28   output <= std_logic_vector ( count );

29 end process;

30 end;

In a similar manner we can implement a BCD counter in Verilog using the code given here:

1 module bcd_counter (

2  clk ,        // clock input

3  rst ,        // reset ( active low )

4  counter_output // counter output

5  );

7 input clk;

8 input rst;

10 output [3:0] counter_output;

11 

12 wire clk;

13 wire rst;

14 

15 reg [3:0] counter_output ;

16 

17 always @ (posedge clk)

18 begin : count

19  if (rst == 1’b0) begin

20  counter_output <= #1 4’b0000;

21  end

22  else begin

23  if(counter_output < 9) begin

24  counter_output <= #1 counter_output + 1;

25  end

26  else

27  counter_output <= #1 4’b0000;

28  end

29 end

30 

31 endmodule

and test it using the same basic counter test bench created for the simple counter, giving the simulation results as shown in Figure 24.7. In the results this time you can see the counter variable in binary and also in unsigned decimal counting up to 1001 (binary) and 9 (decimal), then returning back to 0, giving the decimal counter.

f24-07-9780080971292
Figure 24.7 BCD counter simulation.

24.8 Summary

In this chapter, we have investigated some basic counters and shown how VHDL and Verilog can be used to carry out arithmetic functions or logic functions to obtain the required counting sequence. The possibilities of counters based on these basic types are numerous, possibly infinite, and it is left to the readers to develop their own variations based on these standard types.

A useful exercise would be to modify the basic binary counter by adding an up/down flag so that, depending on this flag, the counter would increment or decrement respectively. Other options would be to extend the shift register to shift left or right depending on a directional flag.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.137.181.52