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
として行う。2**
・複数のプロセス文をまたがって、一つの変数への代入を行わないようにする。つまり、基本的に一つの変数は一つのプロセス中で処理しきること(唯一例外があって、3-state bufferを推定させる記述を使えばこの限りではない。そのためには、必要ないときに'Z'を代入してハイインピーダンスを作ってやる)
・[xilinxのwebiseの場合]stimulate behavioral modelをダブルクリックするときに、選択されているファイルがテストベンチであることを確認しないとまずい。uutを選んであると、入力がすべて無くなる。