FPGAの部屋 FPGAチップ内の配線方法
FC2ブログ

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

FPGAの部屋

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

DCMカスケード接続の罠(CLKDV編)2(解決編)

DCMカスケード接続の罠(CLKDV編)”で、DCMをカスケードする場合に1番目のDCMのLOCKED信号を反転して3クロック遅延して、2番目のDCMのリセットに入れる必要があることを学んだ。しかし、そのVHDLコードを書いて置かなかったので、備忘録として書いておく。(シミュレータはISE13.1のISim、FPGAはVirtex-5, xc5vlx110t-1ff1136使用)

最初に参考資料を示しておく。
1.”Virtex-II/-II Pro/-4/-5、Spartan-3/-3E/-3A/-3ADSP - 2 つの DCM を直列にカスケード接続する場合のルール
2.Virtex-4については、”Virtex-4 FPGA User Guide UG070 (v2.6) December 1, 2008”75ページ、Figure 2-5: Cascading DCMs

DCMモジュールのVHDLソースから示す。100MHzクロックをDCMで1/4倍(clkdv使用)して、2番目のDCMに入力し、25MHzの0度クロックと90度クロックを生成する。

-- DCM module

library ieee;
use ieee.std_logic_1164.all;
-- pragma translate_off
library unisim;
use unisim.vcomponents.all;
-- pragma translate_on
library work;

entity dcm_inst is
    port (
        clkin : in std_logic;
        reset : in std_logic;
        clkout : out std_logic;
        clk90 : out std_logic;
        locked : out std_logic
    );
end dcm_inst;

architecture RTL of dcm_inst is
component DCM
  generic (
     CLKDV_DIVIDE : real := 2.0;
     CLKFX_DIVIDE : integer := 1;
     CLKFX_MULTIPLY : integer := 4;
     CLKIN_DIVIDE_BY_2 : boolean := false;
     CLKIN_PERIOD : real := 10.0;
     CLKOUT_PHASE_SHIFT : string := "NONE";
     CLK_FEEDBACK : string := "1X";
     DESKEW_ADJUST : string := "SYSTEM_SYNCHRONOUS";
     DFS_FREQUENCY_MODE : string := "LOW";
     DLL_FREQUENCY_MODE : string := "LOW";
     DSS_MODE : string := "NONE";
     DUTY_CYCLE_CORRECTION : boolean := true;
     FACTORY_JF : bit_vector := X"C080";
     PHASE_SHIFT : integer := 0;
     SIM_MODE : string := "SAFE";
     STARTUP_WAIT : boolean := false
  );
    port ( CLKIN     : in  std_logic;
           CLKFB     : in  std_logic;
           DSSEN     : in  std_logic;
           PSINCDEC  : in  std_logic;
           PSEN      : in  std_logic;
           PSCLK     : in  std_logic;
           RST       : in  std_logic;
           CLK0      : out std_logic;
           CLK90     : out std_logic;
           CLK180    : out std_logic;
           CLK270    : out std_logic;
           CLK2X     : out std_logic;
           CLK2X180  : out std_logic;
           CLKDV     : out std_logic;
           CLKFX     : out std_logic;
           CLKFX180  : out std_logic;
           LOCKED    : out std_logic;
           PSDONE    : out std_logic;
           STATUS    : out std_logic_vector(7 downto 0)
          );
end component;
component BUFG 
  port (
    I   : in std_logic;
    O    : out std_logic
    ); 
end component;
component IBUFG 
  port (
    I   : in std_logic;
    O    : out std_logic
    ); 
end component;

signal clk_ibuf : std_logic;
signal clkfb : std_logic;
signal gnd, clk_node : std_logic;
signal clkdv_node, clkdv_bufg : std_logic;
signal clk_node2, clk90_node : std_logic;
signal clk90_bufg, clk_bufg : std_logic;
signal dcm1_locked : std_logic;
signal dcm2_reset : std_logic;
begin
    gnd <= '0';
    
    ibufg_inst : ibufg port map(
        i => clkin,
        o => clk_ibuf
    );
    
    dcm1 : dcm generic map(
        CLKFX_DIVIDE => 4,
        CLKFX_MULTIPLY => 1,
        CLKDV_DIVIDE => 4.0
    )port map(
        clkin => clk_ibuf,
        clkfb => clkfb,
        dssen => gnd,
        psincdec => gnd,
        psen => gnd,
        psclk => clk_ibuf,
        rst => reset,
        clk0 => clk_node,
        clk90 => open,
        clk180 => open,
        clk270 => open,
        clk2x => open,
        clk2x180 => open,
        clkdv => clkdv_node,
        clkfx => open,
        clkfx180 => open,
        locked => dcm1_locked,
        psdone => open,
        status => open
    );
    
    bufg_inst : bufg port map(
        i => clk_node,
        o => clkfb
    );
