Nexys4 DDR上的DDR2仿真与设计

1.DDR2

Nexys4 DDR开发板提供的DDR2 SDRAM的大小为128MiB;

2.MIG IP核

DDR2 SDRAM在读、写操作过程中需要进行初始化、刷新、预充电、激活等操作,Xilinx为用户提供了IP核MIG,自动完成DDR2初始化等基本操作,其工作模式为突发传输,突发长度可设置为4或8。

2.1核心架构

image.png

信号线可以分成两部分:以app_为前缀的信号信号线与User FPGA Logic进行交互;以ddr_为前缀的信号负责与DDR2 SDRAM进行交互。
比较重要的几个控制信号为app_en,app_addr,app_cmd,app_wdf_data,app_wdf_wren,app_wdf_end,app_rdy,app_wdf_rdy;
这些信号负责控制数据的读写。


读时序

image.png

写时序

image.png

注意:这里的时钟clk需要使用MIS的输出信号ui_clk,这样才能生成正确的读写时序

2.2 IP核的配置

1)关于IP核的配置,XIlinx提供一个图形化的界面,如下图所示。


image.png

2)输入Component Name


MIG_2.PNG

3)选择要兼容的设备
MIG_3.PNG

4)Clock Period选择Digilent推荐的3077ps,Memory Part选择开发板参考手册中的ddr2芯片型号


MIG_4.PNG

5)Input Clock Period选择100MHz,RTT选择50ohms
MIG_5.PNG

6)System Clock与Reference Clock选择No Buffer,勾选Internal Vref
MIG_6_1.PNG

MIG_6_2.PNG

7)Internal Termination Impedance选择50 ohms
MIG_7.PNG

8)选择Fixed Pin Out


MIG_8.PNG

9)Pin Selection中选择Read XDC/UCF,加载从Digilent官网下载的UCF文件来对管脚进行配置
MIG_9.PNG

10)接下来一路NEXT
MIG IP核的配置参数是参考下图Digilent推荐的配置参数。
image.png

3.DDR2仿真

3.1仿真模型

DDR2在VIVADO上仿真需要去下载相应的仿真模型,以模拟出ddr2的存储环境。

Nexys4 DDR使用的内存芯片为MICRON的MT47H64M16HR-25E,需要去厂商官网下载仿真模型,下载地址为:https://www.micron.com/parts/dram/ddr2-sdram/mt47h64m16hr-25e。需要用到的文件为ddr2_model.v。

3.2仿真例子

4.连续读写测试

过程:先向ddr中连续写入10个数据,然后再依次将数据读出来。

5.参考代码

