FC2カウンター FPGAの部屋 VHDLの共有変数を使用したシミュレーション
FC2ブログ

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

VHDLの共有変数を使用したシミュレーション

たーぼ のハードウェア設計記録さんのVHDL TIPS 「shared variableの使用法」で、VHDLの共有変数を学んだ。たーぼさんに深く感謝いたします。

それを見て、自分のシミュレーションの問題点をVHDLの共有変数を使用して解決しようと思った。
シミュレーションの問題点はこうだ。作業の終了をプロセッサやPCIバス経由でレジスタを読み出すことでしか知ることができない。シミュレーションでは一回シミュレーションを行って、終了した時間を過ぎたころに次のアクションを行っている。本当はプロセッサモデル、PCIモデルを駆動して作業終了レジスタをチェックするのだが、テストベンチを作るのが面倒くさい。作業終了レジスタの値を直接テストベンチから確認できれば、都合が良い。
そういう理由で検証用のコードを書いて、やってみた。
まずは、共有変数を宣言するパッケージから。

library ieee;
use ieee.std_logic_1164.all;
package shared_pkg is
    shared variable SH_DONE : std_logic;
end shared_pkg;


次に本体。プロセッサ周辺IOとしてのカウンタをイメージ。
addressを0にしたときにinput_dataにカウント値を入れて、weを1にしてカウント値をカウンタにロード。
addressを1に、input_data(0)(startビット)を1に、weを1にして書き込むとカウントがスタート。
終了はaddressを1にしてoutput_data(7)(doneビット)を読んだときに1ならば終了。(done='1')
addressを0にして、output_dataを読むと残りのカウントが読める。
ここではdoneビットを共有変数SH_DONEにコピーしておく。

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
-- pragma translate_off
library work;
use work.shared_pkg.all;
-- pragma translate_on

entity test_main is
    port(
        clk : in std_logic;
        reset : in std_logic;
        address : in std_logic;
        input_data : in std_logic_vector(7 downto 0);
        we : in std_logic;
        output_data : out std_logic_vector(7 downto 0)
    );
end test_main;

architecture RTL of test_main is
-- アドレスマップ
-- address=0 : カウント値:スタートの後でクロックごとに減算する。0になったら停止。
-- address=1 : bit0 - start, bit7 - done
signal count, cnt_val : std_logic_vector(7 downto 0);
signal start, done : std_logic;
begin
    start <= '1' when address='1' and we='1' and input_data(0)='1' else '0';
    
    process(reset, clk) begin -- カウント値を保持する
        if reset='1' then
            cnt_val <= (others => '0');
        elsif clk'event and clk='1' then
            if address='0' and we='1' then
                cnt_val <= input_data;
            end if;
        end if;
    end process;
    
    process(reset, clk) begin
        if reset='1' then
            count <= (others => '0');
        elsif clk'event and clk='1' then
            if start='1' then -- カウント値をロード
                count <= cnt_val;
            elsif count/=0 then -- 0でなかったら減算
                count <= count - 1;
            end if;
        end if;
    end process;
    done <= '1' when count=0 else '0';
    -- pragma translate_off
    process(done) begin
        if done'event and done='1' then
            SH_DONE := '1';
        elsif done'event and done='0' then
            SH_DONE := '0';
        end if;
    end process;
    -- pragma translate_on
    
    process(count, address, done) begin
        if address='0' then
            output_data <= count;
        else
            output_data <= done & "0000000";
        end if;
    end process;
end RTL;


次にテストベンチ。
テストベンチはカウント値に32を書いて、スタートさせる。(startビットを1に)
共有変数SH_DONEが1になった(カウントが終了)のを確認後、カウント値に16を書いて、スタートさせる。
共有変数SH_DONEが1になった(カウントが終了)のを確認。

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
library work;
use work.shared_pkg.all;

entity Shared_test_tb is
end Shared_test_tb;

architecture tb of Shared_test_tb is
component test_main
    port(
        clk : in std_logic;
        reset : in std_logic;
        address : in std_logic;
        input_data : in std_logic_vector(7 downto 0);
        we : in std_logic;
        output_data : out std_logic_vector(7 downto 0)
    );
end component;
signal clk : std_logic := '0';
signal reset : std_logic := '1';
signal address : std_logic := '0';
signal input_data : std_logic_vector(7 downto 0) := X"00";
signal we : std_logic := '0';
signal output_data : std_logic_vector(7 downto 0) := X"00";
begin
    Inst_test_main : test_main port map(
        clk => clk,
        reset => reset,
        address => address,
        input_data => input_data,
        we => we,
        output_data => output_data
    );
    
    clk <= not clk after 10 ns;
    
    process begin
        wait for 20 ns;
        reset <= '0';
        
        wait for 20 ns;
        input_data <= CONV_STD_LOGIC_VECTOR(32,8); -- 32カウント
        we <= '1';
        
        wait for 20 ns;
        we <= '0';
        
        wait for 20 ns;
        address <= '1';
        input_data <= "00000001"; -- start
        we <= '1';
        
        wait for 20 ns;
        we <= '0';
        
        while SH_DONE/='1' loop
            wait for 1 ns; -- 終了まで待つ
        end loop;
        wait until clk'event and clk='0'; -- クロック0イベントまで待つ
         
        wait for 20 ns;
        address <= '0';
        input_data <= CONV_STD_LOGIC_VECTOR(16,8); -- 16カウント
        we <= '1';
        
        wait for 20 ns;
        we <= '0';
        
        wait for 20 ns;
        address <= '1';
        input_data <= "00000001"; -- start
        we <= '1';
        
        wait for 20 ns;
        we <= '0';
        
        while SH_DONE/='1' loop
            wait for 1 ns; -- 終了まで待つ
        end loop;
        wait until clk'event and clk='0'; -- クロック0イベントまで待つ
        
        wait for 500 ns;
    end process;
end tb;


これで、うまく行ったようだ。下の階層のdoneビットを見ることができた。このくらいならばaddressを1にしておいて、doneビットを見ればよいのだが、PCIバス・プロトコルやPPCプロセッサ・バスプロトコルなどでは、モデルを駆動しないと見えない。
うまくいけそうです。
Shared_ModelSim_070912.png

2007/09/14:追記
たーぼさんからコメントで init_signal_spyの紹介があった。ModelSimで階層をまたいで、内部の階層のsignalを上位階層のsignalに値をミラーできるそうだ。これはModelSimのUtilパッケージの一部ということだった。本来、ModelSimで下位階層のsignalの値を見るには、init_signal_spyを使用するのだろうが、フルパスで書かなくてはいけないそうなので、結構面倒くさい。直接signalが見えるというのは、魅力的だが、とりあえず共有変数で書くことにする。
  1. 2007年09月13日 20:40 |
  2. シミュレーション
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


管理者にだけ表示を許可する

トラックバック URL
http://marsee101.blog.fc2.com/tb.php/575-91e96abb
この記事にトラックバックする(FC2ブログユーザー)