--    clkout <= clkfb;
    
    bufg_clkdv : bufg port map(
        i => clkdv_node,
        o => clkdv_bufg
    );
    dcm2_reset <= not dcm1_locked;
    
    dcm2 : dcm generic map(
        CLKDV_DIVIDE => 4.0
    )port map(
        clkin => clkdv_bufg,
        clkfb => clk_bufg,
        dssen => gnd,
        psincdec => gnd,
        psen => gnd,
        psclk => clk_ibuf,
        rst => dcm2_reset,
        clk0 => clk_node2,
        clk90 => clk90_node,
        clk180 => open,
        clk270 => open,
        clk2x => open,
        clk2x180 => open,
        clkdv => open,
        clkfx => open,
        clkfx180 => open,
        locked => locked,
        psdone => open,
        status => open
    );
    bufg_clk0_2 : bufg port map(
        i => clk_node2,
        o => clk_bufg
    );
    bufg_clk90 : bufg port map(
        i => clk90_node,
        o => clk90_bufg
    );
    
    clkout <= clk_bufg;
    clk90 <= clk90_bufg;
        
end RTL;


dcm1のリセットを反転して、dcm2のrst端子に入れてシミュレーションすると、下のようにdcm2のLOCKED信号が不定になってしまう。
Cascading_DCMs_110415.png


次に、SRL16プリミティブを使用して3クロック遅延させる。”Spartan-3デバイスでのシフト レジスタ(SRL 16)としてのルックアップテーブルの使用”参照。

(2011/04/23:修正)
3クロック遅延させるためには、A0=1, A1=1, A2=0, A3=0 とすれば良い基本的には3クロックだが、その下の修正コードでは、元のクロックにSRL16Eのクロックを接続しているので、4分周*3クロック=12クロック遅延させる。A0=0, A1=0, A2=1, A3=1。(下記にVirtex-5 ライブラリ ガイド (HDL 用)UG621 (v13.1) 2011 年 3 月 1 日の371ページから引用したSRL16Eのインスタンス例を示す)

SRL16E_inst : SRL16E
generic map (
 INIT => X"0000")
port map (
 Q => Q, -- SRL data output
 A0 => A0, -- Select[0] input
 A1 => A1, -- Select[1] input
 A2 => A2, -- Select[2] input
 A3 => A3, -- Select[3] input
 CE => CE, -- Clock enable input
 CLK => CLK, -- Clock input
 D => D -- SRL data input
);


これを参考に、上のVHDLコードを修正した。VHDLを下に示す。

-- DCM module

library ieee;
use ieee.std_logic_1164.all;
library unisim;
use unisim.vcomponents.all;
library work;

entity dcm_inst is
    port (
        clkin : in std_logic;
        reset : in std_logic;
        clkout : out std_logic;
        clk90 : out std_logic;
        locked : out std_logic
    );
end dcm_inst;

