FPGA读写DDR3

DDR3是一种内存规格,它是SDRAM家族的内存产品。DDR3之前的产品有DDR和DDR2。DDR(Double Data Rate)是双倍速率同步动态随机存储器,严格的说DDR应该叫DDR SDRAM。DDR2是DDR产品的升级产品,它是四倍速率同步动态随机存储器。DDR3在DDR2的基础上实现了更高的性能(增加到八倍)和更低的电压。
DDR内部结构框图如下所示:
在这里插入图片描述

如上图所示:

标号1:逻辑控制单元,用于输入命令解析和模式控制等。
标号2:行地址选通单元。
标号3:内部存储阵列,上图中分为8个bank。
标号4:bank控制逻辑单元。
标号5:IO锁存和DM标记单元,用于刷新和预充电。
标号6:数据读写驱动接口。
标号7:列地址选择单元。
在7系列FPGA芯片中,Xilinx 为用户提供了一个 DDR 控制的 IP核,称为MIG IP 控制器。这样用户可以很方便的通过 MIG 控制器读写 DDR 存储器。7 系列的 DDR 控制器的解决方案如下所示:
在这里插入图片描述
如上图所示,DDR3 控制器包含 3 部分:

  • 用户接口模块(User interface Block);
  • 存储器控制模块(Memory Controller);
  • DDR3 的物理接口(Physical Layer)。
    在vivado中创建DDR3的MIG控制器,步骤如下:
  1. 在vivado中,点击 Project Manager 界面下的 IP Catalog,打开 IP Catalog 界面。如下所示:
    在这里插入图片描述

  2. 在IP Catalog 界面里找到 Memories & Storage Elements\Memory Interface Generators 下的 Memory
    Interface Generator (MIG 7 Series),如下图所示:
    在这里插入图片描述
    3.双击Memory Interface Generator (MIG 7 Series)后出现下图所示界面:
    在这里插入图片描述

如果想了解更多的 MIG 的信息,可以点击左边的 User Guide 按钮来打开 Xilinx 的相关文档来查看。
4.点击 Next,如下图所示:
在这里插入图片描述
5.修改 Component Name 为"ddr3",点击 Next。如下图所示:
在这里插入图片描述

6.继续点 Next。如下图所示:
在这里插入图片描述
7.选择默认的 DDR3 SDRAM。如下图所示:
在这里插入图片描述
8.在clock period选项中选择2500ps,Memory Part 中选择匹配型号如"MT41J256m16xx-125", Data Width 数据宽度选择 32 位,ORDERING选择normal。然后点击next,如下图所示:
在这里插入图片描述

9.选择Input clock Period为 200Mhz, 这个时钟需要跟所用电路板上的外部时钟频率一致,其它设置输出阻抗值和内部的 ODT 内部上拉电阻值来改善 DDR3 的信号完整性,一般不需要修改。如下图所示:
在这里插入图片描述
10.System Clock 选择差分"No Buffer", 因为电路板上没有提供单独的 DDR 参考时钟,所以Reference Clock 选择"Use System Clock"。System Reset
Polarity 选择"ACTIVE LOW",其它保留默认配置。
在这里插入图片描述
11.如果内部端接阻抗为 50 ohms,则不用修改。如下图所示:
在这里插入图片描述

12.点击第二项,我们需要设定一下 DDR 的管脚,点击 Next。
在这里插入图片描述

13.在这个界面里设置 DDR3 的数据、地址和控制信号的 FPGA 管脚分配和 IO 电平。这个手工分配起来还是比较费劲的,为了方便可以点击“Read XDC/UCF”按键直接导入现成的管脚分配文件。导入后 ddr3 的管脚分配如下:
在这里插入图片描述

14.点击Validate,校验通过后点击Next。如下图所示:
在这里插入图片描述
15.这里软件默认设置,直接点击 Next,如下图所示:
在这里插入图片描述

16.这里会出现DDR3的配置信息表,如果检查没问题后可点击next。如下图所示:
在这里插入图片描述

17.选择 Accept,点击 Next。如下图所示:
在这里插入图片描述
18.继续点击Next,如下图所示:
在这里插入图片描述
19.点击 Generate 按钮生成 MIG 控制器。如下图所示:
在这里插入图片描述

20.点击 Generate 按钮生成 MIG 相关的设计文档。如下图所示:
在这里插入图片描述

21.然后就可以生成DDR3的IP核。
以下为DDR3驱动文件:mem_burst.v(此代码来源于网络,只是在学习中添加了注解)

