2007年09月26日

VHDL TIPS 「双方向バスの遅延モデル」

FPGAとメモリモデルを接続する場合等に「双方向バスでネットの遅延を挿入したい」と思った事が何度かある。
安直に「after文」でお互いを代入しても'X'になった以降はずっと'X'のままでうまくいかない。
ではどうすれば双方向バスに遅延を挿入する事が出来るのだろうか?
簡単な様で、これが結構難しい。色々と考えた末に、双方向バスの遅延モデルを作成できたので紹介する。

まずは、モデルの動作を確認した回路。
双方向の遅延モデルだが、テストで入力は使用しないので出力のみを入れた。
このPORTAとPORTBに対してテストベンチから波形を入力する。

linedelay_c

 



波形を入力するテストベンチはこれ。


library IEEE;
use IEEE.std_logic_1164.all;

entity TEST is
end TEST;

architecture BEHAVIOR of TEST is

    component LINEDELAY
        generic
        (
            linedelay       : time
        );
        port
        (
            PORTA           : inout std_logic;
            PORTB           : inout std_logic
        );
    end component;

    signal  PORTA   : std_logic;
    signal  PORTB   : std_logic;
    signal  PORTA_S : std_logic;
    signal  PORTB_S : std_logic;

begin

    C_LINEDELAY : LINEDELAY
    generic map
    (
        linedelay       => 10 ns
    )
    port map
    (
        PORTA           => PORTA_S,
        PORTB           => PORTB_S
    );

    process begin
        PORTA <= 'Z';
        PORTB <= 'Z';
        wait for 100 ns;
        PORTA <= '1';
        wait for 100 ns;
        PORTA <= '0';
        wait for 100 ns;
        PORTA <= 'Z';
        PORTB <= '1';
        wait for 100 ns;
        PORTB <= '0';
        wait for 100 ns;
        PORTB <= 'Z';
        wait for 100 ns;
        PORTA <= '1';
        PORTB <= '0';
        wait for 100 ns;
        PORTA <= '0';
        PORTB <= '1';
        wait for 100 ns;
        PORTA <= '1';
        wait for 100 ns;
        PORTA <= '0';
        PORTB <= '0';
        wait for 100 ns;
    end process;

    PORTA_S <= PORTA;
    PORTB_S <= PORTB;

end BEHAVIOR;


そして動作結果の波形はこんな感じ。

linedelay_w

 

PORTAから出力した値は、すぐにPORTA_Sに反映されるがPORTB_Sには遅延が入っている事が分かる。
逆にPORTBから出力した値は、すぐにPORTB_Sに反映されるがPORTA_Sには遅延が入っている。
また、同時にPORTA_SとPORTB_Sの値を変化させても遅延時間分のショートが'X'で再現されている。

次に肝心なモデルのソースがこれ。


library IEEE;
use IEEE.std_logic_1164.all;

entity LINEDELAY is
    generic
    (
        linedelay       : time
    );
    port
    (
        PORTA           : inout std_logic;
        PORTB           : inout std_logic
    );
end LINEDELAY;

architecture BEHAVIOR of LINEDELAY is

    signal  PORTA_X     : std_logic;
    signal  PORTB_X     : std_logic;

begin

    PROCESS
        VARIABLE last_time : time;
        VARIABLE transact : boolean;
    BEGIN
            WAIT ON PORTA'TRANSACTION, PORTB_X UNTIL (last_time /= NOW or PORTB_X'EVENT);

            if ( PORTA'TRANSACTION'EVENT ) then
                PORTA <= 'Z';
                transact := TRUE;
                last_time := NOW;
            else
                transact := FALSE;
            end if;

            WAIT FOR 0 ns;

            if ( transact = TRUE ) then
                PORTA_X <= transport PORTA after linedelay;
            end if;
            PORTA <= PORTB_X;

    END PROCESS;

    PROCESS
        VARIABLE last_time : time;
        VARIABLE transact : boolean;
    BEGIN
            WAIT ON PORTB'TRANSACTION, PORTA_X UNTIL (last_time /= NOW or PORTA_X'EVENT);

            if ( PORTB'TRANSACTION'EVENT ) then
                PORTB <= 'Z';
                last_time := NOW;
                transact := TRUE;
            else
                transact := FALSE;
            end if;

            WAIT FOR 0 ns;

            if ( transact = TRUE ) then
                PORTB_X <= transport PORTB after linedelay;
            end if;
            PORTB <= PORTA_X;

    END PROCESS;

end BEHAVIOR;


PORTA_XとPORTB_Xに、それぞれの双方向ポートの値をコピーし、それを遅延させて反対側のポートに代入している。
この双方向ポートの値をコピーする時、一度モデル側のドライバを'Z'にして本当の相手側ポートの値を取得する必要がある。
また、変な「WAIT文」、これは双方向ポートのトランザクションは時間単位あたり1度しか動作させたく無いのに対し反対側の双方向ポートをコピーした値のイベントは何回でも動作させるための条件だ。
双方向ポートのトランザクションを時間単位あたり1度しか動作させたくない理由は、自分でトランザクションを発生させているためこの条件が無いとループに陥ってしまうからである。
ただ、双方向ポートの一番最初のトランザクションで動作させているため、トランザクションが発生したデルタ時間の後のデルタ時間に値が変化するような信号ではうまく動作しない。
尚、以前紹介したVHDL TIPS 「アナログスイッチのモデリング」もそうだが、この手の手法はこの制限がつきまとってしまう。



投稿時刻(21:44)│コメント(3)VHDL 

この記事へのコメント

1. Posted by marsee   2007年09月27日 18:14
こんにちは。

私はFPGAとSDRAMモデルをつなぐときには、FPAGのデータの信号とSDRAMのデータの信号を定義して、それぞれ遅延してつないでいます。(遅延時間が短いため、慣性遅延で書いてあります)

constant delay_time : time := 1.5 ns;
signal ddr_dq_sdram, ddr_dq_fpga : std_logic_vector(15 downto 0);

begin

ddr_dq_fpga <= ddr_dq_sdram after delay_time when enable_o='1' else (others => 'Z');
ddr_dq_sdram <= ddr_dq_fpga after delay_time when enable_o='0' else (others => 'Z');

DDRtest_inst : DDRtest port map( -- DDR SDRAMコントローラ
.......
sd_dq => ddr_dq_fpga,
.......
);

MT46V16M16_inst : MT46V16M16 port map( -- DDR SDRAM
...........
Dqs => ddr_dqs_sdram,
...........
);

確か、Xilinxのサンプルを参考にしました。
2. Posted by marsee   2007年09月27日 18:15
enableのロジックを書くのを忘れていました。
CMD <= ( ddr_rasb & ddr_casb & ddr_web );

process(ddr_clk, reset) begin
if reset='1' then
enable_o <= '0';
elsif ddr_clk'event and ddr_clk='1' then
if CMD = "100" then -- Write
enable_o <= '0';
elsif CMD = "101" then -- Read
enable_o <= '1';
else
enable_o <= enable_o;
end if;
end if;
end process;
3. Posted by たーぼ   2007年09月28日 19:52
こんにちは。
なるほど!コマンドを解析して方向を切り替えるのですね。
一般的な双方向バスでは何らかの制御信号でアビトレーションするはずなので、それをモニタして方向を切り替えれば簡単にできそうです。
情報ありがとうございました。

この記事にコメントする

名前:
URL:
  情報を記憶: 評価: 顔   
 
 
 
最新コメント
カテゴリ別表示
月別表示