基于Verilog的BT656视频编码模块设计与SDI板卡验证避坑

发布时间:2026/11/3 17:11:20
基于Verilog的BT656视频编码模块设计与SDI板卡验证避坑

简介:这份文档围绕SD-SDI BT656格式的720×576i数字视频流编码展开,面向FPGA视频板卡开发者、Verilog学习者及课程设计选题学生,帮助梳理PAL制交错视频的时序组织与编码实现思路。压缩包共1个docx文件,约1.12MB,内容将行数据字段、帧场格式、消隐与显示起止行、奇偶场行数范围等基础说明,与EAV/SAV中F、V、H信号及P3~P0保护位逻辑关系放在同一文档中,便于对照理解。文中还给出基于Verilog的编码模块示例,包含27MHz时钟、行列计数、同步字节生成及Y/CrCb数据组织,并附ModelSim仿真验证记录,可帮助读者定位行场边界、保护位计算和图像数据请求等关键实现细节。目前已有579人学习,适合用作视频接口入门、代码参考与实验方案搭建。

1. 某实验室的视频板卡缺口:一颗 SDI 电缆驱动芯片前面到底该接什么

某开发者拿到一块待验证的板卡,后端是 SDI 电缆驱动芯片,前端留给 FPGA 的只有一根 10 位并行总线和一对时钟、复位、同步输入。麻烦在于上游图像源给的是 8 位 Y 与 8 位 CbCr 分离数据,而驱动芯片要的是已经打包好定时参考码、保护位、消隐填充的连续 BT656 流——中间这段“翻译”没有任何现成 IP 愿意为标清 720×576i 单独做一版。这套方案就是补这一段的:用一颗 27MHz 时钟驱动的 Verilog 编码模块AV_GEN_CBB,把 Y/CbCr 交织、EAV/SAV 插入、奇偶场标志、行场消隐全部在硬件里一次生成。它适合正在做视频类板卡验证的人,也适合把它当成课程设计或毕业设计的选题骨架——模块不大,但行场时序、色彩空间交织、保护位编码这三件事一件都跑不掉。

2. 行场时序骨架:1728 拍一行、625 行一帧怎么切

标清 720×576i 的所有参数都被一个数字锁死:27MHz。这不是随便挑的时钟,而是 1728 × 625 × 25 = 27,000,000 的结果。反过来说,只要时钟是 27MHz,一行就必须是 1728 个时钟周期,一帧就必须是 625 行,行频 15625Hz,帧率 25Hz。整个编码器的设计自由度,全部集中在“这 1728 拍里哪一段放有效数据、哪一段放消隐”这一件事上。

2.1 1728 拍的划分:288 拍消隐加 1440 拍有效

BT656 在 4:2:2 采样下,一个像素对应两个采样点(一个 Y、一个 CbCr),所以 720 个像素占 1440 个采样周期。整行 1728 减掉 1440,剩下 288 拍就是行消隐。这个 288 在代码里对应参数H_BLANK_LENGTH,H_LENGTH对应 1728,两个数字一减就是有效区宽度,不再单独定义——少一个参数就少一处改错的机会。

行内各段的实际落点是这样的:hcnt为 0 时输出 0xFF,1 和 2 输出 0x00,3 输出定时参考码;4 到 283 是消隐填充区;284 到 286 又是 0xFF、0x00、0x00;287 是第二个定时参考码;288 到 1727 才是真正的有效数据。也就是说,一行里有两组“FF 00 00 XY”四字节序列,一组在行起点,一组在有效数据之前,两者之间夹着 280 个采样的纯消隐。

这里有个容易被忽略但很关键的点:第一组(hcnt0~3)在物理位置上紧跟上一行有效数据的末尾,它是 EAV;第二组(hcnt284~287)紧跟本行有效数据的开头,它是 SAV。判断依据不是位置,而是那个 XY 字节里的 H 位——EAV 时 H=1,SAV 时 H=0。代码里H在hcnt < 287期间保持为 1,恰好让hcnt=3的那个 XY 带上 H=1,hcnt=287的那个带上 H=0,逻辑自洽,不用额外写分支。

