简介
DDR3 SDRAM常 简称 DDR3 是当今较为常见的一种储存器,在计算机及嵌入式产品中得到广泛应用,特别是应用在涉及到大量数据交互的场合,比如电脑的内存条。对DDR3的读写操作大都借助IP核来完成,本次实验将采用 Xilinx公司 MIG IP核来实现DDR3读写测试。 DDR3相对于SDRAM是双沿触发,读写速度快一倍。相对于DDR2有更高的运行性能和更低的电压。本次实验使用的 DDR3芯片是MT41J256M16HA-125 ,bank位宽为 3,行位宽为 15,列位宽为10,所以它的地址大小等于即 2^28=256M,数据位宽为 16bit,所以容量大小为 256M*16bit,也就是 512MByte。
具体介绍可以看datasheet
DDR 存储
可以把ddr理解为一个方格本,bank就是代表这个本子有多少页,行列地址,可以理解为一页中一行有多少个格子col,一页有多少行row。ddr写数据和地址就是将数据填到相应的格子里。
硬件部分
根据不同的功能引脚有,可将DDR3引脚划分为4类:地址总线引脚、数据总线引脚、控制引脚、电源与参考电压引脚。在原理图上根据分类连接好引脚,可以参考黑金A7的核心板。
布局布线的话,会有硬件工程师和PCB layout工程师进行设计,电源完整性,布局布线,差分,等长,阻抗匹配,EMI问题等。
软件部分
MIG IP核是硬核,左侧是用户接口,右侧是芯片接口。如下图
如果只是单纯想要简洁快速实现DDR读写功能的话,需要着重了解的是用到的用户接口的相关信号,例化配置好 Xilinx公司 MIG IP核 ,然控制各信号进行读写即可。
用户接口信号在后面会详细介绍,下面我们先配置MIG IP。
MIG IP设置
新建MIG IP如下所示,在这里我们不使用AXI接口,下一步;
选择兼容其他相同封装不同型号的FPGA,我们这里不选择。
选择DDR3;
时钟配置Clock Period,即DDR芯片物理侧的IO时钟频率,称之为核心频率;(选择FPGA性能好的片子,DDR频率也可以到更高,手头上的xc7a100tfgg484-2 DDR3能到400M)
物理侧到控制器时钟的比例,可选4:1或2:1;决定了ui_clk的频率;如图配置的话,ui_clk = 400M /4 =100Mhz
选择DDR3的类型,Components指的是DDR3的型号是元件类,服务器用的是RDIMMs和UDIMMs,笔记本那种的插条类是SODIMMs。
数据位宽,由DDR型号决定,但是当FPGA挂了多片DDR时,位宽相应增加,我们这里用了两片,所以位宽为32;
系统时钟输入,建议200M,后面参考时钟可以直接使用系统时钟。
系统时钟是使用200M的差分时钟,低电平复位
50欧电阻,根据硬件而定,不用管,直接NEXT;
新设计 or 管脚已固定
如果硬件已经定了,那么就选下面管脚已固定,然后读取约束文件,设置管脚就好。
引脚约束
DDR3用到的约束引脚较多,建议导入。 用到3种电平: LVCMOS、 SSTL和 DIFF_SSTL。 LVCMOS:全称 Low Voltage Complementary Metal Oxide Semiconductor,低压互补金属氧化物半导体。 SSTL:全称 Stub Series Terminated Logic 短截线串联端接逻辑。 DIFF_SSTL:全称Difference Stub Series Terminated Logic,差分短截线串联端接逻辑。 LVCMOS 的特点是噪声容限大,速度较SSTL 慢;SSTL 速度快,通常要匹配合适的端接电阻,常用于高速内存接口如DDR3;DIFF_SSTL 则是带有差分功能的SSTL。
后面依次NEXT就好了,最后生成。
下面是设置完成后的总结页面;
IP核生成完毕,打开veo文件查看例化文件,就可以添加到自己的工程里使用了。
ddr3_top ddr3_top_inst(
// Inputs
// Differential system clocks and rst
.sys_clk_p (sys_clk_p ), // input sys_clk_p
.sys_clk_n (sys_clk_n ), // input sys_clk_n
.sys_rst (1'b1 ), // input sys_rst
// Inouts
.ddr3_dq (ddr3_dq ), // inout [31:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n ), // inout [3:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p ), // inout [3:0] ddr3_dqs_p
// Outputs
.ddr3_addr (ddr3_addr ), // output [14:0] ddr3_addr
.ddr3_ba (ddr3_ba ), // output [2:0] ddr3_ba
.ddr3_ras_n (ddr3_ras_n ), // output [0:0] ddr3_ras_n
.ddr3_cas_n (ddr3_cas_n ), // output ddr3_cas_n
.ddr3_we_n (ddr3_we_n ), // output ddr3_we_n
.ddr3_reset_n (ddr3_reset_n ), // output ddr3_reset_n
.ddr3_ck_p (ddr3_ck_p ), // output [0:0] ddr3_ck_p
.ddr3_ck_n (ddr3_ck_n ), // output [0:0] ddr3_ck_n
.ddr3_cke (ddr3_cke ), // output [0:0] ddr3_cke
.ddr3_cs_n (ddr3_cs_n ), // output [0:0] ddr3_cs_n
.ddr3_dm (ddr3_dm ), // output [3:0] ddr3_dm
.ddr3_odt (ddr3_odt ), // output [0:0] ddr3_odt
// user interface signals
.app_addr (app_addr ), // input [27:0] app_addr
.app_cmd (app_cmd ), // input [2:0] app_cmd
.app_en (app_en ), // input app_en
.app_wdf_data (app_wdf_data ), // input [255:0] app_wdf_data
.app_wdf_end (app_wdf_end ), // input app_wdf_end
.app_wdf_mask (32'd0 ), // input [31:0] app_wdf_mask
.app_wdf_wren (app_wdf_wren ), // input app_wdf_wren
.app_rd_data (app_rd_data ), // output [255:0] app_rd_data
.app_rd_data_end (app_rd_data_end ), // output app_rd_data_end
.app_rd_data_valid (app_rd_data_valid ), // output app_rd_data_vali
.app_rdy (app_rdy ), // output app_rdy
.app_wdf_rdy (app_wdf_rdy ), // output app_wdf_rdy
.app_sr_req (1'b0 ), // input app_sr_req
.app_ref_req (app_ref_req ), // input app_ref_req
.app_zq_req (1'b0 ), // input app_zq_req
.app_sr_active (app_sr_active ), // output app_sr_active
.app_ref_ack (app_ref_ack ), // output app_ref_ack
.app_zq_ack (app_zq_ack ), // output app_zq_ack
.ui_clk (ui_clk ), // output ui_clk
.ui_clk_sync_rst (ddr_rst ), // output ui_clk_sync_rst
.init_calib_complete (init_calib_complete ),
.device_temp (device_temp )
);
对于mig与DDR3的读写原理我们不需要了解太多,交给mig就可以了。我们需要做的是控制好用户接口,写出正确的用户逻辑,控制好读写时序。想要写好User logic,我们就必须清楚各个用户控制接口的含义:
读写时序
读写时序如下所示:
基本时序如上。app_rdy表示UI已经准备好接收命令了,意思就是说必须要等app_rdy信号拉高了之后,app_en有效,app_cmd命令才能被接收。而且app_rdy信号,不受控制,不管是读还是写,都必须在app_rdy为高的时候进行操作。
写DDR的前提条件是(app_rdy & app_wdf_rdy)全部为高时,给出地址和命令(app_cmd = 3’b000),然后给出写数据的信号(使能与数据),就可以成功写入数据到DDR了。
地址和命令必须时序严格对齐!写数据信号相对来说有三种情况:①完全同步;②可以提前1拍;③最多延迟2拍;但是最好全部时序对齐,不容易出错。
读操作就简单了,在前提条件app_rdy为高时,给出命令(app_cmd = 3’b001)与地址(app_addr),读出的数据往往要晚若干个周期,以valid信号表示数据有效。
连读连写的时序如下图所示
总结:
结合上图分析
(1)写数据时需要先满足app_rdy,app_wdf_rdy,app_en有效,然后发送写命令和地址app_cmd,app_addr,最后写数据app_wdf_wren,app_wdf_data,app_wdf_end
(2)读数据时满足app_rdy,app_en有效,然后发送读命令和读地址app_cmd,app_addr,等待有效信号和数据app_rd_data,app_rd_data_valid