ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

FPGA实战:MIG IP核配置与DDR3控制器调试全攻略

FPGA实战:MIG IP核配置与DDR3控制器调试全攻略 如果你第一次在FPGA上把MIG的Example Design跑通看到仿真里init_calib_done信号拉高的那一刻大概率会觉得DDR3这事也就这样。但等你真正开始往里面写数据、再把数据读出来比对发现自己写的0x5A5A5A5A从DDR3里读回来变成了0x00A5A55A而且不是固定错位是时不时错那么几个bit的时候你才会意识到DDR3控制器这事水有多深。这篇文章我打算从MIG IP核的配置讲起一直到仿真实战和上板调试把整个链路里最容易踩的坑、最容易被忽略的细节都摊开说。内容主要针对Vivado里的MIG IP核、DDR3控制器配置流程、仿真验证方法以及我在实际项目里遇到的各种匪夷所思的问题。适合刚接触FPGA存储设计的同学也适合那些已经跑了Example Design但还没搞懂MIG内部在干什么的工程师。1. 为什么用MIG而不是自己写DDR3控制器很多初学FPGA的人看到DDR3的第一反应是不就是给地址、数据、读写使能吗我写个状态机不就行了等你真正动手就会发现DDR3控制器的复杂度远超一个简单的RAM接口能覆盖的范围这也是为什么Xilinx要专门做一个MIG IP核来解决这个问题。1.1 DDR3协议的真实复杂度DDR3不是SRAM那种给地址就给数据的简单设备。它内部是一个多Bank、多行的存储结构每次读写都要经历激活ACT、读写Read/Write、预充电PRE还要满足tRCD、tCL、tRP、tRFC、tWTR等一系列时序约束。更关键的是DDR3内部有自动刷新Self-Refresh和Auto-Refresh机制你必须周期性地发送刷新命令否则存储单元的电荷会慢慢漏掉数据就丢了。还有一个很多人容易忽略的点DDR3的数据时序不是靠读取延迟固定多少拍来对齐的它要靠DQS和DQ之间的相位关系来采样。DQS可以看作是伴随数据一起传输的时钟信号写的时候FPGA要负责生成DQS读的时候DDR3颗粒会返回DQS。由于PCB走线长度、颗粒内部的延迟差异DQS和DQ的相位关系在每一片板子上都不一样这就需要控制器在初始化阶段做时序训练Training把读/写路径的延迟校准到位。MIG内部帮你搞定了这些事初始化流程里包含了ZQ校准、读写训练、ODT训练等一系列步骤init_calib_done信号拉高就意味着训练通过DDR3可以正常访问了。如果你自己写DDR3控制器光是这套训练逻辑就够你折腾几个月而且换一块PCB可能又要重新调。1.2 MIG的价值边界它管什么、不管什么MIG帮你解决的是物理层PHY和底层存储器的时序控制它给你暴露出来的接口是CMD/Data通道加一些状态信号。但它不帮你解决业务层的缓存管理、地址映射策略、带宽优化这些问题。换句话说MIG解决的是怎么稳定地读写DDR3至于读什么写什么、怎么高效地读高效地写那是你的事。在实际项目中MIG通常是以IP核的形式集成到你的FPGA工程里。Vivado的IP Catalog里搜索MIGMemory Interface Generator选择对应的FPGA型号然后通过GUI完成配置。配置完成后MIG会生成一个带例化模板的IP核你需要把它实例化到顶层并接好时钟、复位、引脚约束才能开始真正的仿真和调试。2. 硬件设计先行引脚、Bank和时钟的隐患配置MIG之前一定要先看你板子上DDR3颗粒的硬件连接。很多人以为MIG配好就完事了结果综合、实现的时候报出一堆引脚约束错误或者上板之后数据错乱问题其实出在硬件设计阶段就埋下了。2.1 DDR3引脚分组与Bank选择DDR3接口的信号可以分成几类地址/控制信号A、BA、CS、RAS、CAS、WE、CKE、ODT、数据信号DQ、DQS、DM以及时钟信号CK/CK#。在FPGA内部这些信号必须落在特定的引脚分组上MIG配置的时候才能通过引脚校验。一个DDR3的byte lane通常包含8位DQ、1位DQSDDR3是差分DQS就是DQS和DQS#、以及可选的DM数据掩码。一个byte lane必须分配到FPGA同一组IO Bank的相邻引脚上而且DQS必须是该Bank的MRCC/SRCC时钟引脚这样才能保证数据在FPGA内部的采样时序一致。选Bank的时候还有几个硬性条件Bank的VCCIO电压必须满足DDR3的IO电平标准通常是1.5VBank的VREF电压要能正确配置。如果你用板的DDR3颗粒比较多比如64位数据位宽那可能要用到两个Bank甚至更多这时候要特别注意跨Bank的地址/控制信号扇出问题。2.2 时钟与VREF设计DDR3的参考时钟CK/CK#必须由FPGA的差分时钟引脚输入一般接到Bank的MRCC引脚上。MIG会基于这个参考时钟生成所有内部时钟以及输出给DDR3颗粒的时钟。参考时钟的频率和MIG配置的DDR3运行频率有对应关系例如DDR3-800400MHz需要200MHz的参考时钟DDR3-1066533MHz需要266.67MHz这个要严格匹配。VREF这块更容易被忽略。DDR3的地址、控制信号和数据信号都相对于VREF进行判决VREF默认是VCCIO的一半0.75V但也可以在MIG里选择通过FPGA内部生成还是由板上电路供电。如果VREF噪声大就会导致数据采样出错上板后表现为偶发性数据bit翻转。我之前调过一块板子VREF没有做滤波结果高速读写时偶尔报ECC错误后来把VREF电源引脚加上去耦电容才稳定下来。2.3 PCB布线的关键规则DDR3布线核心就一个字等长。数据组DQ/DQS/DM信号要组内等长误差控制在±20mil以内DDR3-800可以放宽到±50mil左右地址/控制信号相对于CK做等长匹配误差控制在±20mil到±50mil之间。阻抗方面单端信号一般控制50Ω差分信号DQS、CK控制100Ω。如果你上板后遇到data training失败优先检查这几项DQS和DQ的等长误差是否超标地址/控制和CK之间的时序余量是否足够ODT片内端接设置是否和颗粒规格、PCB阻抗匹配VREF电源质量这些硬件问题MIG配置界面是救不了的但你在做MIG配置的时候必须知道硬件是什么接法比如PCB上ODT电阻是已经焊死还是通过FPGA控制这直接影响你在MIG里怎么选ODT参数。3. MIG IP核配置界面逐项拆解Vivado里打开IP Catalog搜索MIG双击就会进入MIG配置向导。新版的Vivado2019.1之后MIG配置界面已经很友好了但里面的每个选项背后都有讲究我按实际顺序把它们拆开讲。3.1 控制器选项DDR3、速率与位宽进去之后首先要设置的是Controller Options选择Memory Type为DDR3。接下来是Memory Part这里可以选Xilinx自带的颗粒型号也可以自己手动输入颗粒参数点击Create Custom Part比如容量、位宽、Bank数量、tCK、CL、CWL、tRCD、tRP等。如果你用了一个比较偏门的DDR3颗粒自带的库里面没有那就一定要自己建一个参数填错了上板大概率跑不起来。速率方面频率选项通常有800MHz、1066MHz等对应DDR3-800/1066但具体能跑多快取决于你FPGA的速度等级和PCB走线质量。第一次调试建议保守一点选DDR3-800400MHz先跑通稳定之后再往高速去尝试。数据位宽方面常见的配置是32bit或64bit。位宽越大理论上带宽越高但占用的FPGA引脚也越多而且训练复杂度会增加。如果你只是做小批量的数据缓存32bit往往更实用。MIG里还有一个选项是使用AXI4接口还是使用UIUser Interface原生接口。AXI4接口适合IP间互联但会增加额外的转换逻辑和延迟UI接口更接近底层时序控制更直接我一般自己写用户控制逻辑的话都用UI接口减少不必要的中间层。3.2 突发长度Burst Length与CL/CWL的选择DDR3的突发长度固定支持8以及4但在某些模式下不支持这是DDR3的8n预取架构决定的。在MIG配置界面里有的选项是Read Burst Type默认Sequential这个保持默认就行。CLCAS Latency和CWLCAS Write Latency的选项MIG会根据你选择的Rate和颗粒型号自动计算但你要核对一下是否和DDR3颗粒的实际规格一致。CL决定的是读数据返回的延迟CWL决定的是写数据被接收的延迟。如果这两个值和颗粒的实际要求不符数据采样训练就会失败仿真里表现为init_calib_done拉不起来。我遇到过有些国产DDR3颗粒的时序参数和Micron/Samsung的标准型号有差异直接在MIG里选标准型号会出问题最后是手工创建Custom Part把tRCD、tRP、CL这些参数填成实际值才解决。3.3 引脚分配手动约束还是自动分配MIG里最关键的选项之一是Pin/Bank Selection模式有Fixed Pin Out和Automatically两种方式。Fixed Pin Out是指你自己编辑UCF/XDC约束文件指定DDR3的每个信号对应FPGA的哪个引脚Automatically则是让Vivado根据你的DDR3接口定义自动挑选引脚。如果你手上有一份PCB原理图上面FPGA引脚和DDR3颗粒的连接已经确定那就必须用Fixed Pin Out在MIG配置界面导入或创建一个引脚约束文件。这里要特别注意MIG的引脚约束不是随便写的每个Byte Lane的DQS必须落在Bank的时钟引脚上DQ必须分布在同一个Bank的合法IO位置上。如果你违反了这个规则Vivado在实现阶段会报Place 30-69x之类的错误提示引脚位置不合法。如果选择Automatically生成的例化工程里会带一个自动生成的引脚约束但这个约束可能和你的实际PCB不匹配所以大多数实战项目里都选Fixed Pin Out然后根据原理图手动整理一份引脚映射表。3.4 配置完成后的输出文件点Generate之后MIG会生成一堆文件常用的包括mig_7series_0.vIP核的顶层例化文件mig_7series_0/user_design/用户侧接口逻辑mig_7series_0/user_design/constraints/引脚约束和时序约束mig_7series_0_example/MIG自带的Example Design工程mig_7series_0/user_design/rtl/MIG内部RTL代码我强烈建议第一次使用MIG时先通过Vivado的IP例化向导生成Example Design然后直接对Example Design做综合、实现和仿真。因为Example Design里已经包含了完整的DDR3模型仿真用、初始化逻辑、数据校验逻辑traffic generator你只要跑通它就说明IP核配置和仿真是没问题的。4. 时钟与复位仿真和上板最容易翻车的地方MIG的时序逻辑相当复杂但对外表现出的时钟和复位接口其实很简洁。可恰恰是这几个看似简单的引脚让很多人栽了跟头仿真不畅或者上板完全不动大概率是时钟和复位没接对。4.1 sys_clk从哪里来MIG需要外接一个参考时钟sys_clk有的版本叫sys_clk_i这个时钟的频率要和你配置的DDR3工作频率匹配。例如DDR3-800下需要200MHz的参考时钟。这个时钟建议从FPGA的专用差分时钟引脚引入通过IBUFDS后直接接到MIG的sys_clk端口。有一种常见的错误是把sys_rst和sys_clk的极性弄反。sys_clk是单端时钟输入sys_rst是低有效复位。如果你用了MMCM/PLL生成了一个复位信号而这个复位信号是高有效那MIG会永远处在复位状态init_calib_done自然是拉不起来的。仿真里这种问题最明显时钟有复位一直拉高所有信号都不动。4.2 时序收敛与全局时钟资源MIG内部有大量的高扇出时钟信号比如ui_clk、mem_refclk、clk_ref等这些时钟在综合时都需要使用全局时钟资源BUFG。如果你的工程里还有其他的高速逻辑要注意别把BUFG耗尽。还有一种情况是sys_clk接入的不是全局时钟引脚综合实现时会报错或时序违例。上板调试时MIG对时钟抖动非常敏感。如果你用内部PLL/MMCM生成的时钟作为sys_clk一定要确保这个时钟的抖动特性满足DDR3的要求。之前调一块板子时图省事直接用FPGA内部的PLL从100MHz倍频出200MHz给MIG当参考时钟结果训练偶尔失败最后换成板载专用差分振荡器才稳定。这不是说不能这么用而是你要仔细看MIG的时钟要求低抖动时钟对DDR3来说是刚需。4.3 ui_clk和用户逻辑的时钟域MIG的UI接口会输出ui_clk用户逻辑的所有CMD、Data信号都要在ui_clk的时钟域下工作。注意ui_clk和sys_clk不是同一个时钟它们之间存在相位偏移所以如果你有跨时钟域的逻辑比如从另一个时钟域对MIG做读写操作必须经过异步FIFO或寄存器同步不能直接用另一个时钟域的寄存器去驱动MIG的UI信号。我在仿真和上板都遇到过一个问题初始化成功后向MIG发写命令但数据始终写不进去。后面排查发现是我在用户逻辑里用的是sys_clk来驱动app_cmd、app_wdf_data等信号而MIG在ui_clk时钟域采样这些信号两者频率或相位对不上信号直接被打飞了。正确做法是用户逻辑的所有MIG相关信号全部用ui_clk来驱动或者先经过FIFO做跨时钟域处理。5. 跑通Example Design让仿真波形告诉你DDR3在干什么MIG自带的Example Design是最有价值的参考工程它集成了DDR3仿真模型、Traffic Generator、以及必要的初始化逻辑。第一次接触MIG一定要把Example Design的仿真跑起来眼见为实地看到init_calib_done拉高、Write/Read数据比对成功再动手去改自己的用户逻辑。5.1 Example Design的目录与结构在Vivado里配置完MIG后可以在IP Sources里看到mig_7series_0_example这个顶层。它其实是一个独立的仿真综合工程包含了以下关键模块mig_7series_0_example_top顶层例化MIG IP核traffic_gen_top数据产生器负责产生写数据和对读回数据进行校验ddr3_modelDDR3颗粒的仿真模型sim_gen仿真激励产生逻辑Example Design的RTL就在你的IP生成目录下路径一般在project.srcs/sources_1/ip/mig_7series_0_example/mig_7series_0_example.srcs/sources_1/ip/mig_7series_0/mig_7series_0/user_design/。这些代码是学习MIG用户接口时序的绝佳教材。5.2 修改仿真参数提高仿真速度MIG的仿真最大的痛点是慢。DDR3颗粒模型上电后要先完成上电初始化和训练流程这个时间在仿真里可能对应几十微秒甚至上百微秒的仿真时间而RTL仿真如果一条条跑会非常消耗时间。提高仿真速度有几种办法减少仿真时间如果只是验证初始化流程可以在仿真脚本里设置run 50us不要傻等几十毫秒的仿真时间。Example Design自带的仿真脚本默认会跑比较长的时间来完整测试读写但你可以自己改。关闭波形记录如果你用的是Vivado自带的XSim开启波形记录会显著拖慢仿真速度。只需要看几根关键信号的话可以把所有信号加到波形窗口但只跑一小段时间或者用log_wave -recursive *只记录你要的信号不要全量记录。使用Fast Configuration选项部分版本的MIG支持在配置时选择优化训练流程的模式例如减少训练步数但要注意这并不代表上板也能这样上板仍需完整训练。我个人的经验是仿真时把无关的顶层信号全部不记录只观测init_calib_done、err、cmp_data等关键节点能明显感觉到仿真速度翻倍。5.3 仿真波形里看初始化和读写校验的关键节点Example Design仿真跑起来后在波形窗口里你需要重点关注几个节点sys_clk和sys_rst_n确认时钟频率正确、复位按时释放。init_calib_done这是最重要的一根信号它拉高说明MIG内部针对DDR3颗粒完成了ZQ校准、读写训练等步骤颗粒可以被正确访问。如果仿真跑到几十微秒它还不拉高就要排查时钟、复位、颗粒模型参数。traffic_gen_top/err数据校验错误指示。Example Design会不断产生伪随机地址和数据写入DDR3然后读取回来比对如果出现错误err信号会拉高说明数据一致性出了问题。cmp_data和rd_data在Traffic Generator内部rd_data是MIG读回的数据cmp_data是本地期望的数据两个信号相等时数据校验通过。从波形上你还能看到读写命令的握手过程写命令到来时app_en和app_rdy都要拉高一个周期命令才会被接收写数据通道上要同时配合app_wdf_wren和app_wdf_rdy握手。如果MIG的UI接口没有准备好app_rdy或app_wdf_rdy为低那你的命令或数据必须保持住不能丢。Example Design的Traffic Generator正是严格按照这套握手时序来做的建议对照波形研究它为什么能在错误的时序下仍然能高效工作。5.4 第一次仿真常见问题信号不拉高、报Fatal Error如果你第一次跑Example Design仿真遇到init_calib_done一直不拉高或者波形窗口里出现一堆红X先别急着怀疑MIG。最常见的原因是仿真时间不够很多仿真脚本里run all其实是无限跑到你手动停止但如果你设置了run 1000ns这种很短的时间初始化流程根本走不完。DDR3初始化流程包含了几个毫秒级的延时比如tDLLK、tZQinit仿真时间设短了肯定跑不完。另一种情况是颗粒模型报Fatal Error这通常和DDR3模型的参数、MIG配置的时序参数不匹配有关。检查你配置的Part Number是否正确DDR3模型是根据Part Number里的参数来模拟的如果MIG配置的CL、CWL和模型不一致模型会直接断言失败。6. 亲手写一个读写模块UI接口时序与状态机Example Design的Traffic Generator可以帮你验证DDR3颗粒和MIG IP核本身没毛病但实际项目中你不可能一直用官方自带的Traffic Generator。这时候就需要自己写一个用户逻辑模块通过UI接口对DDR3做读写操作。这个模块在项目中通常叫做读写控制器、DDR3缓存管理器或者叫DMA引擎。写这部分逻辑之前先把UI接口的时序搞清楚。6.1 UI接口信号全解析MIG的UI接口是一组简洁的总线核心信号如下app_rdyMIG准备好接收命令app_en用户侧发送命令使能app_cmd命令类型0表示写1表示读app_addrDDR3物理地址注意不是字节地址MIG有地址映射规则app_wdf_rdyMIG准备好接收写数据app_wdf_wren用户侧写数据使能app_wdf_data写数据总线app_wdf_mask写数据掩码app_rd_data读数据总线app_rd_data_valid读数据有效app_rd_data_end读数据最后一个周期指示这里要强调命令通道和数据通道是独立握手的。写入一条数据时你既要把命令通过app_en、app_cmd、app_addr送出去等待app_rdy为高也要把数据通过app_wdf_wren、app_wdf_data送出去等待app_wdf_rdy为高。两个通道可以并行也可以有错位MIG内部会自动把命令和对应数据匹配起来。6.2 写数据通道的时序细节UI接口的写操作时序关键点是app_wdf_wren必须配合app_wdf_rdy。当app_wdf_rdy为高时你在下一个ui_clk上升沿将app_wdf_wren拉高并把数据放到app_wdf_data上。如果MIG内部的数据缓冲还没准备好app_wdf_rdy会是低这个时候你的数据不能丢必须保持不变继续等待。另外要注意app_wdf_mask的使用。比如你写的是32bit位宽app_wdf_mask就是4bit对应每个字节的掩码。如果把某个bit拉高对应的字节就不会被写入DDR3。这个掩码在调试数据一致性时经常被误用一旦掩码设置错误无论你怎么读都是错数据。6.3 读数据通道的响应读命令发出后MIG不会立刻返回数据而是要经过内存阵列的访问延迟tRCD CL等再加上MIG内部流水线延迟。当数据返回到UI接口时app_rd_data_valid会拉高每个有效周期app_rd_data上就是读出的数据。app_rd_data_end在最后一个数据周期拉高你可以用它来判断一次读突发burst是否结束。读响应和写命令之间没有强制的对应关系。也就是说你连续发多个读命令返回的数据是乱序的也可能是有序的取决于MIG内部对Bank的管理策略。现在MIG默认会尽量保持读数据的顺序但严格的对应关系还是建议用app_rd_data_valid配合FIFO来缓存。6.4 一个简单的读写状态机设计从我自己的项目经验来看UI接口读写状态机一般设计成四个主状态IDLE、ISSUE_CMD、WRITE_DATA、READ_DATA。以一次32bit写操作为例IDLE等待用户发起写请求信号。ISSUE_CMD拉高app_en设置app_cmd0和app_addr等待app_rdy为高命令被接受后跳转。WRITE_DATA拉高app_wdf_wren把数据放到app_wdf_data上等待app_wdf_rdy为高数据被接受后回到IDLE。读操作类似ISSUE_CMD发读命令后进入WAIT_RDATA状态等待app_rd_data_valid拉高读取app_rd_data完成一次读操作。这个简单的状态机只适合单次突发如果想要高效率地连续读写就要设计成FIFO模式不断地把待写数据推入数据FIFO把待读地址推入命令FIFO然后一个流水线逻辑同时去驱动命令通道和数据通道。我自己做数据缓存项目时就用两个异步FIFO 一个命令/数据调度状态机把读写请求缓冲起来DDR3的带宽利用率能提高不少。// 简化的UI写接口示例配合app_rdy握手 always (posedge ui_clk) begin if (!ui_clk_sync_rst) begin app_en 1b0; app_wdf_wren 1b0; end else if (write_request) begin app_cmd 2b000; // WRITE app_addr write_addr; app_en 1b1; app_wdf_data write_data; app_wdf_mask 4b0000; app_wdf_wren 1b1; end else if (app_rdy app_en) begin app_en 1b0; end else if (app_wdf_rdy app_wdf_wren) begin app_wdf_wren 1b0; end end实际工程中上面这段代码还要考虑命令通道和数据通道是否同一拍握手成功。如果app_rdy为高但app_wdf_rdy为低命令被接受了但数据没发出去这就可能导致MIG一直等待该命令对应的数据写操作卡住。所以严谨的做法是要分别用两个状态标志位来跟踪命令和数据通道的传输进度确保都完成之后再发下一条命令。7. 上板调试三个真实踩坑记录仿真能跑通不代表上板就一帆风顺。MIG上板调试时我踩过很多坑这里挑三个最有代表性的把完整排查链路写出来希望能帮你少走弯路。7.1 踩坑一位置约束报错Error [Place 30-694]有一块项目的板子FPGA的DDR3接口在原理图上标注得清清楚楚我在MIG配置里把引脚约束填进去结果综合能过一跑到Implementation阶段就报Place错误大致意思是说某个DDR3信号分配的位置不满足Byte Lane约束。排查路径回到MIG配置界面打开Pin Out部分的验证面板看它提示的Bank里哪些引脚是合法的DQS位置。对比原理图发现我分配的DQS引脚恰好不在MRCC/SRCC位置上而是普通IO脚。和硬件工程师确认PCB上DDR3的DQS信号实际连到了FPGA普通IO脚这本身就不符合DDR3接口要求属于原理图设计失误。最终方案是让硬件工程师把DQS信号改板到MRCC/SRCC引脚或者换Bank重新分配DDR3引脚。这个坑的教训是做DDR3硬件设计之前一定要先查FPGA的引脚手册里哪些Bank支持DDR3以及DQS的合法引脚位置而不是画完原理图再回来让软件去适配。MIG配置界面里的引脚校验功能就是用来提前发现这类问题的。7.2 踩坑二数据偶尔错几个bit但不是固定错位另一块板子DDR3读写大部分时候正常但跑高压环境或者频繁读写下偶尔会读到错误的bit。用ILA抓数据发现错误不是一个byte整体错而是单个bit翻转。排查路径先用MIG Example Design上板测试同样出现偶发错误排除我自己读写逻辑的问题。把DDR3时钟频率从DDR3-1066降到DDR3-800错误频率下降说明是时序余量不足。检查DDR3颗粒规格和MIG配置的ODT、驱动强度设置发现ODT阻值选择偏大接收端的反射噪声增大。调整MIG配置把ODT从RZQ/6调为RZQ/4驱动强度从40Ω调到34Ω再次上板错误消失。这个坑的教训是DDR3上板的偶发bit翻转很可能不是FPGA逻辑的问题而是ODT、驱动强度、VREF这些模拟参数没有调好。仿真模型很难模拟信号完整性问题这些参数只能通过实测去调。如果项目里有高速DDR3需求建议预留几个可以修改的MIG参数方便在测试阶段进行微调。7.3 踩坑三初始化偶尔失败复位几次才能成功曾经有一块板子上电后大概有5%的概率init_calib_done拉不起来必须按一下板子上的复位按键重新初始化才能成功。排查路径用ILA抓MIG的初始化状态信号发现失败时卡在Write Leveling阶段。检查时钟发现DDR3参考时钟在上电初期不稳定板载振荡器输出还没稳定MIG就开始初始化了。检查复位时序确认MIG的sys_rst在上电后立即释放没有等待时钟稳定这违反了MIG对复位时序的要求。修改复位逻辑用XILINX的Power-On Reset检测或者增加延迟释放复位的逻辑确保sys_clk稳定后再释放MIG复位。反复上下电测试问题消失。这个坑的教训是MIG的复位在时钟稳定之前释放就会导致初始化失败。所以上电复位的释放条件不要只看外部复位按键一定要和时钟锁定状态联动。8. 数据通路效率优化把DDR3用满的个人经验MIG配置正确、读写能跑通之后下一个问题是性能。DDR3不是SRAM不是你想什么时候读什么时候读都一个速度的它对访问模式非常敏感。如果你的用户逻辑对DDR3的读写请求太碎、太随机实际带宽可能只有理论值的两三成。下面这些优化经验是我在多个视频图像缓存项目里一点点试出来的。8.1 bank group与bank管理DDR3内部有多个Bank Group比如常见的DDR3-1600/1866就采用4-Bank Group架构。MIG的调度器会在后台管理Bank的激活、预充电和刷新但如果你在用户侧发出的读写命令频繁切换BankMIG需要花费额外的时间在ACT和PRE之间切换这会降低效率。优化思路是把你的读写地址做Bit重排尽量让连续的命令落在不同的Bank上这样MIG可以在Bank之间流水线化操作而不是每次都要等同一个Bank完成tRCD/tRP。MIG配置界面里有一项Address MappingActually这个选项在MIG里是通过BANK_COUNT、ROW、COLUMN顺序来体现的你可以调整地址映射方案让满带宽的连续突发访问能够平均分散到各个Bank和Bank Group。8.2 突发长度与数据位宽的选择DDR3一次burst访问是8n预取即一次命令最少搬运8个内部位宽的数据。以32bit DQ的DDR3为例一次burst就是32bit × 8 256bit 32字节。如果你的用户逻辑只写4个字节那剩余的带宽就浪费掉了MIG还必须额外发送ACT、PRE等命令来管理这次访问。所以实际项目中用户逻辑应该尽可能以整burst为单位进行读写。比如你想缓存一帧图像就按行缓存一行数据凑成连续的多个burst再一次性写入DDR3。同时考虑把MIG的用户数据位宽配成与DDR3位宽一致或通过内部fifo拼接/拆分数据总线减少数据格式转换带来的气泡。8.3 使用读写FIFO削峰填谷DDR3最怕的是读一下停一下再读一下每两次访问之间如果间隔太长MIG会反复做Bank Precharge和Activate。解决这个问题的标准做法是用读写FIFO做缓冲把用户逻辑的非连续请求缓存下来然后在MIG侧合并成连续的读写流。写方向用户逻辑把数据写入写FIFO写控制逻辑攒够一个burst的数据量后再一次性发给MIG。读方向读控制逻辑预取一大块数据到读FIFO用户逻辑需要数据时直接读FIFO不必每次都去请求DDR3。这两个FIFO就像水库一样把上游不稳定的小溪整理成下游恒定的水流DDR3的带宽利用率会有非常明显的提升。在我做过的一个4K视频缩放项目里优化前后DDR3的带宽利用率从不到40%提升到了70%以上。关键就是三点连续突发访问、Bank均匀分散、读写FIFO蓄流。如果你的业务对延迟敏感FIFO的深度要小心设计太大引入延迟、太小又起不到削峰填谷的效果这个要结合你具体的数据速率和DDR3带宽来算通常至少要能容纳一整行数据比如视频一行720p像素对应的数据量。9. 关于MIG调试的一点个人体会MIG本身是一个非常成熟的IP核绝大多数时候它工作得比你想象的还要稳定。真正出问题的环节往往是MIG外围的时钟、复位、引脚约束以及你自己写的用户逻辑。所以调试MIG遇到问题时我的顺序永远是先跑Example Design仿真确认IP核配置没问题再上板跑Example Design确认硬件没问题最后才把用户逻辑接入排查。不要一上来就怀疑MIG内部有bug那个概率非常低更常见的是你某个时序握手没满足或者复位时钟没处理干净。另外有一点想提醒大家MIG配置界面里的很多参数比如ODT、驱动强度、CWL一旦改动了都要重新生成IP核并重新跑一遍实现而且仿真模型也要同步更新。我见过有人直接手改MIG生成的RTL结果仿真能跑、上板一塌糊涂最后发现是因为改了RTL但没有更新对应的时序约束文件。MIG生成的IP核最好只通过配置界面去改参数不要直接编辑内部RTL否则后面升级Vivado版本或者重新生成IP时你的手改全都会丢而且排查问题时会非常头疼。DDR3的调试是个细活但一旦把MIG的运行机制和UI接口时序吃透了整个存储子系统对你来说就不再是黑盒。以后无论是接DDR4、LPDDR还是更高带宽的存储颗粒原理都是相通的。希望这篇文章能帮你把该避的坑都提前避开少熬几个调DDR3的通宵。
返回列表