FC2カウンター FPGAの部屋 Spartan3A Starter KitのDDR2 SDRAMコントローラIOテストモジュールのシミュレーション3
fc2ブログ

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

FPGAの部屋

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

Spartan3A Starter KitのDDR2 SDRAMコントローラIOテストモジュールのシミュレーション3

”Spartan3A Starter KitのDDR2 SDRAMコントローラIOテストモジュールのシミュレーション2”の続き。CoreGenで作った非同期FIFOが使用にマッチしないので、”同期FIFOと非同期FIFO”で自作した非同期FIFOで再チャレンジした。
最初は自分で非同期FIFOを自作せずに、www.asic-world.comの Asynchronous FIFOを使わせてもらって何とかしようとしたのだが、どうもうまく動かない。いろいろ修正したのだが、よくわからないところがあるので、限界を感じて自分でスクラッチから書くことにした。
まずはバイナリ→グレイ変換だが、ウィキペディアのグレイコードに書いてあった。”最上位桁から1であれば残り下桁を反転、0であれば残り下桁を変化させない。 例えば、2進数が1010であれば、グレイコードは1111となる。”(ウィキペディアのグレイコードから転載)を忠実にVerilogにした。それが下のコード。

for (j=3; j<=0; j=j-1) begin
    if (j=3) // 最上位はそのままグレーコードに
        wgray[3] <= inc_wp[3];
    else
        if (wgray[i-1]) // 上位の桁が1だったら
            wgray[i] <= ~inc_wp[i];
        else // 0だったらそのまま
            wgray[i] <= inc_wp[i];
end


エラーチェックしてないので間違っているかも?
でも、なんとはなしにwww.asic-world.comの非同期FIFOのコードを眺めていたら、なるほど、これでいいんだと思った。

wgray <= {inc_wp[3], inc_wp[2:0] ^ inc_wp[3:1]}; // 結局上位の桁をXORすればOK


これも後でインプリメントした時の違いを見てみることにしようか?あまり変わらないかもしれないけど。。。
これでいいと思って、非同期FIFO全体をVerilogで書いて前回のシミュレーション環境でシミュレーションしてみたんだけど、どうもグレイコードの0110がバイナリの0100に変換するときに0101に変換してしまう。おかしい。。。
その変換のVerilogコードは下。

assign rdbinary = {rgrayd2[3], rgrayd2[2:0] ^ rgrayd2[3:1]};


自分で計算してみるが、グレイコード0110は0101になってしまう。あれれ、おかしいぞ。何かおかしい。もう一度グレイ→バイナリ変換のサイトを見てみると、よく見ると最上位からバイナリとグレイの-1桁目を排他的論理和取っているじゃないですか。。。間違ってしまった。下のVerilogコードに変更。

// Read Gray Counterの値をバイナリ変換
always @* begin : READ_GRAY2BINARY
    integer j;
    
    for(j=3; j>=0; j=j-1) begin
        if (j==3)
            rdbinary[j] <= rgrayd2[j];
        else
            rdbinary[j] <= rgrayd2[j] ^ rdbinary[j+1];
    end
end


これでだいたいOKかな。シミュレーションしてみたところ、OKのようだ。
DDR2_read_sim_1_090425.png

ddr2_dqに来たデータがdq_data、ddr2_dqsに届いたクロックがdqs_clkに伝搬している。dqs_clkはすでに遅延させている。sd_loop_outが非同期FIFOのWrite Enableとなる。その非同期FIFOのデータをrdd_afifo_rd_enが1のときに、rdd_afifo_doutに出力している。だいたいOKそう。。。

これで苦労してFloorplanした非同期FIFOの最初のRAM素子の位置の制約がパーになってしまったので、もう一度やり直す必要がある。なんかFloorplannerでやってみたら、ddr2_dqのIOパッドが見えなかったので、Floorplan Editorでやる必要があるかな?

一応、非同期FIFOの全ソースを張っておきます。何か間違いがあったら教えてください。

// async_fifo.v
// 4bitアドレス、15深度
// Dual-Port RAMはRAM16X1Dプリミティブを使用する
// 出力は同期化されていないので、上のモジュールでFFを通す必要がある

`default_nettype none
`timescale 1ns / 1ps

