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;
Show the MyHDL (Python) version
MyHDL, design.py
from myhdl import block, Signal, modbv, always, instance, delay, StopSimulation

@block
def counter(clk, rst, en, count):
    @always(clk.posedge)
    def logic():
        if rst:
            count.next = 0
        elif en:
            count.next = count + 1      # modbv wraps 15 -> 0
    return logic

@block
def tb():
    clk = Signal(bool(0))
    rst = Signal(bool(1))
    en  = Signal(bool(0))
    count = Signal(modbv(0)[4:])
    dut = counter(clk, rst, en, count)

    @always(delay(5))
    def clkgen():
        clk.next = not clk

    @instance
    def stim():
        yield delay(12);  rst.next = 0
        yield delay(10);  en.next = 1
        yield delay(60);  en.next = 0
        yield delay(20);  en.next = 1
        yield delay(120); raise StopSimulation
    return dut, clkgen, stim

inst = tb()
inst.config_sim(trace=True)
inst.run_sim()

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
Show the VHDL testbench
VHDL, tb.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
entity tb is end entity;
architecture sim of tb is
  signal clk : std_logic := '0';
  signal rst : std_logic := '1';
  signal en  : std_logic := '0';
  signal count : unsigned(3 downto 0);
begin
  dut : entity work.counter port map (clk=>clk, rst=>rst, en=>en, count=>count);
  clk <= not clk after 5 ns;
  process begin
    wait for 12 ns;  rst <= '0';
    wait for 10 ns;  en  <= '1';         -- count up
    wait for 60 ns;  en  <= '0';         -- hold
    wait for 20 ns;  en  <= '1';         -- resume, wraps F -> 0
    wait for 120 ns; std.env.stop;
  end process;
end architecture;

The MyHDL version keeps the design and its testbench in one design.py.

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

Try it live

Open this lesson in a playground, edit the code, and re-run it.

Verilog → VHDL → MyHDL →

Put it to work

Tools that apply what this lesson covers.