平台:XC7K325T板卡
DDR3:两片MT41J256M16TW-107,共1GB,数据总线32bit
环境:Vivado 2019.2
IP:Memory Interface Generator(MIG 7 Series)
官方手册:ug586 (7Series Devices Memory Interface Solutions v4.2)
本实验使用了两片镁光的 MT41J256M16TW-107 DDR3芯片:
单片数据总线为16bit、深度32M、8个Bank,总共16 x 32M x 8 = 512MB
上图是具体不同型号的DDR3所对应的参数,可见以 256M16 结尾的存储器有 256M个深度、16bit位宽,256M中可拆分为8个Bank,每个Bank深度为32M,总共 32M x 8 x 16 = 512MB。
本实验中用了两片DDR3,将地址总线接一起,数据总线并列成32bit,总共1GB。
上图是 256 Meg x 16的功能框图,中间的从Bank0~Bank7 memory array 的8个正方形就是是存储器内部的8个Bank。Row address MUX(行地址复用器)和Bank control logic(逻辑块控制器)共同控制Bank x row address latch and decoder(某逻辑块的行地址锁存),这样就锁定了将要操作的存储空间的Bank和row;再通过column address latch(列地址锁存)选通一列,这样就唯一地确定了DDR3中的一块存储空间。
DDR SDRAM是以牺牲速率为代价换取同等面积下的存储大小,拥有比同等面积的SRAM更大的存储空间,但同时也导致了DDR的控制极为复杂,DDR是靠往电容中存储电荷来存储二进制信息,但电容总有漏电流存在,这就代表了电容中的数据不能长时间存在,所以在DDR的控制步骤中需要定时对数据空间充电(刷新),上图中的 Refresh counter 就是定时对DDR进行刷新所用的定时器。
DDR的操作如此复杂,在实际工程开发中不可能自己去一行一行编写驱动,包括在数字芯片设计中,对DDR的使用也均是购买其他公司已经成熟的IP。Xilinx公司就提供了一款DDR/SDRAM控制器IP:Memory Interface Generator(MIG),MIG 可以大大简化DDR操作时序,繁杂的DDR物理接口时序已经在IP中替我们完成了,我们需要做的只是对IP的 User Interface 进行操作,节省了开发时间。
如下图,是 用户端逻辑—MIG控制器—DDR3物理层 的连接关系图:
app_addr[ADDR_WIDTH – 1:0]:input信号,当前请求的地址数据。 app_cmd[2:0]:input信号,当前操作命令,3‘b000为写操作、3’b001为读操作。 app_en:input信号,高电平表示当前地址数据有效。 app_rdy:output信号,高电平表明UI已准备好接受命令,只有当此信号为1时读写操作才能被有效执行。比如假设地址数据使能、写数据使能,但 app_rdy 为0,也无法将数据写入MIG的FIFO中。 app_rd_data[APP_DATA_WIDTH – 1:0]:output信号,当前读取到的数据。 app_rd_data_end:output信号,此高电平有效输出指示当前时钟周期是app_rd_data []上输出数据的最后一个周期。 仅当app_rd_data_valid为active-High时有效。(这个信号在用户逻辑中没用) app_rd_data_valid:output信号,高电平表示当前的 app_rd_data 数据有效。 app_wdf_data[APP_DATA_WIDTH – 1:0]:input信号,当前将要写入的数据。 app_wdf_end:input信号,此高电平有效输入指示当前时钟周期是 app_wdf_data 上输入数据的最后一个周期。 app_wdf_mask[APP_MASK_WIDTH – 1:0]:input信号,当前输入数据的掩码,mask为1的位对应的数据将被屏蔽掉。(这个信号本实验用不到,写0) app_wdf_rdy:output信号,该输出表明写数据FIFO已准备好接收数据。 当app_wdf_rdy = 1’b1和app_wdf_wren = 1’b1时,接受写入数据。 app_wdf_wren:input信号,高电平表示 app_wdf_data 数据有效。 init_calib_complete:output信号,高电平表示MIG控制器校准结束,可以对DDR进行操控(使用时必须先等待此信号,否则无法正确操作)。 ui_clk:output信号,MIG输出的用户端时钟,在4:1模式下是DDR主频的1/4。 ui_clk_sync_rst:output信号,MIG输出的用户端复位信号,高电平复位。 rst:MIG复位信号。 clk:MIG输入时钟,也是DDR的主频。 其他信号用不到,不用关注。 |
ddr3_dq:DDR3数据总线 ddr3_dqs_n:DDR3数据选通反相端 ddr3_dqs_p:DDR3数据选通同相端 ddr3_addr:DDR3地址总线 ddr3_ba:DDR3块地址输入 ddr3_ras_n、ddr3_cas_n、ddr3_we_n:DDR3命令输入端 ddr3_reset_n:DDR3复位 ddr3_ck_p:DDR3输入时钟同相端 ddr3_ck_n:DDR3输入时钟反相端 ddr3_cke:DDR3时钟使能 ddr3_cs_n:DDR3片选,用于使能命令解释器 ddr3_dm:DDR3输入数据掩码 ddr3_odt:DDR3启用内部终端匹配电阻 |
在 IP Catalog 中搜索 Memory Interface Generator,双击打开出现以下页面,点击 Next ,出现 MIG Output Options 。
1)MIG Output Options
2)Pin Compatible FPGAs
引脚兼容的FPGA页面列出了所选系列中具有相同封装的FPGA。 如果从MIG工具生成的引脚需要与其他任何FPGA兼容,则应使用此选项来选择必须与引脚兼容的FPGA。
单击“下一步”显示“存储器类型选择”页面。
3)Memory Selection
此页面显示所选FPGA系列支持的所有存储器类型。选择DDR3 SDRAM控制器类型,单击下一步以显示“控制器选项”页面。
4)Controller Options
此页面显示了可以选择的各种控制器选项。
如果设计具有多个控制器,则对每个控制器重复“控制器选项”页面。 此页面最多分为九个部分。 分区数取决于所选的内存类型。 控制器选项页面还包含以下下拉菜单,用于修改设计的不同功能:
5)Memory Parameter Option
该功能允许选择各种有关存储模式的寄存器值。在初始化期间,将模式寄存器的值加载到加载模式寄存器中。
1、读取突发类型和长度-DDR2和DDR3 SDRAM仅支持突发长度8(BL8)。
2、输出驱动器阻抗控制-输出驱动器阻抗控制设置DRAM上的输出驱动器阻抗。 列出的选择取决于所选的特定DRAM。 RZQ为240Ω。 例如,如果选择RZQ / 6,则输出驱动阻抗为40Ω。
6)FPGA Options
1、系统时钟–此选项为sys_clk信号对选择时钟类型(单端,差分或无缓冲区)。 选择“无缓冲区”选项时,不会在RTL代码中实例化IBUF原语,并且不会为系统时钟分配引脚。
如果在没有执行更改的情况下实现了从MIG为No Buffer选项生成的设计,则设计可能会由于未针对sys_clk_i信号实例化的IBUF而无法实现。 因此,对于无缓冲区方案,需要将sys_clk_i信号连接到内部时钟。
对于已经分配了系统输入时钟且满足第210页上的“时钟”中指定的所有规则的设计,必须选择“无缓冲区”选项。
2、系统复位电平–可以选择系统复位的电平(sys_rst)。 如果将选项选择为active-Low,则参数RST_ACT_LOW设置为1;如果设置为active-High,则参数RST_ACT_LOW设置为0。
3、降低I/O功耗–该选项通过在控制器处于空闲状态时自动禁用DQ和DQS IBUF来降低平均I/O功耗。
4、HR Bank的内部终端匹配电阻–内部终端选项可以设置为40、50或60Ω或禁用。 此选择仅适用于HR Bank。
然后在 IO planning Options 中选择 fix pin out ,在下一个页面中绑定xdc文件至DDR3引脚:
在上图中点击 Read XDC/UCF ,绑定xdc文件,确认无误后点击 Validate 验证IO绑定有效性,有效性验证通过后点击 Next 。
后续的几个页面与配置无关,均点击 Next 即可。
1)命令路径
如下图所示,当用户逻辑app_en信号被断言并且app_rdy信号从UI被断言时,UI会接受命令并将其写入FIFO。 每当取消声明app_rdy时,UI都会忽略该命令。 用户逻辑需要将app_en以及有效的命令和地址值保持为高,直到断言app_rdy为止(断言在这里就是有效的意思)。
说白了就是当app_en和app_rdy都有效时app_addr才会被打入FIFO中,编写Verilog时可以在改变app_cmd后判断app_rdy信号是否有效,若有效则拉高app_en将命令打入。
2)写数据路径
如下图所示,写数据通道的地址和数据的时序各自独立,共兼容三种时序关系:
1.写数据与相应的写命令一起出现。
2.在相应的写入命令的前一个周期写入数据。
3.写数据在相应的写命令之后出现,但不应超过两个时钟周期的限制。
对于注册了写命令后输出的写数据,addr / cmd允许的最大数据延迟为两个时钟。(如下图,“注册写命令” 是指app_en拉高后再拉低,如果没拉低就没有最大延迟2个周期的限制)
当断言app_wdf_wren且app_wdf_rdy为高电平时,写数据被寄存在写FIFO中(图1-76)。 如果取消断言app_wdf_rdy,则用户逻辑需要将app_wdf_wren和app_wdf_end保持为高电平以及有效的app_wdf_data值,直到断言app_wdf_rdy。
实际上当app_wdf_rdy拉高的同时将app_wdf_wren和app_wdf_end立即拉高即可。
3)读数据路径
下图是4: 1模式下的读取时序,UI会按请求的顺序返回读取的数据,并且在断言app_rd_data_valid时有效。 app_rd_data_end信号指示每个读取命令突发的结束,并且在用户逻辑中不需要。
可以使用一个D触发器,在app_rd_data_valid断言时将app_rd_data的数据打入触发器中,但需要注意的是此时缓存后的数据和app_rd_data_valid差一个时钟,若想使用app_rd_data_valid指示其他模块,需要将其打一拍与缓存后的数据对齐。
1、Brust Length 8
突发传输(Brust):是指在相邻存储单元中连续进行数据传输,提高数据传输带宽的技术。
突发长度(Brust Length):指连续进行数据传输的次数
Brust Length 8(BL8):指突发长度为8,每次突发传输遍历连续的8个存储空间,每次突发传输结束后 app_addr 需要 +8,在本实验中单次传输32bit数据,所以一次BL8的突发传输读写的数据长度为8x32=256bit。
2、PHY与控制器时钟比率
DDR3是时钟双边沿采样,即一个时钟周期内的上升沿和下降沿均可以进行数据操作,相比于普通的SDRAM,可以将带宽提高2倍,本实验中的输入时钟为400MHz,那么MIG控制器对DDR3的接口速度为400M x 2 = 800M。
本实验中的时钟比率为4:1,则用户时钟 ui_clk 为 400MHz / 4=100MHz。那么如下图所示,一个用户时钟周期对应DDR3的4个时钟周期、8个时钟边沿,也就是在一个用户时钟周期内,DDR3可以被突发读写8次,也正好对连续的8个地址空间进行操作,将长度为256bit的数据突发8次,每次并行传输32bit。
在本实验中,使用了一个状态机:在空闲状态 STATE_IDLE 接收到 init_calib_complete 的断言后等待1秒,进入写操作状态 STATE_WRITE ,在此状态中写1024个周期(不是1024个数据,因为中间有很多等待app_rdy的过程),然后进入等待状态 STATE_WAIT ,在此状态中将命令信号app_cmd先设置为读取模式,然后等待8个周期,进入读操作状态 STATE_READ ,在此状态中读1024个周期,然后进入结束状态 STATE_END ,在此状态中初始化操作信号,然后跳回读操作状态 STATE_WAIT ,并一直读取下去(意思就是只复位后写入一次,然后一直在读)。
为什么本实验只写一次然后一直读呢?是因为要验证写入数据是否全部有效,所以就写了一次,怕多次写入后覆盖掉前面写操作中可能存在的漏写现象。
module ddr3_read_write
(
input wire sys_clk , // 系统时钟
output wire calib_done , // 校验完成,拉高表示初始化结束
input wire rstn , // 异步复位
output wire locked , // MMCM锁定信号
// DDR3物理接口
inout [31:0] ddr3_dq , // DDR3数据总线
inout [3:0] ddr3_dqs_n , // DDR3数据选通反相端
inout [3:0] ddr3_dqs_p , // DDR3数据选通同相端
output [14:0] ddr3_addr , // DDR3地址总线
output [2:0] ddr3_ba , // DDR3块地址输入
output ddr3_ras_n , // DDR3命令输入端
output ddr3_cas_n , // DDR3命令输入端
output ddr3_we_n , // DDR3命令输入端
output ddr3_reset_n , // DDR3复位
output [0:0] ddr3_ck_p , // DDR3输入时钟同相端
output [0:0] ddr3_ck_n , // DDR3输入时钟反相端
output [0:0] ddr3_cke , // DDR3时钟使能
output [0:0] ddr3_cs_n , // DDR3片选,用于使能command decoder
output [3:0] ddr3_dm , // DDR3输入数据掩码
output [0:0] ddr3_odt // DDR3启用内部终端匹配电阻
);
// MIG DDR3 IP核例化线网定义
(*mark_debug="true"*) wire[28:0] app_addr ; // 用户要访问的DDR3地址
(*mark_debug="true"*) wire[2:0] app_cmd ; // 命令:读、写命令
(*mark_debug="true"*) wire app_en ; // MIG IP用户接口使能
(*mark_debug="true"*) wire[255:0] app_wdf_data ; // 待写入数据
(*mark_debug="true"*) wire app_wdf_end ; // 与写使能一起使用
(*mark_debug="true"*) wire app_wdf_wren ; // 写使能
(*mark_debug="true"*) wire [255:0] app_rd_data ; // 读出的数据
(*mark_debug="true"*) wire app_rd_data_end ; //
(*mark_debug="true"*) wire app_rd_data_valid ; // 读出的数据有效标志位
(*mark_debug="true"*) wire app_rdy ; // DDR3就绪信号,表示没有正在读写
(*mark_debug="true"*) wire app_wdf_rdy ; // 写就绪,表示没有正在写
(*mark_debug="true"*) wire ui_clk ; // IP对外提供的用户时钟
wire ui_rst ; // IP对外提供的复位信号
wire ddr3_clk ; // MIG IP时钟信号
// MIG DDR3 IP核例化register定义(状态机用)
reg[28:0] app_addr_reg ; // 用户要访问的DDR3地址
reg[2:0] app_cmd_reg ; // 命令:读、写命令
reg app_en_reg ; // MIG IP用户接口使能
reg [255:0] app_wdf_data_reg ; // 待写入数据寄存器
(*mark_debug="true"*) reg [255:0]app_rd_data_reg ; // 读出的数据寄存器(滞后一拍)
(*mark_debug="true"*) reg app_rd_data_valid_reg ; // 读出数据有效标志寄存器(与app_rd_data_reg对齐而滞后一拍)
// 寄存器与DDR3 IP核连线
assign app_addr = app_addr_reg ;
assign app_cmd = app_cmd_reg ;
assign app_en = app_en_reg ;
assign app_wdf_data = app_wdf_data_reg ;
assign app_wdf_wren = app_en_reg & app_rdy & app_wdf_rdy & ( app_cmd_reg==3'b0 ) ;
assign app_wdf_end = app_wdf_wren ;
// 状态机相关定义
(*mark_debug="true"*) reg [3:0]state_reg ; // 状态寄存器
reg [31:0]fsm_cnt_reg ; // 状态机计数器
parameter STATE_IDLE = 'd0 ; // 空闲状态
parameter STATE_WRITE = 'd1 ; // 写状态
parameter STATE_WAIT = 'd2 ; // 写完等待状态
parameter STATE_READ = 'd3 ; // 读状态
parameter STATE_END = 'd4 ; // 结束状态
parameter FSM_IDLE_TIME = 100000000 ; // 空闲状态持续的周期:1秒
parameter FSM_WRITE_TIME = 1024 ; // 写数据持续的周期
parameter FSM_WAIT_TIME = 8 ; // 写完等待的周期
parameter FSM_READ_TIME = 1024 ; // 读状态持续的周期
// 三段状态机:状态转移
always@(posedge ui_clk or posedge ui_rst)begin
if(ui_rst)begin
state_reg<='d0;
fsm_cnt_reg<='d0;
end
else begin
case (state_reg)
STATE_IDLE:begin
if(calib_done=='b1)begin
if(fsm_cnt_reg
在工程中开启 ILA分析仪核,加入mark_debug的信号,综合、布线、生成比特流,下载至板卡中。按下板卡上的复位键,在1秒之内点击 ILA中的 Run trigger 按钮 出现如下界面:
上图为ILA抓取的DDR3正在进行写操作时的时序图 。可见,当app_rdy、app_wdf_wren、app_wdf_end和app_en均断言时,DDR3开始写入数据。
上图为ILA抓取的DDR3正在读取数据的时序图。可见,当 app_rd_data_valid 断言后,开始正确输出数据到 app_rd_data_reg 中。