FC2カウンター FPGAの部屋 Spartan3A Starter KitのDDR2 SDRAMコントローラのインプリメント(再度インプリ)
FC2ブログ

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

FPGAの部屋

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

Spartan3A Starter KitのDDR2 SDRAMコントローラのインプリメント(再度インプリ)

”Spartan3A Starter KitのDDR2 SDRAMコントローラIOテストモジュールのシミュレーション3”で非同期FIFOを自作してシミュレーションがうまくいったようなので、インプリメントをしてみた。Floorplannerで、分散RAMの位置固定を、もう一度やってみると、うまくいったのでddr2_dqから最短の遅延で非同期FIFOのRAMに接続することができた。ucfファイルを書き換えたので、一度インプリメントし直す。上手く行ったか?さっそくTiminig Analyzerを立ち上げて、結果を見てみた。そうすると、クロックの立ち上がりのクロック解析はエラーが出ていて、クロックの立下りのクロック解析結果は解析するパスがないとのこと。え、FALLINGのパスがない?おかしい。なぜだろうということで、FPGA Editorを立ち上げて見てみることにした。
DQ0用のクロックの立下りのときのデータをサンプルするrdd_afifo_dout_0_OBUFを見てみた。下がそのSLICEMの図。
spa3A_skit_io_inp_1_090426.png

ここでクロックの部分を拡大してみる。すると、CLKにつながっているノードはdqs_clkではなく、DQS2intclk_FIFO_FALL_not0000というノードが接続されている。しかもCLK_Bが選択されて反転されていない。
spa3A_skit_io_inp_2_090426.png

こちらの予定と違っている。ちなみにクロックの立ち上がりでサンプルする方のSLICEM、rdd_afifo_dout_16_OBUFを見てみると、CLKにdqs_clkが入っている。
spa3A_skit_io_inp_3_090426.png

さらにDQS2intclk_FIFO_FALL_not0000を探してみると、dqs_clkを反転してDQS2intclk_FIFO_FALL_not0000ノードに出すだけでした。
spa3A_skit_io_inp_4_090426.png

DQS2intclk_FIFO_FALL_not0000ノード全体はこんな感じで、各クロックの立下りをクロックとする
分散RAM(SLICEM)に行っているようです。
spa3A_skit_io_inp_5_090426.png

これは大誤算でした。上のモジュールでdqs_clkを反転して非同期FIFOに与えているのだが、RAM16X1Dプリミティブが立ち上がり動作なので、こうなってしまったのか?本来は、分散RAM(SLICEM)の中でCLK_Bを使うことによってクロックを反転したいんだが。。。
これでは困るので、プリミティブで書かないで、分散RAMを推定させる記述をしてみる。(XSTでRAMを推定させる話は、VHDLだけど、”VHDLでのブロックRAMや分散RAMの初期化(外部データファイル)”を参照)
だが、結果は同じだった。
ここで、async_fifo.vの上位のモジュールはrddata_afifo.v でここで async_fifo.v を呼び出しているところを見ると、以下のようなVerilogコードになっている。

    // 16ビット幅の非同期FIFOをインスタンシエーション(DQSクロックの立ち上がりでデータをサンプル、下位)
//    DQS2intclk_FIFO DQS2intclk_FIFO_RISE(
    async_fifo DQS2intclk_FIFO_RISE(
        .din    (din[15:0]),
        .rd_clk    (clk),
        .rst    (reset),
        .rd_en    (rd_en),
        .wr_en    (wr_en),
        .wr_clk    (dqs_clk),    // DQSクロックの立ち上がりをAFIFOに入力する
        .almost_empty    (almost_empty_rise),
        .almost_full    (almost_full_rise),
        .dout    (dout[31:16]),
        .empty    (empty_rise),
        .full    (full_rise)
    );
    
    // 16ビット幅の非同期FIFOをインスタンシエーション(DQSクロックの立下りでデータをサンプル、下位)
//    DQS2intclk_FIFO DQS2intclk_FIFO_FALL(
    async_fifo DQS2intclk_FIFO_FALL(
        .din    (din[15:0]),    // dinは立ち上がりと同じ
        .rd_clk    (clk),
        .rst    (reset),
        .rd_en    (rd_en),
        .wr_en    (wr_en),
        .wr_clk    (~dqs_clk),    // DQSクロックの立下りをAFIFOに入力する。
        .almost_empty    (almost_empty_fall),
        .almost_full    (almost_full_fall),
        .dout    (dout[15:0]),
        .empty    (empty_fall),
        .full    (full_fall)
    );


DQS2intclk_FIFO_FALLの.wr_clkにdqs_clkを入れるところで反転していた。

もしかしてと思い、async_fifo_rise.v と async_fifo_fall.v に分割して、fallの方は、generate のRAM16X1Dのインスタンス時のwr_clkに入力するところで、~wr_clk (~dqs_clk)としてみた。

    generate 
    genvar i;
        for (i=15; i>=0; i=i-1) begin : DPRAM_GEN
            RAM16X1D #(
                .INIT(16'h000)
            ) RAM16X1D_inst (
                .WCLK(~wr_clk),
                .WE(wr_en),
                .A0(wp[0]),
                .A1(wp[1]),
                .A2(wp[2]),
                .A3(wp[3]),
                .D(din[i]),
                .SPO(),
                .DPRA0(rp[0]),
                .DPRA1(rp[1]),
                .DPRA2(rp[2]),
                .DPRA3(rp[3]),
                .DPO(dout[i])
            );
        end
    endgenerate    


