2007年09月26日
VHDL TIPS 「双方向バスの遅延モデル」
FPGAとメモリモデルを接続する場合等に「双方向バスでネットの遅延を挿入したい」と思った事が何度かある。
安直に「after文」でお互いを代入しても'X'になった以降はずっと'X'のままでうまくいかない。
ではどうすれば双方向バスに遅延を挿入する事が出来るのだろうか?
簡単な様で、これが結構難しい。色々と考えた末に、双方向バスの遅延モデルを作成できたので紹介する。
まずは、モデルの動作を確認した回路。
双方向の遅延モデルだが、テストで入力は使用しないので出力のみを入れた。
このPORTAとPORTBに対してテストベンチから波形を入力する。
波形を入力するテストベンチはこれ。
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;
そして動作結果の波形はこんな感じ。
PORTAから出力した値は、すぐにPORTA_Sに反映されるがPORTB_Sには遅延が入っている事が分かる。
逆にPORTBから出力した値は、すぐにPORTB_Sに反映されるがPORTA_Sには遅延が入っている。
また、同時にPORTA_SとPORTB_Sの値を変化させても遅延時間分のショートが'X'で再現されている。
次に肝心なモデルのソースがこれ。
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 「アナログスイッチのモデリング」もそうだが、この手の手法はこの制限がつきまとってしまう。
この記事へのコメント
私は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のサンプルを参考にしました。
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;
なるほど!コマンドを解析して方向を切り替えるのですね。
一般的な双方向バスでは何らかの制御信号でアビトレーションするはずなので、それをモニタして方向を切り替えれば簡単にできそうです。
情報ありがとうございました。