ddr2写控制

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2018/07/23 20:29:22
// Design Name: 
// Module Name: ddr2_write_control
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// ddr2写入控制
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module ddr2_write_control(
    clk_in,
    rst_n,
    ADDR_I,
    DATA_I,
    STB_I,
    ACK_O,
    read_en,
    //ddr_ signals
    app_en,
    app_wdf_wren,
    app_wdf_end,
    app_cmd,
    app_addr,
    app_wdf_data,
    app_rdy,
    app_wdf_rdy
    );

    parameter DQ_WIDTH          = 16;
    parameter ECC_TEST          = "OFF";
    parameter ADDR_WIDTH        = 27;
    parameter nCK_PER_CLK       = 4;

    localparam DATA_WIDTH       = 16;
    localparam PAYLOAD_WIDTH    = (ECC_TEST == "OFF") ? DATA_WIDTH : DQ_WIDTH;
    localparam APP_DATA_WIDTH   = 2 * nCK_PER_CLK * PAYLOAD_WIDTH;  //突发长度为8
    localparam APP_MASK_WIDTH   = APP_DATA_WIDTH / 8;

    input clk_in;
    input rst_n;  
    input [26:0] ADDR_I;    //读取地址、偏移
    input [127:0] DATA_I;  //需要写入的数据
    input STB_I;    //选通信号
    output reg ACK_O;    //可以接收数据标志位,高有效
    output reg read_en;

    // Wire declarations
    output reg app_en, app_wdf_wren, app_wdf_end;
    output reg [2:0] app_cmd;
    output reg [ADDR_WIDTH-1:0] app_addr;
    output reg [APP_DATA_WIDTH-1:0] app_wdf_data;
    input app_rdy, app_wdf_rdy;

    //生成写入数据的信号值
    //----------FSM--------
    reg [2:0] cstate;

    parameter IDLE = 3'b001;
    parameter WRITE = 3'b010;

    reg [3:0] write_count;


    always @(posedge clk_in)
    begin
        if(rst_n) begin
            app_cmd <= 3'b1;
            app_en <= 1'b0;
            app_wdf_data <= 128'h0;
            app_addr <= 27'h0;
            app_wdf_end <= 1'b0;
            app_wdf_wren <= 1'b0;
            write_count <= 0;
            read_en <= 0;
            ACK_O <= 0;
            cstate <= IDLE;
        end
        else if(STB_I) begin
            case(cstate)
                IDLE:begin
                    if(app_rdy & app_wdf_rdy) begin
                        app_wdf_data <= DATA_I;
                        app_cmd <= 3'b0;
                        app_addr <= ADDR_I;
                        app_wdf_wren <= 1'b1;
                        app_wdf_end <= 1'b1;
                        app_en <= 1'b1;
                        ACK_O <= 0;  //可以接收数据
                        write_count <= write_count + 1;
                        cstate <= WRITE;
                    end
                    else cstate <= IDLE; 
                end
                WRITE:begin
                    app_en <= 1'b0;
                    app_cmd <= 3'b1;
                    ACK_O <= 1;
                    app_wdf_wren <= 1'b0;
                    app_wdf_end <= 0;
                    if(write_count == 3)
                        read_en <= 1;
                    cstate <= IDLE;
                end
                default:cstate <= IDLE;
            endcase
        end
        else begin
            app_en <= 0;
            app_wdf_wren <= 0;
            app_wdf_end <= 0;
            ACK_O <= 0;
            cstate <= IDLE;
        end
    end


endmodule

ddr2读控制

`timescale 1ns / 1ps
//////////////////////////////////////////////////////////////////////////////////
// Company: 
// Engineer: 
// 
// Create Date: 2018/07/23 20:32:16
// Design Name: 
// Module Name: ddr2_read_control
// Project Name: 
// Target Devices: 
// Tool Versions: 
// Description: 
// 
// Dependencies: 
// 
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
// 
//////////////////////////////////////////////////////////////////////////////////


module ddr2_read_control(
    clk_in,
    rst_n,
    enable,
    //ddr2_signal
    app_en,
    app_cmd,
    app_addr,
    app_rd_data,
    app_rdy,
    app_rd_data_end,
    app_rd_data_valid
    );

    input clk_in;
    input rst_n;
    input enable;
    output reg app_en;
    output reg [2:0] app_cmd;
    output reg [26:0] app_addr;
    input [127:0] app_rd_data;
    input app_rdy;
    input app_rd_data_end;
    input app_rd_data_valid;

    reg [26:0] app_addr_tmp;

    //读取FSM
    reg [4:0] cstate;
    
    localparam IDLE = 5'b0_0001;
    localparam READ = 5'b0_0010;
    localparam WAIT = 5'b0_0100;
    localparam ADDR_ACCUMULATE = 5'b0_1000;
    localparam WAIT_FOR_CONFIG = 5'b1_0000;

    always @(posedge clk_in)
    begin
        if(rst_n) begin
            app_en <= 0;
            app_addr_tmp <= 27'h0;
            cstate <= IDLE;
        end
        else if(enable) begin
            case(cstate)
            IDLE:begin
                app_en <= 1;
                app_addr <= app_addr_tmp;
                app_cmd <= 3'b001;
                cstate <= READ;
            end
            READ:begin
                if(app_rdy) begin
                    app_en <= 1'b0;
                    app_addr_tmp <= app_addr_tmp + 27'h8;
                    cstate <= IDLE;
                end
            end
            default:cstate <= IDLE;
            endcase
        end  
    end

endmodule

github代码地址:https://github.com/juxiping/ddr2_write_read

推荐阅读更多精彩内容