Counters

A counter is the "hello world" of sequential logic: a flip-flop bank whose next value is a function of its current value. Every real design is full of them — timeouts, addresses, dividers, timestamps.

The design is a 4-bit up-counter with the two control inputs that essentially every register in every design should have:

Watch three things in the waveform: nothing happens before reset deasserts; the count only advances while en is high; and when the count hits 4'hF it wraps to 0 — binary overflow is free modulo arithmetic.

Experiment: make it count down when a dir input is high; or make a BCD counter that wraps at 9 (you'll need an if, not just overflow).

The design

Verilog — design.v
// 4-bit up-counter with synchronous reset and enable.
module counter (
    input  wire       clk,
    input  wire       rst,     // synchronous, active high
    input  wire       en,
    output reg  [3:0] count
);
    always @(posedge clk) begin
        if (rst)
            count <= 4'd0;
        else if (en)
            count <= count + 4'd1;
        // no final else: hold (a register holds by default)
    end
endmodule
Show the VHDL version
VHDL — design.vhd
-- 4-bit up-counter with synchronous reset and enable.
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity counter is
    port (
        clk   : in  std_logic;
        rst   : in  std_logic;
        en    : in  std_logic;
        count : out unsigned(3 downto 0)
    );
end entity;

architecture rtl of counter is
begin
    process (clk) begin
        if rising_edge(clk) then
            if rst = '1' then
                count <= (others => '0');
            elsif en = '1' then
                count <= count + 1;
            end if;
        end if;
    end process;
end architecture;

The testbench

Verilog — tb.v
`timescale 1ns/1ns
module tb;
    reg clk = 0, rst = 1, en = 0;
    wire [3:0] count;

    counter dut (.clk(clk), .rst(rst), .en(en), .count(count));

    always #5 clk = ~clk;

    initial begin
        $dumpfile("wave.vcd"); $dumpvars(0, tb);
        #12 rst = 0;
        #10 en  = 1;      // count 0,1,2,...
        #60 en  = 0;      // hold
        #20 en  = 1;      // resume; runs long enough to wrap F -> 0
        #120 $finish;
    end
endmodule

Simulated waveform

This trace was produced by actually simulating the code above with Icarus Verilog.

22 44 66 88 110 132 154 176 198 220 t (ns) count[3:0] x 0 1 2 3 4 5 6 7 8 9 A B C D E F 0 1 2 clk en rst

Open this lesson in the playground →