2007年07月27日

シミュレーションデルタ

VHDLを理解する上で「シミュレーションデルタ」は重要な項目だと思う。
しかし、理解せずに設計を行っても、設計/シミュレーションが問題なく行える事が多く存在自体を知らない人もいる(かつての私...)。
しかし、これを理解出来ていないとシミュレーションと実機の動作不一致を引き起こす事がある。

以下はその例だ。


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;

entity DD_TEST is
end DD_TEST;

architecture behavior  of DD_TEST is

    signal  CLK0    : std_logic;
    signal  CLK1    : std_logic;
    signal  COUNT   : std_logic_vector(3 downto 0) := (others => '0');
    signal  CNT_OK  : std_logic_vector(3 downto 0) := (others => '0');
    signal  CNT_NG  : std_logic_vector(3 downto 0) := (others => '0');

begin

    -- クロック生成
    process begin
        CLK0 <= '0';
        wait for 10 ns;
        CLK0 <= '1';
        wait for 10 ns;
    end process;

    -- クロックを複製
    CLK1 <= CLK0;

    -- CLK0でCOUNTをインクリメント
    process ( CLK0 ) begin
        if ( CLK0'event and CLK0 = '1' ) then
            COUNT <= COUNT + '1';
        end if;
    end process;

    -- COUNTをCLK0で1段遅らせる
    process ( CLK0 ) begin
        if ( CLK0'event and CLK0 = '1' ) then
            CNT_OK <= COUNT;
        end if;
    end process;

    -- COUNTをCLK1で1段遅らせる
    process ( CLK1 ) begin
        if ( CLK1'event and CLK1 = '1' ) then
            CNT_NG <= COUNT;
        end if;
    end process;

end behavior ;



同じ「COUNT」を「CLK0 」と「CLK1 」で1クロック遅延させたのだが結果は以下のように「CNT_OK」と「CNT_NG 」で異なる。

delta_out.gif

 

 


実機では期待通りの動作を行うのでシミュレーションと実機の動作不一致が起こっている。

なぜこのような結果になるかというと、
・CLK0とCLK1は同じように見えるが、信号の代入によってシミュレータの内部では1デルタサイクルのずれ(デルタ遅延)が発生している。
・CLK0でインクリメントしたCOUNTも、CLK0に比べて1デルタサイクルずれている。
ここでCNT_NGはCNT1の立ち上がりエッジ(CLK0の立ち上がりエッジ処理の次のデルタサイクル)で処理されるが、シミュレーションサイクルの処理規定により、この時既にCOUNTが更新されてしまっている。

前例は、故意に動作不一致を起こすコードを記述したが、次の例ではVHDLの記述制約のために動作不一致を起こした例を紹介する。


library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.std_logic_unsigned.all;

library unisim;
use unisim.vcomponents.all;

-- クロックバッファを入れてクロックイネーブルを生成するモジュール
entity CLKGEN is
    port
    (
        CLK_IN      : in    std_logic;
        CLK_OUT     : out   std_logic;
        CKE         : out   std_logic
    );
end CLKGEN;

architecture behavior  of CLKGEN is

    signal  CLK_BUFF    : std_logic;
    signal  COUNT       : std_logic_vector(3 downto 0) := (others => '0');

begin

    -- Xilinxのグローバルクロックバッファ
    U_BUFG : BUFG port map ( I => CLK_IN, O => CLK_BUFF );
    -- VHDLの制約(portでoutに指定した信号は内部で参照出来ない)のため、
    -- クロックイネーブルを作るクロックを一度、ローカル信号に入れる。

    -- クロックの出力
    CLK_OUT <= CLK_BUFF;

    -- クロックイネーブルを作るためのカウンタ
    process ( CLK_BUFF ) begin
        if ( CLK_BUFF'event and CLK_BUFF = '1' ) then
            COUNT <= COUNT + '1';
        end if;
    end process;

    -- クロックイネーブル
    process ( CLK_BUFF ) begin
        if ( CLK_BUFF'event and CLK_BUFF = '1' ) then
            if ( COUNT = (COUNT'range => '0') ) then
                CKE <= '1';
            else
                CKE <= '0';
            end if;
        end if;
    end process;

end behavior ;


このコンポーネントは、あるFPGAのクロック関連の回路を生成するために作られたものだ。このように関連性のある回路を一つのコンポーネントにまとめる事は頻繁に行われていると思う。
ソースコード中のコメントにもあるように、VHDLではポートに出力する信号は内部で参照出来ない制約がある。そのためこの例の場合、出力するクロックと内部で使用するクロックが1デルタサイクルずれて、このコンポーネントから出力するクロックでクロックイネーブルを使用するとシミュレーションと実機の動作不一致を起こす。(ただしこの例ではクロックイネーブルの位相は動作に関係ないので事実上問題はないが)

ちなみに、以前紹介したソースコードで
   wait for 0 ns;
のような記述をしたが、これはデルタ遅延を発生させている。



投稿時刻(21:19)│コメント(2)VHDL 

この記事へのコメント

1. Posted by marsee   2007年07月28日 13:22
説明を読んでよく理解できました。
デルタ遅延には、悩まされることが良くあります。シミュレーション時にだけ遅延を入れたりしています。出力はなるべく、必要がなくても内部信号にいったん代入してから出すようにしています。内部信号を使用する出力信号とデルタ遅延をあわせるため。
2. Posted by たーぼ   2007年07月30日 13:15
私も問題が起こりそうな所は内部でデルタ遅延をあわせて出力するようにしています。
昔、この問題でシミュレーションと実機の動作不一致が起こりModelsimのバグかと思うほど悩み実機あわせをした経験があります。
そして最近、新人君のソースコードでこの問題が起こりそうな記述を見つけて説明しようと思ったのですが、記述方法にプライドがあるらしく聞き入れてもらえませんでした...

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
最新コメント
ブログ内検索
カテゴリ別表示
月別表示
問い合わせ・連絡先
ta_bo__@livedoor.com

本サイトは以下のアクセス解析を使用しております。
基礎化粧品