module mem_burst

#(

    parameter MEM_DATA_BITS = 64,

    parameter ADDR_BITS = 24

)

(

    input rst,                                 /*复位*/

    input mem_clk,                               /*接口时钟*/

    input rd_burst_req,                          /*读请求*/

    input wr_burst_req,                          /*写请求*/

    input[9:0] rd_burst_len,                     /*读数据长度*/

    input[9:0] wr_burst_len,                     /*写数据长度*/

    input[ADDR_BITS - 1:0]
    rd_burst_addr,        /*读首地址*/

    input[ADDR_BITS - 1:0]
    wr_burst_addr,        /*写首地址*/

    output rd_burst_data_valid,                  /*读出数据有效*/

    output wr_burst_data_req,                    /*写数据信号*/

    output[MEM_DATA_BITS - 1:0]
    rd_burst_data,   /*读出的数据*/

    input[MEM_DATA_BITS - 1:0]
    wr_burst_data,    /*写入的数据*/

    output rd_burst_finish,                      /*读完成*/

    output wr_burst_finish,                      /*写完成*/

    output burst_finish,                         /*读或写完成*/

    output[ADDR_BITS-1:0]                       app_addr,     //读写地址

    output[2:0]                  app_cmd,         //读写命令,001代表读;000代表写

    output                                      app_en,          //app使能

    output [MEM_DATA_BITS-1:0]    app_wdf_data,        //写入DDR3 MIG的数据

    output       app_wdf_end,        //表示写入DDR3 MIG的数据完成。

    output [MEM_DATA_BITS/8-1:0]                app_wdf_mask,

    output                app_wdf_wren,  //表示可以向DDR3 MIG写入数据。

    input [MEM_DATA_BITS-1:0]         app_rd_data,   //从DDR3 MIG中读出数据。

    input                 app_rd_data_end,  //表示从DDR3 MIG中完成读数据

    input                                      
    app_rd_data_valid,         //读数据有效

    input        app_rdy,          //表示DDR3 MIG已经准备好

    input                       app_wdf_rdy,

    input                    ui_clk_sync_rst,  

    input     init_calib_complete  //初始化校验完成

);



