简介:基于FPGA的视音频光端机完整代码工程,面向监控、广播、教育等领域从事FPGA与嵌入式开发的工程师,也适合需要快速搭建光纤视频/音频传输方案的硬件开发者,解决长距离传输中视音频同步、多业务接入与复用设计问题。压缩包共198个文件,以Xilinx工程文件为主:.v/.vhd是核心逻辑源码,.ucf负责管脚约束,.bit可直接烧写,.ngc为综合后的网表,.xise为工程入口;同时包含.bin、.mcs等数据与镜像,以及txt、html说明文档。另有ut、log、twr、xreport、prj等综合实现报告与日志,可辅助还原从编码、综合到布局布线的完整流程,整体仅1.4MB,结构紧凑,已有491人学习下载。代码覆盖NTSC/PAL/SECAM多制式视频采集与模数转换、AES3/SPDIF音频同步处理、E1/T1及VoIP电话接入、以太网MAC帧封装与校验、开关量远程控制等关键模块,并经过实际项目验证且商业化落地,适合作为光端机产品开发的基础参考,可直接复用或二次修改。
1. 视音频光端机的代码核心:FPGA 才是传输链路的真正主角
第一次接触光端机的人,往往以为难点全在光模块上,拿到手才发现图像卡顿、音频爆音、无彩色的怪问题都出在 FPGA 这一侧。视音频光端机本质上不是“光收发”,而是把 SDI、HDMI、CVBS 这类接口的信号在 FPGA 里统一成同一种并行数据流,再做并串转换、编码、上光模块;接收端再解出来还原成原始接口格式。这份资源的核心价值,就是把这套收发链路用 Verilog 完整落地,同时给出音频嵌入与提取、异步 FIFO、高速收发器配置和仿真脚本,适合正在做视频传输项目、被 SDI 时序或音频同步折磨过的硬件工程师与 FPGA 开发者。
2. 方案选型与顶层架构:先回答“为什么是 FPGA”,再谈代码怎么写
一份光端机代码包,看起来是一堆 Verilog 文件,实际上它背后是一整套方案选型的结论。拿到代码后别急着打开工程,先理解作者为什么用 FPGA 而不是专用芯片,这对你后续改接口、加通道、换光模块都至关重要。本章从方案对比和顶层模块划分两个角度,把代码骨架讲清楚。
2.1 专用芯片、ASIC 与 FPGA:三选一看什么
很多人会先问:市面上有现成的光端机方案芯片,为什么还要在 FPGA 上写?答案在灵活性。视音频光端机的使用场景往往不是标准的一进一出,而是涉及多种视频制式、音频格式、传输距离甚至加密要求。专用芯片通常只支持某一种或某几种协议组合,性能稳定但改不动;ASIC 适合量产十万片以上的场景,研发成本和流片风险不适合项目早期。
| 方案维度 | 专用传输芯片 | FPGA | ASIC |
|---|---|---|---|
| 接口灵活性 | 固定协议栈,换制式基本重做 | 逻辑可重配,SDI/HDMI/CVBS 通吃 | 固定,只能做微调 |
| 研发周期 | 短,但受制于芯片手册 | 中等,需要掌握时序约束 | 很长,以季度计 |
| 单颗成本 | 低,但 NRE 不低 | 中等,批量后优势不明显 | 大流量生产才划算 |
| 调试可控度 | 黑匣子,只能读寄存器 | 内部信号全可见,可插 ILA | 几乎不可调 |
| 典型场景 | 单格式监控图传 | 广播级、多格式、多通道设备 | 航天、大批量军品 |
在这套代码里,FPGA 的位置非常明确:它不做模拟处理,也不负责光信号调制,那些由光模块和接口芯片完成;FPGA 只做数字域的格式转换、时序重建、帧缓存和编解码。这也是为什么这份资源里你会看到大量状态机、FIFO 和位对齐逻辑,而不是模拟电路相关的内容。如果你的项目只需要固定传输一路 1080p HDMI,那专用芯片更省事;但凡涉及多格式输入、音频嵌入、自定义帧结构,FPGA 的调试可控度就体现出来了。
选型时要关注一个常被忽略的指标:可用 IO 数量和高速收发器数量。视音频光端机通常至少需要两组高速收发器,一组接 SDI 解码芯片,一组接光模块,如果后续要扩展双通道、四通道,收发器数量直接决定选型上限。代码包里一般会写明目标 FPGA 的型号和资源占用率,先对照一下再决定是否移植到其他器件。
2.2 发送端顶层模块划分:一份代码怎么放下三路视频
打开这份光端机代码,第一眼会看到顶层模块。发送端和接收端是镜像关系,发送端负责采集、格式识别、音频嵌入、并串转换;接收端负责解串、时钟恢复、视频还原、音频提取。先看发送端,因为它集中了大部分数据通路设计逻辑。
// 发送端顶层:三路视频输入 -> 统一数据流 -> 高速串行发送 module video_tx_top ( input wire refclk_p, // 光模块参考时钟,差分输入 input wire refclk_n, input wire [9:0] sdi_data, // SDI 解码芯片输出的 10bit 并行数据 input wire sdi_rx_clk, // SDI 解码芯片输出的恢复时钟 input wire [15:0] audio_l, // 左声道 16bit PCM 音频 input wire [15:0] audio_r, // 右声道 16bit PCM 音频 output wire sfp_tx_p, // 光模块差分发送正 output wire sfp_tx_n // 光模块差分发送负 ); wire [15:0] video_data; // 统一后的视频数据总线 wire video_clk; wire [23:0] tx_data; // 进入串行器的并行数据 wire tx_clk; // 1. SDI 并行流里拆出视频数据和一个稳定像素时钟 sdi_video_extract u_extract ( .sdi_data (sdi_data), .sdi_clk (sdi_rx_clk), .video_data (video_data), .video_clk (video_clk) ); // 2. 音频嵌入到视频消隐区,形成带音频的传输帧 audio_embed u_audio ( .video_data (video_data), .video_clk (video_clk), .audio_l (audio_l), .audio_r (audio_r), .tx_data (tx_data), .tx_clk (tx_clk) ); // 3. 并串转换:FPGA 内部并行总线转成高速差分信号给光模块 serdes_tx u_serdes ( .tx_data (tx_data), .tx_clk (tx_clk), .refclk_p (refclk_p), .refclk_n (refclk_n), .txp (sfp_tx_p), .txn (sfp_tx_n) ); endmodule代码逻辑很直白:第一步把 SDI 并行数据中夹杂的时序基准信号、消隐数据清掉,只保留有效视频;第二步把 PCM 音频按 ANC 格式嵌进视频消隐区;第三步统一进串行器。这里的 refclk 是光模块参考时钟,一般取 125MHz 或 156.25MHz,具体要看线速率,下一章会专门展开。
参数上需要关注三个点:sdi_data的位宽由 SDI 解码芯片决定,常见的并行接口是 10bit 或 20bit;tx_data位宽取决于串行器的串行因子,10 倍串行就用 20bit,20 倍就用 16bit;audio_l/r是外部音频 ADC 送来的 PCM 流,位宽通常在 16 到 24bit 之间,代码会按 16bit 截断还是高位对齐,决定了你音质损耗的感知程度。
接收端顶层和发送端几乎对称:光模块差分进来,先做解串,再做时钟数据恢复,然后从数据流里分离出视频和音频。大部分代码工作量其实不在顶层,而在sdi_video_extract和audio_embed这两个子模块里,下面两章会逐个拆解。
3. 接口协议与音频嵌入:SDI、HDMI、CVBS 的代码入口
视音频光端机代码里最劝退的一关,不是 FPGA 语法,而是接口协议。SDI、HDMI、CVBS 三者格式完全不同——SDI 是串行数字信号,HDMI 是 TMDS 差分对,CVBS 是模拟复合视频。FPGA 代码不会直接去接这三种物理接口,而是通过相应的解码芯片把信号转换成并行数字总线。因此这一章的重点,是搞清楚并行总线上数据长什么样,以及怎么在代码里把视频、音频、同步信息准确拆出来或塞进去。
3.1 SDI 的格式识别:从并行流里抓出 TRS
SDI 是目前广播和监控领域最主流的视频传输标准,常见速率分三档:SD-SDI 270Mbps 对应标清,HD-SDI 1.485Gbps 对应 720p/1080i,3G-SDI 2.97Gbps 对应 1080p。解码芯片把串行数据恢复成并行 10bit 流后,视频行数据里会周期性出现一种叫 TRS(Timing Reference Signal)的特殊序列,它是 0x3FF、0x000、0x000、0xXYZ 四个字,其中 0xXYZ 的低四位标记这是行起始 SAV 还是行结束 EAV,以及隔行场信息。
FPGA 要做的第一件事,就是从连续的 10bit 流里识别这个序列,锁定每一行的起点和终点,再按照 SMPTE 标准把有效视频像素提出来。这段逻辑写得好不好,直接决定显示端是否抖动。
// SDI 并行流 TRS 识别:锁定行起始,产生像素有效信号 reg [39:0] shift40; // 拼装最近四个 10bit 并行字 reg trs_found; reg v_active; always @(posedge sdi_clk) begin shift40 <= {shift40[29:0], sdi_data}; // 每次移入一个新字 // 匹配 TRS 前缀 0x3FF 0x000 0x000 if (shift40[39:10] == 30'h3FF0000000) begin trs_found <= 1'b1; // 根据第 4 个字判断是 SAV 还是 EAV if (shift40[9:0] == 10'h200) // XYZ = 0x200 表示 SAV v_active <= 1'b1; else // EAV 则结束有效区 v_active <= 1'b0; end else begin trs_found <= 1'b0; end end这段代码是简化的原理示意,实际工程里要处理多行缓冲和场同步判断,但核心逻辑就是这个:用移位寄存器拼出近四个字,匹配固定前缀,再根据 XYZ 字判断进入还是退出有效视频区。这里sdi_clk是解码芯片输出的恢复时钟,频率与 SDI 速率相关:HD-SDI 时通常为 148.5MHz,3G-SDI 时可能是 148.5MHz 双沿采样。判断 TRS 时要注意,0x3FF 和 0x000 都是保留字,正常视频数据不会出现,所以误匹配概率很低。
有些代码包会在 TRS 识别后多加一级 CRC 校验,用来检测每一行的数据完整性,这在长距离光纤传输中很有意义,能直观看到误码是随机丢位还是整行错乱。
3.2 CVBS 与 HDMI:不直接处理,而是做转换
很多非广播级项目用的是 HDMI 输入或模拟 CVBS 输入。CVBS 经过解码芯片后输出的是 BT.656 格式并行数据,它和 SDI 的逻辑类似,只是行场同步藏在数据流的特定位置。HDMI 则麻烦一些,TMDS 信号需要专门的 PHY 芯片转成并行 RGB 或 YUV 数据,同时还要处理音频信息帧和 HDCP,代码上通常不做解密,而是做透传或强制转为 SDI 后再传输。
这套资源里对于 HDMI 的常见处理流程是:HDMI 接收芯片输出 24bit RGB 和像素时钟,FPGA 做色彩空间转换(RGB 转 YUV)和时序重建,然后塞进 SDI 标准的数据结构里;CVBS 则走 BT.656 提取 YUV 分量,再插值成标清或高清格式。这个过程不涉及复杂算法,但要注意色彩空间转换时用到的小数系数,直接截断会产生可见色偏,建议用带符号定点乘法并做好舍入。
如果你拿到代码后发现 HDMI 通道偶尔花屏,多半不是 FPGA 逻辑问题,而是 HDMI 芯片的 EDID 没有正确配置,输入分辨率超出预期。代码包里通常会在初始化模块中写死一组 EDID 参数,改分辨率时要同步修改,否则接收芯片会输出异常时序。
3.3 音频嵌入 ANC 包:爆音往往从这里来
SDI 传输音频的基本方式,是把 PCM 数据打包成 ANC(Ancillary Data)辅助数据包,插入到垂直消隐区的空闲行里。ANC 包的格式是固定的:起始字 0x000、0x3FF、0x3FF、0x1FF,然后是数据 ID(DID)、数据块号(DBN)、数据计数(DC)、16bit 数据字和校验和。接收端按照同样的规则把这些包从视频流里抓出来,再重建音频时钟。
音频嵌入的代码坑很多,最常见的问题是音频采样率与视频帧率不同步。视频是 59.94Hz,音频是 48kHz,一帧视频对应约 800 个音频采样,但帧率不是整数关系,导致每帧实际嵌入的音频样本数会多一个或少一个。处理不当就会周期性出现音频 FIFO 溢出或下溢,听上去就是每隔几秒一顿爆音。
// 音频 ANC 包组包:在垂直消隐区插入音频数据 // 本示意只展示包头与第一个音频数据的组装顺序 reg [9:0] anc_buf [0:32]; integer i; always @(posedge tx_clk) begin if (in_vblank && anc_trigger) begin anc_buf[0] <= 10'h000; // ANC 起始字 anc_buf[1] <= 10'h3FF; anc_buf[2] <= 10'h3FF; anc_buf[3] <= 10'h1FF; anc_buf[4] <= 10'h2F; // DID:音频控制包标识 anc_buf[5] <= 10'h1F; // DBN:数据块号 anc_buf[6] <= 10'h3F0; // DC:后续数据字数 anc_buf[7] <= audio_word_1; // 左声道采样 anc_buf[8] <= audio_word_2; // 右声道采样 // ... 后续为音频数据与校验和 for (i = 0; i < 32; i = i + 1) tx_data <= anc_buf[i]; end end原理上这就是往消隐区写一串固定格式的数据,但实际工程中还有两个隐藏要求。第一,ANC 包只能放在行消隐或场消隐的特定位置,不能覆盖 TRS 和有效视频区域;第二,音频字在 SDI 中的排布不是简单的 PCM 顺序,而是按声道对 sep 交织,左右声道要交替放置,否则接收端恢复出来的立体声左右会反转。如果你解码后发现人声发闷、左右感觉不自然,先检查这一层。
音频嵌入的代码完成后,建议用接收端把 ANC 数据解出来比对原始 PCM,而不是直接听声音,这样能快速定位是数据错位还是校验和计算有误。
4. 跨时钟与高速串行收发器:光链路参数决定视音频质量
光端机最容易“显示正常但隐隐不对”的问题,往往藏在跨时钟域和高速串行收发器配置里。视频像素时钟、音频主时钟、光模块发送时钟,三个时钟彼此独立,谁和谁都不相干,数据跨时钟传递必须用异步 FIFO 做缓冲。而高速串行收发器的参考时钟、线速率、并行位宽又是另一组参数,设错一个,整个链路直接黑屏或者满屏雪花。本章把这两块讲透。
4.1 异步 FIFO:视频和音频跨时钟的缓冲设计
光端机里的异步 FIFO 至少有三种使用场景:视频数据从 SDI 恢复时钟域跨越到串行器发送时钟域;音频数据从音频主时钟域跨越到视频时钟域;接收端解串后的数据跨越到视频输出芯片的时钟域。每一种跨越,缓冲深度算法都不一样。
视频数据跨时钟的深度计算依据是“一次突发最多能滞留多少数据”。假设视频写入时钟是 148.5MHz,读出时钟是 150MHz,虽然平均速率相同,但两者的相位和频率偏差会导致读写指针相对位置持续漂移。如果 FIFO 深度不足,一段时间后必定溢出或读空。常见的经验法则是:深度至少能容纳两个视频行的数据量,再乘一个 1.5 的安全系数。
// 异步 FIFO 例化:视频时钟域 -> 串行器时钟域 fifo_async #( .WIDTH (16), // 数据位宽:16bit YUV 或 20bit 原始数据 .DEPTH (512), // 深度:按 1080p 两行数据量再放大 1.5 倍 .FWFT (1) // 首字直通模式,降低读取延迟 ) u_fifo_video ( .wr_clk (video_clk), .wr_en (video_valid), .wr_data (video_data_in), .rd_clk (encode_clk), .rd_en (fifo_rd_en), .rd_data (video_data_out), .rd_empty (fifo_empty), .wr_full (fifo_overflow), .almost_full (fifo_almost_full) );这个例化里两个参数最关键:DEPTH和FWFT。深度不是随便填的,写满和读空都会导致视频丢行或重复行,表现是屏幕偶尔闪一下。FWFT首字直通模式能让读侧在 FIFO 非空时立刻输出数据,不需要额外的读延迟周期,对实时视频有利,但要付出的代价是组合逻辑路径变长,时序紧张时反而要关闭。
音频 FIFO 的深度计算和视频不同,音频数据均匀且持续,没有大突发,深度更多取决于音频时钟与视频时钟的频率精度。普通晶振频偏是 50ppm 级别,一秒钟的时钟偏差最大是 50 微秒,对应 48kHz 采样率大约差 2 个采样点。如果你的音频 FIFO 只有 8 个采样点深度,那么在若干秒后必然溢出或读空。稳妥做法是深度 32 点以上,并让软件或逻辑定期根据 FIFO 水位微调音频写入速率。
调试时不要只盯着满空信号,要额外把fifo_overflow信号引到 ILA 里,看它出现的时间点是否和爆音发生时刻吻合。如果每次都精确对应,说明深度算小了;如果对不上,问题更可能在 ANC 组包逻辑上。
4.2 高速串行收发器:参考时钟、线速率和复位顺序
视音频光端机里的高速收发器承担两件事:发送端把并行数据变成高速串行差分信号给光模块,接收端把光模块传来的高速信号恢复成并行数据。各家 FPGA 对收发器的命名不同,但配置思路一致:参考时钟频率、线速率、并行数据位宽、编码方式四者必须匹配。
| 视频格式 | 有效数据率 | 线速率(8b/10b 编码后) | 参考时钟 | 并行位宽 |
|---|---|---|---|---|
| SD-SDI 270Mbps | 270Mbps | 270Mbps | 27MHz | 10bit @ 27MHz |
| HD-SDI 1.485Gbps | 1.485Gbps | 1.485Gbps | 148.5MHz | 20bit @ 74.25MHz |
| 3G-SDI 2.97Gbps | 2.97Gbps | 2.97Gbps | 148.5MHz | 20bit @ 148.5MHz |
| 自定义 10bit 传输 | 约 1.25Gbps | 1.25Gbps | 125MHz | 20bit @ 62.5MHz |
参考时钟选择有一条铁律:收发器内部 PLL 的倍频系数要落在合理范围内,否则抖动指标恶化。比如 1.485Gbps 线速率用 148.5MHz 参考时钟是 10 倍频,很常见;如果用 125MHz 参考时钟则要乘 11.88,虽然也能锁定,但时钟恢复电路的眼图余量通常会变差。
复位顺序是另一大翻车点。高速收发器的 TX 和 RX 侧各自有 PLL,上电后必须先等待参考时钟稳定,再释放 TX PLL 复位,等 TX 复位完成后再释放 RX PLL 复位,最后等 RX 的 CDR 锁定。顺序乱了,链路偶尔能建起来,偶尔建不起来,玄学味道极重。
// 高速收发器复位状态机:确保 TX/RX PLL 按顺序启动 localparam IDLE = 3'd0; localparam RST_TX = 3'd1; localparam RST_RX = 3'd2; localparam WAIT_PLL = 3'd3; localparam RUN = 3'd4; reg [2:0] state; reg tx_reset; reg rx_reset; reg enable_data; always @(posedge refclk) begin case (state) IDLE: begin tx_reset <= 1'b0; rx_reset <= 1'b0; if (refclk_locked) state <= RST_TX; end RST_TX: begin tx_reset <= 1'b1; // 先复位 TX 侧 PLL state <= RST_RX; end RST_RX: begin tx_reset <= 1'b0; rx_reset <= 1'b1; // TX 稳定后再复位 RX 侧 state <= WAIT_PLL; end WAIT_PLL: begin rx_reset <= 1'b0; if (tx_pll_locked && rx_pll_locked) state <= RUN; end RUN: begin enable_data <= 1'b1; // 建链完成,开始发送数据 end default: state <= IDLE; endcase end这个复位状态机的关键点在于两个反馈信号:tx_pll_locked和rx_pll_locked。如果直接用固定延时替代 PLL 锁定信号,环境温度变化后可能出现偶发性建链失败。我一般会在状态机前后加至少 100 微秒的稳定时间,等 PLL 完全锁定再放数据。
光模块方面,SFP 封装的光模块通过 I2C 接口读到的状态寄存器里,有一个 RX LOS(信号丢失)位。调试光纤链路时先读这个位,如果它一直有效,说明光模块没有收到光信号,问题出在光纤或对端发送,而不是 FPGA 逻辑。
5. 避坑手记:光端机调试中五个高频翻车点
代码能综合、上板不跑,这是所有光端机项目的必经之路。以下五条踩坑记录来自我在多个视频传输项目里的调试经历,每一条都曾让人烧掉几天时间,写出来供你排查时对照。
5.1 五条踩坑记录:现象、原因、解决
第一条:黑屏且偶尔出现绿色横条
现象是接收端显示设备信号灯亮,但屏幕完全黑色,偶尔闪出绿色条纹。第一反应通常是视频数据没对,但查了 FIFO 和 TRS 识别都没问题。原因其实在高速收发器的 RX 复位始终没有正确释放,CDR 一直没锁定,解串出来的数据全是无效值。解决方法是把收发器的复位状态机和 PLL 锁定信号绑定,外加软复位按键,强制复位顺序重走一遍,链路拉起后画面稳定。从那以后每次上板,我都会先检查rx_pll_locked和rx_los两个信号的状态。
第二条:图像正常但每十几秒爆音一次
现象是画面完全正常,但耳机里每隔十几秒出现一次“啪”的爆音,时间间隔没有明显规律。查音频 FIFO 的水位,发现它每隔一段时间就会满一次。原因不是音频数据错了,而是音频 FIFO 的读时钟取了视频时钟的分频,而不是独立的音频主时钟。视频时钟本身有频偏,分频出来的音频时钟和真实 48kHz 存在累积偏差,FIFO 周期性溢出。解决方法是让音频 FIFO 的读时钟来自独立的音频晶振或音频 ADC 的 MCLK,深度加到 64 点,并开启水位中断,由逻辑定期校准。
第三条:光纤从 1 公里延长到 5 公里后误码率飙升
现象是短距光模块下一切正常,换成长距模块后画面开始出现马赛克和闪动,用误码仪测出误码率在 1E-9 到 1E-6 之间波动。原因不是 FPGA 代码,而是光模块发射功率和接收灵敏度的余量不足。5 公里标称的模块实际在 1 公里内正常工作,不代表长距没问题。解决方法是把收发器 RX 侧的眼图参数拉出来看,同时用光功率计测接收端光功率,确保余量在 5dB 以上。如果光功率正常但仍误码,则检查光纤法兰接头是否污染,这是最容易被忽略的物理层问题。
第四条:3G-SDI 信号上屏花屏但 1.485Gbps 时正常
现象是同一台设备,输出 HD-SDI 信号时显示正常,切到 3G-SDI 分辨率就花屏。原因在于 3G-SDI 速率提升到 2.97Gbps 后,PCB 布线和连接器的阻抗不连续影响被放大,SDI 输出端的回损指标劣化。解决方法是检查 FPGA 到 SDI 驱动芯片之间的走线是否满足阻抗控制,差分对等长是否做到位,另外确认输出共模电压接近规范值。这类问题用示波器看眼图最直观,眼图张开度在后半段明显收窄就是信号完整性问题。
第五条:ModelSim 仿真全对,上板后随机死机
现象是仿真波形完美,但实际设备运行几分钟后总线挂死,这种最让人头疼。原因几乎全是跨时钟域信号没有做同步处理。单 bit 信号跨时钟域直接用了两级寄存器同步,这没错;但多 bit 数据总线如果直接打两拍,各 bit 的到达时间不同,就会出现数据错位。解决方法是所有跨时钟域的多 bit 信号必须走异步 FIFO 或握手协议,不允许“打两拍”硬传。这类玄学问题,把代码里所有跨时钟域的assign全部搜出来,逐个改成 FIFO,基本能根治。
5.2 排查工具与调试习惯
光端机排查不能靠“瞪眼”,三样工具必须会用。第一是 FPGA 片内逻辑分析仪(ILA),把 TRS 识别、FIFO 满空、PLL 锁定三个关键信号拉出来,触发条件设为rx_pll_locked == 0,在死机时能看到最后的状态。第二是误码统计寄存器,很多代码包里会带一个简单的 BERT 模块,发送端发伪随机序列,接收端统计错误数,这个比肉眼盯画面可靠得多。第三是光功率计和示波器,物理层问题必须用物理工具定位。
调试习惯上,我强烈建议你把测试点设计放在代码里,而不是靠飞线去量。至少预留一个测试引脚,通过复用方式输出 PLL 锁定、FIFO 水位、SDI 有效信号,这样上板后用示波器点几下就能判断是哪一级出了问题。另一个习惯是每次修改代码后,先把收发器复位时间加长一点再测,避免把“复位没等够”的偶发问题和真实逻辑 bug 混在一起排查。
6. 拿到代码后的验证顺序:仿真、回环、眼图
这套资源下载下来之后,别急着改 HDMI 通道或加音频接口,先按“仿真、回环、眼图”三步跑一遍,确认代码在你自己环境下能工作,再谈定制。第一步是仿真。把代码包里自带的 testbench 打开,看有没有默认的 1080p 测试激励,如果没有,自己写一个最小激励:送一组固定图像数据,跑完整个发送链路,重点观察 TRS 识别信号和 FIFO 是否溢出。
// 最小发送端 testbench:产生一个视频时钟和图像激励 module tb_video_tx(); reg refclk = 0; reg sdi_clk = 0; reg [9:0] sdi_data = 0; wire sfp_tx_p, sfp_tx_n; // 148.5MHz 视频时钟模拟 always #3.367 sdi_clk = ~sdi_clk; // 125MHz 参考时钟模拟 always #4.0 refclk = ~refclk; // 简单激励:先送 TRS 序列,再送连续视频数据 initial begin #100; repeat (100) begin @(posedge sdi_clk) sdi_data <= 10'h3FF; @(posedge sdi_clk) sdi_data <= 10'h000; @(posedge sdi_clk) sdi_data <= 10'h000; @(posedge sdi_clk) sdi_data <= 10'h200; // SAV repeat (1920) @(posedge sdi_clk) sdi_data <= 10'h1FF; @(posedge sdi_clk) sdi_data <= 10'h3FF; @(posedge sdi_clk) sdi_data <= 10'h000; @(posedge sdi_clk) sdi_data <= 10'h000; @(posedge sdi_clk) sdi_data <= 10'h2C0; // EAV end end // 顶层例化与断言略,完整代码见下载包 endmodule仿真通过后,第二步做回环测试。把发送端差分信号直接在 PCB 上短接回接收端,或者用一根短光纤做外部回环,确认收发链路完整。回环测试时读rx_pll_locked和误码计数器,如果误码为零,说明 FPGA 内部数据通路没问题。
第三步是上真光模块,用示波器测 SFP 输出端的眼图。眼图应该看到清晰的“眼睛”轮廓,眼宽至少占 70% 以上的码元周期,否则就要检查参考时钟抖动和 PCB 走线。我在调试某项目时,就是因为跳过了眼图检查,直接把设备送去现场,结果信号完整性在全速率的 10% 余量下过了,到场后温度升高就不稳定,白白跑了一趟返修。从那以后,每次拿到光端机代码,我都会强制自己走一遍仿真、回环、眼图三步,哪怕时间再紧。希望帮到你。
本文还有配套的精品资源,点击获取