FPGA实现CameraLink转SFP光口:Aurora 8B10B架构与工程实践 📅 发布时间:2026/9/8 6:07:57 👁 浏览次数: FPGA实现CameraLink转SFP光口Aurora8B10B架构拆解与工程实现记录做图像类FPGA项目的人迟早会遇到CameraLink接口。工业相机、医疗设备、高速检测线满世界都是CameraLink。但CameraLink有个很痛的问题传输距离。线缆超过5米就开始心慌10米基本属于玄学电信号衰减、时钟抖动、地电位差随便一个都能让你画面闪成雪花。而另一头SFP光口轻轻松松跑几百米到几公里速率还能干到几Gbps。所以CameraLink转SFP光口这个需求在工业现场一直非常真实。这篇博文就完整拆解我用Xilinx FPGA实现CameraLink转SFP光口的全过程涉及GT Transceivers Wizard的线速率配置、Aurora 8B10B核的编解码机制、4套工程源码的组织方式以及大量上板调试时才可能踩到的坑。适合已经有一定FPGA基础、正在做视频传输产品、或者正在被相机接口协议折磨的朋友。如果你只是刚会把LED点亮这篇文章部分内容可能稍深但思路部分仍然值得先看。1. 整体设计思路为什么是Aurora 8B10B而不是别的1.1 CameraLink转光口的本质是什么先想清楚我们要干的活。CameraLink接口本质上是一组LVDS差分信号对数据通道在Base配置下是4对 像素时钟对再加上串行通信SerTC/SerTFG和相机控制信号CC1-CC4。像素时钟从一个低速的20MHz到最高的85MHz都有数据速率由像素时钟直接决定。正确地讲CameraLink转光口要做的事情并不是“格式转换”那么简单而是要解决两个问题把CameraLink的并行TTL/LVDS信号和像素时钟打包成可以在高速串行链路上传输的连续比特流。在接收端把这些比特流重新还原成CameraLink的时序让后端图像采集卡感知不到中间经过了光纤。所以这个项目从架构上看天然就是两个大方向发射端做CameraLink接收打包高速串行发送接收端做高速串行接收解包CameraLink发送。1.2 方案选型Aurora 8B10B、自定义协议、SRIO三者对比设计一开始最纠结的问题是光口上的传输协议到底用什么我当时列过一张对比表这里直接放出来给各位参考方案协议开销实现难度灵活性适用场景Aurora 8B10B低约20%低直接用Xilinx IP核中帧格式需自己定义点对点视频传输、无主机场景自定义8B10B协议低高需要自己写编解码和对齐逻辑高有特殊封装需求、不想被IP核限制SRIO / PCIe中高很高高需要和CPU交互、多设备组网最终选了Aurora 8B10B。一个核心原因是Aurora是Xilinx官方免费提供的轻量级传输协议聚焦在“可靠地把数据从A点搬到B点”至于数据是什么含义完全由用户上层定义。这种特性特别适合视频流场景——我们只需要一个稳定的管道不需要像TCP/IP那样复杂的握手和重传机制。再加上8B10B编码天然解决了DC平衡和时钟恢复问题对于跨设备的光口传输来说硬件上最稳。最重要的是Aurora 8B10B IP核内部直接集成了GT Transceivers的适配逻辑。我们只需要配置好GT线速率和参考时钟剩下的通道对齐、字节序处理、错误检测IP核全包了。省下的开发时间不是一两天而是一两个月。1.3 4套工程源码为什么会是4套很多人看到“4套工程”第一反应是为什么不直接搞一个万能工程第一版我也是这么想的做了个CameraLink Full配置自适应线速率的超级工程结果调试起来痛苦万分。问题不在于代码逻辑而在于不同应用场景下的约束差异太大了。CameraLink有Base、Medium、Full三种配置数据传输量差好几倍光口速率从1.25G到6.6G甚至10G都有参考时钟源在不同板卡上接的bank还不一样。一个工程想兼容所有场景意味着大量的参数化和条件编译出问题的时候排查链路极其困难。所以我干脆把工程拆成4套每一套都针对一个典型使用场景做深度优化工程序号适用场景CameraLink配置光口速率说明工程一工业相机图像采集Base单通道2.5Gbps最常见需求像素时钟≤60MHz工程二高分辨率/高帧率相机Full三通道5.0Gbps像素时钟≥60MHz需要通道绑定工程三光纤自发自收测试无需CameraLink2.5Gbps用来验证GT和Aurora链路排除光路问题工程四远距离中继/扩展Base6.6Gbps面向更长距离、更高可靠性的场景这样拆分之后每个工程结构清晰调试问题的时候不会被无关逻辑干扰。工程三尤其重要第一次上板调试的朋友务必先把回环工程跑通再接入CameraLink数据这是能让你少掉一半头发的方法。2. GT Transceivers Wizard与Aurora 8B10B核心配置细节2.1 线速率与参考时钟先把账算明白GT Transceivers是Xilinx系列FPGA内部的高速串行收发器在7系列叫GTP/GTX/GTH/GTY在UltraScale系列叫GTH/GTY。它们承担了把并行数据变成高速串行比特流的工作是光口方案的物理层基础。而GT Transceivers Wizard这个IP核则是用来配置这些收发器的参数。但是在打开Vivado之前几个关键数字必须先算清楚否则后面全是坑。第一个数字线速率。线速率 有效数据带宽 / 编码效率。Aurora 8B10B的编码效率是8/10也就是80%。CameraLink Base配置的像素时钟最高85MHz数据位宽是64bit8个字节其中图像数据最多64bit加上控制信号看具体封装。那么有效数据带宽就是 85MHz × 64bit 5.44Gbps。但实际工程中我们通常不会跑到这么满一般留出20%左右的余量。所以2.5Gbps的线速率只能覆盖到55MHz左右像素时钟的场景这也是为什么工程二需要Full配置5G线速率。第二个数字参考时钟。GT的参考时钟和线速率之间有一个固定的分频关系。对于GTX收发器通常是线速率除以某个数等于参考时钟。比如线速率2.5Gbps参考时钟用125MHz2500 / 20 125。这个“20”是GT内部固定的参数不同器件可能略有差异配置的时候需要对照数据手册确认一下PLL的反馈分频范围。我当时用的一个Zynq-7000系列的板子GTX参考时钟接在了MGTREFCLK0上接了125MHz的晶振。选这个频率非常普遍因为Aurora核官方例程就是围绕125MHz参考时钟设计的资料多出问题容易查。2.2 GT Transceivers Wizard的几项关键选择在Vivado里创建GT Transceivers Wizard IP核时以下参数是我实测下来最影响成败的Protocol Stack选择Start from Scratch不采用预设协议模板。这样GT核会更干净方便后面让Aurora核来控制它。Line Rate和前面计算的保持一致建议打成固定值写在工程文档里。Reference Clock选择实际板卡上的时钟频率不要贪方便乱选。GT核会产生一个低速user clock输出这个时钟就是后面Aurora核和用户逻辑的工作时钟。TX/RX Data Width建议让GT核自动生成由线速率和参考时钟决定通常Aurora核会接管这个配置。Encoding/Decoding选择8B10B编码。如果你打算让Aurora核来处理编码GT核里可以不需要选但让GT核也选上8B10B并不冲突因为Aurora核的编码器和GT核的编码器是串联关系实际情况下最终只由一层的设置生效。这里有个容易迷糊的点Aurora 8B10B核在IP配置界面里本身就有线速率和参考时钟的设置而GT Transceivers Wizard里也有。它们之间的关系是GT核是底层物理层Aurora核是上层链路层。在Vivado里创建Aurora 8B10B核时如果你勾选了“Include GT Transceivers Wizard”那么Aurora核会自动帮你例化一个GT核此时你只需要配置Aurora核的线速率它会自动把参数传给GT核。但配合我调试的工程习惯我通常会手动分开建立两个核GT核负责物理层Aurora核使用外部GT接口模式。这么做的好处是GT核的调试接口可以直接用ila观察链路不通的时候能快速定位问题出在物理层还是链路层。2.3 Aurora 8B10B核的接口形态怎么选Aurora 8B10B IP核配置时有几种接口形态Framing接口帧接口和Streaming接口流接口。Framing接口提供帧起始/结束信号用户数据被组织成一帧一帧发送适合数据包结构明显的场景。Streaming接口没有帧边界的概念数据像是流水一样源源不断适合视频流这种持续突发数据的场景。我选择了Streaming接口。原因很简单CameraLink送出来的图像数据是一行一行持续的每行之间有行消隐帧之间有帧消隐但总的数据方向永远是从相机到采集卡几乎不会反方向传大量数据。用Framing接口反而还要额外处理帧边界标识增加了逻辑负担。另外Aurora核有一项“Little Endian”和“Big Endian”的字节序选项这个必须和你的数据打包模块保持一致。图像数据如果这里搞反了出来的画面颜色通道会两两对调RGB会变成BGR别问我怎么知道的。2.4 时钟域这个项目最容易被忽略的暗坑CameraLink侧的工作时钟是相机给的像素时钟PaClockAurora核侧的工作时钟是GT恢复出来的user clock。这两个时钟既不频率相同也不相位相关所有跨越这两个时钟域的数据都必须经过异步FIFO。这是整个项目里最容易出问题的地方。很多朋友上来就把像素时钟域的数据直接接到Aurora核的接口上结果链路一跑起来就偶尔丢一拍图像上出现一条横线。原因就是跨时钟域没有做异步处理。异步FIFO的深度建议至少是图像一行数据的1.5倍这样即使行消隐期间因为时钟频率抖动带来的瞬时累积差值也不会瞬间写满或读空。我当时用的FIFO深度是2048×64bit实测下来各种时钟抖动都能扛住。3. CameraLink侧解码与数据映射实操剖析3.1 CameraLink时序先感动自己再感动采集卡CameraLink协议里最重要的是三组信号FVAL帧有效、LVAL行有效、DVALID数据有效它们和像素数据D[0:27]一起被封装到4对LVDS数据通道中每像素时钟周期传输28bit数据。Base配置下一个像素时钟周期里CameraLink物理层实际传的数据是28bit其中低24bitD0-D23是图像数据高4bit是控制信号FVAL、LVAL、DVALID、SPARE。所以当我们在FPGA内部接收时一个周期拿到的是28bit但图像传感器通常输出的是8bit、10bit、12bit或16bit的像素深度也就是说需要连续几个像素时钟的数据才能拼出一个完整的像素。举个例子一个8bit灰度相机像素时钟每个周期输出8bit有效数据。那么28bit里面只有D[7:0]是图像数据D[8:23]可能是接地的也可能被相机厂商复用了。这个时候数据映射就很关键如果直接把28bit塞进64bit的数据通路接收端还原时就会错位。更常用的做法是在发射端把连续几个像素时钟的8bit数据拼接成64bit甚至128bit的Aurora帧充分利用带宽。比如8bit灰度相机4个像素时钟拼出32bit再两个拼出64bit效率100%一点都不浪费。3.2 从CameraLink信号到Aurora帧数据打包的完整链路我最终实现的数据通路是这样的以工程一Base配置为例CameraLink接口芯片DS90CR288A或兼容型号先把LVDS信号还原成28bit并行数据和像素时钟送入FPGA的IOB。FPGA内部用一个双沿寄存器把28bit数据打拍同步到像素时钟域经过一个简单的跨时钟域打拍电路保证数据稳定。数据进入异步FIFO写时钟是像素时钟读时钟是Aurora核的user clock。从FIFO读出的数据按照协议要求拼接成64bit送入Aurora核的TX接口。Aurora核完成8B10B编码、加帧头、加CRC可选交给GT核变成高速串行差分对信号。SFP光模块把电信号变成光信号送进光纤。接收端就是逆过程光信号 → SFP → GT → Aurora核 → 64bit数据 → 异步FIFO → CameraLink发送逻辑 → 输出28bit数据和像素时钟 → 到CameraLink发送芯片DS90CR287A→ LVDS信号 → 后端设备。整体链路看起来简单但实际编码的时候你会发现因为数据位宽不一致28bit到64bit需要做的位宽转换和字节对齐操作非常繁琐。这里有一个小技巧可以在异步FIFO之前做位宽转换也就是先把4个28bit数据拼成112bit然后以112bit为单位统一处理。这样虽然FIFO的宽度变大了但后续的字节对齐逻辑反而更好写因为112bit刚好是8个14bit映射关系非常整齐。如果你喜欢简单也可以只存32bit然后每个时钟处理一个32bit剩下的交给状态机慢慢拼代价是逻辑会复杂一点。3.3 像素时钟和数据有效信号的同步策略CameraLink相机输出的FVAL和LVAL信号并不是标准的同步信号它们的拉高拉低有可能发生在任何像素时钟沿。如果直接拿这两个信号当前沿触发很容易因为亚稳态导致偶尔多采或少采一个像素。我的处理方式是不要把FVAL、LVAL当作同步信号而是当成普通数据采用两级寄存器同步然后通过边沿检测生成有效脉冲。因为图像数据的有效与否是相对连续的即使边沿检测有1个时钟周期的抖动也不影响整帧图像的数据完整性。如果你比较强迫症也可以把FVAL和LVAL一起放进数据包里作为标志位传出去让接收端自己去解析。另外一个值得注意的细节是DVALID信号在行消隐和帧消隐期间的行为。有些相机在消隐期DVALID会拉低有些相机则保持高但数据无效。遇到这种相机在发送端就必须把消隐期间的数据全部填充为某种特定值比如全0或全FF否则接收端会在消隐期也收到看似有效但实际无意义的数据导致图像采集卡解析时出现固定位置的花屏。4. 4套工程源码的架构划分与核心模块实现4.1 工程目录怎么设计才能不让自己迷路每次看到有人把整个工程的所有.v文件塞在同一个目录里我都替他捏把汗。一个成熟的FPGA工程目录结构应该让新加入的人甚至三个月后的自己一眼就能看清楚模块边界。我这4套工程都遵循了下面这个标准的Xilinx工程目录结构prj/ ├── rtl/ │ ├── top/ │ │ └── top_cameralink_sfp.v │ ├── cameralink/ │ │ ├── cl_rx_decode.v │ │ ├── cl_pixel_assembler.v │ │ └── cl_tx_encode.v │ ├── aurora/ │ │ ├── aurora_link.v │ │ └── aurora_stream_wrapper.v │ ├── cross_clock/ │ │ └── async_fifo_wrapper.v │ └── common/ │ ├── data_packer.v │ └── data_unpacker.v ├── ip/ │ ├── gt_quad/ │ ├── aurora_8b10b_core/ │ └── async_fifo/ ├── constraints/ │ ├── pin.xdc │ ├── timing.xdc │ └── debug.xdc └── sim/ └── tb_cameralink_sfp.v这个结构把RTL代码、IP核、约束文件、仿真文件完全分开。Aurora核和GT核相关的代码集中在aurora目录和ip目录里这样想换板卡或者换线速率时只需要替换ip目录里的核配置其余的上层逻辑完全不用改。4.2 4套工程代码差异到底在哪里很多人买工程或者下载工程时最担心的是“源码虽然给了但我不知道该用哪个”。这里我把4套工程的差异点明确写出来工程一Base配置2.5GCameraLink Base模式单Link通道数1像素时钟范围20MHz~60MHz光口速率2.5Gbps参考时钟125MHz适用于绝大多数500万像素以内的工业相机代码结构最简洁建议所有新手先从这个工程开始理解工程二Full配置5.0GCameraLink Full模式三Link通道数3像素时钟范围60MHz~85MHz光口速率5.0Gbps参考时钟125MHz需要Aurora核配置为多通道模式3通道聚合关键是三个Link的数据必须按顺序交错合流接收端再逆序还原适用于千万像素级别、高帧率相机工程三回环测试2.5G不需要CameraLink相机板内自发自收TX和RX短接或光模块回环GT核和Aurora核配置同工程一核心代码是数据产生器和比对器用于验证光路和链路层排查设备问题工程四Base扩展6.6GCameraLink Base模式单Link但光口速率升到6.6Gbps适用于只有Base接口但像素时钟特别高的相机需要注意GT核的PLL配置6.6G对PCB布线和SFP模块质量要求都更高4.3 核心代码CameraLink像素组装模块简化版考虑到篇幅贴一段像素组装模块的简化框架代码展示数据拼接的基本思路module pixel_assembler #( parameter PIXEL_BITS 8 )( input wire clk_pix, // 相机像素时钟 input wire rst_n, input wire clkin_fval, // 帧有效两级同步后 input wire clkin_lval, // 行有效两级同步后 input wire [27:0] clkin_data, // CameraLink 28bit数据 output reg [63:0] axi_data_out, // 64bit数据给Aurora output reg axi_tvalid, output reg pix_sof, // 帧起始标记 output reg pix_sol // 行起始标记 ); // 像素缓冲连续4个像素时钟拼出32bit reg [31:0] pixel_buf; reg [1:0] pixel_cnt; always (posedge clk_pix or negedge rst_n) begin if (!rst_n) begin pixel_buf 32d0; pixel_cnt 2d0; axi_tvalid 1b0; end else begin case (pixel_cnt) 2d0: pixel_buf[7:0] clkin_data[7:0]; 2d1: pixel_buf[15:8] clkin_data[7:0]; 2d2: pixel_buf[23:16] clkin_data[7:0]; 2d3: begin pixel_buf[31:24] clkin_data[7:0]; axi_tvalid LVAL; // 行有效期间数据才有效 axi_data_out {pixel_buf[31:8], clkin_data[7:0]}; // 4像素拼32bit end endcase pixel_cnt pixel_cnt 1b1; end end endmodule这段代码的核心思想非常直白因为8bit灰度相机每个像素时钟只有8bit有效数据所以连续采4个像素拼成32bit再加上32bit空闲空间组成64bit一次发给Aurora核。实际工程里当然不会这么粗糙至少还要考虑行有效信号与数据的对齐以及消隐期间的填充值但核心思想就是“攒够一拍就发出去”。接收端的解包模块是逆向操作把64bit拆成4个8bit按顺序输出到CameraLink发送芯片的输入端。相比之下接收端反而更简单因为Aurora核的RX接口已经有完整的数据对齐你只需要控制FVAL/LVAL和数据的时序关系符合CameraLink规范即可。5. 上板调试与典型问题排查实录5.1 怎么判断“链路已经通了”我第一次做这个项目的时候最痛苦的不是写代码而是写完代码之后不知道它到底能不能用。后来养成了一个习惯建立一个分阶段的验证流程。第一阶段只验证GT物理层。用IBERTIntegrated Bit Error Ratio Tester核或者用Vivado自带的Hardware Manager里的GT工具把TX和RX短接或者光模块回环看误码率。误码率低于1e-15基本认为物理层OK。第二阶段验证Aurora链路层。跑工程三自发自收。用计数器产生递增数据接收端比对。如果比对全对说明Aurora通道对齐、字节序、时钟恢复都没问题。第三阶段接入CameraLink数据。先用信号发生器模拟CameraLink的时序不接真实相机。信号发生器产生固定的测试图案比如彩条看接收端能否还原出同样的图案。第四阶段接真实相机。这时候才会遇到真正的像素时钟抖动、行场消隐不规则、数据位宽不一致等问题。这四个阶段走完才敢说这个工程真正“能用了”。跳过任何一步都会在后面的某个时刻让你花更多时间来填坑。5.2 链路点不亮先从物理层找原因最常见的故障现象是什么Aurora核的channel_up信号一直拉不高。这时候别急着查代码先确认这些事光模块插好了吗SFP笼子的锁扣有没有扣到底光口防尘帽有没有拿掉光纤是单模还是多模和光模块是否匹配用了千兆光口但插了万兆光纤模块也会点不亮。GT参考时钟的引脚约束是否正确参考时钟的频率实测是多少GT核的复位和Aurora核的复位时序是否正确两个核的复位顺序有严格要求通常是GT先复位完成再释放Aurora核的复位。如果你用XC7系列或Zynq-7000这类带GTP/GTX的器件GTX的参考时钟输入引脚必须约束在对应的MGTREFCLK引脚上不能随意接普通IO。很多第一次做光口的朋友会忽略这一点看原理图时钟走线发现没走专用引脚结果怎么配置GT核都不工作。5.3 花屏和丢帧多半在跨时钟域假设物理层和链路层都OK了channel_up拉高了但接上相机后画面不对要么花屏要么偶发丢帧那问题十有八九出现在CameraLink侧的数据组装和跨时钟域处理上。花屏的排查顺序我总结为先看行长度对不对。CameraLink相机每行像素数是固定的如果发射端打包时行数据长度和接收端解包时预期的长度不一致画面会出现斜条纹或错位。再看FVAL/LVAL极性。有些相机的FVAL/LVAL是低有效代码里默认高有效就会整屏花掉。然后查字节序。颜色通道对调、图像左右或上下颠倒基本都是字节序和位序的问题。最后查异步FIFO的读写指针。如果FIFO在消隐期间没有做适当的复位或水位管理积累的读写指针偏差会在长时间运行后导致溢出或下溢。丢帧的另一个常见原因是异步FIFO的写侧和读侧时钟频率有一个微小的、非整数倍的频率差比如像素时钟60.0001MHzAurora user clock 125MHz长期运行下来每几秒钟FIFO就会满一次。解决办法是在发送端检测FIFO的空/满状态正常工作时满标志触发拉低tvalid或者牺牲一两个像素的消隐周期来主动排空FIFO。这个方法对动态画面影响很小对静态测试图影响尤其小但强烈建议不要在全帧数据都有效的情况下去丢数据否则采集卡会报数据不连续错误。5.4 时序收敛当你的设计跑到85MHz就怂了CameraLink Full配置下像素时钟85MHz对应的用户逻辑频率并不高但GT和Aurora核的工作时钟比如125MHz或156.25MHz加上跨时钟域FIFO的读写整体逻辑的时序压力还是有的。有一次做个工程二综合后时序报告显示Aurora核到FIFO读侧的逻辑建立时间违例频率只能跑到约80MHz。查来查去发现问题竟然是FIFO的读数据输出直接接了一长串的组合逻辑中间还穿过了两个MUX。解决方式很经典在FIFO输出和Aurora接口之间插入一级流水线寄存器用工作时钟打一拍。多一个时钟周期的延迟换来的是干干净净的寄存器到寄存器路径。画面显示上这个延迟根本感知不到。另一个技巧是对GT和Aurora相关信号设置false path约束。GT的复位信号、状态信号以及和用户逻辑完全无关的硬件状态信号都不需要参与时序分析。不加这些约束的话Vivado会在这些本不需要关注的路径上浪费大量布线资源导致真正关键的路径反而无法收敛。5.5 常见问题速查表最后把调试过程中最常遇到的现象、原因和解决办法整理成表格方便现场排查故障现象可能原因排查思路与解法channel_up一直为低光路不通、GT参考时钟错误、光纤类型不匹配先看SFP指示灯再查GT配置用IBERT验证物理层channel_up正常但数据全错字节序/位序不对或Aurora核收发数据位宽配置不一致发送固定测试图案如0x5A5A观察接收端数据变化规律画面花屏、行错位行长度不匹配或打包时把控制信号混进了数据确认每行像素数检查FVAL/LVAL孤儿信号偶发丢帧异步FIFO溢出/下溢加大FIFO深度或者在消隐期间主动排空FIFO颜色通道互调CameraLink数据位映射错误对照相机手册确认D[7:0]等的映射关系检查数据拼接顺序长时间运行后故障未处理的跨时钟域问题或FIFO未周期性复位检查FIFO水位必要时增加sof/sol同步机制时序违约、频率上不去组合逻辑过长或未正确设置false path插入中间寄存器检查XDC约束6. 工程源码使用建议与后续扩展方向拿到4套工程源码之后不要急着上板先把每个工程的README看清楚确认哪一套符合你的相机型号和SFP模块规格。上面4个工程我都提供了完整的XDC约束、IP核配置和仿真测试平台Vivado版本在2018.3以上直接打开就能综合布线。工程源码使用的几个关键步骤这里再强调一遍先跑工程三回环测试验证你的板卡光口基本环境。再把工程一的Aurora核线速率改成你板卡SFP支持的速率注意参考时钟频率要跟着线速率重新计算。确认CameraLink接口芯片型号和原理图接线核对数据位宽映射。综合实现后用Hardware Manager抓取channel_up和user_clk。channel_up拉高就说明链路层OK了此时再接相机。接上相机后先跑低分辨率模式再逐步提升像素时钟。这个项目后续还有很多可以扩展的方向。比较推荐的两个方向一个是把光口速率升级到10G以上用Aurora 64B/66B核就是Aurora 10G方向吞吐率提升非常明显另一个是加入DDR3/DDR4缓存实现Image Buffer或者多路视频复用让一套光纤方案同时传多个相机数据。如果做的是采集卡还可以尝试在接收端加一个AXI4-S接口把图像数据导给PS或者PCIe/H2C核直接变成一张图像采集卡的数字通路。我个人实际操作下来最深的体会是CameraLink转SFP光口这个项目真正难的其实不是FPGA代码本身而是你把系统当作一个完整产品来设计的能力。时钟怎么规划、复位链怎么做、跨时钟域处理、物理层验证、端到端联调每一步都要按部就班。Aurora 8B10B和GT Transceivers Wizard帮我们屏蔽了高速串行底层的大多数细节但那些没有被屏蔽的部分往往才是决定项目成败的关键。希望这篇博文能给正在这条路上摸索的朋友一些参考少走一些我当年走过弯路。