赞
踩
以前在学习和工作中,都是使用的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存储芯片连接方式如下图示。
和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存储器芯片连接的通信通道。
信号名称 | 输入/输出 | 功能描述 |
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的接口信号。
可以发现,当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]}。
在IP Catalog中搜索“mig”,即可找到Memory Interface Generator,然后双击打开;
选择引脚兼容的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控制器的设计。
首先,需要生成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跑一下仿真,上手做设计其实难度也不大。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。