FC2カウンター FPGAの部屋 ISERDESのお勉強の続きのその後2
FC2ブログ

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

FPGAの部屋

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

ISERDESのお勉強の続きのその後2

ISERDESのお勉強の続きで1:10のDDRのISERDESをインプリメントしてみたが、今度はシミュレーションをしてみた。ISERDESの1:8のSDRのタイミングチャートがユーザーズマニュアルにあったが、このタイミングチャートだとすると、今やっている1:10のDDRのタイミングと合わない気がするが、とりあえずBitslipを使ってみたので、記録しておく。
どうやら、DDRはCEが1になったclkdivの立ち上がりと同時のclk_inや次のclk_inの立下りからではなく、次のclk_inの立ち上がりからシリアルーパラレル変換するようだ。タイミングを調整して、このタイミングにあわせる。
この前のISERDESのVerilogファイルはこのように変更した。

`default_nettype none
`timescale 1ps/1ps
module serial_parallel_converter (
    Din,
    clk_in,                       
    rst,
    bitslip,
    enable,
    recv_data,
    q_out
);
    input  Din;
    input  clk_in;
    input  rst;
    input bitslip;
    input enable;
    output [9:0] recv_data;
    output [9:0] q_out;
    wire    Din;
    wire    clk_in;
    wire    rst;
    wire    bitslip;
    wire    enable;
    wire    [9:0] recv_data;
    wire    [9:0] q_out;
    wire   iserdes_clkout;
    wire   iobclk;
    wire   clkdiv;
    wire   shiftdata1;
    wire   shiftdata2;
    wire [9:0] data_internal;
    reg    [9:0] data;
    
    // Instantiate ISERDES for forwarded clock
     ISERDES fwd_clk (
         .O(iserdes_clkout),
         .Q1(), 
         .Q2(), 
         .Q3(), 
         .Q4(), 
         .Q5(), 
         .Q6(), 
         .SHIFTOUT1(), 
         .SHIFTOUT2(),
         .BITSLIP(1'b0),
         .CE1(1'b1), 
         .CE2(1'b1),
         .CLK(iobclk),
         .CLKDIV(clkdiv),
         .D(clk_in),
         .DLYCE(1'b0),
         .DLYINC(1'b0),
         .DLYRST(1'b0),
         .OCLK(1'b0), 
         .REV(1'b0),  
         .SHIFTIN1(1'b0), 
         .SHIFTIN2(1'b0),
         .SR(rst)
      );
    defparam fwd_clk.BITSLIP_ENABLE  =  "TRUE"; 
    defparam fwd_clk.DATA_RATE =  "DDR";
    defparam fwd_clk.DATA_WIDTH =  4;   
    defparam fwd_clk.INTERFACE_TYPE =  "NETWORKING"; 
    defparam fwd_clk.IOBDELAY =  "NONE"; 
    defparam fwd_clk.IOBDELAY_TYPE =  "DEFAULT"; 
    defparam fwd_clk.IOBDELAY_VALUE = 0; 
    defparam fwd_clk.NUM_CE = 1; 
    defparam fwd_clk.SERDES_MODE =  "MASTER"; 
    
    // Instantiate Master ISERDES for data channel
    // 1:10 Deserialization Factor
    ISERDES data_chan_master (
        .O(), 
        .Q1(data_internal[0]),
        .Q2(data_internal[1]),
        .Q3(data_internal[2]), 
        .Q4(data_internal[3]),
        .Q5(data_internal[4]),
        .Q6(data_internal[5]),
        .SHIFTOUT1(shiftdata1),
        .SHIFTOUT2(shiftdata2),
        .BITSLIP(bitslip),
        .CE1(enable), 
        .CE2(1'b1),
        .CLK(iobclk),
        .CLKDIV(clkdiv),
        .D(Din), 
        .DLYCE(1'b0),
        .DLYINC(1'b0),
        .DLYRST(1'b0),
        .OCLK(1'b0), 
        .REV(1'b0),
        .SHIFTIN1(1'b0), 
        .SHIFTIN2(1'b0),
        .SR(rst)
    );
    defparam data_chan_master.BITSLIP_ENABLE =  "TRUE"; 
    defparam data_chan_master.DATA_RATE =  "DDR";
    defparam data_chan_master.DATA_WIDTH =  10;   
    defparam data_chan_master.INTERFACE_TYPE =  "NETWORKING";         
    defparam data_chan_master.IOBDELAY =  "NONE";                 
    defparam data_chan_master.IOBDELAY_TYPE =  "DEFAULT";         
    defparam data_chan_master.IOBDELAY_VALUE =  0;                
    defparam data_chan_master.NUM_CE =  1;                        
    defparam data_chan_master.SERDES_MODE =  "MASTER"; 
    //
    // Instantiate Slave ISERDES for data channel
    // 1:10 Deserialization Factor

    ISERDES data_chan_slave (
        .O(), 
        .Q1(), 
        .Q2(), 
        .Q3(data_internal[6]),
        .Q4(data_internal[7]),
        .Q5(data_internal[8]),
        .Q6(data_internal[9]),
        .SHIFTOUT1(), 
        .SHIFTOUT2(),
        .BITSLIP(bitslip),
        .CE1(enable), 
        .CE2(1'b1),
        .CLK(iobclk),
        .CLKDIV(clkdiv),
        .D(1'b0), 
        .DLYCE(1'b0),
        .DLYINC(1'b0),
        .DLYRST(1'b0),
        .OCLK(1'b0), 
        .REV(1'b0),
        .SHIFTIN1(shiftdata1), 
        .SHIFTIN2(shiftdata2),
        .SR(rst)
    );
    defparam data_chan_slave.BITSLIP_ENABLE =  "TRUE"; 
    defparam data_chan_slave.DATA_RATE =  "DDR";
    defparam data_chan_slave.DATA_WIDTH =  10;   
    defparam data_chan_slave.INIT_Q1  =  1'b0;   
    defparam data_chan_slave.INIT_Q2  =  1'b0;   
    defparam data_chan_slave.INIT_Q3  =  1'b0;        
    defparam data_chan_slave.INIT_Q4  =  1'b0;   
    defparam data_chan_slave.INTERFACE_TYPE  =  "NETWORKING";         
    defparam data_chan_slave.IOBDELAY  =  "NONE";                 
    defparam data_chan_slave.IOBDELAY_TYPE  =  "DEFAULT";         
    defparam data_chan_slave.IOBDELAY_VALUE  =  0;                
    defparam data_chan_slave.NUM_CE  =  1;                        
    defparam data_chan_slave.SERDES_MODE  =  "SLAVE";             
    defparam data_chan_slave.SRVAL_Q1  =  1'b0;                   
    defparam data_chan_slave.SRVAL_Q2  =  1'b0;                   
    defparam data_chan_slave.SRVAL_Q3  =  1'b0;                   
    defparam data_chan_slave.SRVAL_Q4  =  1'b0;
    //
    BUFIO bufio1 (
        .O(iobclk),
         .I(iserdes_clkout)
                 );
    // To get a 1:10 deserialization factor in DDR mode, 
    // set the clock divide factor to "5"
    BUFR bufr1 (
        .O(clkdiv),
        .CE(1'b1),
        .CLR(1'b0),
        .I(iobclk)
    );
    defparam bufr1.BUFR_DIVIDE  =  "5";
    
    always @(posedge rst, posedge clkdiv) begin

        if (rst)
            data <= 0;
        else
            data <= data_internal;
    end
    assign q_out = data;
    assign recv_data = data_internal;
endmodule
    


次にテストベンチ。

`default_nettype none
`timescale 1ps/1ps
module serial_parallel_converter_tb;
    reg Din = 1'b0;
    reg clk_in = 1'b0;
    reg rst = 1'b1; // reset
//     reg bitslip = 1'b1; // bitslip mode
    reg enable = 1'b0;
    wire [9:0] recv_data;
    wire [9:0] q_out;
    
    wire [9:0] para10bits;
    wire outbit;
    integer j;
    
    parameter PERIOD = 5000; // 5ns, 200MHz
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;

    initial    // Clock process for clk
    begin
        #OFFSET;
        forever
        begin
            clk_in = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) clk_in = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end
    
    serial_parallel_converter UUT (
        .Din(Din),
        .clk_in(clk_in),
        .rst(rst),
        .bitslip(1'b0),
        .enable(enable),
        .recv_data(recv_data),
        .q_out(q_out)
    );
    
    initial begin // 10bitを出力
        #100000;    // GSRリセットを待つ
        #(PERIOD);
        rst = 1'b0;
        #(PERIOD);
        enable = 1'b1;
        
        @(posedge UUT.clkdiv); // clkdivの立ち上がりまでWait
        #(PERIOD/2); // 1clk_inの半分の時間を進める
        #(PERIOD/4);
        Out10bits(10'b01_0100_1010);
        Out10bits(10'b01_0100_1010);
        Out10bits(10'b01_0100_1010);
        Out10bits(10'b10_0101_1011);
        #100000
        $stop;
    end
    
    task Out10bits;
        input [9:0] para10bits;
        integer i;
        begin
            for (i=0; i<10; i=i+1)
                Out1bit(para10bits[9-i]);
        end
    endtask
    
    task Out1bit;
        input outbit;
        begin
            Din = outbit;
            #(PERIOD/2);
        end
    endtask
    
//     always @* begin // q_outが10'b01_0100_1010の1つ前の10'b10_1001_0100になったらbitslipを0にする
//         if (recv_data == 10'b10_1001_0100)
//             bitslip = 1'b0;
//     end
    
    
endmodule
`default_nettype wire


最終的にbitslipは使用しなかったが、クロックと送ってきたビット列が合わずにワード同期が取れない場合は、bitslipを最初1にして、ビット列を移動することができる。そのときに、送ったデータと同一になったらbitslipを0にしてビット列の移動を止めれよいが、ISERDESのQ出力は1段多くラッチされているので、1つ手前でbitslip信号を0にしないと行き過ぎてしまう。
シミュレーションすると下のようになる。
ISERDES_1_10_071129.png

rstが0になって、enable(CE1)がclkdivの前でアサートされたクロックエッジの次のclk_inの立ち上がりエッジからデータ(Din)をサンプルするようだ。
サンプルされたデータはclkdivでラッチされて、次のclkdivの立ち上がりで出力([9:0]data_internal)されるようだ。
これでNETWORKINGモードでは、うまく行ったようだ。ずーと同じタイミングでデータ転送されればこれでOKだと思う。もしビット列が間違っていても、あらかじめ参照パターンがわかっていれば、bitslipを使って必要なだけビット列を入れ換えれば良い。
だが、DDRの受信の場合は、必ずしもそのタイミングに合うというわけではないので、bitslipでビット列を入れ替えるのは難しい。さらにclkdivは、clk_in(DQS)が途中で止まってしまうので、DQSから分周して作るのは難しいと思う。そこで、グローバルクロックから入れたらどうだろうと思っている。
さらに、DDR2-SDRAMコントローラのアプリケーションノートはMEMORYモードで使用しているので、検証してみたい。
  1. 2007年11月29日 20:54 |
  2. Virtex4のお勉強
  3. | トラックバック:0
  4. | コメント:0

コメント

コメントの投稿


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

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