【FPGA】 DDR3读写(基于User Interface)

发布时间 2023-08-15 22:03:48作者: ALright壹

【FPGA】 DDR3读写(基于User Interface)

DDR3概述

DDR3 (double data rate 3 synchronous dynamic RAM) 第三代双倍数据速率同步动态随机存储器

  • 同步:数据的速去和写入时钟同步
  • 动态:数据掉电无法保存,需要周期性刷新才能保持数据
  • 随机存取:能够对任意地址进行操作
  • 双倍数据速率:时钟的上升沿和下降沿都发生数据传输

DDR3存储器模型

用户可通过MIG IP核控制DDR3

MIG IP核的配置

  1. 修改component name
  2. 选择是否使用AXI4接口(此处选用User Interface接口)

  1. clock period

    物理层时钟周期,即DDR3芯片的输入时钟

  2. PHY to Controller Clock Ratio

    DDR3输入时钟和MIG控制器时钟频率比例,此处选择4:1说明MIG控制器时钟为100MHz,因此用户侧逻辑也会按照100MHz进行编写

    由于DDR3是时钟双边沿读写,假设DDR3的位宽是16bits,则带宽为400MHz*2*16bits

    而对应的用户侧则通过扩展的方式匹配,其位宽为400MHz*2*16bits/100MHz = 128bits

  3. Vccaux_io

  4. Memory Type

    DDR3内存类型,如果是DDR3芯片颗粒,则选用components,如果是内存条的形式,则选择对应的类型

  5. Memory Part

    根据DDR3型号选择,此处选用位宽为16bits

  6. Data Width

  1. ECC

  2. Data Mask

    是否启用数据掩码,即可通过app_data_mask端口去控制写入部分位(例如高8位)数据

  3. Number of Bank Machines

    与使用的DDR3有关,如果拉满则效率最高,看需求

  4. ORDERING

    Strict即保持数据的读写顺序,Normal即允许控制器改变执行cmd的顺序,提高读写速率

  1. Input Clock Period

    输入时钟周期,通过MIG内部的PLL得到最终输入DDR3的时钟

  1. system clock

    系统时钟类型

  2. reference clock

    DDR3的正常运行需要200MHz参考时钟,如果前面选择了200MHz的输入时钟,则此处可以复用为User System Clock

  3. system reset polarity

  1. 后者是因为硬件已经设计好了,引脚是固定的
  2. 前者会自动选择最优的引脚去驱动

MIG IP核的使用

MIG控制器的三种时序:写命令、写数据、读数据都有对应的FIFO,因此操作起来更为灵活,限制较小

写命令

写数据

当app_wdf_rdy为高电平时,即可拉高app_wdf_en写入数据

数据可在命令之前、同时或最大不慢于2个周期写入

读数据

写入读命令后,数据可能在若干个周期后读出,伴随app_rd_data_valid信号拉高

如何对自己的DDR3读写模块进行仿真?

由于仿真需要DDR3模型,而自己写一个DDR3模型过于复杂,因此可通过利用example design的代码,并换上自己的DDR3读写模块实现,具体可参考该UP的教学ddr3搭建仿真平台_哔哩哔哩_bilibili

1、Open IP Example Design会自动生成一个项目

2、找到项目imports文件夹下的这些文件

并复制到原先的工程文件夹中,同时vivado中add sim文件

3、修改example_top.v文件,换成自己的模块

top_module.v:

`timescale 1ns / 1ps
module top_module(
    // inouts
    ddr3_dq,
    ddr3_dqs_n,
    ddr3_dqs_p,

    // outputs
    ddr3_addr,
    ddr3_ba,
    ddr3_ras_n,
    ddr3_cas_n,
    ddr3_we_n,
    ddr3_reset_n,
    ddr3_ck_p,
    ddr3_ck_n,
    ddr3_cke,
    ddr3_cs_n,
    ddr3_dm,
    ddr3_odt,

    init_calib_complete,
    // inputs
    sys_clk_i,
    sys_rst

    );

    inout [15:0] ddr3_dq;
    inout [1:0] ddr3_dqs_n;
    inout [1:0] ddr3_dqs_p;

    output [13:0] ddr3_addr;
    output [2:0] ddr3_ba;
    output ddr3_ras_n;
    output ddr3_cas_n;
    output ddr3_we_n;
    output ddr3_reset_n;
    output [0:0] ddr3_ck_p;
    output [0:0] ddr3_ck_n;
    output [0:0] ddr3_cke;

    output [0:0] ddr3_cs_n;
    output [1:0] ddr3_dm;
    output [0:0] ddr3_odt;

    output init_calib_complete;

    input sys_clk_i;
    input sys_rst;


    wire [27:0] app_addr;
    wire [2:0] app_cmd;
    wire app_en;
    wire [127:0] app_wdf_data;
    wire app_wdf_end;
    wire app_wdf_wren;
    
    wire [127:0] app_rd_data;
    wire app_rd_data_end;
    wire app_rd_data_valid;
    wire app_rdy;
    wire app_wdf_rdy;

    wire app_sr_req;
    wire app_ref_req;
    wire app_zq_req;
    wire app_sr_active;
    wire app_ref_ack;
    wire app_zq_ack;

    wire ui_clk;
    wire ui_clk_sync_rst;
    wire [15:0] app_wdf_mask;

    wire sys_clk_i;
    wire sys_rst;


// mig_16bits instance
    mig_16bits mig_16bits_inst(
    // memory ports
        .ddr3_addr(ddr3_addr),
        .ddr3_ba(ddr3_ba),
        .ddr3_cas_n(ddr3_cas_n),
        .ddr3_ck_n(ddr3_ck_n),
        .ddr3_ck_p(ddr3_ck_p),
        .ddr3_cke(ddr3_cke),
        .ddr3_ras_n(ddr3_ras_n),
        .ddr3_reset_n(ddr3_reset_n),
        .ddr3_we_n(ddr3_we_n),
        .ddr3_dq(ddr3_dq),
        .ddr3_dqs_n(ddr3_dqs_n),
        .ddr3_dqs_p(ddr3_dqs_p),
        .init_calib_complete(init_calib_complete),

        .ddr3_cs_n(ddr3_cs_n),
        .ddr3_dm(ddr3_dm),
        .ddr3_odt(ddr3_odt),
    // user app ports
        .app_addr(app_addr),
        .app_cmd(app_cmd),
        .app_en(app_en),
        .app_wdf_data(app_wdf_data),
        .app_wdf_end(app_wdf_end),
        .app_wdf_wren(app_wdf_wren),

        .app_rd_data(app_rd_data),
        .app_rd_data_end(app_rd_data_end),
        .app_rd_data_valid(app_rd_data_valid),
        .app_rdy(app_rdy),
        .app_wdf_rdy(app_wdf_rdy),
        .app_wdf_mask(app_wdf_mask),
        
        .app_sr_req(1'b0),
        .app_ref_req(1'b0),
        .app_zq_req(1'b0),

        .app_sr_active(app_sr_active),
        .app_ref_ack(app_ref_ack),
        .app_zq_ack(app_zq_ack),
        
    // clock ports
        .ui_clk(ui_clk),
        .ui_clk_sync_rst(ui_clk_sync_rst),
    
        .sys_clk_i(sys_clk_i),
        .sys_rst(sys_rst)
    );



// mig data read write test
    wire test_reset;
    mig_test mig_test_inst(
        .init_calib_complete(init_calib_complete),
        .ui_clk(ui_clk),
        .ui_rst_in(ui_clk_sync_rst),
        
        .app_addr(app_addr),
        .app_cmd(app_cmd),
        .app_en(app_en),
        .app_wdf_data(app_wdf_data),
        .app_wdf_end(app_wdf_end),
        .app_wdf_mask(app_wdf_mask),
        .app_wdf_wren(app_wdf_wren),
        .app_wdf_rdy(app_wdf_rdy),

        .app_rd_data(app_rd_data),
        .app_rd_data_end(app_rd_data_end),
        .app_rd_data_valid(app_rd_data_valid),
        .app_rdy(app_rdy), 

        .test_result(test_reset)
    );


endmodule

mig_test.v:

向DDR3中写入16个数据之后读出

`timescale 1ns / 1ps
module mig_test(
    init_calib_complete,
    ui_clk,
    ui_rst_in,
    
    app_addr,
    app_cmd,
    app_en,
    app_wdf_data,
    app_wdf_end,
    app_wdf_mask,
    app_wdf_wren,
    app_wdf_rdy,

    app_rd_data,
    app_rd_data_end,
    app_rd_data_valid,
    app_rdy
    );

    input init_calib_complete;
    input ui_clk;
    input ui_rst_in;
    wire ui_rst;
    assign ui_rst = ~ui_rst_in;

    output wire [27:0] app_addr;
    reg [27:0] app_addr_write = 0;
    reg [27:0] app_addr_read = 0;

    output wire [2:0] app_cmd;

    output reg app_en = 0;

    output reg [127:0] app_wdf_data = 0;
    output reg app_wdf_end = 0;
    output reg [15:0] app_wdf_mask = 0;
    output reg app_wdf_wren = 0;
    input app_wdf_rdy;

    input [127:0] app_rd_data;
    input app_rd_data_end;
    input app_rd_data_valid;
    input app_rdy;

    reg [127:0] rd_data_reg = 0;

    wire wr_done;
    wire rd_done;

    reg [7:0] counter_write_cmd = 16;
    reg [7:0] counter_write = 16;
    reg [7:0] counter_read_cmd = 0;
    reg [7:0] counter_read = 0;

    localparam STATE_IDLE = 4'b0001;
    localparam STATE_WRITE = 4'b0010;
    localparam STATE_READ = 4'b0100;
    localparam STATE_END = 4'b1000;

    reg [3:0] state = STATE_IDLE ;
    reg [3:0] next_state = STATE_IDLE ;