architecture RTL of dcm_inst is
component DCM
  generic (
     CLKDV_DIVIDE : real := 2.0;
     CLKFX_DIVIDE : integer := 2;
     CLKFX_MULTIPLY : integer := 4;
     CLKIN_DIVIDE_BY_2 : boolean := false;
     CLKIN_PERIOD : real := 10.0;
     CLKOUT_PHASE_SHIFT : string := "NONE";
     CLK_FEEDBACK : string := "1X";
     DESKEW_ADJUST : string := "SYSTEM_SYNCHRONOUS";
     DFS_FREQUENCY_MODE : string := "LOW";
     DLL_FREQUENCY_MODE : string := "LOW";
     DSS_MODE : string := "NONE";
     DUTY_CYCLE_CORRECTION : boolean := true;
     FACTORY_JF : bit_vector := X"C080";
     PHASE_SHIFT : integer := 0;
     SIM_MODE : string := "SAFE";
     STARTUP_WAIT : boolean := false
  );
    port ( CLKIN     : in  std_logic;
           CLKFB     : in  std_logic;
           DSSEN     : in  std_logic;
           PSINCDEC  : in  std_logic;
           PSEN      : in  std_logic;
           PSCLK     : in  std_logic;
           RST       : in  std_logic;
           CLK0      : out std_logic;
           CLK90     : out std_logic;
           CLK180    : out std_logic;
           CLK270    : out std_logic;
           CLK2X     : out std_logic;
           CLK2X180  : out std_logic;
           CLKDV     : out std_logic;
           CLKFX     : out std_logic;
           CLKFX180  : out std_logic;
           LOCKED    : out std_logic;
           PSDONE    : out std_logic;
           STATUS    : out std_logic_vector(7 downto 0)
          );
end component;
component BUFG 
  port (
    I   : in std_logic;
    O    : out std_logic
    ); 
end component;
component IBUFG 
  port (
    I   : in std_logic;
    O    : out std_logic
    ); 
end component;

signal clk_ibuf : std_logic;
signal clkfb : std_logic;
signal gnd, clk_node : std_logic;
signal clkdv_node, clkdv_bufg : std_logic;
signal clk_node2, clk90_node : std_logic;
signal clk90_bufg, clk_bufg : std_logic;
signal dcm1_locked : std_logic;
signal dcm2_reset : std_logic;
signal dcm2_reset_SRL16E : std_logic;
begin
    gnd <= '0';
    
    ibufg_inst : ibufg port map(
        i => clkin,
        o => clk_ibuf
    );
    
    dcm1 : dcm generic map(
        CLKFX_DIVIDE => 8,
        CLKFX_MULTIPLY => 2,
        CLKDV_DIVIDE => 4.0
    )port map(
        clkin => clk_ibuf,
        clkfb => clkfb,
        dssen => gnd,
        psincdec => gnd,
        psen => gnd,
        psclk => clk_ibuf,
        rst => reset,
        clk0 => clk_node,
        clk90 => open,
        clk180 => open,
        clk270 => open,
        clk2x => open,
        clk2x180 => open,
        clkdv => clkdv_node,
        clkfx => open,
        clkfx180 => open,
        locked => dcm1_locked,
        psdone => open,
        status => open
    );
    
    bufg_inst : bufg port map(
        i => clk_node,
        o => clkfb
    );
--    clkout <= clkfb;
    
    bufg_clkdv : bufg port map(
        i => clkdv_node,
        o => clkdv_bufg
    );
    dcm2_reset <= not dcm1_locked;
    
    SRL16E_inst : SRL16E generic map(
        INIT => X"FFFF")
    port map(
        Q => dcm2_reset_SRL16E,
        A0 => '0',
        A1 => '0',
        A2 => '1',
        A3 => '1',
        CE => '1',
        CLK => clk_ibuf,
        D => dcm2_reset
    );
    
    dcm2 : dcm generic map(
        CLKDV_DIVIDE => 4.0
    )port map(
        clkin => clkdv_bufg,
        clkfb => clk_bufg,
        dssen => gnd,
        psincdec => gnd,
        psen => gnd,
        psclk => clk_ibuf,
        rst => dcm2_reset_SRL16E,
        clk0 => clk_node2,
        clk90 => clk90_node,
        clk180 => open,
        clk270 => open,
        clk2x => open,
        clk2x180 => open,
        clkdv => open,
        clkfx => open,
        clkfx180 => open,
        locked => locked,
        psdone => open,
        status => open
    );
    bufg_clk0_2 : bufg port map(
        i => clk_node2,
        o => clk_bufg
    );
    bufg_clk90 : bufg port map(
        i => clk90_node,
        o => clk90_bufg
    );
    
    clkout <= clk_bufg;
    clk90 <= clk90_bufg;
        
end RTL;


修正後シミュレーション結果を下に示す。
Cascading_DCMs_2_110415.png

lockedも'X'状態にならないで、正常に'1'になった。
これで問題は解決した。

(注)
SRL16Eプリミティブを使用していると、インプリメント時にunisimをuseする必要があった。

library unisim;
use unisim.vcomponents.all;

  1. 2011年04月15日 14:02 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

