FSMのためのテストベンチ / 自販機 / モジュラーテストベンチ

pp.551-555

  • stimulus generatorが、投入される一連のコインに相当するトランザクションを生成する。
  • ドライバーがコインを1個ずつDUVに与える。
  • スコアボードがDUVの入出力をモニタリングする。
  • スコアボード内部では、プレディクターがDUV出力の期待値を決定し、その値がコンパレーターによって実際の出力と照合される。
  • ニッケルのシーケンスが"10000"、ダイムのシーケンスが"01000"の場合は、最初のクロックでニッケルが投入され、次のクロックでダイムが投入される。これがdriverプロセスで実行される。
  • 5クロックで2つの1ビット(ニッケルとダイム)を投入するので、全部で210とおりの組み合わせ全部を試す。stimuli_generatorプロセスは、10ビット値"0000000000"~"1111111111"を生成して、前半5ビットと後半5ビットとをそれぞれニッケルのシーケンス、ダイムのシーケンスに割り振ることで、210とおりの組み合わせ全部を生成する。
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity vending_machine_fsm_tb is
end;

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

    constant CLOCK_PERIOD: time := 20 ns;
    constant COMBINATIONAL_DELAY: time := 6 ns;

    type coins_transaction_type is record
        nickels: std_logic_vector(1 to 5);
        dimes: std_logic_vector(1 to 5);
    end record;

    signal transaction_data: coins_transaction_type;
    signal transaction_done: boolean;

    procedure wait_clock_edge is begin
        wait until rising_edge(clock);
        wait for COMBINATIONAL_DELAY;
    end;
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
    );

    clock <= not clock after CLOCK_PERIOD / 2;
    reset <= '1', '0' after 50 ns;

    stimuli_generator: process
        variable seq: std_logic_vector(1 to 10);
    begin
        wait until not reset;

       -- Generate all possible 10-bit sequences
        for i in 0 to 2**10-1 loop
            seq := std_logic_vector(to_unsigned(i, 10));
           -- Break down each 10-bit sequence into sequences of nickels and dimes
            transaction_data <= (
                nickels => seq(1 to 5),
                dimes => seq(6 to 10)
            );
            wait on transaction_done'transaction;
        end loop;

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

    driver: process
    begin
        wait on transaction_data'transaction;

        for i in 1 to 5 loop
            nickel_in <= transaction_data.nickels(i);
            dime_in <= transaction_data.dimes(i);
            wait_clock_edge;
        end loop;

        transaction_done <= true;
    end process;

    response_checker: process
        variable amount_predicted, amount_duv: natural range 0 to 15;
        variable dispense_predicted: std_logic;
    begin
        wait_clock_edge;

       -- Predictor for the 'amount' output
        if reset = '1' or amount_predicted = 15 then
            amount_predicted := 0;
        elsif dime_in then
            amount_predicted := minimum(amount_predicted + 10, 15);
        elsif nickel_in then
            amount_predicted := amount_predicted + 5;
        end if;

       -- Predictor for the 'dispense' output
        amount_duv := to_integer(unsigned(testbench.amount));
        dispense_predicted := '1' when amount_duv = 15 else '0';

       -- Comparator - assert that DUV outputs and predictor outputs match
        assert amount_predicted = amount_duv
            report "amount IS NOT as expected: " & to_string(amount_duv)
            severity failure;
        assert dispense_predicted = dispense
            report "dispense IS NOT as expected: " & to_string(dispense)
            severity failure;
    end process;
end;