procedure for testbench

procedureを使うと、シミュレーションが楽に書ける。

architecture test of test_module is

procedure pe(signal CLK : in std_logic) is
begin
    wait until CLK'event and CLK='1';
end pe;

procedure pe( num : in integer;
                          signal CLK : in std_logic) is
begin
    for I in 1 to num loop
        wait until CLK'event and CLK='1';
    end loop;
end pe;

begin
end test;

注意するのは"signal"の記述で、これが
ある→procedureの中でその信号の変化を読める(この場合だと、CLKの変化を読んでいる。一方、numは「変化」を読んでいる訳ではない。
ない→procedureの中でその信号の変化は読まない。
ということ。
もうちょっと巨大なprocedureとしては、こんなのがある。

architecture test of test_module is

    procedure write_reg(
                                        signal CLK : in std_logic;
                                        Rd_dt_in : in std_logic_vector(31 downto 0);
                                        Rd_dt_out : out std_logic_vector(31 downto 0);
                                        signal Sof1_out : out std_logic) is
    constant dly_rd_dt : time := 1ns;
    constant dly_Sof1 : time := 2ns;

    begin
        wait until CLK'event and CLK='1';
            Rd_dt_out<=Rd_dt_in after dly_rd_dt;
       wait until CLK'event and CLK='1';
           Sof1_out<='0' after dly_Sof1;
       wait until CLK'event and CLK='1';
           Sof1_out<='1' agter dly_Sof1;
    end ;

begin
...
...
end test;

とかで書き込める。

state machineの作り方

ポイントは三つ。
1、state のタイプ宣言は以下の通り。

type STATE is ( STA0 , STA1 , STA2 );
signal CURRENT_STATE,NEXT_STATE : STATE;

現在のステートと次のステートを作っておく。
「次」とは、次のクロックのことを指す。

2、clkでステートを入れ替える。

3、(NEXT_STATEを作る+current stateに応じた出力を出す)processのセンシティビティリストは、
・CURRENT_STATE
・CURRENT_STATEに加えて、次のSTATEを決定するのに必要なsignal
を入れる。
あと、忘れがちなcase記法を復習しておく。

case STATE is
    when <STATE1> =>
        ....
    when <STATE2> =>
        ....
        ....
    when others =>
        ....
end case;

ここの when は、以下のように書くことも許されている。

when <STATE1>|<STATE2>|...|<STATEn> =>....
when <STATE1> to <STATEn> =>....

クロックを入れたい場合(たとえばstate Aに入ってから10クロック目にstate Bに移行したい)は、以下のようにする。

architecture....
    ....
    signal count : Integer range 0 to 15;
    ....
begin
    ....
--counter process
process(clk,reset)begin
    if(reset='1')then
        count<=0;
    elsif(clk'event and clk='1')then
        if(CURRENT_STATE=stateA)then
            count<=count+1;
        else
            count<=0;--important!!
        end if;
    end if;    
end process;

process(CURRENT_STATE,sig1,sig2,count)begin
    case CURRENT_STATE is
        when stateA =>
            ....
            if(count=10)then
                NEXT_STATE<=stateB;
            else
                NEXT_STATE<=stateA;
            end if;
        when stateB=>
            ....
    end case;
end process;

まとめて一例を。

entity MULSTATE is
    port( clk,reset : std_logic;
             sig : std_logic;
             out1,out2 : std_logic);
end MULSTATE;

architecture RTL of MULSTATE is
--state definition
type STATE is (STA0,STA1,STA2);
signal CURRENT_STATE,NEXT_STATE : STATE;

begin

--Flip Flop for remember the state.
process ( clk , reset ) begin
    if(reset='1')then 
        CURRENT_STATE<=STA0;  --load the initial state
    elsif ( clk'event and clk ='1')then
        CURRENT_STATE<=NEXT_STATE;-- load the next state
    end if;
end process;

--state exchange process
process(CURRENT_STATE,sig)begin
    case CURRENT_STATE is
        when STA0 =>
            out1<='0';
            out2<='0';--output some signals to the external module
--set next state
            if(sig='0')then
                NEXT_STATE<=STA2;
            else
                NEXT_STATE<=STA1;
            end if;
        when STA1=>
            out1<='1';
            out2<='0';--output some signals to the external module
--set next state
            if(sig='0')then
                NEXT_STATE<=STA0;
            else
                NEXT_STATE<=STA2;
            end if;
        when STA2=>
            out1<='1';
            out2<='1';--output some signals to the external module
--set next state
            if(sig='0')then
                NEXT_STATE<=STA1;
            else
                NEXT_STATE<=STA0;
            end if;
    end case;
end process;

end RTL;

bit列の一斉初期化

Y<=( 3=>0, 2=>0, 1=>0, 0=>0); 

の省略として、次のように書ける。

Y<=(3 downto 0 => 0);

もしくは、othersを使って、

Y<=(others=>0);

と書く。
これらはもちろん、テストベンチの中でしか使えない。。。というか、使わないことにしておくこと。
唯一VHDL中で使えるのは、constant宣言の時のみで、

...
signal ...
...
constant <constant name> : STD_LOGIC_VECTOR( <length> downto 0 ) :=(others=>'0');
...
begin
... 

などとして使える。

TestBenchの一例

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.std_logic_unsigned.all;
USE ieee.numeric_std.ALL;
 
ENTITY testbench IS
END testbench;
 
ARCHITECTURE behavior OF testbench IS 
 
 
    COMPONENT syncFIFO
    PORT(
         clk : IN  std_logic;
         reset : IN  std_logic;
         din : IN  std_logic_vector(3 downto 0);
         dout : OUT  std_logic_vector(3 downto 0);
         rd : IN  std_logic;
         wr : IN  std_logic;
         full : OUT  std_logic;
         empty : OUT  std_logic
        );
    END COMPONENT;
    


   signal clk : std_logic := '0';
   signal reset : std_logic := '0';
   signal din : std_logic_vector(3 downto 0) := (others => '0');
   signal rd : std_logic := '0';
   signal wr : std_logic := '0';


   signal dout : std_logic_vector(3 downto 0);
   signal full : std_logic;
   signal empty : std_logic;


   constant clk_period : time := 10ns;
 
BEGIN
 
   uut: syncFIFO PORT MAP (
          clk => clk,
          reset => reset,
          din => din,
          dout => dout,
          rd => rd,
          wr => wr,
          full => full,
          empty => empty
        );

 
   clk_process :process
   begin
		clk <= '0';
		wait for clk_period/2;
		clk <= '1';
		wait for clk_period/2;
   end process;
 
   process
   begin		
      wait for clk_period;
		
		wait until (clk'event and clk='1');wait for 1ns;
			reset<='1';
		wait until (clk'event and clk='1');wait for 1ns;
			reset<='0';
		
		wr<='1';
		rd<='0';
		din<="0000";
		for i in 0	to 10 loop
			wait until (clk'event and clk='1');wait for 1ns;
			din<=din+'1';
		end loop;
		
		wr<='0';
		rd<='1';
		for i in 0	to 10 loop
			wait until (clk'event and clk='1');wait for 1ns;
		end loop;	
		
		wr<='1';
		rd<='0';
		din<="0000";
		for i in 0	to 10 loop
			wait until (clk'event and clk='1');wait for 1ns;
			din<=din+'1';
		end loop;
		
		wr<='0';
		rd<='1';
		for i in 0	to 10 loop
			wait until (clk'event and clk='1');wait for 1ns;
		end loop;	

		wr<='1';
		rd<='1';
		din<="0000";
		for i in 0	to 20 loop
			wait until (clk'event and clk='1');wait for 1ns;
			din<=din+'1';
		end loop;

    wait;
   end process;

END;

FPGAメモ

・「カウンタを作るとき、最大値を超えたら勝手に元に戻るだろう」という発想は危険。きちんと初期値に戻るよう記述してあげないと(何故か)シミュレーションが途中で止まる。

・Integerの範囲指定は、
signal : INTEGER range 0 to 2** -1 := ;
として行う。2**を忘れてとかにしがち。

・複数のプロセス文をまたがって、一つの変数への代入を行わないようにする。つまり、基本的に一つの変数は一つのプロセス中で処理しきること(唯一例外があって、3-state bufferを推定させる記述を使えばこの限りではない。そのためには、必要ないときに'Z'を代入してハイインピーダンスを作ってやる)

・[xilinxのwebiseの場合]stimulate behavioral modelをダブルクリックするときに、選択されているファイルがテストベンチであることを確認しないとまずい。uutを選んであると、入力がすべて無くなる。