Verilog HDLでの初期化の推論

Xilinx社で行われたSpartan-6ファミリ デザイン セミナで、XSTのみHDLでの初期化の推論ができると聞いてきた。Synplfyではどうかと思ってやってみた。使用したのは訳あってISE10.1.03とSynplify Pro 9.6.2。使用したFPGAはSpartan-3E1200(xc3s1200e-fg320)。
下のソースを使用した。

`default_nettype none
`timescale 1ns / 1ps

module init_test(
    input clk,
    input enable,
    output [15:0] cnt_out
    );
    
    reg [15:0] cnt = 16'd1; // 初期化(ここの値がFFの初期値になっていればOK)
    
    always @(posedge clk) begin
        if (enable)
            cnt <= cnt + 1;
    end
    assign cnt_out = cnt;
        
endmodule


まずはSynplify Proで論理合成、インプリメントした。
Verilog_RST_1_100706.png

FPGA Editorでcnt[0], cnt[1]を見てみたが、ちゃんとcnt[0]のFFがINIT1, cnt[1]のFFがINIT0になっている。
Verilog_RST_2_100706.png

Synplify ProでもVerilogでの初期化の推論が出来ているようだ。
次に論理合成ツールをXSTに変えてやってみた。
同様に、FPGA Editorでcnt<0>, cnt<1>を見てみたが、ちゃんとcnt<0>のFFがINIT1, cnt<1>のFFがINIT0になっている。
Verilog_RST_3_100706.png

XSTでもSynplify ProでもVerilog HDLでの初期化の推論が出来ていた。
  1. 2010年07月06日 17:31 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

”CMOSカメラから画像を入力してディスプレイへ出力”のSRAMのデータ出力イネーブル用FFをIOBへ入れる

CMOSカメラから画像を入力してディスプレイへ出力15(できた!!!)”でCMOSカメラからの画像をディスプレイに出力することができた。そこから画像のいろいろなエッジ検出を試みてきた。今までは、動作周波数が低いので、あまりIOBにFFを入れるとか考慮していなかったので、やってみようと思った。
一番問題なのは、SRAMへのデータをWriteしてから、すぐにリードなので、FPGAの出力用ドライバーをすぐにOFFする必要があることだ。”CMOSカメラから画像を入力してディスプレイへ出力16(VHDLソースを公開)”を元にすると、SRAM_Controller.vhd から出力されたmem_data_oe が、最上位のCamDispCntrl_SRAM.vhd のIOBUF のT 入力に反転されて入力され、SRAMへのデータ出力イネーブルとなっている。その部分のソースを下に示す。

    n_mem_data_oe <= not mem_data_oe;
    
    MEM_DATA_GEN : for i in 15 downto 0 generate
        IOBUF_inst : IOBUF port map(
            O => input_mem_data(i),
            IO => mem_data(i),
            I => mem_data_out(i),
            T => n_mem_data_oe
        );
    end generate MEM_DATA_GEN;


mem_data_oe はどうやって出力しているかというと、SRAM_Controller.vhd の中にあるステートマシンの出力だ。その部分のソースを下に示す。

    -- n_mem_we, mem_data_oe を生成する。48MHzのステートマシンで出力
    process(clk48) begin
        if clk48'event and clk48='1' then
            if reset='1' then
                cs_we <= idle_we;
            else
                cs_we <= ns_we;
            end if;
        end if;
    end process;
    
    process(cs_we, r_w, cam_href_3d) begin
        case cs_we is
            when idle_we =>
                n_mem_we_node <= '1';
                mem_data_oe <= '0';
                if r_w='0' and cam_href_3d='1' then -- hrefの間だけSRAMにWriteする
                    ns_we <= we_active;
                else
                    ns_we <= idle_we;
                end if;
            when we_active => -- r_w が1時48MHzクロック1クロック間だけ n_mem_we を0にする
                n_mem_we_node <= '0';
                mem_data_oe <= '1';
                ns_we <= we_holdoff;
            when we_holdoff => -- この時はまだr_wが0
                n_mem_we_node <= '1';
                mem_data_oe <= '0';
                ns_we <= idle_we;
        end case;
    end process;
    n_mem_we <= n_mem_we_node;


