Loading header...

Exercise: VHDL Counter and Testbench

This exercise makes VHDL feel real: write a small hardware block, then write a testbench that proves the reset, enable, count, and wrap behavior.

Learning Objectives

You will practice:

  • writing synthesizable VHDL;
  • building a clocked counter;
  • using assertions in a testbench;
  • running a simulation before hardware.

Task

Create counter4.vhd:

  • inputs: clk, rst, en;
  • output: count : std_logic_vector(3 downto 0);
  • synchronous reset;
  • count increments only when en = '1';
  • wraps naturally from 15 to 0.

Design Code

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

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

architecture rtl of counter4 is
    signal count_r : unsigned(3 downto 0);
begin
    process(clk)
    begin
        if rising_edge(clk) then
            if rst = '1' then
                count_r <= (others => '0');
            elsif en = '1' then
                count_r <= count_r + 1;
            end if;
        end if;
    end process;

    count <= std_logic_vector(count_r);
end architecture;

Testbench Sketch

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity counter4_tb is
end entity;

architecture tb of counter4_tb is
    signal clk   : std_logic := '0';
    signal rst   : std_logic := '1';
    signal en    : std_logic := '0';
    signal count : std_logic_vector(3 downto 0);
begin
    clk <= not clk after 5 ns;

    dut : entity work.counter4
        port map (
            clk => clk,
            rst => rst,
            en => en,
            count => count
        );

    process
    begin
        wait for 20 ns;
        rst <= '0';
        en <= '1';

        wait for 10 ns;
        assert count = "0001" report "counter did not increment" severity error;

        wait for 150 ns;
        assert count = "0000" report "counter did not wrap as expected" severity error;

        assert false report "test complete" severity note;
        wait;
    end process;
end architecture;

Suggested GHDL Commands

ghdl -a counter4.vhd
ghdl -a counter4_tb.vhd
ghdl -e counter4_tb
ghdl -r counter4_tb --vcd=counter4.vcd
gtkwave counter4.vcd

Verification Checklist

  • Reset holds count at zero.
  • Enable low holds the previous value.
  • Enable high increments once per rising edge.
  • Count wraps from 1111 to 0000.
  • Testbench fails if the design is wrong.

Extension Challenge

Add a terminal_count output that is high when count equals 15. Decide whether it should be combinational or registered, then explain the tradeoff.

Common Mistakes

  • Checking output before the next rising clock edge.
  • Forgetting that signal updates happen after the process suspends.
  • Using arithmetic without numeric_std.
  • Writing a testbench that only generates waveforms but checks nothing.

Summary

This exercise reinforces the VHDL loop: write synthesizable RTL, analyze it, elaborate the testbench, run the simulation, inspect waveforms, and use assertions to catch errors.

Next: Packages, numeric_std, and Project Structure.

Further Reading

  • GHDL quick start
  • GTKWave manual
  • VHDL assertion examples

Mind Map

mindmap root((VHDL Counter Exercise)) Core concept Counter increments Testbench checks sequence Reset then count Applications Applications Clocked verification Reusable counter Checks Design checks Expected count Wrap behavior Common mistakes Wrong range Common mistakes No wait for edge