FPGA是否具有高速通信接口和高速内存接口已经是高端FPGA和低端FPGA的分水岭了。低端FPGA一般只提供低速通用IO,速度最高的就是LVDS了。但是LVDS只提供了PHY层的逻辑,即串转并,还需要自己实现数据链路层和应用层的逻辑。
高速FPGA的高速接口非常的丰富,一般包括用于高速通信的Serdes,PCIe硬核;用于高速存储接口的DDR3、DDR4接口。另一方面,同样的接口,不同厂家实现的方案也不同,比如Xilinx提供的DDR接口是用FPGA逻辑实现的软核,而PCIe是直接硬核的资源;而PGL22G这款FPGA的DDR接口是使用硬核的HMEMC。
DDR内存控制器是非常复杂的一个IP,在实现的过程中会使用了非常多的资源,直接使用FPGA资源实现DDR控制器的缺点就是占用资源,但是优点是实现灵活,根据引脚分配规则可以分布在各个BANK上,这对于引脚分配和PCB布局布线是非常方便的,尤其在使用多个颗粒或DIMM实现32位或者64位总线内存的时候。而使用硬核的DDR控制器刚好相反,占用逻辑资源少,但是只能在固定位置。比如PGL22G包含两个硬核HMEMC,分别在BANK L1/L2和BANK R1/R2,可以看到HMEMC的位置靠近器件封装的两侧,可以直接和引脚相邻,这样的布局应该是方便布线和时序。
图1.HMEMC在PGL22G中的位置
根据PANGO官方的数据手册,HMEMC是可以bypass,从而使用软核实现,个人感觉有这个需求的应该是很少的吧?
图2.HMEMCdatasheet描述
这里可以看出DDR控制器的组成有两个主要部分:DDRC和DDRPHY,如图3图4所示。这个看综合后RTL视图也可以看到,例化了DDR控制器之后,会在ipcore目录下生成相应的RTL代码。主要是对DDR控制器硬核资源进行配置和例化,而一些复位控制,链路训练等是通过逻辑资源实现的。有兴趣的可以直接看源代码,只不过可读性差了点。
图3.HMEMC硬核DDRC
图4.HMEMC硬核DDRPHY
再来看HMEMC的使用方法,可以通过IP_Compiler例化DDR控制器,最多支持3组AXI,1个128bit,2个64bit。在配置IP的时候可以选择使用哪组,或者全部都用。支持多组AXI总线是有必要的,因为项目中经常有多个模块需要访问DDR,支持多个端口可以减少使用外部的Crossbar做多端口的仲裁和互联。DDR控制器输入的时钟不需要硬件上额外提供,直接使用系统时钟即可,在内部使用PLL倍频到DDR速率。这里我有一点困惑,因为系统时钟是50Mhz,但是DDR控制器最高速率支持1066Mhz,也就是PLL的输出频率是533Mhz,并不是整数倍分频的。这里PGL22G的PLL真的可以做到这一点,还是工具有问题?(Xilinx的FPGA中只有MMCM才能实现分数倍频率综合)
DDR3带宽测试
带宽测试的基本方法是:向DDR里写入数据(可以是递增的数或伪随机数),然后读出进行比对,如果出错则立即停止,否则对整个地址空间进行读写,然后记录所用的时间,即可计算出带宽。
首选对DDR控制器进行配置和例化,选择一组128bit的AXI总线,DDR速率配置为800Mhz。顶层主要包括两个模块,ipsl_hmemc_top和ddr_wr_ctrl,ddr_wr_ctrl使用DDRIP输出的100Mhz时钟ui_clk作为时钟。通过状态机,首先对整个DDR空间进行写入测试,然后再读出整个空间进行比对,写入的数据为递增的数据。对整个写入和读出过程进行计时,计时精度1us。
图5.DDR读写测试工程RTL视图
计时值的读出是通过debug_core读出的,这样可以不使用其他通信接口而简化设计。这里需要注意如果计时值在顶层没有输出连接的话,会被综合器优化掉,在添加调试信号的时候会搜索不到这个网络。这里有两种方法可以避免未连接的端口被优化掉:一种是使用Synplify的综合Directive如“Syn_keep”、“ syn_preserve”告诉综合器不要优化;另外一种是直接将计数值通过组合逻辑连接到IO上。这里使用第二种方法,通过组合逻辑连接到LED上。计数值直接通过debug_core读出,测试的开始触发是通过检测按键的边沿。
// LED active low
assign led = {clk_led, pll_lock&ddrphy_rst_done&ddr_init_done,(|test_time), err_flag};
DDR工作在800Mhz,物理位宽16bit,理论物理带宽为800Mhz*16bit = 12.8Gbps;
AXI总线频率100Mhz,总线位宽128bit,理论带宽为100Mhz*128bit = 12.8Gbps;
二者带宽是匹配的,即通过AXI总线可以测出DDR的最大带宽。
ddr_band_width = 128MB / test_time
在操作DDR控制器AXI接口的时候有几个地方一定要注意,特别是对AXI4协议不太熟悉的朋友。axi_awsize指的是AXI的总线的有效位宽,这里需要和总线的实际进行区别,比如128bit的总线是可以传输64bit/32bit/16bit/8bit的数据,称为Narrow Burst。所以axi_awsize才决定了实际传输有效数据的位宽,这里设为4,即2^4=16Byte。axi_awburst指定了地址的增长模式,这里设为2’b01,即地址递增。
assign axi_awid = 4'h0 ;
assign axi_awlen = 8'hFF ;
assign axi_awsize = 3'b100; // 128b = 16B = 2^4
assign axi_awburst = 2'b01 ; // incremental burst
通过布局布线的结果,我们可以很直观的看到,使用硬核资源,只占用了非常少的逻辑资源。
图6.HMEMC布局布线结果