次にFPGA Editor でSRAMのデータ入出力パッドを見てみよう。下にその図を示す。
iob_ff_1_091119.jpg

やはり出力用バッファのアウトプットイネーブルで、IOBのFFは使用されていない。これを使用するようにVHDLソースを書き換えましょうというのが、今回の趣旨だ。
さて、mem_data_oe を1つのFFということで、別に定義してみよう。ステートマシンの方にはnext_mem_data_oe という、mem_data_oe の1クロック前の状態を表すsignal を定義することにする。SRAMへのデータ出力イネーブルmem_data_oe はSRAMのデータ出力パッド1個に対して1個必要なため、16個宣言した。さらに、IOB制約をVHDLコード上に書いた。下にSRAM_Controller.vhdの一部を示す。

signal mem_data_node : std_logic_vector(15 downto 0);
signal next_mem_data_oe : std_logic;

attribute iob : string;
attribute iob of n_mem_data_oe : signal is "TRUE";
attribute keep : string;
attribute keep of n_mem_data_oe : signal is "TRUE";
attribute keep of next_mem_data_oe : signal is "TRUE";
begin

.....

    -- n_mem_we, mem_data_oe を生成する。48MHzのステートマシンで出力
    process(clk48) begin
        if clk48'event and clk48='1' then
            if reset='1' then
                cs_we <= idle_we;
            else
                cs_we <= ns_we;
            end if;
        end if;
    end process;
    
    process(cs_we, r_w, cam_href_3d) begin
        case cs_we is
            when idle_we =>
                n_mem_we_node <= '1';
                if r_w='0' and cam_href_3d='1' then -- hrefの間だけSRAMにWriteする
                    ns_we <= we_active;
                    next_mem_data_oe <= '1';
                else
                    ns_we <= idle_we;
                    next_mem_data_oe <= '0';
                end if;
            when we_active => -- r_w が1時48MHzクロック1クロック間だけ n_mem_we を0にする
                n_mem_we_node <= '0';
                ns_we <= we_holdoff;
                next_mem_data_oe <= '0';
            when we_holdoff => -- この時はまだr_wが0
                n_mem_we_node <= '1';
                ns_we <= idle_we;
                next_mem_data_oe <= '0';
            when others =>
                n_mem_we_node <= '1';
                ns_we <= idle_we;
                next_mem_data_oe <= '0';
        end case;
    end process;
    n_mem_we <= n_mem_we_node;
    
    -- mem_data_oeの出力
    process(clk48) begin
        if clk48'event and clk48='1' then
            if reset='1' then
                n_mem_data_oe <= (others => '1');
            else
                for i in 15 downto 0 loop
                    n_mem_data_oe(i) <= not next_mem_data_oe;
                end loop;
            end if;
        end if;
    end process;


最上位のCamDispCntrl_SRAM.vhd のIOBUF にn_mem_data_oe を1本ずつ入れた。

    MEM_DATA_GEN : for i in 15 downto 0 generate
        IOBUF_inst : IOBUF port map(
            O => input_mem_data(i),
            IO => mem_data(i),
            I => mem_data_out(i),
            T => n_mem_data_oe(i)
        );
    end generate MEM_DATA_GEN;


これをインプリメントしたところ無事にmem_data のIOBに出力イネーブル用のFFが入った。mem_data<0> の様子を下図に示す。
iob_ff_2_091119.png

これでSRAMへの出力データのドライブオン、ドライブオフ時間が最短になることと思う。

(2009/11/20: 追記)
keep制約をかけないとIOBにn_mem_data_oe が入らないときがあるので追加しました。
  1. 2009年11月19日 21:25 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

Spartan3Eの乗算器

このところ、乗算をしているので、私としては初めてSpartan3Eの乗算器をつかったはず。。。ということで確かめてみた。
まずは、”画像のエッジ検出5(2X2の画素の演算でやってみた)”の2X2演算器。下の演算をしているので、乗算器を使っているはず。

diff_cal_val <= "011"*(cam_ydata_2d) - ("000"&bram_data_1d) - ("000"&bram_data) - ("000"&y_data_1d);


Project NavigatorのDevice Utilization Summary を見ると、Number of MULT18X18SIOsが1になっていて、1個の乗算器を使用している。
MULT18X18_1_091114.png

つぎにFPGA Editor で見てみよう。
MULT18X18_2_091114.png

