ARTICLE DETAIL

资讯详情

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

高云FPGA实战:GW2A的DDR3控制器配置与LVDS接口通信详解

高云FPGA实战:GW2A的DDR3控制器配置与LVDS接口通信详解 1. 为什么选高云GW2A-LV18PG256C8做DDR3LVDS实战国产FPGA这几年是真起来了高云GW2A系列算是其中出货量很大的一条产品线。我选择GW2A-LV18PG256C8这块芯片做DDR3和LVDS的实战项目核心原因有三个。第一个原因是性价比。LV18这颗芯片的逻辑资源大概是18K LUT级别内置了硬核DDR3控制器同时还支持丰富的LVDS IO。这个配置放在同价位的进口芯片上基本找不到之前用某国际大厂的芯片做类似项目光DDR3软核授权就得额外掏一笔钱。GW2A把DDR3控制器直接做成硬核省掉的授权成本和时间成本非常可观。第二个原因是高云的工具链这几年进步明显。云源软件Gowin YunYuan从早期的各种不顺手到现在已经比较稳定了。我这次项目从建工程到跑通DDR3读写和LVDS通信整个开发流程走下来遇到问题基本能在官方文档和社区里找到对应方案这是一个生态逐步成熟的信号。第三个原因是我踩过坑之后的经验积累。早些年用FPGA做项目DDR3布线是个让人头大的事阻抗匹配、等长控制、分组走线每一处都需要仔细处理。而高云GW2A官方提供了DDR3参考设计和配套的PCB封装严格按照官方设计来做一次成功的概率会高很多。这次项目我把DDR3控制器从配置到读写验证完整走了一遍再把LVDS通信接口打通最终发现两者结合能应用到很多实际场景里比如图像采集与显示、高速数据采集系统。这篇文章我尽量把整个流程写清楚适合手里有高云GW2A开发板、想从零跑通DDR3和LVDS的工程师参考。软件版本是云源软件芯片型号就是标题里的GW2A-LV18PG256C8开发板用的是第三方厂商做的核心板。2. DDR3控制器配置与读写验证实操2.1 硬核控制器的基本特性GW2A系列内置的DDR3硬核控制器官方名称叫DDR3 Memory Controller支持DDR3/DDR3L颗粒数据位宽可以配置为16位、32位我这次用的是16位数据总线搭配一颗常见的DDR3L颗粒容量512MB。控制器IP通过云源软件的IP Core Generator生成配置界面比较直观和Xilinx的MIG、Intel的EMIF工具流程类似但参数设置上更精简一些。配置控制器之前先要明确几个关键参数内存颗粒型号、行列地址位宽和Bank数量数据位宽与ECC是否启用内存时钟频率我配的是400MHz即DDR3-800控制器接口模式高云提供AXI4接口和Native接口两种模式高频内存接口设计里时钟是个关键变量。DDR3控制器的输入时钟和用户逻辑时钟是两回事控制器内部有PLL负责把输入时钟倍频到内存时钟。配置的时候需要输入一个参考时钟频率我用的开发板上有50MHz有源晶振通过PLL内部倍频到400MHz给DDR3物理层使用用户逻辑侧时钟则是100MHz。注意DDR3控制器对参考时钟的占空比和抖动有要求如果开发板上的时钟源质量一般建议先通过示波器确认时钟波形再开始配置。2.2 配置界面关键选项解析打开IP Core Generator选择DDR3 Memory Controller配置界面分几个标签页。Memory Type选择DDR3L因为我板上用的颗粒是低电压版本工作电压1.35V。如果选错成DDR3电压1.5V可能导致初始化失败甚至损坏颗粒这一点要仔细核对颗粒丝印。Data Width选16ECC选项默认关闭这个项目数据量不大不需要ECC纠错能力。Address Configuration部分需要对照颗粒的datasheet填写。我用的这个颗粒是8Bank、行地址15位、列地址10位这些参数如果填错控制器初始化阶段就会卡住读不到正确的模式寄存器配置结果。Frequency Configuration是重要部分。High-Speed Mode选DDR3-800Input Clock Frequency填50PLL Reference Clock Frequency也填50。控制器会自动计算PLL的倍频分频系数。还有一项是CAS Latency这个参数需要根据颗粒的时序参数表来设置一般DDR3-800频率下CL6比较常见但最终要颗粒手册为准。接口模式选择界面我建议新手直接选Native接口。之前很多教程推荐AXI4接口但高云这个硬核控制器的AXI4从机接口封装得不算特别顺手突发长度限制和地址对齐规则有点绕。Native接口简单直接用户逻辑自己写状态机控制读写对理解DDR3时序本质很有帮助。等Native接口跑通了再切到AXI4做大数据量传输也不迟。2.3 初始化时序和用户逻辑设计DDR3控制器上电后会有一段初始化过程包括复位、时钟稳定等待、DQS gate训练、ZQ校准等一系列内部操作。这个过程由硬核自动完成用户逻辑只需要在初始化完成信号拉高之后再向控制器发起读写请求。初始化完成信号在Native接口里叫init_done设计状态机前要把它作为总使能信号。没有init_done置位之前不要向控制器发送任何读写命令否则命令会被直接忽略还会扰乱内部状态。我设计的读写验证状态机分四步等待init_done拉高向特定地址顺序写入一串固定数据0xAA55AA55、0x55AA55AA交替或递增计数写完成之后等待一段时间再向相同地址发起读请求比较读回数据和写入数据是否一致通过LED或串口打印结果Native接口用户逻辑的核心信号包括cmd、cmd_en命令通道支持写、读、刷新等命令addr32位地址输入wr_data、wr_data_en、wr_data_mask写数据通道rd_data、rd_data_valid读数据通道这里有个容易踩坑的地方高云DDR3控制器的地址映射逻辑。Native接口地址是经过编码的地址不是直接用行列地址映射比如功能地址中的某些位对应Bank地址某些位对应行地址。如果写读操作使用的地址不一致数据校验会失败误以为是控制器配置错误。实际排查方法是用一个简单的递增地址模式从0开始连续写读对比哪个bit位对不上结合官方地址映射表文档里叫Address Mapping反查原因。写突发长度固定为8即一次写命令连续传输8个数据所以写128个数据实际上只需要发起16次写命令每次突发跨越两个256MB存储空间的概念需要心里有数。读操作同理读128个数据需要16次读命令。2.4 仿真验证和板级实测配置完控制器、写完用户逻辑不要急着上板。官方IP生成目录下自带了DDR3的仿真模型通常是一个带时序参数的Verilog模型文件搭建一个简单的testbench把控制器实例化和仿真模型连起来先做仿真验证。仿真时需要注意几个点初始化过程仿真时间比较长建议把仿真时间设置到至少200us等待init_done信号拉高读写数据比较逻辑在testbench里做不要让用户逻辑直接输出比较结果这样便于仿真波形查看如果仿真中出现初始化失败优先检查时钟频率配置和地址配置然后再查IP版本是否和软件版本匹配仿真通过后烧录到开发板。我这次实测时遇到一个比较隐蔽的问题开发板上的DDR3颗粒没有正确连接复位引脚导致控制器初始化一直失败。检查原理图发现DDR3复位信号被拉到了FPGA的一个普通IO上而高云硬核控制器的初始化流程里复位信号需要特殊时序配合后来按官方参考设计调整后问题解决。板级实测阶段我习惯的做法是先在内部生成一个递增计数器作为写数据源写进去再读出来读出的数据经过一个比较模块结果输出到LED灯。全对则LED常亮绿色有错误则LED闪烁。这样调试效率很高不需要每次都用逻辑分析仪看。3. LVDS接口通信实现细节3.1 GW2A的LVDS资源与引脚约束DDR3读写跑通之后我做的第二部分是LVDS通信。LVDSLow Voltage Differential Signaling是FPGA板间通信中特别常用的一种电平标准。相比单端信号LVDS用两根差分线传输共模抑制能力强速率可以跑到几百Mbps甚至Gbps级别。GW2A-LV18PG256C8的引脚支持LVDS25、LVDS33等不同电平标准但并不是所有IO都能做LVDS只有特定的差分对引脚才支持。这一点在引脚约束时要格外小心如果随便选两个普通IO配置成LVDS编译会直接报错或者更隐蔽的是编译能过但实际信号完全不对。查阅芯片引脚手册Package Pinout支持LVDS的引脚通常以P/N成对出现比如IO_L1P、IO_L1N这种命名方式。我在工程里用的是Bank 2的一对差分引脚约束文件里的写法是IO_PORT lvds_tx_p LOCATION: A2, IO_STANDARD: LVDS25; IO_PORT lvds_tx_n LOCATION: A1, IO_STANDARD: LVDS25;这里有个细节高云软件里LVDS差分引脚必须同时指定P和N不能只约束P端否则编译时软件无法自动推断N端位置。同时接收端和发送端的IO_STANDARD必须一致都是LVDS25否则信号无法正确解析。提示如果开发板原理图上标注了差分对但引脚命名和芯片手册不一致要优先以芯片手册为准。开发板厂商有时会把差分对引到排针上但排针走线阻抗不连续超过200Mbps速率就很容易产生误码。3.2 LVDS发送与接收的两种实现路径高云FPGA实现LVDS接口有两条路径我这次都验证了。第一条路径是用原语Primitive高云提供了ELVDS_IO这类原语可以配置成发送或接收模式发送时把并行数据串行化输出到差分引脚接收时把差分输入解串成并行数据。优势是可控性强缺点是时序要自己约束好。第二条路径是用IP核。云源软件里有专门的高速率LVDS接口IP支持1:7、1:8等串化比适合把并行数据转成高速串行流常用于摄像头MIPI CSI-2需要额外协议层但底层物理层就是LVDS和显示接口。IP的优势是底层DDR寄存器和延迟链已经自动约束好用户只需要关注并行侧逻辑。我的测试工程比较简单发送端用一个计数器产生并行数据经过LVDS IP发送出去接收端用另一块开发板或者回环测试接收解串后对比数据。单板回环测试时发送差分对直接通过短跳线连接到接收差分对。实际测试时短距离回环和长距离传输的体验差异很大。用5厘米短跳线连接时1.2Gbps速率稳定无误差。换成20厘米的杜邦线双绞连接后误码率明显提升这是因为杜邦线阻抗完全不受控差分信号完整性被破坏。板间高速LVDS通信建议使用专用的差分走线PCB上的阻抗控制在100欧姆差分阻抗。3.3 多通道LVDS的位对齐处理如果只是单通道LVDS回环调试比较简单。但实际项目中往往用到多通道LVDS比如3路RGB接口转LVDS显示或者4通道图像数据传输这时会遇到一个棘手问题通道间偏移Skew。多通道LVDS信号经过PCB走线和连接器各通道的传播延迟天然存在差异接收端解串后各通道并行数据的bit位置可能错位。比如通道0解串后第1个bit是bit0通道1解串后第1个bit可能成了bit6直接拼接数据必然出错。解决方法是做通道对齐常用方案是发送端在数据里嵌入训练序列接收端检测到训练序列后调整各通道的解串bit偏移最终让所有通道对齐。高云的LVDS IP里有一个硬件特性叫Bit Slip专门用来调整解串器的bit偏移。我在测试工程里实现了一个简化的对齐逻辑复位后发送端周期性发送固定训练字0xF0F0接收端监听并行数据当捕获到训练字后对该通道设置一个标志如果多个通道捕获训练字的时刻不同就通过调整对应通道的Bit Slip值直到对齐这个过程如果完全用手写逻辑做工作量不小。好在高云LVDS IP提供了可选的对齐状态输出可以省去一部分设计工作。如果项目里用的是普通IO口自己搭的LVDS收发器Bit Slip就需要自己实现了核心思路都是在接收端串行数据流里做延迟选择调整解串边界。3.4 时序约束与时钟域处理LVDS接口调试中时序约束是最容易被忽略但又至关重要的环节。高云软件里对LVDS差分信号的约束和普通单端信号约束差异很大。首先是输入延迟约束。接收端LVDS信号从引脚进入经过输入缓冲和延迟链从系统同步时钟角度看需要设定input delay约束。这个约束值根据PCB走线长度和串化比估算如果约束值设置不当接收端采样点大概率落在数据翻转区域内出现随机误码。我习惯的做法是先跑一遍静态时序分析看软件给出的setup/hold裕量再根据裕量调整约束值。如果某个通道的时序裕量为负首先检查该通道PCB走线是否有额外过孔或者长度补偿不当。第二个是跨时钟域处理。LVDS接收解串后的并行数据是在接收时钟域Rx Clock下的而用户逻辑和DDR3控制器工作在另一个时钟域两者之间需要做异步FIFO或者握手。我直接调用了高云的异步FIFO IPGowin_FIFO读写时钟分别接接收时钟和用户时钟。FIFO深度按最大数据突发量设置避免溢出。第三个是发送端的位对齐。发送侧并行数据进入串化器之前需要保证数据在正确的时钟沿采样通常用发送时钟的上升沿打一拍再进串化器。高云LVDS IP内部已经处理了这部分如果自己搭逻辑就要格外注意这一点。4. 工程架构设计与整体联调4.1 系统层次划分这个项目整体上分成三个模块DDR3存储模块、LVDS通信模块和顶层控制模块。顶层控制负责接收外部命令我用板上按键和拨码开关来做把数据从LVDS接口接收并写入DDR3或者从DDR3读出数据通过LVDS发送出去。顶层模块的端口定义大致是输入系统时钟50MHz、复位按键、拨码开关控制读写方向、LVDS接收差分对输出LED状态指示、LVDS发送差分对、预留的调试串口逻辑内部实例化DDR3控制器IP、LVDS IP、异步FIFO和数据通路控制状态机。DDR3控制器IP通过Native接口连接到用户逻辑LVDS IP的并行侧数据经过异步FIFO与DDR3数据总线对接。这样的设计结构便于单独调试DDR3部分写一个测试状态机LVDS部分写一个回环测试逻辑各自验证通过后再将两者连起来。4.2 数据通路设计与流控机制数据通路分为两个方向。接收链路是LVDS接收并行数据先进入异步FIFO再通过写状态机写入DDR3。发送链路是DDR3读数据进入另一个异步FIFO再送入LVDS发送端串化输出。流控机制在联调阶段非常重要。LVDS接收速率可能和DDR3写速率不匹配如果接收端数据速率高于DDR3写入带宽FIFO会溢出。我的做法是LVDS接收侧FIFO接近满时暂停接收后面的数据回压信号给发送端DDR3写操作通过突发方式批量进行每次写满一个Page之后仲裁切换发送链路的FIFO空时读状态机自动暂停避免读空数据这套流控机制在数据回环测试中表现稳定连续传输数分钟没有出现FIFO溢出或读空的问题。注意DDR3控制器不支持任意长度的单笔读写突发长度是8的倍数所以FIFO深度和数据传输块大小尽量设计为8的整数倍。实际项目中我把每次DDR3读写的块大小定为64字节对应8次突发逻辑上更简单。4.3 多Die约束与布局考量GW2A-LV18这颗芯片虽然逻辑资源不大但高云更早的GW2A系列部分型号有多Die结构多逻辑单元阵列拼接在布局布线上有一些特殊性。我这次用的LV18不是多Die结构但一些用户可能在更大容量型号上做项目简单说明一下多Die约束的注意事项。高云的多Die FPGA在约束里的体现主要是逻辑锁定Logic Lock区域和引脚规划的物理位置约束。多Die架构的Die间互联延迟比Die内部大如果设计的关键路径横跨了两个Die时序收敛会比较吃力。布局阶段就要把相关逻辑尽量放在同一个Die内尤其是高速接口的收发逻辑和对应IO所在的Die要绑定在一起。云源软件里有物理约束编辑器可以查看引脚对应的Die归属通过设置逻辑区域约束把关键模块限制在指定的Die范围内。如果不做约束布局器可能把用户的逻辑分散到多个Die导致时序问题很难查。4.4 整体联合调试过程联合调试第一步先把DDR3控制器自测跑通此时LVDS部分不参与直接用板上拨码开关控制写读操作确认DDR3读写数据正确。第二步是单独测试LVDS回环把发送差分对用短跳线连到接收差分对确认串行数据能正确收发。第三步再把两套逻辑通过数据通路连接起来采用回环模式测试LVDS发出去的数据进来后写DDR3再从DDR3读出通过LVDS发送到另一端另一端接收后比对。这个回环测试能同时验证DDR3的读写正确性、LVDS的收发一致性、异步FIFO的跨时钟域处理。实测在室温25度环境下连续运行2小时数据比对无错误整个链路是稳定的。调试过程中我用的工具主要是逻辑分析仪抓DDR3控制器接口信号和示波器看LVDS差分波形。如果手头没有逻辑分析仪也可以把关键信号引到空闲的IO上通过板载LED观察状态。不过DDR3控制器接口信号太多用LED观察效率太低建议至少准备一台便宜的逻辑分析仪带宽要求并不高主要是抓控制信号状态。5. 遇到过的坑与排查思路5.1 DDR3初始化失败的几种典型原因DDR3控制器初始化失败是很多人第一个遇到的拦路虎表现是init_done信号长时间不拉高。我调试过程中总结了几种典型原因。第一种是颗粒参数配置错误。特别是行地址和列地址的位宽需要对照颗粒手册仔细填写。有个朋友把行地址15位填成了14位初始化过程中校准逻辑一直在试图访问不存在的地址导致初始化超时。第二种是时钟频率配置错误。控制器对DDR3颗粒的时钟频率有严格要求超过颗粒额定速率会导致时序不稳定。比如标称DDR3L-1600的颗粒配置成DDR3-1600跑没有问题但如果颗粒实际是DDR3L-1333还硬要跑1600就会出现偶发初始化失败。第三种是参考时钟质量差。高云硬核控制器对输入参考时钟的抖动比较敏感如果开发板上的时钟走线过长或者和高速信号交叉抖动会叠加到DDR3时钟上。排查方式是用示波器看波形有条件的用频谱分析仪看相位噪声。第四种是复位时序问题。DDR3颗粒的复位信号和时钟之间有时序要求如果复位信号释放太晚或太早颗粒无法进入正确的初始化状态。高云参考设计里对复位信号做了专门处理照着参考设计接就对了。5.2 LVDS误码与信号完整性排查LVDS通信如果出现误码排查思路和DDR3完全不一样重点放在信号完整性上。先用示波器查看差分信号眼图。如果眼图闭合说明信号质量有问题。常见原因包括链路上的阻抗不连续、发送端驱动能力设置不当、接收端端接电阻不匹配。LVDS标准要求接收端100欧姆差分端接有些开发板上的端接电阻已经做在板内外部再接一个会导致阻抗偏低幅度减小。通道间偏移就通过软件配置的Bit Slip来修正。如果回环测试正常但两个板卡之间传输误码大概率是链路质量出了问题。我遇到过的情况是连接器引脚氧化导致的偶发误码更换连接器后就好了当时排查了很久。5.3 工具链相关的坑云源软件在DDR3和LVDS的IP生成上不同版本有小幅差异。我在用某个新版本软件打开旧IP工程时偶尔会出现IP核被重新生成的提示重新生成后参数全部恢复默认之前的配置就丢了。所以在工程开始前建议固定软件版本避免中途升级。编译报错的排查路径也有讲究。高云软件的报错信息相对友好但偶尔会给出误导性的提示。遇到编译错误优先查看综合日志定位到具体模块再解决。有一次报错提示引脚约束冲突实际原因是另一个模块占用了同一对引脚在综合日志里才能看清楚真正的资源占用关系。约束文件的管理也要注意。高云工程支持.cst文件类似Xilinx的.xdc。如果一个工程里存在多个约束文件要注意优先级和冲突处理规则避免出现部分引脚约束被覆盖的情况。我在工程里只用了一个top.cst把时钟、LED、按键、DDR3接口引脚、LVDS引脚全部集中管理方便排查也方便复用。5.4 常见问题速查表问题现象可能原因处理办法init_done一直为低颗粒参数配置错误重新核对行列地址位宽和Bank数init_done为低参考时钟频率不对检查PLL配置和实际输入时钟频率init_done拉高但读写数据错误用户逻辑地址映射错误对照官方地址映射表反查init_done拉高但写读不一致Native接口命令时序不对在仿真中检查命令通道的时序读写数据偶发错误电源纹波大或温度过高检查DDR3供电电压纹波加强散热LVDS回环正常但远距离误码走线阻抗不连续换成专用的100欧姆差分走线或双绞线LVDS误码且单通道数据错位通道间偏移过大调整Bit Slip做通道对齐LVDS信号无法解析引脚约束了普通IO而非差分对查阅芯片引脚手册确认差分对位置仿真通过但上板不工作复位逻辑跨时钟域对复位信号做异步复位同步释放处理上板偶发死机FIFO溢出或读空检查流控逻辑增大FIFO深度6. 总结一下我踩过的坑和心得这个项目从开始规划到最终跑通我花在DDR3上的时间比预想的多不少。最大的感受是FPGA里跑DDR3真正的难点不在逻辑代码而在对颗粒时序和控制器行为模型的理解。用户逻辑的读写状态机其实很简单谁都能写出来。但一旦遇到写进去读出来不一致、初始化不成功这类问题如果不懂控制器内部的初始化流程和时序参数就只能瞎猜。所以我建议新手在接触高云DDR3 IP时花点时间看一下官方用户手册里的时序图理解init_done之前控制器到底做了哪些事情。这样排查问题时有方向而不是靠运气。还有一个经验是在调试LVDS时一定要有一根高质量的双绞线或者同轴电缆做回环测试。我第一次调试时直接用了几根普通杜邦线绕在一起当差分线用结果怎么调都有误码一度以为代码或者IP配置有问题。后来换用一根短的屏蔽双绞线误码率瞬间降为零。这个教训印象非常深刻信号完整性在高速调试里永远是第一位的。最后分享一个小技巧工程里保存一份完整的IP配置截图放在工程目录下的docs文件夹里。每次用新版软件打开工程如果IP被重新生成可以快速对照截图恢复参数。这个习惯帮我省了很多重复配置的时间。后续如果大家有需要我还可以写一篇关于高云GW2A系列驱动DDR3做图像缓存的进阶文章结合帧缓存和行列切换的应用场景来谈控制器的优化配置。这个内容我这次项目也做了一部分预研后面有时间整理出来。
返回列表