// 行内数据段生成:0~287 为消隐区,288~1727 为有效区 always @(posedge clk_27) if (rst_27 == 1'b1) DATA <= 8'd0; else begin if (hcnt == 0) DATA <= 8'hff; // EAV 前导 else if (hcnt < 3) DATA <= 8'h00; // 两个 0x00 else if (hcnt == 3) DATA <= SYNC_BYTE; // EAV 的 XY 字,H=1 else if (hcnt < 284) DATA <= hcnt[0] ? 8'h10 : 8'h80; // 消隐填充 else if (hcnt == 284) DATA <= 8'hff; // SAV 前导 else if (hcnt < 287) DATA <= 8'h00; else if (hcnt == 287) DATA <= SYNC_BYTE; // SAV 的 XY 字,H=0 else begin if (req_img_h == 1'b1) DATA <= hcnt[0] ? crcb : y; // 有效区交织 else DATA <= hcnt[0] ? 8'h10 : 8'h80; end end

消隐填充用8'h10和8'h80交替,不是随手填的常数:0x10是 8 位量化下的数字黑电平(对应 10 位下的 0x040),0x80是色度零值(对应 10 位下的 0x200)。很多板子第一次上电看到画面偏绿或者左边一条彩条,问题就出在消隐期填了 0x00 而不是 0x10/0x80——显示器把全零当成超黑,钳位电路直接跑飞。这个细节参考资料里往往一句话带过,实际调试时却是血泪经验。

2.2 625 行与两场:V_BLANK_START1/2的位置决定成败

垂直方向比水平方向麻烦,因为隔行扫描把一帧劈成了两个场。625 行里,第一场占 0 到 312,第二场占 313 到 624,两场各 288 行有效加若干行消隐。代码里用两个参数V_BLANK_START1 = 23和V_BLANK_START2 = 336分别标出两场垂直消隐的起点,这两个数字不是拍脑袋的,它们来自 BT656 对 PAL 的行编号约定。

具体到状态机:V信号在vcnt等于V_BLANK_START1-1或V_BLANK_START2-1时被拉低,在vcnt等于起点加 287 时被拉高。也就是说 V=0 的两个区间分别是 22~309 和 335~622,各 288 行;V=1 的区间是 0~21、310~334、623~624,合计 49 行。288 加 288 加 49 正好 625。这个“49 行垂直消隐”是 PAL 制式的硬指标,改任何一个数都会导致下游芯片锁不住场同步。

F场标志的处理更简单:vcnt == 0时清零,vcnt == 312时置一。它的物理含义是“当前处于第一场还是第二场”,同时它也是保护位 P2、P1、P0 的输入之一,所以必须在 EAV/SAV 生成之前就稳定下来。这里用组合逻辑直接赋值而不是打拍,是因为F只在行边界翻转,距离下一个 XY 字至少还有几十拍,建立时间绰绰有余。

2.3 计数器骨架:同步复位与外来同步信号的关系

整个模块的节拍来源是hcnt和vcnt两个 12 位计数器。hcnt计到 1727 归零,同时vcnt加一;vcnt计到 624 归零,一帧结束。除此之外还有一条外部同步通路syn_in_r:模块内部对它打两拍得到syn_d1和syn_d2,检测到上升沿就把两个计数器同时清零。这条通路的意义在于多机同步或者外触发场景,平时可以悬空不用。