次に当然、上のモジュールでの呼び出しも以下のように変更した。

    // 16ビット幅の非同期FIFOをインスタンシエーション(DQSクロックの立ち上がりでデータをサンプル、下位)
//    DQS2intclk_FIFO DQS2intclk_FIFO_RISE(
    async_fifo_rise DQS2intclk_FIFO_RISE(
        .din    (din[15:0]),
        .rd_clk    (clk),
        .rst    (reset),
        .rd_en    (rd_en),
        .wr_en    (wr_en),
        .wr_clk    (dqs_clk),    // DQSクロックの立ち上がりをAFIFOに入力する
        .almost_empty    (almost_empty_rise),
        .almost_full    (almost_full_rise),
        .dout    (dout[31:16]),
        .empty    (empty_rise),
        .full    (full_rise)
    );
    
    // 16ビット幅の非同期FIFOをインスタンシエーション(DQSクロックの立下りでデータをサンプル、下位)
//    DQS2intclk_FIFO DQS2intclk_FIFO_FALL(
    async_fifo_fall DQS2intclk_FIFO_FALL(
        .din    (din[15:0]),    // dinは立ち上がりと同じ
        .rd_clk    (clk),
        .rst    (reset),
        .rd_en    (rd_en),
        .wr_en    (wr_en),
        .wr_clk    (dqs_clk),    // DQSクロックの立下りをAFIFOに入力する。
        .almost_empty    (almost_empty_fall),
        .almost_full    (almost_full_fall),
        .dout    (dout[15:0]),
        .empty    (empty_fall),
        .full    (full_fall)
    );



こうしたらうまく行きました。やった~。しかし上のモジュールやるとだめで、プリミティブの入力のときに反転するとうまくいくんだ~ということがわかった。コードを書くときには注意しないといけない。
FPGA EditorでFALL側の分散RAMを見るとCLK入力にdqs_clkが入力されていて、CLK_Bが選択されているのがわかる。
spa3A_skit_io_inp_6_090426.png

これでだいたいIO部分とリード部分は行けたと思う。今度は本体を作ることにしよう。

<追記>
とりあえず何の役にも立たないプロジェクトですけど、Veritakのプロジェクトとテストベンチ付きでだれかほしい方はいらっしゃいますか?もし、いらしたら、公開しようかと思います。どこかの方がバグをリポートしていただければ、私にもすごいメリットがありますし。。。

<もう一度追記>
うまくいかない記述とうまくいく記述のXSTでの論理合成結果を比べてみた。
うまくいかない記述(上のモジュールでdqs_clkを反転しているもの)は、XSTのRTLスケマで見ると、上のモジュール(rddata_afifo.v) のところでasync_fifo_fall.v に対してインバータが入っている。言ってみれば当然なのかも?
spa3A_skit_io_inp_7_090428.png

うまくいく記述(async_fifo_fall.v でRAM16X1D直前でwr_clkを反転)は、上のモジュールはそのままで、aync_fifo_fall.v のWCLKの直前でインバータが入っている。
spa3A_skit_io_inp_8_090428.png

そういえば、XSTのKeep HierarchyオプションがYesでやっていたので、これかな?と思って、Noに変更してみたが結果は同じだった。やはり直前でやらないとSLICEMのCLK_Bは使用されないようだ。

  1. 2009年04月27日 21:39 |
  2. Spartan3A Starter Kit
  3. | トラックバック:0
  4. | コメント:4

コメント

たっく

論理的には、同じなんですよね。UCFで強制される部分があるのでしょうか?
  1. 2009/04/28(火) 06:02:47 |
  2. URL |
  3. #-
  4. [ 編集 ]

論理的には同じはずですよね?
何でしょうか?どこにNOTをつけるかでXSTがどのレベルでインバータをつけるかを決めているんでしょうかね?XSTの論理合成後の結果を検証してみようと思います。
  1. 2009/04/28(火) 08:46:24 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

たっく

XSTの検証ありがとうございました。よく分かりました。

もしよろしければ、公開していただけますでしょうか? (今回のASyncFiFOは、すごく実用的だと思いました。)
  1. 2009/04/29(水) 01:39:52 |
  2. URL |
  3. #-
  4. [ 編集 ]

たっくさん、こんにちは。

async_fifo.vはほとんどブログに張っているのですが、全体をまとめて公開するようにします。といいましても、おもにREADのタイミングが良い所に行けるかどうかのIOだけのスケルトンモデルです。あまり実用にならないとは思います。
一度、FPGA内部素子でDDR2を受けてみたいと思っていまして、今回やってみられたのはうれしいです。この先Spartan6が出ると、DDR2などの専用マクロがあるので、FPGAの汎用内部素子でやることに意味がなくなると思っています。
  1. 2009/04/29(水) 05:33:46 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

コメントの投稿


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

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