第四章:FPGA中LVDS资源:Xilinx 7系列LVDS原语与Altera/Intel LVDS IP核
好,咱们进入正题。这一章聊的是FPGA里那些跟LVDS打交道的硬核资源。说白了,就是告诉你怎么用Xilinx和Altera(现在叫Intel了)的器件,把差分信号送出去、接进来。
我刚开始接触LVDS的时候,总觉得这东西很神秘。后来发现,其实FPGA厂商早就帮你把大部分麻烦事处理好了。你只需要调用几个原语,或者拖一个IP核,剩下的交给工具链。但话说回来,你要是不知道这些原语背后在干什么,出了问题可就抓瞎了。
4.1 Xilinx 7系列LVDS原语:IBUFDS、OBUFDS、IOBUFDS
Xilinx 7系列里,LVDS相关的原语就那么几个。我个人习惯把它们分成三类:输入、输出、双向。咱们一个一个看。
4.1.1 IBUFDS:差分输入缓冲器
IBUFDS,全称是Input Buffer Differential Signal。它的作用很简单:把一对差分信号(P和N)转成单端信号,送到FPGA内部逻辑去处理。
我在项目中遇到过一个问题:板子上LVDS时钟进来,结果内部逻辑死活采不到正确的数据。查了半天,发现是IBUFDS的IOSTANDARD没配对。嗯,这里要注意,IBUFDS的例化其实很简单,但约束不能省。
// IBUFDS 例化示例
IBUFDS #(
.DIFF_TERM("TRUE"), // 使能内部差分终端电阻,100欧姆
.IOSTANDARD("LVDS") // 指定IO标准为LVDS
) ibufds_inst (
.O(sig_single), // 输出:单端信号,连接到内部逻辑
.I(sig_p), // 输入:差分正极(P)
.IB(sig_n) // 输入:差分负极(N)
);
这里有个关键参数:DIFF_TERM。你想想看,如果板子上已经焊了100欧姆的终端电阻,那这个参数就该设成"FALSE"。否则两个电阻并联,阻抗变成50欧姆,信号质量会变差。我曾经因为这个被折腾了一整天。
4.1.2 OBUFDS:差分输出缓冲器
OBUFDS是输出的对应物。它把FPGA内部的单端信号,转成一对差分信号送出去。
// OBUFDS 例化示例
OBUFDS #(
.IOSTANDARD("LVDS") // 指定IO标准
) obufds_inst (
.O(sig_p), // 输出:差分正极(P)
.OB(sig_n), // 输出:差分负极(N)
.I(sig_single) // 输入:来自内部逻辑的单端信号
);
这个原语没什么复杂的。但我要提醒你一句:OBUFDS的输出电流是固定的,大约3.5mA。这意味着你的传输线阻抗必须匹配,否则反射会搞得你怀疑人生。
4.1.3 IOBUFDS:双向差分缓冲器
IOBUFDS用得少一些,但如果你要做双向LVDS总线(比如某些ADC/DAC的控制接口),它就派上用场了。
// IOBUFDS 例化示例
IOBUFDS #(
.IOSTANDARD("LVDS")
) iobufds_inst (
.IO(sig_p), // 双向:差分正极
.IOB(sig_n), // 双向:差分负极
.I(sig_in), // 输入:要发送的数据
.O(sig_out), // 输出:接收到的数据
.T(tri_enable) // 控制:1=输出,0=高阻(输入模式)
);
注意那个T引脚。当T=1时,IOBUFDS处于输出模式;T=0时,处于输入模式。这个切换时机要把握好,我曾经见过有人没加死区时间,结果收发冲突,直接把IO口烧了。
4.2 Altera/Intel LVDS IP核
Altera这边,做法跟Xilinx不太一样。他们更推荐用IP核,而不是直接例化原语。原因嘛,我觉得是因为Altera的LVDS硬核跟SerDes绑定得更紧,用IP核能自动处理很多细节。
4.2.1 ALTLVDS IP核
在Quartus里,你可以在MegaWizard Plug-In Manager里找到ALTLVDS。这个IP核支持发送和接收两种模式,还能做串并转换。
我个人习惯用它的接收模式来做高速ADC数据采集。举个例子,一个10位、800Mbps的LVDS ADC,你直接用ALTLVDS接收,它会自动把串行数据转成10位并行,同时生成位时钟和帧时钟。
| 参数 | 说明 | 我的建议 |
|---|---|---|
| deserialization factor | 串并转换因子(1-10) | 根据ADC位数设,别设太大,否则PLL压力大 |
| input data rate | 输入数据速率(Mbps) | 必须跟ADC输出一致 |
| pll reference clock frequency | PLL参考时钟频率 | 建议用整数倍关系,别用小数PLL |
| use external PLL | 是否使用外部PLL | 多通道同步时建议开启 |
这里有个坑:ALTLVDS的PLL锁定时间。上电后PLL需要一段时间才能锁定,这段时间内输出的数据是不可靠的。我一般会在逻辑里加一个锁定检测,等PLL锁定后再开始采集数据。
4.2.2 LVDS SERDES IP核
对于更高速度的应用(比如1Gbps以上),Altera提供了LVDS SERDES IP核。它比ALTLVDS更底层,支持更高的速率和更灵活的配置。
说实话,这个IP核我用的不多。因为大多数情况下,ALTLVDS已经够用了。但如果你要做像JESD204B这样的高速接口,那就必须上SERDES了。
4.3 LVDS引脚约束规则
这部分我觉得是最容易出问题的。很多新手把代码写好了,综合也过了,结果一跑实现,报一堆错误。为什么?引脚约束没做对。
4.3.1 差分引脚对
LVDS信号必须成对出现。在Xilinx里,P和N引脚是固定的,比如IO_L1P_T0_15和IO_L1N_T0_15是一对。你不能随便把P和N分配到任意两个引脚上。
在约束文件里,你只需要约束P引脚,工具会自动把N引脚分配好。但要注意,P和N必须是同一个BANK里的相邻引脚。
# Xilinx XDC 约束示例
set_property PACKAGE_PIN A8 [get_ports data_p]
set_property IOSTANDARD LVDS [get_ports data_p]
# N引脚会自动分配,不需要手动约束
4.3.2 电气标准与驱动强度
LVDS的电气标准必须明确指定。在Xilinx里是IOSTANDARD LVDS,在Altera里是LVDS。千万别忘了,否则工具会默认用LVCMOS,那信号根本没法看。
驱动强度方面,LVDS是固定的,不需要你设。但有些FPGA支持可编程的预加重(Pre-emphasis),这个在长距离传输时很有用。我做过一个项目,传输距离超过1米,不加预加重眼图都闭上了。
4.3.3 终端电阻
这个前面提过。Xilinx的IBUFDS内部可以集成100欧姆终端电阻,Altera的某些器件也支持。但我的建议是:能用外部电阻就用外部电阻。为什么?因为内部电阻的精度和温度稳定性不如外部电阻。高速信号对阻抗匹配很敏感,差一点就可能出问题。
DIFF_TERM is not supported on this pin。查了半天,发现是那个BANK不支持内部终端。
4.3.4 时钟与数据的相位关系
LVDS接口通常伴随一个源同步时钟。这个时钟跟数据之间的相位关系,必须在约束里明确。是中心对齐还是边沿对齐?这个决定了你的时序裕量。
我一般会在原理图阶段就跟硬件工程师确认好。如果时钟和数据是中心对齐的,那接收端需要用PLL做相位调整。如果是边沿对齐的,那可能需要用IODELAY来微调。
嗯,说到IODELAY,这又是一个大话题。咱们后面章节再细聊。
好了,这一章的内容就到这里。LVDS资源其实不复杂,但细节很多。你只要记住:原语是基础,IP核是工具,约束是关键。把这三点吃透了,LVDS接口对你来说就是小菜一碟。