確かに乗算器を使っている。当たり前か?

次に、”画像のエッジ検出7(3X3演算のシミュレーション)”の3X3演算を見てみよう。演算式を下に示す。

Laplacian_cal_val <= "01000"*(XnYn) - ("00000"&Xnm1Ynm1) - ("00000"&XnYnm1) - ("00000"&Xnp1Ynm1) - ("00000"&Xnm1Yn) - ("00000"&Xnp1Yn) - ("00000"&Xnm1Ynp1) - ("00000"&XnYnp1) - ("00000"&Xnp1Ynp1) ;
-- マトリクスの絶対値


Project NavigatorのDevice Utilization Summary を見ると、Number of MULT18X18SIOsがない。
MULT18X18_3_091114.png

乗算器を使用していない。。。これは被乗数が8で3ビット左シフトすれば良いからか?
Project Navigator のProcesses ウインドウのSynthesize のView RTL Schematic で見てみたが、やはり乗算器は生成されていなかった。XSTは2のn乗の掛け算の時には乗算器を使わないのではないだろうか?
  1. 2009年11月14日 19:36 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

Spartan3Aのプログラマブル遅延素子3

”Spartan3Aのプログラマブル遅延素子1””Spartan3Aのプログラマブル遅延素子2”では、IOBバッドからSLICEに行く経路の遅延であるIBUF_DELAYを変更してその値を調べた。今度はIOBパッド内のFFへの遅延であるIFD_DELAYの値を調べてみる。

前回のVerilog-HDLの記述ではin_ff は、IOBのFFを使用することが出来なかったので、Verilog-HDLソース上にIOB制約を追加することにした。ソースを下に示す。

`timescale 1ns / 1ps

module IOB_Delay_test(
  input clk,
  input reset,
  input testin,
  output testout
  );
  
  (* IOB="TRUE" *)reg in_ff;
//  reg in_ff;
  reg in2_ff;
  
  always @(posedge clk) begin
        if (reset) begin
             in_ff <= 1'b0;
             in2_ff <= 1'b0;
        end else begin
             in_ff <= testin;
             in2_ff <= in_ff;
        end
  end
  
  assign testout = in2_ff;
endmodule


これでインプリメントしてFPGA Editorで見てみると、IOB内のFFを使っていることがわかった。
Spa3A_IDEALY_9_090311.png

上のFPGA Editorの図を見ると、最初からDLY5が入っているのがわかる。以前に”DCMを使わないクロックを使いIOB内のFFでデータを受ける”でもやったみたが、セットアップ時間をマイナスの値にしないようにするためや、Xilinxのアンサー”13309 - 10.1 タイミング - 遅延エレメントが使用されるとデータ有効ウィンドウが大きくなる (Tiopickd、Tioickpd)”にあるようにデータ有効ウインドウが大きくなるためだと思われる。
Timing Analyzerの解析結果を下に示す。
Spa3A_IDEALY_10_090311.png

Maximumのデータパス遅延が4.135ns、Minimumのクロックパス遅延が1.849ns、上の図には見えていないがMaximumのクロックパス遅延が2.215ns となっていた。

さて、”ISE10.1のFPGA EditorでIOBやSLICEのパラメータを変更”と同様の方法でFPGA Editorを使用してIFD_DELAYをいろいろな値に変更して、Tiopickdがどのように変化するかをTiming Analyzerで見て表にしみた。その表を下に示す。
Spa3A_IDEALY_11_0903112.png

IFD_DELAYは4タップあるので、Coarse Delayを入れると8タップということになる。上の表を見るとDLY5のところで不連続になっているので、ここでCoarse Delayが入ったのだろう。IBUF_DELAYに比べてタップごとの遅延が大きい。最大の遅延は同じくらいなので、タップごとのステップが大きいようだ。
実はこの値はSpartan3Aのデータシートの25ページのTable 20: Setup and Hold Times for the IOB Input Path (Continued) に載っている。ホールド時間のTioickpdの値も26ページに載っている。
そこでデータウインドウをIFD_DELAY_VALUE = 0 (DLY0) の時とIFD_DELAY_VALUE = 8 (DLY8) の時で比較してみよう。有効データの必要があるウインドウはセットアップ時間 - ホールド時間だ。

IFD_DELAY_VALUE = 0  1.51ns - 0.67ns = 0.84ns
IFD_DELAY_VALUE = 8  6.73ns - 3.81ns = 2.92ns


上の計算より、IFD_DELAY_VALUE = 8 (DLY8) の時のほうが有効データの必要があるウインドウが広がっていることがわかる。
  1. 2009年03月12日 06:23 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

Spartan3Aのプログラマブル遅延素子2

”Spartan3Aのプログラマブル遅延素子1”でクロックにIBUF_DLY_ADJプリミティブを入れたらBUFGが入らなくなってしまった。とりあえず、BUFGを明示的に入れてみようと思う。下がソース。

`timescale 1ns / 1ps