// next state logic
    always @(*) begin
        if (~ui_rst | ~init_calib_complete) begin
            next_state = STATE_IDLE;
        end
        else begin
            case (state)
                STATE_IDLE:
                begin
                    next_state = STATE_WRITE;
                end
                STATE_WRITE:
                begin
                    if(wr_done) begin
                        next_state = STATE_READ;
                    end
                    else begin
                        next_state = STATE_WRITE;
                    end
                end
                STATE_READ:
                begin
                    if(rd_done) begin
                        next_state = STATE_END;
                    end
                    else begin
                        next_state = STATE_READ;
                    end
                end
                STATE_END:
                begin
                    next_state = STATE_END;
                end 
                default: ;
            endcase
        end
    end

// state change
    always @(posedge ui_clk) begin
        if(~ui_rst | ~init_calib_complete) begin
            state <= STATE_IDLE;
        end
        else begin
            state <= next_state;
        end
    end

// output logic
    // app_cmd
    assign app_cmd = (state == STATE_WRITE)?3'b000:3'b001;

    //app_en
    always @(*) begin
        if(~ui_rst) begin
            app_en <= 0;
        end
        else begin
            // write data
            if(state == STATE_WRITE && app_rdy && counter_write_cmd != 0) begin
                app_en <= 1;
                // counter_write_cmd <= counter_write_cmd - 1;
            end
            else if(state == STATE_READ && app_rdy && counter_read_cmd != 8'd16) begin
                app_en <= 1;
                // counter_read_cmd <= counter_read_cmd + 1;
            end
            else begin
                app_en <= 0;
            end
        end
    end

    // counter_write_cmd / counter_read_cmd
    always @(posedge ui_clk) begin
        if(~ui_rst) begin
            counter_write_cmd <= 16;
            counter_read_cmd <= 0;
        end
        else begin
            if(state == STATE_WRITE && app_rdy && app_en && counter_write_cmd != 0) begin
                counter_write_cmd <= counter_write_cmd - 1;
            end
            else if(state == STATE_READ && app_rdy && app_en && counter_read_cmd != 16) begin
                counter_read_cmd <= counter_read_cmd + 1;
            end
        end
    end

    // app_wdf_data
    always @(posedge ui_clk) begin
        if(~ui_rst) begin
            app_wdf_data <= 0;
            app_wdf_wren <= 0;
            app_wdf_end <= 0;
        end
        else if(state == STATE_WRITE && app_wdf_rdy && counter_write != 0) begin
            app_wdf_wren <= 1;
            app_wdf_data <= app_wdf_data + 1'd1;
            app_wdf_end <= 1;
            counter_write <= counter_write - 1'd1;
        end
        else begin
            app_wdf_wren <= 0;
            app_wdf_end <= 0;
        end
    end

    // app_addr_write
    always @(posedge ui_clk) begin
        if(~ui_rst) begin
            app_addr_write <= 0;
        end
        else begin
            if(state == STATE_WRITE && app_rdy && counter_write_cmd!=0) begin  
                app_addr_write <= app_addr_write + 28'd8;
            end
        end
    end

    // app_addr_read
    always @(posedge ui_clk) begin
        if(~ui_rst) begin
            app_addr_read <= 0;
        end
        else begin
            if(state == STATE_READ && app_rdy && counter_read_cmd!=16) begin
                app_addr_read <= app_addr_read + 28'd8;
            end
        end
    end

    // app_addr
    assign app_addr = (state == STATE_WRITE)?app_addr_write:app_addr_read;

    // app_rd_data
    always @(posedge ui_clk) begin
        if (~ui_rst) begin
            rd_data_reg <= 0;
        end
        else begin
            if(app_rd_data_valid) 
            begin
                rd_data_reg <= app_rd_data;
                counter_read <= counter_read + 1'd1;
            end
        end
    end
    // wr_done
    assign wr_done = (counter_write_cmd == 0)&&(counter_write == 0);
    // rd_done
    assign rd_done = (counter_read_cmd == 16)&&(counter_read == 16);
    
endmodule