module async_fifo (
    din,
    rd_clk,
    rd_en,
    rst,
    wr_clk,
    wr_en,
    almost_empty,
    almost_full,
    dout,
    empty,
    full);

    input wire    [15 : 0] din;
    input wire    rd_clk;
    input wire    rd_en;
    input wire    rst;
    input wire    wr_clk;
    input wire    wr_en;
    output wire    almost_empty;
    output wire    almost_full;
    output wire    [15 : 0] dout;
    output wire    empty;
    output wire    full;

    reg [3:0] wp, rp;
    wire [3:0] inc_wp, inc_rp;
    reg [3:0] wgray, rgray;
    reg [3:0] rgrayd1, rgrayd2;
    reg [3:0] wgrayd1, wgrayd2;
    reg [3:0] rdbinary, wrbinary;
    
    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
    
    
    // Write Clock Domain
    assign inc_wp = wp + 1;
    always @(posedge wr_clk, posedge rst) begin : WRITE_COUNTERS
        integer j;
        
        if (rst) begin
            wp <= 4'h0;
            wgray <= 4'h0;
        end else begin
            if (wr_en) begin
                wp <= wp + 4'h1;
                wgray <= {inc_wp[3], inc_wp[2:0] ^ inc_wp[3:1]}; // 結局上位の桁をXORすればOK
            end
        end
    end
    
    // Read Gray Counterからのグレーコードをwr_clkで同期化する
    always @(posedge wr_clk, posedge rst) begin
        if (rst) begin
            rgrayd1 <= 4'h0;
            rgrayd2 <= 4'h0;
        end else begin
            rgrayd1 <= rgray;
            rgrayd2 <= rgrayd1;
        end
    end
    
    // Read Gray Counterの値をバイナリ変換
    always @* begin : READ_GRAY2BINARY
        integer j;
        
        for(j=3; j>=0; j=j-1) begin
            if (j==3)
                rdbinary[j] <= rgrayd2[j];
            else
                rdbinary[j] <= rgrayd2[j] ^ rdbinary[j+1];
        end
    end
    
    assign full = (wp == rdbinary-4'h1) ? 1'b1 : 1'b0;
    assign almost_full = (wp==rdbinary-4'h2) ? 1'b1 : 1'b0;
    
    
    //Read Clock Domain
    assign inc_rp = rp + 1;
    always @(posedge rd_clk, posedge rst) begin : READ_COUNTERS
        integer j;
        
        if (rst) begin
            rp <= 4'h0;
            rgray <= 4'h0;
        end else begin
            if (rd_en) begin
                rp <= rp + 4'h1;
                rgray <= {inc_rp[3], inc_rp[2:0] ^ inc_rp[3:1]}; // 結局上位の桁をXORすればOK
            end
        end
    end
    
    // Write Gray Counterからのグレーコードをrd_clkで同期化する
    always @(posedge rd_clk, posedge rst) begin
        if (rst) begin
            wgrayd1 <= 4'h0;
            wgrayd2 <= 4'h0;
        end else begin
            wgrayd1 <= wgray;
            wgrayd2 <= wgrayd1;
        end
    end
    
    // Write Gray Counterの値をバイナリ変換
    always @* begin : WRITE_GRAY2BINARY
        integer j;
        
        for(j=3; j>=0; j=j-1) begin
            if (j==3)
                wrbinary[j] <= wgrayd2[j];
            else
                wrbinary[j] <= wgrayd2[j] ^ wrbinary[j+1];
        end
    end
    
    assign empty = (wrbinary == rp) ? 1'b1 : 1'b0;
    assign almost_empty = (wrbinary-4'h1==rp) ? 1'b1 : 1'b0;
endmodule


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

コメント

たっく

こんにちは。
非同期の同期は、難しいですね。RTLでは、動いてしまうので、検証も難しいです。グレーコードにしても組み合わせ回路自体はヒゲがでるので、empty/full ロジックは、見直しが必要かと思います。
  1. 2009/04/26(日) 13:11:23 |
  2. URL |
  3. #-
  4. [ 編集 ]

たっくさん、ご指摘ありがとうございます。

fullは使いませんし、emptyは自分のクロックドメインで同期化したものを組み合わせ回路で出力しているので、後の段で同期化しようと思っています。それでもクリティカルパスができてしまった時のためにFFを一段入れるとすると、almost_emptyも使おうと思っています。
  1. 2009/04/26(日) 13:38:44 |
  2. URL |
  3. marsee #f1oWVgn2
  4. [ 編集 ]

たっく

自クロックドメインにしてあるのを見落としてました。ごめんなさい。
ですので、FFは不要ですね。!emptyでDataを拾えばいい訳ですね。
  1. 2009/04/27(月) 06:22:50 |
  2. URL |
  3. #-
  4. [ 編集 ]

コメントの投稿


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

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