Testing And Benchmarking in VHDL

Here’s an idiomatic VHDL example that demonstrates the concept of testing and benchmarking:

-- File: int_min.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

entity int_min is
    Port ( a : in  SIGNED(31 downto 0);
           b : in  SIGNED(31 downto 0);
           min_out : out SIGNED(31 downto 0));
end int_min;

architecture Behavioral of int_min is
begin
    min_out <= a when a < b else b;
end Behavioral;

-- File: int_min_tb.vhd
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
use IEEE.MATH_REAL.ALL;

entity int_min_tb is
end int_min_tb;

architecture Behavioral of int_min_tb is
    component int_min is
        Port ( a : in  SIGNED(31 downto 0);
               b : in  SIGNED(31 downto 0);
               min_out : out SIGNED(31 downto 0));
    end component;

    signal a, b, min_out : SIGNED(31 downto 0);
    type test_case is record
        a, b, expected : INTEGER;
    end record;
    type test_case_array is array (natural range <>) of test_case;
    constant test_cases : test_case_array := (
        (0, 1, 0),
        (1, 0, 0),
        (2, -2, -2),
        (0, -1, -1),
        (-1, 0, -1)
    );

    -- For benchmarking
    constant NUM_CYCLES : integer := 1000000;
begin
    -- Instantiate the Unit Under Test (UUT)
    uut: int_min port map (a => a, b => b, min_out => min_out);

    -- Test process
    test_proc: process
    begin
        for i in test_cases'range loop
            a <= to_signed(test_cases(i).a, 32);
            b <= to_signed(test_cases(i).b, 32);
            wait for 10 ns;
            assert min_out = to_signed(test_cases(i).expected, 32)
                report "Test case " & integer'image(i) & " failed: " &
                       "Expected " & integer'image(test_cases(i).expected) &
                       ", got " & integer'image(to_integer(min_out))
                severity error;
        end loop;
        report "All tests completed";
        wait;
    end process;

    -- Benchmark process
    benchmark_proc: process
        variable start_time, end_time : time;
    begin
        start_time := now;
        for i in 1 to NUM_CYCLES loop
            a <= to_signed(1, 32);
            b <= to_signed(2, 32);
            wait for 1 ns;
        end loop;
        end_time := now;
        report "Benchmark completed in " & time'image(end_time - start_time);
        wait;
    end process;

end Behavioral;

This VHDL example demonstrates testing and benchmarking concepts similar to the Go example. Here’s a breakdown of the code:

  1. We define an int_min entity that finds the minimum of two signed 32-bit integers.

  2. In the testbench (int_min_tb), we create a set of test cases similar to the table-driven tests in the Go example.

  3. The test_proc process runs through each test case, applies inputs to the Unit Under Test (UUT), and checks the output against the expected value. It reports any errors encountered.

  4. The benchmark_proc process performs a simple benchmark by running the int_min entity for a fixed number of cycles and measuring the time taken.

To run this VHDL code:

  1. Save the int_min entity in a file named int_min.vhd.
  2. Save the testbench in a file named int_min_tb.vhd.
  3. Use a VHDL simulator like GHDL, ModelSim, or Vivado Simulator to compile and run the testbench.

For example, using GHDL:

$ ghdl -a int_min.vhd
$ ghdl -a int_min_tb.vhd
$ ghdl -e int_min_tb
$ ghdl -r int_min_tb --wave=int_min_tb.ghw

This will run the testbench and generate a waveform file (int_min_tb.ghw) that you can view with a waveform viewer like GTKWave.

The testbench will report the results of each test case and the time taken for the benchmark. This approach allows for systematic testing and performance measurement of VHDL designs, similar to the testing and benchmarking capabilities in Go.