module IOB_Delay_test(
    input clk,
    input reset,
    input testin,
    output testout
    );
    
    reg in_ff, in2_ff;
    wire ibuf_out, clk_bufg;

    IBUF_DLY_ADJ  #(
        .DELAY_OFFSET("OFF"),      //  Enable  Initial  Delay  Offset,  "OFF"  or  "ON"
        .IOSTANDARD("DEFAULT")    //  Specify  the  input  I/O  standard
    )IBUF_DLY_ADJ_inst  (
        .O(ibuf_out),      //  Buffer  output
        .I(clk),      //  Buffer  input  (connect  directly  to  top-level  port)
        .S(3'b000)        //  3-bit  buffer  delay  select  input
    );    
    
    BUFG BUFG_inst (
        .O(clk_bufg),
        .I(ibuf_out)
    );
    
    always @(posedge clk_bufg) begin
        if (reset) begin
            in_ff <= 1'b0;
            in2_ff <= 1'b0;
        end else begin
            in_ff <= testin;
            in2_ff <= in_ff;
        end
    end
    
    assign testout = in2_ff;
endmodule


これでインプリメントして、Timing Analyzerで見るとBUFGが入っているのがわかる。
Spa3A_IDEALY_6_090304.png

これでいろいろなIBUF_DELAY_VALUEの値を調べてみる。これは、clock pathのTiopidを比較することによって行う。

上のソースで、IBUF_DLY_ADJのS(3'b000)の値を変化させて、Timing Analyzer を見ているのだが遅延は変化がない。しかも、セットアップ時間で見たTiopidの値とclock to output時間でみたTiopidの値が違う。上のセットアップ時間のTiming AnalyzerではTiopidは1.495ns だが、下のclock to output時間では1.869nsとなっている。
Spa3A_IDEALY_7_090304.png

どうしてこうなるかわからないがFPGA Editorで見てみると、S()の値を変化してもIBUF_DELAYはDLY1のままで変化がないことがわかった。やはり動的遅延だからなのだろうか?Timing Analyzerで解析していないのだろうか?
そうかわかりました、セットアップ時間で見たTiopidの値はMinimum path、clock to output時間でみたTiopidの値はMaximum pathだった。より厳しい値を使っている。当然か。。。

それでは、”Spartan3Aのプログラマブル遅延素子1”の一番最初のソースに戻して、FPGA EditorでIOパッドのIBUF_DELAYのDLYを変更して、Timing Analyzerで見てみることにする。

下に結果を示す。
Spa3A_IDEALY_8_090304.png

DLY9のところでCoarse Delay が入るので、DLY8よりも遅延が減るようだ。DLYの量が増えるほど、MAXとMINの差が開く。大体300ps~550ps位の遅延タップのようだ。(FPGAはSpartan3Aのxc3s700a-4fg484)
  1. 2009年03月05日 06:38 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:0

Spartan3Aのプログラマブル遅延素子1

Sparatan3Eの時もプログラマブルな遅延素子があったが、動的に遅延を変更することが出来なかった。Spartan3AはIBUF_DELAY_VALUEのうち8タップを変更することが出来る。下にSpartan-3 Generation FPGA User Guideの323ページの”Figure 10-10: Spartan-3A/3AN/3A DSP Programmable Dynamic Input Delay Elements”を引用する。
Spa3A_IDEALY_1_090302.png

上の図でCoarse Delayが前段の遅延素子でこれを入れるか、入れないかでIFD_DELAY_VALUEは2X4タップ、IBUF_DELAY_VALUEは2X8タップの遅延素子になる。IBUF_DELAY_VALUEの8タップはS[2:0]で選択することが出来て、動的に遅延を変更することが出来るようだ。

とりあえずSpartan3Aのxc3s700a-4fg484 でIBUF_DELAY_VALUEを測定してみよう。
まずはIOB_Delay_test.vをインプリメントしてみた。ソースを下に示す。

`timescale 1ns / 1ps

module IOB_Delay_test(
    input clk,
    input reset,
    input testin,
    output testout
    );
    
    reg in_ff, in2_ff;
    
    always @(posedge clk) begin
        if (reset) begin
            in_ff <= 1'b0;
            in2_ff <= 1'b0;
        end else begin
            in_ff <= testin;
            in2_ff <= in_ff;
        end
    end
    
    assign testout = in2_ff;
endmodule


次にUCF。遅延を見るだけなので位置は固定していない。

NET "clk" TNM_NET = clk;
TIMESPEC TS_clk = PERIOD "clk" 8 ns HIGH 50%;
NET "testin" OFFSET = IN 5 ns BEFORE "clk" RISING;
NET "testout" OFFSET = OUT 7 ns AFTER "clk";


これでインプリメントを行い、ProcessesウインドウのImplement Design -> View/Edit Routed Design (FPGA Editor) をダブルクリックしてFPGA Editor でclk 入力パッドのIOBを見てみる。
Spa3A_IDEALY_2_090303.png

入力パッドの遅延、IBUF_DELAY_VALUEが0 (IBUF_DELAYがDLY0) になっている。この時の入力の遅延をTiming Analyzerで見ると、下のようになる。
Spa3A_IDEALY_3_090303.png

右上の画面のを見ると、データのパスが1.718ns 、クロックのパスが1.844ns となっていて、クロックのパスの方が、0.126ns 遅延が多くなっている。これだとクロックがデータの真ん中だといい感じ。そしてclk のTiopiが0.730ns になっている。これを覚えておいて、今度はclk にIBUF_DLY_ADJ を挿入してインスタンシエーションしてみる。ソースは下のように変更した。IBUF_DLY_ADJはIBUFの遅延を変更できるプリミティブだ。S入力の値を変えることでダイナミックに遅延を変更することが出来る。Figure 10-10のCoarse Delay を入れるためには、DELAY_OFFSETをONにする。

`timescale 1ns / 1ps

module IOB_Delay_test(
    input clk,
    input reset,
    input testin,
    output testout
    );
    
    reg in_ff, in2_ff;
    wire ibuf_out;

    IBUF_DLY_ADJ  #(
        .DELAY_OFFSET("OFF"),      //  Enable  Initial  Delay  Offset,  "OFF"  or  "ON"
        .IOSTANDARD("DEFAULT")    //  Specify  the  input  I/O  standard
    )IBUF_DLY_ADJ_inst  (
        .O(ibuf_out),      //  Buffer  output
        .I(clk),      //  Buffer  input  (connect  directly  to  top-level  port)
        .S(3'b000)        //  3-bit  buffer  delay  select  input
    );    
    always @(posedge ibuf_out) begin
        if (reset) begin
            in_ff <= 1'b0;
            in2_ff <= 1'b0;
        end else begin
            in_ff <= testin;
            in2_ff <= in_ff;
        end
    end
    
    assign testout = in2_ff;
endmodule


これでインプリメントして、FPGA Editorで見てみると下のようになる。
Spa3A_IDEALY_4_090303.png

S入力の入力が増えて、IBUF_DELAYもDLY1になった。これでTiming Analyzer で見てみよう。
Spa3A_IDEALY_5_090303.png

クロックのパスを見ると1.844ns が2.841ns と遅延が変化した。その差は0.997nsだ。だが良く見ると、最初のインプリはBUFGが入っているが、今回はBUFGが入っていない。
とりあえず最初のTiming AnalyzerのクロックパスのTiopi = 0.730ns と次のTiming AnalyzerのクロックパスのTiopid = 1.495ns で比べてみると、0.765nsとなった。
次回ははBUFGを入れてインプリしなおしてみてみようと思う。BUFGを使うか使わないかはわからないが。。。
  1. 2009年03月04日 06:09 |
  2. FPGAチップ内の配線方法
  3. | トラックバック:0
  4. | コメント:2
»