QDR SRAM接口FPGA 详细Verilog代码

QDR SRAM介绍

QDR 具有独立的读、写数据通路,均使用DDR,在每个时钟周期内会传输四个总线宽度的数据 (两个读和两个写),这就是QDR四倍数据速率的由来。
这里用到的是典型2字突发的QDR,对于4字突发的QDR操作类似,稍作改动就行。针对每个读或写请求,2 字突发器件传输两个字。DDR 地址总线用于在前半个时钟周期允许读请求,在后半个时钟周期允许写请求。
首先看接口的时序图
QDR SRAM接口FPGA 详细Verilog代码_第1张图片
时序图,表明了 2 字突发 QDR II 存储器接口上的并发读 / 写操作。时钟有三组差分时钟,其中C时钟是发送寄存器的发送时钟,K时钟是目的寄存器用的采样时钟,CQ时钟是经过QDR器件延时,跟输出Q同步的时钟。
在K时钟的前半个周期,DDR 地址总线允许读地址传输给存储器;在时钟的后半个周期,DDR 地址总线允许写地址出现其中。因此,低有效的读控制 (/R) 和写控制 (/W) 控制可在同一时钟周期内有效。
设计目标就是要把QDR接口封装简化,把DDR接口都转化为FPGA内部可用的SDR,这样对用户侧而言,读写控制分离,读写地址分离,操作起来更简便。接口的原理图如下

QDR SRAM接口FPGA 详细Verilog代码_第2张图片

时钟关系

可用看出两组发送给QDR的时钟,同频,满足C时钟相位为0和K时钟相位为270的关系。
QDR SRAM接口FPGA 详细Verilog代码_第3张图片

写数据通路

QDR SRAM接口FPGA 详细Verilog代码_第4张图片
从QDR SRAM的时序图中可以看出,写数据和地址的时序要求一样,因此处理起来也一样。多根数据总线用generate for生成,代码如下。地址通道做相同的处理

