资讯动态

FPGA光端机代码设计:SDI视频解串、音频嵌入与TDM复用实战

发布时间:2026/10/21 5:58:22 来源:尧图企业网站定制
简介基于FPGA实现的视音频光端机完整工程代码包面向监控、广播、教育等领域的远程视音频传输适合FPGA工程师、通信系统设计者及电子专业学习者参考可解决多路视频/音频编码传输、同步处理以及以太网、电话接口等扩展功能集成问题。资源共198个文件、压缩包约1.4MB涵盖VHDL/Verilog源码、Xilinx工程文件、综合布线报告和仿真调试记录等多种类型便于对照源码与工程产物进行移植和二次开发。已有491人学习下载。该工程经实际项目验证并成功商业化版本标识Duoyewu1202包含发送、接收、从机等完整功能模块及加扰/解扰器等中间文件。读者可从中提取光端机主机与从机的分层实现思路、多路音视频同步处理方式、E1电话与以太网控制通道的FPGA设计框架以及完整工程目录结构和综合、布局布线中间产物适合用作毕业设计或工程参考。1. 光端机代码的核心FPGA 里先走通视音频再谈光纤光端机项目最容易翻车的地方往往不在光纤链路而在 FPGA 里的视音频处理这一关。市面上的视音频光端机代码本质是把 SDI 视频、伴随音频和辅助数据塞进一根光纤的逻辑工程输入端要把串行 SDI 恢复成并行像素流音频要跨时钟域塞进视频消隐区或者独立复用通道光口侧再做线速率匹配和帧同步。这套代码适合正在做视频传输设备、矩阵延长器、采集卡方案的工程师也适合想从零搭建一套可测试光端机逻辑的开发者。它解决的核心问题就一句话让视频不花屏、音频不爆音、长传不丢帧而不是只把光纤点亮。2. SDI 视频进 FPGA 这一级时钟恢复与像素流提取是第一道门槛2.1 三种 SDI 速率下输入侧方案为什么不能省SDI 输入信号进 FPGA 之前物理接口上是 75Ω 同轴线缆里的单端串行码流经过板端巴伦或耦合电路转成差分对之后才进到 FPGA 的高速串行收发器。SD、HD、3G 三档速率对应的数据率分别是 270Mbps、1.485Gbps、2.97Gbps 左右。三档速率的编码方式都是 NRZI 加扰码接收端必须先把时钟恢复出来再做解扰和串并转换最后才能拿到带时序基准码的 YUV 数据流。格式数据率典型分辨率接收端复杂度SD-SDI270Mbps576i / 480i低普通 LVDS 尚可处理HD-SDI1.485Gbps720p / 1080i高必须用高速串行收发器3G-SDI2.97Gbps1080p60高必须用高速串行收发器我的建议是三档全部走高速串行收发器不要为了省资源把 SD-SDI 单独用普通引脚去接。原因在于接收端不光要采数据还要做时钟恢复和均衡。高速串行收发器把这些物理层脏活都包掉了逻辑侧只需要处理已经恢复好的并行数据。如果 SD 档单独用 LVDS 引脚方案整个工程就得维护两套接收逻辑后续维护时很容易因为时序边界问题出现「信号弱就花屏」这种查不出来的毛病。2.2 像素流恢复从串行位流里把行场同步抠出来串行 SDI 码流恢复成并行数据之后第一件事是找时序基准码。SDI 标准里定义了两种基准码有效视频开始 SAV 和有效视频结束 EAV。在 FPGA 逻辑里通常用移位寄存器去匹配特定的 16 位特征值一旦匹配上就认为一行开始了然后按照行场消隐的参数去数像素。这一层做得扎不扎实直接决定后面 ANC 数据嵌入和音频定位准不准。module sdi_rx_pixel_extract #( parameter H_ACTIVE 1920, // 有效像素数 parameter V_ACTIVE 1080, // 有效行数 parameter LINE_TOTAL 2200 // 一行总像素数含消隐 ) ( input wire clk_pix, // 像素时钟 148.5MHz input wire rst_n, input wire sdi_data, // 解码后的并行 Y/C 数据流 output reg f_active, // 有效视频窗口标志 output reg f_sav, // 检测到 SAV 标志 output reg f_eav, // 检测到 EAV 标志 output reg [10:0] pix_cnt, // 行内像素计数 output reg [10:0] line_cnt // 场内行计数 ); reg [15:0] shift_reg; wire is_sav (shift_reg {8hFF, 8h00}); wire is_eav (shift_reg {8h00, 8hFF}); always (posedge clk_pix or negedge rst_n) begin if (!rst_n) begin shift_reg 16b0; pix_cnt 0; f_active 0; end else begin shift_reg {shift_reg[14:0], sdi_data}; if (is_sav) begin pix_cnt 0; f_active 1b1; f_sav 1b1; f_eav 1b0; end else if (is_eav) begin f_active 1b0; f_sav 1b0; f_eav 1b1; line_cnt line_cnt 1b1; end else begin f_sav 1b0; f_eav 1b0; if (f_active pix_cnt H_ACTIVE - 1) pix_cnt pix_cnt 1b1; end end end endmodule这段逻辑的核心思路是用移位寄存器找基准码特征字找到 SAV 就认为有效像素从下一拍开始找到 EAV 就认定一行结束。pix_cnt只在有效窗口里递增这样后续的 ANC 数据包插入位置就能按像素计数对齐。LINE_TOTAL这个参数在 1080p60 下取 2200这里没有直接引用到状态机里用于辅助判断行窗口边界后期接时序约束时也需要用它核对总行长时间。要注意shift_reg匹配的特征字是简化写法真实工程里还要先做解扰再做匹配因为 SDI 线路上传的是加扰后的数据。2.3 音频嵌入把音频包塞进行消隐期SDI 视频并不是单独传画面它允许在行消隐期插入 ANC 辅助数据包音频就是通过这种方式和视频走在同一条链路里的。每个音频包遵循固定的包格式DID 标识数据类型DBID 标识子类型DC 是数据块长度后面是数据块和校验字。音频嵌入逻辑要在 SAV 之后、EAV 之前找到合适的 ANC 空位然后把采样字数进去。这里的难点是时序每行消隐窗口有限塞多了会把下一行视频挤掉塞少了音频带宽不够所以要按采样率精确计算每帧需要多少个包。计算校验字是 ANC 包里最容易错的一步。标准规定校验字是数据块字段所有字节的累加和取低 8 位。发端算错一个字节收端解嵌时就可能整个包丢弃表现就是画面正常但声音一段一段断流。这种故障非常像时钟问题实际去查往往是校验和错位。function [7:0] anc_checksum; input [7:0] data_block; integer i; reg [7:0] sum; begin sum 8b0; for (i 1; i 255; i i 1) begin if (i data_block) sum sum data_block; end anc_checksum sum; end endfunction上面的函数是一个简化示意真实工程里校验和要把除 CS 本身之外的 DID、DBID、DC 和所有数据字节全部累加。我一般会在发送侧维护一个累加器每写入一个字节就更新一次累加和最后一个字节写入前把累加值填进 CS 位置顺序反了必然收端报错。ANC 包的最大数据块长度是 255 字节但实际可用的行消隐窗口由视频格式决定。做嵌入逻辑之前先按行总长减有效像素算出窗口再除以包长确认每行最多能塞几个包这一步算错了到后面音频通道数量一多必然爆掉。3. 音频这一侧比视频更容易翻车时钟域与采样率转换3.1 三种音频接口怎么接进 FPGAI2S / SPDIF / AES-EBU音频接入光端机的形式比视频要杂得多。板级信号多数是 I2S三根线位时钟、帧时钟、数据线简单但传输距离短。专业设备场景里常见 SPDIF 或者 AES-EBU它们是串行编码格式自带时钟信息需要先解码恢复出采样时钟和串行数据再转成内部并行总线。选哪条路取决于你的光端机是接摄像机、接调音台还是接矩阵输出。接口格式信号线时钟来源典型用途I2S位时钟/帧时钟/数据主设备提供板级音频ADC/DAC 直连SPDIF单线同轴/光纤接收端从码流恢复民用音频设备延长AES-EBU差分对/110Ω接收端从码流恢复专业音频设备互连我一般会这样定方案光端机里只收一路视频时音频就用视频 ANC 嵌入通道音频接口直接做成 I2S 输入如果光端机同时处理多路视频和独立音频音频单独走 AES-EBU 进 FPGA再在光口复用层单独开一个音频时隙。不要把 AES 解码后的数据直接塞进视频的 ANC 通道两边的时钟域差了十万八千里硬塞进去会周期性出现采样点错位。3.2 采样率与视频帧率对不上时用音频 FIFO 兜住音频采样率和视频帧率根本不是一个源头视频像素时钟来自 SDI 恢复出来的参考时钟音频采样时钟来自本地晶振或者 SPDIF 解码恢复时钟。两个时钟之间必然存在微小频差长时间累积下来音频数据要么溢出要么读空。常用做法是在音频进复用器之前放一个异步 FIFO用写时钟吃进音频采样用视频帧同步产生的读时钟把数据读走同时用近空、近满标志去调整塞包节奏。算 FIFO 深度时先算带宽48kHz 采样率、32bit 字长、双声道一路音频速率是 48k × 32 × 2 3.072Mbps。对 1080p60 视频每帧时间约 16.67ms一帧里应承载 48000 / 60 ≈ 800 个采样点。如果主时钟频差是 50ppm一帧的误差就是 800 × 50 / 1e6 0.04 个采样点看起来很小但一秒钟 60 帧累积下来就是 2.4 个采样点。这个数量级意味着 FIFO 做成 32 深度也能撑几秒但实际工程里频差往往更大加上突发写入的特性我通常直接做到 128 到 512 深度并把近空阈值设在 1/4、近满阈值设在 3/4。3.3 音频 FIFO 读写指针与阈值设计代码异步 FIFO 的读写指针要跨时钟域最稳妥的方式是读写侧各自计数再用格雷码跨时钟域同步指针。阈值信号在读侧产生因为真正需要判断近空近满的是消费端也就是读取音频包做复用的那一侧。module audio_async_fifo #( parameter DEPTH 512, parameter WIDTH 32 ) ( input wire wr_clk, input wire wr_en, input wire [WIDTH-1:0] wr_data, input wire rd_clk, input wire rd_en, output wire [WIDTH-1:0] rd_data, output reg fifo_near_empty, output reg fifo_near_full ); reg [9:0] wr_ptr; reg [9:0] rd_ptr; reg [9:0] wr_ptr_gray; reg [9:0] rd_ptr_gray; reg [9:0] wr_ptr_sync; reg [9:0] rd_ptr_sync; parameter NEAR_EMPTY_TH DEPTH / 4; parameter NEAR_FULL_TH DEPTH * 3 / 4; wire [9:0] used wr_ptr - rd_ptr; always (posedge rd_clk or negedge rst_n) begin if (!rst_n) begin fifo_near_empty 1b1; fifo_near_full 1b0; end else begin fifo_near_empty (used NEAR_EMPTY_TH); fifo_near_full (used NEAR_FULL_TH); end end endmodule这个模块只画了核心逻辑实际接 RAM 时还要处理读写指针的二进制转格雷码。参数DEPTH取 512 时used计算直接用二进制就行但如果深度不是 2 的幂减法和阈值判断都要重新算。近空和近满信号要允许一定滞后避免在临界点反复抖动我习惯在阈值比较后再加一级寄存器消除亚稳态的同时也起到滤波作用。复用侧看到near_full就暂时停塞音频包看到near_empty就补静音包这两种动作都不会造成爆音。4. 一根光纤的复用方案TDM 帧让视频音频互不干扰4.1 为什么复用层要自己做 TDM 而不是简单并串点对点传输一路视频时很多代码直接把 SDI 码流透传到光口收端恢复出串行码流再接后端设备这种透明传输架构最简单也确实够用。但光端机一旦要求一芯传多路、或者要在传视频的同时插一路独立音频、甚至传 RS422 控制数据透明模式就撑不住了。这时需要在 FPGA 里做时分复用 TDM把视频有效数据、音频采样、辅助数据按固定帧结构排布再加上帧头和同步字收端按同一帧结构解出来。TDM 帧结构的设计原则是帧头要足够特殊确保接收端在误码情况下也能快速对齐视频数据段要整块搬运不要在 FPGA 里逐像素做仲裁否则带宽利用率会很难看。帧头选多字节组合比单字节可靠得多。我见过有人用单字节做帧头结果视频数据里连续出现相同字节时接收端频繁假同步花屏像呼吸灯一样有节奏地闪排查半天才意识到是帧头不够长。4.2 TDM 帧结构与线速率预算帧段内容典型开销同步段帧头 4 字节 保留 2 字节固定开销视频段一帧视频有效像素的打包数据占绝大多数带宽音频段该帧对应的全部音频采样点3.072Mbps 量级辅助段串口数据、状态信息、EDH 校验小保护间隔防止相邻帧穿越的空白时隙少量视频段和数据率是最需要精算的部分。以 3G-SDI 无压缩视频为例物理码率 2.97Gbps里面包含消隐和 ANC 数据。如果光链路只是把这路码流完整搬到对端线速率必须高于 2.97Gbps再叠加 TDM 帧头和保护间隔开销通常要留 5% 到 20% 的裕量。实际做参数时我会先列一个算式所需线速率 视频有效码率 音频总带宽 辅助数据带宽然后整体乘上 1.1 到 1.3 的设计冗余。工程里常见档位是 3.2G、3.5G、4.25G 这类线速率。如果 FPGA 的高速收发器不支持恰好够用的档位就往上一档选别省这一点点资源等温度一上来误码率飙升时没有后悔药。4.3 复用状态机与接收端帧同步复用器的发送端是一个循环状态机按帧结构把不同来源的数据依次放进发送总线。状态机最忌讳的是在某一帧出错后永远等不到下一个触发条件所以每个状态都要配超时看门狗超时强制回到帧头状态。module tdm_tx_controller #( parameter VIDEO_LEN 3840, // 每帧视频段占用的时钟周期数 parameter AUDIO_LEN 64, // 每帧音频段占用周期数 parameter SYNC_WORD 32hFA57_A55A ) ( input wire clk, input wire rst_n, input wire video_ready, input wire audio_ready, output reg [31:0] tdm_data, output reg tdm_valid, output reg [ 1:0] frame_phase ); localparam PH_SYNC 2d0; localparam PH_VIDEO 2d1; localparam PH_AUDIO 2d2; localparam PH_AUX 2d3; reg [15:0] phase_cnt; reg [ 1:0] state; always (posedge clk or negedge rst_n) begin if (!rst_n) begin state PH_SYNC; phase_cnt 0; end else begin case (state) PH_SYNC: begin tdm_data SYNC_WORD; tdm_valid 1b1; if (phase_cnt 3) begin state PH_VIDEO; phase_cnt 0; end else begin phase_cnt phase_cnt 1b1; end end PH_VIDEO: begin if (video_ready phase_cnt VIDEO_LEN) begin phase_cnt phase_cnt 1b1; end else begin state PH_AUDIO; phase_cnt 0; end end PH_AUDIO: begin if (audio_ready phase_cnt AUDIO_LEN) begin phase_cnt phase_cnt 1b1; end else begin state PH_AUX; phase_cnt 0; end end default: begin state PH_SYNC; phase_cnt 0; end endcase end end endmodule这个状态机里VIDEO_LEN和AUDIO_LEN是设计时按带宽预算算好的固定值实际工程里音频段长度可能要按 FIFO 的填充程度动态调整做成动态长度会增加接收端解析复杂度一般不建议第一次做就上动态方案。接收端的帧同步是用一个滑动窗口对帧头做匹配连续匹配成功两次才确认为同步态连续丢帧头超过一定次数就回到搜索态。同步态的滞回设计很重要否则一两个误码就让系统频繁重同步音频必然咔哒声不断。5. 光端机开发避坑黑屏、花屏、爆音这几类坑都在哪5.1 输入悬空导致时钟恢复跑飞现象光端机只插光纤不插视频源时输出端出现满屏滚动噪点偶尔还带音频杂音。接入真实信号后要等好几秒才恢复正常。原因高速串行收发器在没有输入信号时时钟恢复电路会漂移到随机频率恢复出的并行数据全是无效位。逻辑侧如果没有对信号有效状态做判决这些无效数据就会一路穿到输出端。解决在接收侧加 LOS 检测或者利用 SDI 解码器自带的信号锁定指示。逻辑里一旦检测到信号未锁定直接把视频输出置为蓝场或黑场音频输出置为静音数据同时向上层上报状态。从那以后我接光端机第一件事就是确认信号锁定指示信号有被引出来没有这个信号就相当于盲调。5.2 音频 FIFO 深度不够造成间歇性爆音现象设备运行十几分钟到半小时后音频出现一次短促爆音画面完全正常。重启后又能好一阵。原因视频路径和音频路径的时钟频差持续累积FIFO 写满或者读空。深度只有 32 时频差累积到阈值的时间恰好是几十分钟。画面不受影响是因为视频路径是同步架构它不依赖这个音频 FIFO。解决把音频 FIFO 深度加大到 512 或 1024同时把近空、近满标志引到调试接口实测读写指针波动范围。如果频差实在太极端就要考虑在音频路径加异步采样率转换把输入采样率重采样到与视频参考时钟对齐的采样率。5.3 回环测试自欺欺人现象用一根短光纤把光端机的 TX 和 RX 直接连起来测试一切正常误码率零。但接到实际链路后出现偶发花屏尤其在线缆长度超过标称值或者经过跳接点的时候。原因短光纤回环绕过了链路中最残酷的部分——光功率衰减和反射。光纤很短时接收光功率远高于灵敏度门限即使眼图很烂也能恢复出数据。真实链路里的损耗和色散会直接让误码率上去。解决回环测试要分级做。第一级用短光纤验证逻辑第二级加可调光衰减器把接收光功率压到灵敏度临界点附近再确认误码率仍然达标第三级接真实的布线和对端设备做联调。每一级都要记录光功率读数光功率负得越多还不出错说明系统裕量越足。5.4 复位顺序不对导致首帧错位现象上电后第一次出画面时屏幕顶部有一条横向错位带之后一直正常。但如果上电瞬间视频源已经在向外发信号错位带会明显加宽。原因视频处理逻辑和音频 FIFO 的复位释放顺序不同步。复位释放瞬间视频状态机可能从一个半截行状态开始计数而接收端在帧头搜索时需要完整的一帧才能对齐。局部错位是因为第一个成功对齐的帧其实丢掉了前半部分。解决做一个统一的复位管理模块先释放视频通道复位间隔若干时钟周期后再释放音频和复用通道复位。复位释放后再强制接收状态机进入帧头搜索态不要允许它从当前任意状态继续跑。这个坑我当时查了两个通宵最后抓波形才发现是复位先后的事。5.5 差分信号阻抗不匹配导致长线花屏现象短距离测试正常线缆超过 50 米后开始出现零星花屏换线缆品牌后症状消失。原因SDI 输入端的差分走线阻抗控制不好或者耦合电容摆放位置不对导致信号反射叠加在数据眼图上。长线信号本来就弱反射造成的抖动让收发器无法稳定恢复时钟。解决严格按差分 100Ω 阻抗控制板级走线耦合电容尽量靠近连接器放置并且在进入 FPGA 前预留匹配电阻位置。量产前用示波器在 FPGA 引脚端实测眼图如果眼图闭合严重直接让硬件改板不要指望逻辑侧均衡能兜住一切。6. 最后一道工序用回环链路把该踩的坑提前踩完做完逻辑之后最值得投入时间的是一套可重复的验证流程我把它整理成一份自检清单每次改完代码都按顺序跑一遍。第一步是短光纤回环验证同步字能否稳定对齐观察接收侧帧同步状态机的锁定指示若锁定指示在锁定与搜索间来回跳优先查帧头长度和误码阈值。第二步是长时间老化跑至少 12 小时期间每秒统计一次误码计数和 EDH 错误计数出现任何一个非零错误就停下查波形不要带着偶发错误继续测因为偶发误码在老化测试里会滚雪球。第三步是音视频齐测播放带彩条和 1kHz 音频的测试信号用后端设备监看画面与听音同时观察音频 FIFO 的填充水位记录水位曲线如果呈锯齿状说明复用带宽和采样率匹配还需要调整。第四步是光功率临界测试加可调衰减器把接收光功率压到接近灵敏度门限确认误码率仍低于预期这一步做的是整个系统的降级表现。验证项通过标准失败时先查帧同步锁定锁定指示无抖动帧头长度、同步阈值老化误码12 小时无累积误码线速率裕量、电源纹波音频水位读写指针无周期性触顶FIFO 深度、采样率偏差光功率临界门限附近无误码均衡档位、阻抗匹配这些验证结束后再把配置固化到默认参数之后每次改版都强制走一遍而不是只在交付前测一次。第一次做光端机逻辑时我自认为已经把代码写得滴水不漏结果在光功率临界测试那一步直接暴露了均衡档位配置偏小的问题要不是事前压了光功率这块短板会一直埋到客户现场才爆。从那以后我每次交付光端机代码前都会先按这套流程压一遍该踩的坑提前在实验室里踩完希望帮到你。本文还有配套的精品资源点击获取

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