// 行场计数器:外部同步沿优先复位,否则自由运行 always @(posedge clk_27) if (rst_27 == 1'b1) begin hcnt <= 12'd0; vcnt <= 12'd0; syn_d1 <= 1'b0; syn_d2 <= 1'b0; end else begin syn_d1 <= syn_in_r; syn_d2 <= syn_d1; if ((syn_d1 == 1'b1) && (syn_d2 == 1'b0)) begin hcnt <= 12'd0; // 外部同步上升沿,强制对齐帧头 vcnt <= 12'd0; end else if (hcnt == (H_LENGTH - 1)) begin hcnt <= 12'd0; if (vcnt == (V_LENGTH - 1)) vcnt <= 12'd0; else vcnt <= vcnt + 12'd1; end else begin hcnt <= hcnt + 12'd1; end end

打两拍是必须的,syn_in_r相对clk_27是异步信号,直接进比较器会有亚稳态风险,仿真波形上看不出来,上板之后偶发的整帧错位就是这么来的。这种问题在 ModelSim 里跑一万帧都复现不了,属于典型的“仿真通过、硬件玄学”。

提示:H_LENGTH和V_LENGTH用 parameter 而不是 localparam,是为了在例化时能改成一帧 525 行的 NTSC 配置。但要注意,一旦改动V_LENGTH,V_BLANK_START1/2以及场消隐的 287 偏移量都必须同步改,这几个参数之间是有耦合的,不是彼此独立。

3. EAV/SAV 与保护位:F/V/H 三个状态位怎么变成 P3~P0

定时参考码是 BT656 里最“形式主义”的部分——四个字节里三个是固定值,只有最后一个 XY 字携带信息。但恰恰是这个 XY 字,下游的 SDI 接收芯片靠它判断行场边界,错一位整幅画面就会撕裂或者上下翻滚。参考资料里讲保护位往往只给一张真值表,实际写代码时才知道表里那八个组合是怎么落到硬件上的。

3.1 XY 字的位定义与 SYNC_BYTE 的拼装

一个 XY 字节的 8 个比特从高到低是:固定位 1、场标志 F、垂直消隐 V、水平消隐 H、保护位 P3、P2、P1、P0。最高位恒为 1 是硬性规定,作用是让接收端在任意位置都能通过“找到 1”来建立字节边界。剩下七位里,F/V/H 三个是状态位,P3~P0 四个是校验位,用来对这三位做纠错校验。

保护位的生成规则是四个异或组合:P3 = V^H,P2 = F^H,P1 = F^V,P0 = F^V^H。这组关系的设计意图是让接收端能够检测出状态位中的单比特错误并纠正——四位保护位覆盖了三位状态位的全部 8 种合法组合,任何一位翻转都会落到非法码字上。手写代码时这四行异或必须一个字符都不差,参考资料里给真值表容易看花眼,直接照抄公式反而更稳。

// F/V/H 状态位的生成:H 控制行消隐,V 控制场消隐,F 标记奇偶场 always @(posedge clk_27) if (rst_27 == 1'b1) begin H <= 1'd0; V <= 1'd1; F <= 1'd0; end else begin // H:行内前 287 拍为消隐期 if (hcnt < (H_BLANK_LENGTH - 1)) H <= 1'd1; else H <= 1'd0; // V:两场各有一段 288 行的场消隐 if ((vcnt == (V_BLANK_START1 - 1)) || (vcnt == (V_BLANK_START2 - 1))) V <= 1'd0; else if ((vcnt == (V_BLANK_START1 + 287)) || (vcnt == (V_BLANK_START2 + 287))) V <= 1'd1; // F:半帧翻转,0 为第一场,1 为第二场 if (vcnt == 0) F <= 1'd0; else if (vcnt == 312) F <= 1'd1; end // 保护位与 XY 字拼装 assign P3 = V ^ H; assign P2 = F ^ H; assign P1 = F ^ V; assign P0 = F ^ V ^ H; always @(posedge clk_27) if (rst_27 == 1'b1) SYNC_BYTE <= 8'd0; else SYNC_BYTE <= {1'b1, F, V, H, P3, P2, P1, P0};