assign app_wdf_mask = {MEM_DATA_BITS/8{1'b0}};   
// app_wdf_mask清零

//以下为数据读写的几种状态

parameter IDLE = 3'd0;

parameter MEM_READ = 3'd1;

parameter MEM_READ_WAIT = 3'd2;

parameter MEM_WRITE  = 3'd3;

parameter MEM_WRITE_WAIT = 3'd4;

parameter READ_END = 3'd5;

parameter WRITE_END = 3'd6;

parameter MEM_WRITE_FIRST_READ = 3'd7;

reg[2:0] state;    //状态转换标志

reg[9:0] rd_addr_cnt;   //读地址计数

reg[9:0] rd_data_cnt;      //读数据计数

reg[9:0] wr_addr_cnt;     //写地址计数

reg[9:0] wr_data_cnt;     //写数据计数

reg[2:0] app_cmd_r;

reg[ADDR_BITS-1:0] app_addr_r;

reg app_en_r;

reg app_wdf_end_r;

reg app_wdf_wren_r;

assign app_cmd = app_cmd_r;

assign app_addr = app_addr_r;

assign app_en =app_en_r;

assign app_wdf_end = app_wdf_end_r;

assign app_wdf_data = wr_burst_data;

assign app_wdf_wren = app_wdf_wren_r & app_wdf_rdy;

assign rd_burst_finish = (state == READ_END);

assign wr_burst_finish = (state == WRITE_END);

assign burst_finish = rd_burst_finish | wr_burst_finish;

assign rd_burst_data = app_rd_data;

assignrd_burst_data_valid = app_rd_data_valid;

assign wr_burst_data_req = (state == MEM_WRITE) & app_wdf_rdy ;



always@(posedge mem_clk or posedge rst)

begin

    if(rst)

    begin

             app_wdf_wren_r <= 1'b0;

    end

    else if(app_wdf_rdy)

             app_wdf_wren_r <= wr_burst_data_req;

end



always@(posedge mem_clk or posedge rst)

begin

    if(rst)  
//复位后系数清零

    begin

             state <= IDLE;

             app_cmd_r <= 3'b000;

             app_addr_r <= 0;

             app_en_r <= 1'b0;

             rd_addr_cnt <= 0;

             rd_data_cnt <= 0;

             wr_addr_cnt <= 0;

             wr_data_cnt <= 0;

             app_wdf_end_r <= 1'b0;

    end

    else if(init_calib_complete ===  1'b1)

    begin

             case(state)

                     IDLE:

                     begin

                              if(rd_burst_req)  //IDLE状态下,如果是读数据请求

                              begin

                                      state <= MEM_READ;      //下一个状态设置为READ。

                                      app_cmd_r <= 3'b001;            //设置为读命令

                                      app_addr_r <= {rd_burst_addr,3'd0}; //设置读地址

                                      app_en_r <= 1'b1;           // app使能

                              end

                              else
				if(wr_burst_req)  //IDLE状态下,如果是写数据请求

                              begin

                                      state <= MEM_WRITE; //下一个状态设置为WRITE。

                                      app_cmd_r <= 3'b000; //设置为写命令

                                      app_addr_r <= {wr_burst_addr,3'd0}; //设置写地址

                                      app_en_r <= 1'b1;  // app使能

                                      wr_addr_cnt <= 0;

                                      app_wdf_end_r <= 1'b1;

                                      wr_data_cnt <= 0;

                              end

                     end

                     MEM_READ:

                     begin

                              if(app_rdy)  //READ状态下,读地址

                              begin

                                      app_addr_r <= app_addr_r + 8;

                                      if(rd_addr_cnt == rd_burst_len - 1)  //地址计数完成后,进入MEM_READ_WAIT状态

                                      begin

                                              state <= MEM_READ_WAIT;

                                               rd_addr_cnt <= 0;

                                              app_en_r <= 1'b0;

                                      end

                                      else //地址计数没有完成的情况下,继续地址计数

                                              rd_addr_cnt <= rd_addr_cnt + 1;

                              end

                              

                              if(app_rd_data_valid)  //读数据

                              begin

                                      if(rd_data_cnt == rd_burst_len - 1)  //判断数据是否读完

                                      begin

                                              rd_data_cnt <= 0;

                                              state <= READ_END;

                                      end

                                      else  //如果数据没读完,继续读数据

                                      begin

                                              rd_data_cnt <= rd_data_cnt + 1;

                                      end

                              end

                     end

                     MEM_READ_WAIT:

                     begin

                              if(app_rd_data_valid)   //如果在读等待状态,读数据有效,则继续读数据

                              begin

                                      if(rd_data_cnt == rd_burst_len - 1)

                                      begin

                                              rd_data_cnt <= 0;

                                              state <= READ_END;

                                      end

                                      else

                                      begin

                                              rd_data_cnt <= rd_data_cnt + 1;

                                      end

                              end

                     end

                     MEM_WRITE_FIRST_READ:

                     begin

                              app_en_r <= 1'b1;

                              state <= MEM_WRITE;

                              wr_addr_cnt <= 0;

                     end

                     MEM_WRITE:

                     begin

                              if(app_rdy)   //写地址

                              begin

                                      app_addr_r <= app_addr_r + 'b1000;

                                      if(wr_addr_cnt == wr_burst_len - 1)

                                      begin

                                              app_wdf_end_r <= 1'b0;

                                              app_en_r <= 1'b0;

                                      end

                                      else

                                      begin

                                              wr_addr_cnt <= wr_addr_cnt + 1;

                                      end

                              end

                                      

                              if(wr_burst_data_req)  //写数据

                              begin

                                      

                                      if(wr_data_cnt == wr_burst_len - 1)

                                      begin

                                              state <= MEM_WRITE_WAIT;

                                      end

                                      else

                                      begin

                                              wr_data_cnt <= wr_data_cnt + 1;

                                      end

                              end

                              

                     end

                     READ_END:

                              state <= IDLE;

                     MEM_WRITE_WAIT:

                     begin

                              if(app_rdy)

                              begin

                                      app_addr_r <= app_addr_r + 'b1000;

                                      if(wr_addr_cnt == wr_burst_len - 1)

                                      begin

                                              app_wdf_end_r <= 1'b0;

                                              app_en_r <= 1'b0;

                                              if(app_wdf_rdy)


                                                       state <= WRITE_END;

                                      end

                                      else

                                      begin

                                              wr_addr_cnt <= wr_addr_cnt + 1;

                                      end

                              end

                              else if(~app_en_r & app_wdf_rdy)

                                      state <= WRITE_END;

                              

                     end

                     WRITE_END:

                              state <= IDLE;

                     default:

                              state <= IDLE;

             endcase

    end

猜你喜欢

转载自blog.csdn.net/csdnqiang/article/details/108081393