generate
    genvar var1;
    for(var1=0;var1<18;var1=var1+1)
      begin:  
        gen_QDR_D
        ODDR #(
            .DDR_CLK_EDGE("SAME_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
            .INIT        (1'b0       ), // Initial value of Q: 1'b0 or 1'b1
            .SRTYPE      ("SYNC"     )  // Set/Reset type: "SYNC" or "ASYNC"
        ) O_QDR_D_inst (
            .Q (O_QDR_D[var1]        ), // 1-bit DDR output
            .C (I_user_clk0          ), // 1-bit clock input
            .CE(1'b1                 ), // 1-bit clock enable input
            .D1(I_user_wr_data1[var1]), // 1-bit data input (positive edge)
            .D2(I_user_wr_data2[var1]), // 1-bit data input (negative edge)
            .R (1'b0                 ), // 1-bit reset
            .S (1'b0                 )  // 1-bit set
        );
      end
endgenerate

时钟通路

由于数据要经过DDR输出,为了保证时钟和数据具有相同的延时,构造相同的时钟通路,对CLK0和CLK270都进行如下处理
QDR SRAM接口FPGA 详细Verilog代码_第5张图片
参考代码如下,这里只是一个差分时钟CLK270的处理,对另一个差分时钟CLK0做相同处理。

ODDR #(
    .DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
    .INIT        (1'b0           ), // Initial value of Q: 1'b0 or 1'b1
    .SRTYPE      ("SYNC"         )  // Set/Reset type: "SYNC" or "ASYNC"
) O_QDR_K_p_inst (
    .Q (O_QDR_K_p), // 1-bit DDR output
    .C (I_user_clk270), // 1-bit clock input
    .CE(1'b1), // 1-bit clock enable input
    .D1(1'b0), // 1-bit data input (positive edge)
    .D2(1'b1), // 1-bit data input (negative edge)
    .R (1'b0), // 1-bit reset
    .S (1'b0)  // 1-bit set
);
ODDR #(
    .DDR_CLK_EDGE("OPPOSITE_EDGE"), // "OPPOSITE_EDGE" or "SAME_EDGE"
    .INIT        (1'b0           ), // Initial value of Q: 1'b0 or 1'b1
    .SRTYPE      ("SYNC"         )  // Set/Reset type: "SYNC" or "ASYNC"
) O_QDR_K_n_inst (
    .Q (O_QDR_K_n), // 1-bit DDR output
    .C (I_user_clk270), // 1-bit clock input
    .CE(1'b1), // 1-bit clock enable input
    .D1(1'b1), // 1-bit data input (positive edge)
    .D2(1'b0), // 1-bit data input (negative edge)
    .R (1'b0), // 1-bit reset
    .S (1'b0)  // 1-bit set
);

读数据通路

QDR SRAM接口FPGA 详细Verilog代码_第6张图片

//******************************************************************************
//                               input data path
//******************************************************************************

//-------------------------------------QDR_Q-------------------------------------
generate
    genvar var3;
    for(var3=0;var3<18;var3=var3+1)
      begin:  
        gen_QDR_Q
        IDDR #(
            .DDR_CLK_EDGE("SAME_EDGE_PIPELINED"), // "OPPOSITE_EDGE", "SAME_EDGE"//    or "SAME_EDGE_PIPELINED"
            .INIT_Q1     (1'b0       ), // Initial value of Q1: 1'b0 or 1'b1
            .INIT_Q2     (1'b0       ), // Initial value of Q2: 1'b0 or 1'b1
            .SRTYPE      ("SYNC"     )  // Set/Reset type: "SYNC" or "ASYNC"
        ) I_QDR_Q_inst (
            .Q1(W_rd_data1[var3]), // 1-bit output for positive edge of clock
            .Q2(W_rd_data2[var3]), // 1-bit output for negative edge of clock
            .C (W_dly_clk0), // 1-bit clock input
            .CE(1'b1), // 1-bit clock enable input
            .D (I_QDR_Q[var3]), // 1-bit DDR data input
            .R (1'b0), // 1-bit reset
            .S (1'b0)  // 1-bit set
        );
      end
endgenerate

IDELATY延时调整算法

其中IDELAY的延时调整算法如图所示,分别找到CQ的上升沿(DDR输出:01->10)和下降沿(DDR输出:10->01),然后delay_cnt取中间值,使CLK0对准CQ的中间。由于相同的延迟,CLK0也对准数据采样窗口的中间。当然,最简单的是直接上板子,输入一个正弦波,延时用vio输入,用lia查看波形。可以手动调整延时到能看到稳定的波形就行了,然后在代码里面把delay_cnt写死。也可以调两个最坏的情况,取中间的延时,跟自动调整算法一样。
QDR SRAM接口FPGA 详细Verilog代码_第7张图片

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYCTRL IDELAYCTRL_inst1 (
      .RDY(W_delay_rdy),       // 1-bit output: Ready output
      .REFCLK(I_ref_clk_200m), // 1-bit input: Reference clock input
      .RST(~I_reset_n)         // 1-bit input: Active high reset input
   );

   (* IODELAY_GROUP = "delay1" *) 
   IDELAYE2 #(
      .CINVCTRL_SEL("FALSE"),         // Enable dynamic clock inversion (FALSE, TRUE)
      .DELAY_SRC("IDATAIN"),          // Delay input (IDATAIN, DATAIN)
      .HIGH_PERFORMANCE_MODE("TRUE"), // Reduced jitter ("TRUE"), Reduced power ("FALSE")
      .IDELAY_TYPE("VAR_LOAD"),       // FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
      .IDELAY_VALUE(0),               // Input delay tap setting (0-31)
      .PIPE_SEL("FALSE"),             // Select pipelined mode, FALSE, TRUE
      .REFCLK_FREQUENCY(200.0),       // IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
      .SIGNAL_PATTERN("CLOCK")        // DATA, CLOCK input signal
   )
   IDELAYE2_inst1 (
      .CNTVALUEOUT(), // 5-bit output: Counter value output
      .DATAOUT(W_dly_clk0),       // 1-bit output: Delayed data output
      .C(W_fc_clk),              // 1-bit input: Clock input
      .CE(1'b0),                 // 1-bit input: Active high enable increment/decrement input
      .CINVCTRL(1'b0),           // 1-bit input: Dynamic clock inversion input
      .CNTVALUEIN(W_delay_cnt),  // 5-bit input: Counter value input
      .DATAIN(1'b0),             // 1-bit input: Internal delay data input
      .IDATAIN(I_uae_clk0),       // 1-bit input: Data input from the I/O
      .INC(1'b0),                // 1-bit input: Increment / Decrement tap delay input
      .LD(1'b1),                 // 1-bit input: Load IDELAY_VALUE input
      .LDPIPEEN(1'b0),           // 1-bit input: Enable PIPELINE register to load data input
      .REGRST(1'b0)              // 1-bit input: Active-high reset tap-delay input
   );

代码很简单,把I_user_clk0延迟一下,对齐CQ的中间,这样也就对齐了数据QDR_Q的中间了。

你可能感兴趣的:(FPGA)