H的比较用的是H_BLANK_LENGTH - 1而不是H_BLANK_LENGTH,差这一拍的影响是 SAV 那个 XY 字里的 H 位会对不上。我在第一次写这块时就是把 287 写成了 288,仿真波形上 XY 值看着“差不多”,等到接收端解出来把 SAV 当 EAV,画面直接左右偏移 4 个采样——这种错误只能靠逐字节比对参考码字查出来。

3.2 保护位真值表与自查方法

把 F/V/H 的 8 种组合展开,XY 字节应该如下表。这张表建议在测试平台里当断言用,比肉眼看波形可靠得多。

FVHP3P2P1P0XY(二进制)
00000001000_0000
00110011000_1001
01010111000_1011
01100101000_0110
10001111001_1100
10111101001_0110
11011001001_0011
11101011001_0101

对照表里的两个常用值就能快速判断状态:第一场有效数据前的 SAV 是 F=0、V=0、H=0,XY=0x80;第一场场消隐期内的 EAV 是 F=0、V=1、H=1,XY=0x86。仿真时抓data_BT656[9:2],在有效数据开始前一拍应该是 0x80,消隐期内应该是 0x86 或 0xB6(第二场 V=1、H=1 时 F=1)。如果抓到 0x9C 或者别的值,基本可以断定F的翻转点写错了。

3.3 保护位的下游用途:为什么不能省

很多简化版实现会把 P3~P0 直接填 0,理由是“接收端不做纠错”。这在短距离、同源时钟的实验环境里确实能出图,但一旦接上 SDI 电缆驱动芯片跑长线,接收端在均衡之后可能有个别比特误码,没有保护位就只能靠场同步丢失来重锁,表现为画面偶发一闪。这套方案的定位是给板卡验证用的,保护位必须完整生成,省掉这四个异或省不出资源,却省掉了整个链路的容错能力。

从资源角度看,四个异或加五个触发器,在任何一款 FPGA 上都只占个零头。真正的成本在于验证:保护位错了不会立刻出问题,而是等你在整机联调时才发现“偶尔黑一下”,到那时候排查范围已经从编码模块扩大到整条链路。所以我的习惯是在编码模块旁边挂一个独立的参考码检查器,把每一组 FF 00 00 XY 都抓下来算一遍异或,对不上就打一个 error 脉冲出去。

4. 有效数据通道:req_img_v/req_img_h握手与 Y/CbCr 交织

编码器对外只需要两个请求信号:req_img_v告诉上游“本场开始了,可以送行”,req_img_h告诉上游“本行开始了,可以送像素”。这种设计把寻址责任完全交给编码器,上游只需要一个 FIFO 或者一块双口 RAM 按拍吐数据。握手协议极简,但里头的参数计算是整份代码里最容易改错的地方。

4.1 参数化起点:640×512 的图像窗怎么落在 720×576 的框里

模块内部的IMG_WIDTH = 640、IMG_HEIGHT = 512并不是最终输出分辨率,而是上游实际能提供的图像窗口。它比 720×576 小一圈,所以必须居中塞进有效区。水平方向的起点由一行REG_H_START = H_BLANK_LENGTH + ((H_LENGTH - H_BLANK_LENGTH)/2 - IMG_WIDTH)算出:288 + 720 - 640 = 368。物理含义是有效区共 1440 个采样,640 像素占 1280 个采样,左右各留 80 个采样,起点落在 288 + 80 = 368。这个式子把除法写成了“有效区一半减去图像宽度”,看着绕,实际是为了避免综合出除法器——(H_LENGTH - H_BLANK_LENGTH)/2在 1728/288 这组参数下正好是 720,除 2 是移位,综合器直接吃掉。

垂直方向更绕一点:REG_V_START1 = V_BLANK_START1 + (576 - IMG_HEIGHT)/4= 23 + 16 = 39。为什么除以 4 而不是 2?因为一场只占整帧一半的行,垂直居中时整帧的偏移量要再折半才落到场的行号上,所以是 (576-512)/2/2 = 16。这个“除以 4”在换成 720×576 全屏输出时会变成 0,看起来像 bug,其实是公式本身在设计时就考虑了两种情形的通用性。

注意:这两个REG_*参数是用parameter声明的,但它们的值依赖其他 parameter。Verilog-2001 里 parameter 的求值顺序按声明顺序进行,只要H_LENGTH、IMG_WIDTH这几个声明在REG_H_START之前就没问题。一旦顺序写反,综合工具不会报错,只会算出一个常数 0,然后整幅图像被推到左上角。

4.2 行请求用场请求门控:省一个与门也省一处分支

req_img_h的生成逻辑里藏了一个很实用的技巧:它在hcnt == REG_H_START时被赋值为req_img_v,而不是直接赋 1。这样行请求天然被场请求门控——非有效场期间,req_img_v是 0,行请求永远起不来,不需要额外写一个“并且当前在有效场内”的条件。

// 行场请求:行请求的置位条件直接取场请求,实现天然门控 always @(posedge clk_27) if (rst_27 == 1'b1) begin req_img_v <= 1'd0; req_img_h <= 1'd1; end else begin // 行请求窗口:从 REG_H_START 起,持续 IMG_WIDTH*2 拍(Y/C 各一拍) if (hcnt == REG_H_START) req_img_h <= req_img_v; else if (hcnt == (REG_H_START + IMG_WIDTH*2)) req_img_h <= 1'd0; // 场请求窗口:两场各取 IMG_HEIGHT/2 行 if ((vcnt == REG_V_START1) || (vcnt == REG_V_START2)) req_img_v <= 1'd1; else if ((vcnt == (REG_V_START1 + IMG_HEIGHT/2)) || (vcnt == (REG_V_START2 + IMG_HEIGHT/2))) req_img_v <= 1'd0; end

IMG_WIDTH*2这个乘法在综合时会被化简成常数,不用担心。但要注意hcnt是 12 位,REG_H_START + IMG_WIDTH*2 = 368 + 1280 = 1648,没溢出;如果哪天把IMG_WIDTH改到 720,1648 就变成 1808,超出了 1727 的行长,行请求永远清不掉,上游 FIFO 直接读穿。这种参数组合越界不会报错,只会让画面右边一截重复上一行的内容。

req_img_v的置位窗口是REG_V_START1到REG_V_START1 + 256,也就是每场取 256 行,两场拼起来 512 行。这里的 256 来自IMG_HEIGHT/2,隔行扫描下每场只更新一半的行,这也是为什么垂直起点要除以 4 的原因——整帧偏移量的一半落在一场里。如果上游给的是一整帧的逐行数据,那就得改成场间切换读取地址,或者干脆放弃隔行,改成逐行输出 576 行然后上下场重复。

4.3 交织规则:hcnt[0]这一个比特决定 UYVY 顺序

有效区里DATA <= hcnt[0] ? crcb : y,也就是偶数拍给 Y、奇数拍给 CbCr。这是 4:2:2 的标准排布:每个像素一个 Y,每两个像素共享一组 CbCr,所以时间上呈现 Y0 C0 Y1 C1 的交替。用hcnt[0]而不是单独维护一个交替标志,省掉一个触发器,而且天然不会错拍——交替相位和行内位置是绑死的。

真正需要注意的是hcnt[0]在有效区的起始相位。有效区从 288 开始,288 是偶数,所以第一个采样是 Y,符合预期。如果H_BLANK_LENGTH被改成奇数,第一个采样就变成 CbCr,整行颜色全错、亮度正常,看起来像“画面能出但是发灰”。这类问题在波形上一眼就能看出来,但如果你是直接上板看现象,很容易误判成色彩空间转换公式写错了。

输出位宽上,data_BT656 = {DATA, 2'b00}把 8 位数据左移两位凑成 10 位,这是 SD-SDI 的标准量化方式。低两位补 0 而不是补随机值,是为了让 10 位接收端解回 8 位时高位对齐。有些实现会在这里补DATA[7:6]做抖动,理论上能改善量化噪声,但对板卡验证场景来说没必要,多两位抖动反而让参考码字从 0x3FF 变成别的值,接收端可能认不出来。

// 10 位输出:8 位量化值左移两位,低两位补零 // 0xFF -> 0x3FC(近似 10 位满幅),0x00 -> 0x000 assign data_BT656 = {DATA, 2'b00}; // 奇偶场指示:直接取场标志,供外部 FIFO 或存储器分场写入 assign odd_even = F;

odd_even这个输出看似多余,实际是给上游存图用的:上游按odd_even把两场数据分别写进两块 RAM,读的时候再按同样的标志切换,就实现了隔行到逐行的转换。如果上游只有一块 RAM,那就得靠req_img_v的两次置位间隔来推断当前场,逻辑会复杂不少。

5. 避坑与排查:五个最容易翻车的时序细节

这份代码的规模不大,两百行出头,但每一处错误都能让接收端彻底不出图,而且现象往往高度相似——黑屏、花屏、滚动。真正难的不是写,是出问题之后往哪儿看。下面这几条是我踩过或者见别人踩过的,按“看现象、找原因、怎么改”整理。

5.1 现象是全黑但同步正常,先查消隐填充值

接收端能锁住同步(说明 XY 字和保护位是对的),但屏幕全黑,示波器上能看到行场信号。原因基本只有一个:消隐期填了 0x00 而不是 0x10/0x80。全零在数字视频里不是黑色,是“超黑”,接收端的钳位电路会把整个信号电平往下拉,连有效区都被判成黑。

解决办法就是把消隐填充改成8'h10(Y)和8'h80(CbCr),并且注意 10 位下的对应值是 0x040 和 0x200。如果代码里填的是8'h00而忘了改,仿真波形上看不出差别——ModelSim 不会告诉你“这个值是超黑”,只有上板才暴露。

5.2 现象是画面上下翻滚或整场错位,查F的翻转点

场同步能锁但画面不稳定,或者第二场的数据跑到了第一场的位置。根因通常是F的翻转时刻不对:代码里F在vcnt == 0清 0、vcnt == 312置 1,这两个点必须和V_BLANK_START1/2严格对应。如果 312 写成 311 或者 313,第二场的 XY 字里 F 位就会提前或延后一拍,接收端按场标志分场时把边界切错,表现出来就是画面上下抖。

排查方法是抓连续两场的 EAV 码字,第一场的 V=1 时段应该是 F=0、H=1,第二场应该是 F=1、H=1。如果看到的 F 位在两场之间没有翻转,或者翻转位置飘了,就回去核对 312 这个数。它来自 625 行的一半,但必须是 312 而不是 312.5,因为场边界只能落在整行上。

5.3 现象是画面右侧重复或截断,查REG_H_START + IMG_WIDTH*2

图像左边正常、右边一截重复上一行的内容,或者干脆少了四分之一。这个现象几乎可以锁定行请求的清除条件:hcnt == (REG_H_START + IMG_WIDTH*2)算出来的值必须小于H_LENGTH。按 640 的宽度算是 1648,安全;一旦把IMG_WIDTH改成 720,这个值变成 1808,超过 1727,条件永远不成立,req_img_h一直为高,上游数据被无限读下去。

解决方式有两种:一是把清除条件改成取模或者加边界保护,二是干脆把IMG_WIDTH限制在(H_LENGTH - REG_H_START)/2以内。我更倾向第二种,在参数定义处加一行注释写清上限,比在运行时做保护省资源。这类越界在综合报告里不会有任何警告,只能靠算。

5.4 现象是仿真通过但上板偶发错帧,查异步同步信号

ModelSim 里跑几十万帧都正常,上板之后每隔几分钟闪一次或者整帧错位。十个里有八个是syn_in_r没打够拍。代码里打了两拍得到syn_d1/syn_d2再检测上升沿,这个结构是对的,但如果syn_in_r来自另一个时钟域且抖动较大,两拍不够,需要三级同步。

还有一个隐蔽的坑:复位信号rst_27如果也是异步的,直接进if (rst_27 == 1'b1)会有复位释放时的亚稳态风险。稳妥做法是复位也打两拍再做同步释放。这个问题在仿真里完全不可见,因为仿真器的复位是理想的。

5.5 现象是颜色整体偏绿或偏紫,查 Y/CbCr 的输入顺序

画面能出、同步正常、亮度也对,就是颜色不对。这时候要查的不是编码器,是上游给数据的顺序。代码里偶数拍取y、奇数拍取crcb,如果上游按 CbYCrY 的顺序送,颜色通道就整体错位一位。表现出来就是人脸发绿或者天空发紫,看着像色彩空间转换公式错了,实际是通道顺序错了。

最快的确认方法是在有效区里连续抓 8 个采样,对照输入端的读写指针看相位。如果发现 Cb 和 Cr 互换(画面偏色但不至于错位),那是crcb这个 8 位总线内部的高低字节顺序问题,跟编码器无关。这类问题我一般会在模块端口上加注释写明“crcb 高字节为 Cr、低字节为 Cb”,避免后面接手的人再猜一遍。

6. 从仿真波形到上板验收:一份可以照着走的检查清单

编码模块做完只是第一步,能不能交差取决于你能不能证明它是对的。仿真阶段建议按“码字正确、时序正确、数据正确”三层来验:第一层抓 EAV/SAV 的 XY 值,对照第 3 章那张真值表逐个比对,这一步能把保护位和 F/V/H 的错误全挡掉;第二层量req_img_h的高电平宽度是否等于IMG_WIDTH*2,req_img_v每场是否只置位一次,这一步挡掉参数计算错误;第三层才是灌一张渐变图或者彩条,看输出的 Y/CbCr 顺序对不对。

上板阶段我一般按下面这张表推进,每一步都有明确的通过标准,不通过就不往下走:

阶段观察点通过标准
时钟与复位27MHz 时钟质量、复位释放时刻时钟抖动在可接受范围,复位在时钟稳定后释放
静态码流示波器抓data_BT656的消隐段稳定出现 FF 00 00 XY,XY 值符合真值表
行场锁定接收芯片的锁定指示上电后能稳定锁定,不掉锁
图像内容监视器画面彩条位置正确,无偏移、无重复、无黑边
长时间运行连续跑数小时无偶发闪断、无场错位

答辩或者交付时最容易被问到的两个问题,提前准备一下:一是“为什么行消隐是 288 而不是 280”,答案是 288 包含了 EAV/SAV 两组定时参考码各 4 个采样,加上 280 个纯填充;二是“为什么保护位要四个异或而不是直接填零”,答案是接收端要靠它做单比特纠错,长线传输时这是唯一的容错手段。这两个问题答得出来,说明你是真读过标准而不是抄了份代码。

提示:ModelSim 里仿真一整帧需要跑 1728×625 = 108 万个时钟周期,跑十帧就是一千多万拍,波形文件会很大。建议只 dump 前两行加一整场的关键信号,其余用$monitor打日志,比波形好查。

从那以后我每次做完这类时序编码模块,都会强制走一遍“先比对码字、再量脉宽、最后看图”的三步流程,哪怕时间再紧也不跳过第一步。因为码字错了后面全是白忙,而码字对了至少说明骨架没歪。希望这套流程和这份代码能帮到你,少走几段我走过的弯路。

本文还有配套的精品资源,点击获取