以前在学习和工作中,都是使用的Intel的开发平台,前些时间在某宝买了块米联客的ZYNQ7035板卡来学习。因为对Xilinx的平台并不熟悉,所以首先使用MIG核设计一个DDR3控制器测试一下PL端的DDR3来练手。本文就简单记录一下使用ZYNQ7035开发平台测试PL端DDR3的过程吧。
一、软硬件环境
本文用于验证DDR3控制器所使用的软件版本为Vivado 2021.1,FPGA板卡硬件详情如下:
-
FPGA板卡是MIZ7035FC,FPGA芯片型号是XC7Z035FFG900-2;
-
PL端搭载4片镁光DDR3L存储器芯片,兼容MT41K256M16 RE-125,单片DDR3L容量为512MB*16bit,内存主频高达1600MHz,每片的容量为2^15*2^10*2^3*16bit=4Gbit,通过4片DDR3扩展组成了16Gbit容量的DDR3内存,4片DDR3L存储芯片连接方式如下图示。
二、MIG IP核介绍
-
MIG内部结构
和Quartus一样,Xilinx的Vivado开发套件同样为用户提供了高效、灵活的外部存储器接口解决方案,能够快速实现FPGA与外部DDR SDRAM高速存储器之间的数据交互。7系列MIS所提供的MIG(Memory Interface Generator) IP核包含了用户接口、存储器控制器以及物理层(PHY),结构示意图如图2.1所示。7系列FPGA的MIG(Memory Interface Generator)是一个集成了控制器与物理层的存储器接口,可以用于连接FPGA内部的用户逻辑与外部的DDR3/DDR2存储器芯片,其内部结构如下图所示。MIG IP核用户手册--UG586。
可以看到,整个MIG IP核的左侧User Interface(UI,用户接口),用于连接控制MIG IP核的用户逻辑;右侧是Physical Interface,即用于和DDR3存储器芯片交互的物理层接口,产生DDR3芯片工作所需的具体控制、命令和数据等时序,并直接驱动DDR3芯片的引脚。在实际使用时,我们只需要搞清楚左侧的UI的接口时序即可控制MIG IP核完成对DDR3存储器的数据读写操作了;然后对Physical Interface只需要做物理上的引脚分配,将其与DDR3存储器的引脚连接到一起就可以了。
-
User FPGA Logic:即用户逻辑,是MIG IP核之外的部分,用于向MIG产生读操作、写操作相关的请求、地址和数据等;
-
User Interface Block and User Interface:用于连接MIG与用户逻辑的接口,用户逻辑通过UI与MIG进行交互,控制MIG执行读数据或写数据操作;
-
Memory Controller and Native Interface:Memory Controller用于处理UI接收到的各种请求,并对其进行排序以优化数据吞吐量和传输延时;Native Interface则是向User FPGA Logic提供了向存储器发起读写请求以及数据存取机制;
-
Physical Layer and Physical Interface:Physical Layer是用于产生DDR3存储器所需的地址、数据、控制等信号时序的;Physical Interface则是MIG和DDR3存储器芯片连接的通信通道。
-
User Interface侧接口说明
信号名称 | 输入/输出 | 功能描述 |
sys_clk_i | input | MIG系统时钟输入,用于生成内部逻辑、移相器参考时钟等 |
sys_rst | input | 系统异步复位信号,低电平有效,复位脉冲至少需要5ns |
ui_clk | output | 用户接口时钟,频率为DDR3工作时钟频率的1/2或1/4 |
ui_clk_sync_rst | output | 用户接口的同步复位时钟,高电平有效 |
init_calib_complete | output | ddr3初始化完成,高电平有效 |
app_addr | input | UI侧读/写请求对应的地址 |
app_cmd | input | UI侧用于选择读操作或写操作的命令,1表示读,0表示写 |
app_en | input | UI侧输入使能信号,只有app_rdy有效时,app_en才有效 |
app_rdy | output | UI准备好接收读写操作对应的命令 |
app_wdf_rdy | output | MIG内部的FIFO可以接收数据的标志信号 |
app_wdf_data | input | 用户逻辑写入MIG的数据 |
app_wdf_end | input | 当前周期是写入的最后一个数据 |
app_wdf_mask | input | app_wdf_data的掩码信号,每一个bit对应一个byte |
app_wdf_wren | input | app_wdf_wren和app_wdf_rdy都有效时,app_wdf_data才会被写入 |
app_rd_data | output | MIG从DDR3读取并输出给用户逻辑的数据 |
app_rd_data_end | output | 表示当前周期输出的数据是此次读操作的最后一个数据 |
app_rd_data_valid | output | app_rd_data_valid为高时,表示app_rd_data有效 |
注:1、app_wdf_end和app_rd_data_end什么时候拉高,跟Memory Controller和DDR3的时钟比例有关系。2、我们使用MIG时,用户逻辑与MIG交互时可以使用上面的User Interface,也可以使用AXI4接口,这里先只介绍User Interface的接口信号。
-
User Interface侧接口时序
-
命令通道
可以发现,当app_rdy和app_en同时为高电平时,命令(app_cmd)和地址(app_addr)才会被MIG所接收。
-
写数据通道
对于写操作,命令(app_cmd、app_addr、app_en、app_rdy)和数据(app_wdf_data、app_wdf_wren、app_wdf_end)可以不用同步寄存,也就是说--命令可以先于数据被输入,也可以后于数据被输入,但是数据和命令之间的延迟不可以超过2个时钟周期;并且,只有在app_wdf_rdy有效时数据才能寄存,这是因为MIG内部有一个FIFO用于存储接收到的数据,只有当此FIFO处于ready(app_wdf_rdy为1)状态且app_wdf_wren与app_wdf_end都有效时才能接收数据app_wdf_data。另外,当UI侧的数据速率与PHY侧的DDR3时钟速率比为4:1时,app_wdf_end与app_wdf_en两者的时序一样;当UI侧的数据速率与PHY侧的DDR3时钟速率比为2:1时,app_wdf_end与app_wdf_en两者的时序不一样,app_wdf_end每拉高两个周期,app_wdf_en在第二个周期拉高;这块跟我们对MIG IP核的配置有关系。
-
读数据通道
读操作的时序比较简单,只需要在MIG准备好(app_rdy为1)时,给入命令即可控制MIG从DDR3突发数据返回给用户逻辑。
需要注意的是,虽然三个通道相互独立,但是写数据通道与读数据通道共用命令通道,因此,在UI侧不能同时给入读操作的命令和写操作的命令,这一点也和DDR3存储器的分时读写特点是一致的。
-
地址映射
UI侧输入的地址和Memory Controller的地址有两种映射方式:BANK_ROW_COLUMN和ROW_BANK_COLUMN,可以在配置MIG IP核时选择不同的映射方式。例如:当DDR3的Bank地址位宽为3、Row地址位宽为15、Column地址位宽为10时,则BANK_ROW_COLUMN方式下,app_addr的值为{Bank[2:0],Row[14:0],Column[9:0]},ROW_BANK_COLUMN方式下,app_addr的值为:{Row[14:0],Bank[2:0],Column[9:0]}。
三、MIG IP核配置
-
在IP Catalog中搜索“mig”,即可找到Memory Interface Generator,然后双击打开;
- 在接下来的界面中直接点击"Next",进入下一个界面,根据自己的需求配置基本信息,然后点击"Next";
-
选择引脚兼容的FPGA,此次使用的芯片是xc7z035-ffg900-2,所以保持默认型号即可,直接点击"Next"进入下一步;
-
存储器型号选择”DDR3 SDRAM“,然后点击"Next"进入下一步;
-
控制器配置:
Clock Period:配置为2000ps即可,DDR3的工作时钟频率为500MH,实际的传输频率为1GHz,这个根据自己项目中所需要的数据传输带宽设置即可。
PHY to Controller Clcok Rate:PHY与控制器的时钟频率比(一般是2:1或4:1)。2:1时,UI侧app_wdf_data的位宽是DDR3物理内存接口位宽的4倍,延迟更低但是会因为FPGA逻辑的时序限制导致内存接口频率折损;4:1时,UI侧app_wdf_data的位宽是DDR3物理内存接口位宽的8倍,数据速率较高。一般来说,当工作频率较高时,选择4:1比较合适。
Memory Part:选择自己的板卡上DDR3存储器型号或者是兼容的型号就行;
Memory Voltage:DDR3存储器的工作电压,板卡上的DDR3是LPDDR3,所以其工作电压设置为1.35V即可;
Data Width:DDR3 RANK的实际数据位宽,这里选择64(16*4);
Data Mask:勾选则会产生实际的数据掩码引脚,不勾选的话能够提高引脚效率,一般SDRAM都有数据掩码的功能,所以这里勾选上;
Number of Bank Machines:用于对部分或全部BANK进行控制,最大可选8个,值越大可以操作的内从容量越大,消耗的资源也越多;
ORDERING:用来控制MIG是否对它收到的读/写指令进行重新排序以提高总线效率,Normal表示允许,Strict 表示禁止,严格按照UI输入的顺序执行。此处选择 Normal,保证控制器具有较高的效率。
-
Memory Option:
Input Clock Period:MIG核系统输入时钟周期,需要是PLL生成的时钟;
Read Burst Type and Length:突发类型选择,突发类型有顺序突发和交叉突发两种,选择顺序突发(Sequential),突发长度固定为 8,用于配置DDR3的模式寄存器;
Output Driver Impdance Control:DDR3输出驱动阻抗控制,此处设置为 RZQ/7,RZQ的值为240Ω,根据DDR3的芯片手册可知;
RTT:DDR3的片上终结电阻配置,可进行动态控制,选择 RZQ/4即60Ω;
Controller Chip Select Pin:片选管脚引出使能,选择enable即可,表示生成片选信号 cs#并控制外部DDR3;
BANK_ROW_COLUMN:地址映射方式,选择BANK_ROW_COLUMN形式即可。
-
FPGA Options:
System Clock:配置MIG的输入系统时钟的类型,可以是单端时钟、差分时钟或No Buffer模式,这里选择No Buffer模式即可,由PLL提供时钟;
Reference Clock:MIG核的参考时钟输入,设置为“Use System Clcok”,表示使用System Clcok作为参考时钟使用;
System Reset Polarity:系统复位信号的极性,设置为低电平有效即可;
Debug Signals Control:用于设置是否需要把MIG IP核的一些如校准状态之类的调试信号引出来,如果引出则会被自动添加到 ILA或VIO,此处可以不需要引出调试信号,选择 “OFF”即可;
Sample Data Depth:采样深度设置,当“Debug Signals Control”设置“OFF”时,此选项不可配置;
IO Power Reduction:IO 管脚节省功耗设置,启用此功能;
XADC Instantiation:XADC补偿使能,若使能则MIG核会调用XADC进行温度监控。
-
接下来两个界面可以直接选择默认配置即可;
-
Pin Selection:这个界面主要是配置DDR3芯片的引脚编号及电平属性,首先点击Read XDC/UCF,导入板卡配套的UCF文件,然后点击Validata进行验证,验证不报错才能点击“Next”进行下一步操作;
-
后面的步骤就不需要再多说了,直接点击”Next“,直到最后一个界面时点击”Generate“生成IP核即可完成MIG核的配置。
配置完IP核之后,我们可以通过对Vivado提供的Example Design工程仿真以进一步搞清楚MIG IP核的UI接口时序,方便后续完整DDR3控制器的设计。
四、MIG IP核仿真
-
首先,需要生成Example Design工程,右键选中刚才生成的MIG IP核,在出现的菜单栏中会有一个Open IP Example Design,点击Open IP Example Design即可生成Open IP Example;
-
找到刚才生成的Example Design工程,双击.xpr文件即可打开Vivado工程;
-
在Vivado工程首页直接点击“Run Simulation”即可打开仿真,这个过程可能会需要一会时间;打开仿真后可以在Scope中将MIG核的信号添加到波形窗口,以便观察MIG的UI侧信号时序,当然,如果有需要也可以把DDR3存储器模型也添加到波形窗口,这样可以观察到DDR3存储器的PHY层时序,有助于我们更好的理解DDR3的工作原理;
-
经过大约55us的运行后,MIG完成对DDR3的初始化校准,然后接下来就可以对DDR3进行读写测试了,可以看到MIG的UI侧信号时序跟我们之前分析的是一致的;
-
可以发现,每产生一个读/写命令,app_addr相应的偏移8个地址单元,这是因为MIG的UI侧数据总线位宽为512,而DDR3的PHY层数据总线位宽为64,两者之间是8倍的关系;另外,观察UI侧的时钟频率与PHY侧的时钟信号可以发现,UI侧的时钟(ui_clk)周期为5000ps,而PHY侧的时钟(ddr3_ck_p_fpga)的周期为1250ps,两者之间是4倍的关系,但是由于UI侧是单边沿传输,而PHY侧是双边沿传输,因此,两者之间的数据传输速率是4*2倍的关系,这样数据和地址就一一对应上了。
搞清楚UI接口信号时序后,接下来就可以利用MIG来设计完整的DDR3控制器了。
五、工程设计与仿真
相比较而言,Xilinx的MIG核仿真比较简单,只需要在我们的Testbench中例化DDR3仿真模型就可以直接使用Xsim仿真了,可以不需要设计仿真脚本。
genvar r,i;
generate
for (r = 0; r < CS_WIDTH; r = r + 1) begin: mem_rnk
if(DQ_WIDTH/16) begin: mem
for (i = 0; i < NUM_COMP; i = i + 1) begin: gen_mem
ddr3_model u_comp_ddr3
(
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p_sdram),
.ck_n (ddr3_ck_n_sdram),
.cke (ddr3_cke_sdram[r]),
.cs_n (ddr3_cs_n_sdram[r]),
.ras_n (ddr3_ras_n_sdram),
.cas_n (ddr3_cas_n_sdram),
.we_n (ddr3_we_n_sdram),
.dm_tdqs (ddr3_dm_sdram[(2*(i+1)-1):(2*i)]),
.ba (ddr3_ba_sdram[r]),
.addr (ddr3_addr_sdram[r]),
.dq (ddr3_dq_sdram[16*(i+1)-1:16*(i)]),
.dqs (ddr3_dqs_p_sdram[(2*(i+1)-1):(2*i)]),
.dqs_n (ddr3_dqs_n_sdram[(2*(i+1)-1):(2*i)]),
.tdqs_n (),
.odt (ddr3_odt_sdram[r])
);
end
end
if (DQ_WIDTH%16) begin: gen_mem_extrabits
ddr3_model u_comp_ddr3
(
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p_sdram),
.ck_n (ddr3_ck_n_sdram),
.cke (ddr3_cke_sdram[r]),
.cs_n (ddr3_cs_n_sdram[r]),
.ras_n (ddr3_ras_n_sdram),
.cas_n (ddr3_cas_n_sdram),
.we_n (ddr3_we_n_sdram),
.dm_tdqs ({ddr3_dm_sdram[DM_WIDTH-1],ddr3_dm_sdram[DM_WIDTH-1]}),
.ba (ddr3_ba_sdram[r]),
.addr (ddr3_addr_sdram[r]),
.dq ({ddr3_dq_sdram[DQ_WIDTH-1:(DQ_WIDTH-8)],
ddr3_dq_sdram[DQ_WIDTH-1:(DQ_WIDTH-8)]}),
.dqs ({ddr3_dqs_p_sdram[DQS_WIDTH-1],
ddr3_dqs_p_sdram[DQS_WIDTH-1]}),
.dqs_n ({ddr3_dqs_n_sdram[DQS_WIDTH-1],
ddr3_dqs_n_sdram[DQS_WIDTH-1]}),
.tdqs_n (),
.odt (ddr3_odt_sdram[r])
);
end
end
endgenerate
然后,在Vivado界面Run Simulation,这个过程会需要点时间,大约仿真55us后,DDR3初始化训练校准完成(init_calib_complete拉高),然后我们可以看到我们设计的用户逻辑在对DDR3进行数据写入和读出,而且,读出的数据和写入的数据是一样的,证明我们设计的DDR3控制器基本是可以实现对DDR3的读写的。实际上,上板运行也是正确无误的。
六、总结
本文主要简单介绍了Vivado中使用MIG核设计DDR3控制器的过程,相比较而言,Vivado的MIG核的接口时序比Quartus的EMIF核的接口时序要略微复杂,但是其仿真过程要简单的多,相对来说比较容易上手。实际上,不管多么复杂的IP核,只要弄清楚了UG,能够有一个Example跑一下仿真,上手做设计其实难度也不大。