FSMのためのテストベンチ / 自販機 / リニアテストベンチ

pp.549-551
ここでは、想定されるすべてのケースのうち3つのみをテストする(テキストにはないダイム、ダイムを足した)。

library ieee;
use ieee.std_logic_1164.all;

entity vending_machine_fsm_tb is
end;

architecture testbench of vending_machine_fsm_tb is
    signal clock: std_ulogic := '0';
    signal reset: std_ulogic;
    signal nickel_in: std_ulogic;
    signal dime_in: std_ulogic;
    signal dispense: std_ulogic;
    signal amount: std_ulogic_vector(3 downto 0);

    constant CLOCK_PERIOD: time := 20 ns; -- 50 MHz
    constant COMBINATIONAL_DELAY: time := 6 ns;
begin
    duv: entity work.vending_machine_fsm port map (
        clock => clock,
        reset => reset,
        nickel_in => nickel_in,
        dime_in => dime_in,
        dispense => dispense,
        amount => amount
    );

   -- 50 MHzクロックを生成
    clock <= not clock after CLOCK_PERIOD / 2;

    test_sequencer: process
        type test_data_type is record
            nickel: std_logic;
            dime: std_logic;
            dispense: std_logic;
            amount: std_logic_vector(3 downto 0);
        end record;
        type test_data_array_type is array (natural range <>) of test_data_type;

       -- クロック立ち上がりを待ってさらに信号が落ち着くのを少し待つためのプロシージャ
        procedure wait_clock_edge is
        begin
            wait until rising_edge(clock);
            wait for COMBINATIONAL_DELAY;
        end;

       -- デフォルト状態(全部ゼロ)にしてから少し待つためのプロシージャ
        procedure reset_with_default_inputs is
        begin
            nickel_in <= '0';
            dime_in <= '0';
            reset <= '0';
            wait_clock_edge;
        end;

       -- 全体をリセットして初期状態にあるかどうかを確かめるためのプロシージャ
        procedure test_reset is
        begin
            report "test_reset";
            reset_with_default_inputs;
            assert dispense = '0'
                report "dispense IS NOT 0 following reset"
                severity failure;
            assert amount = "0000"
                report "amount IS NOT 0000 following reset"
                severity failure;
        end;

       -- テストデータをDUVに与えるとともに、期待値と実際の出力とを照合するためのプロシージャ
        procedure test_input_sequence(name: string; seq: test_data_array_type) is
        begin
            report "test_input_sequence: " & name;
            reset_with_default_inputs;

            for i in seq'range loop

               -- テストデータをDUVに与えてクロックを待つ、をテストデータの数だけ繰り返す。
                nickel_in <= seq(i).nickel;
                dime_in <= seq(i).dime;
                wait_clock_edge;
                
               -- 期待値とDUVの出力とを照合する。
                assert dispense = seq(i).dispense
                    report "dispense IS NOT " & to_string(seq(i).dispense)
                    severity failure;
                assert amount = seq(i).amount
                    report "amount IS NOT " & to_string(seq(i).amount)
                    severity failure;
            end loop;
        end;

    begin
        test_reset; -- 全体をリセットして初期状態にあるかどうかを確かめて、

        test_input_sequence(
            name => "nickel, nickel, nickel",
            
           -- これが、ニッケルを3回連続して投入するときのテストデータと期待値
            seq => (
                (nickel => '1', dime => '0', dispense => '0', amount => 4d"05"),
                (nickel => '1', dime => '0', dispense => '0', amount => 4d"10"),
                (nickel => '1', dime => '0', dispense => '1', amount => 4d"15"),
                (nickel => '0', dime => '0', dispense => '0', amount => 4d"00")
            )
        );

        test_input_sequence(
            name => "nickel, dime",
            
           -- これが、ニッケル、ダイムの順に投入するときのテストデータと期待値
            seq => (
                (nickel => '1', dime => '0', dispense => '0', amount => 4d"05"),
                (nickel => '0', dime => '1', dispense => '1', amount => 4d"15"),
                (nickel => '0', dime => '0', dispense => '0', amount => 4d"00")
            )
        );

        test_input_sequence(
            name => "dime, dime",
            
           -- これが、ダイム、ダイムの順に投入するときのテストデータと期待値
            seq => (
                (nickel => '0', dime => '1', dispense => '0', amount => 4d"10"),
                (nickel => '0', dime => '1', dispense => '1', amount => 4d"15"),
                (nickel => '0', dime => '0', dispense => '0', amount => 4d"00")
            )
        );

        report "End of testbench. All tests completed successfully.";
        std.env.finish;
    end process;
end;

実行結果: