本帖最后由 UT发布 于 2025-3-25 18:59 编辑
HDMI 广泛应用于音视频领域,探索基于HDMI 实现的原理,为广大音视频领域的客户提供HDMI视频编解码开源技术。在FPGA开发中实现HDMI接口功能时,通常存在两种典型的技术路径: 第一种方案采用专用HDMI编码芯片实现,通过外接物理层转换器件完成信号处理。这种架构下,FPGA主要负责视频时序生成和像素数据预处理,将RGB/YUV格式的视频流通过并行总线传输至编码芯片,由后者完成TMDS编码、串行化处理和差分驱动等物理层操作。 第二种方案则完全基于FPGA的可编程逻辑资源实现,利用IO引脚直接模拟HDMI协议栈。该方案中,开发者需要通过硬件描述语言实现完整的TMDS编码器,将24位像素数据及其同步信号转换为三组差分数据流(D0-D2)和独立的差分时钟通道。 两种方案各有优劣:专用芯片方案开发周期短、信号完整性易保证,但增加BOM成本和PCB复杂度;全FPGA方案具有更高的设计灵活性,能够实现自定义分辨率/刷新率,但需要消耗大量逻辑资源和SerDes模块,对时序收敛要求更高。本文主要介绍第二种方案。 1 基础知识SerDes是一种通过时分复用(TDM)和差分信号传输技术实现并行数据与高速串行信号转换的接口。其核心组件包括: 1、串行器(Serializer):将低速并行信号转换为高速串行低压差分信号(LVDS),并集成发射器模块。 2、解串器(Deserializer):接收高速串行信号后,通过时钟与数据恢复(CDR)电路、串并转换电路还原为并行信号。 3、关键技术优势: 差分传输:抗噪声和抗干扰能力显著优于单端信号。 时钟恢复技术:消除传统并行接口的时钟偏移问题,支持更高传输速率。 编码与均衡:采用8b/10b编码实现直流平衡,结合自适应均衡(如CTLE、DFE)降低误码率(<10^-17)。 1.1 ISERDESE2
1.1.1参数功能
1.1.2信号功能
1.3 数据转换
1.3.1串并转换支持的位宽ISERDESE2 在SDR模式下数据转换的位宽可以为2、3、4、5、6、7、8bit,在DDR模式时,数据转换位宽为4、6、8bit,2个ISERDESE2级联使用,DDR模式可以支持10、14bit。如下图所示通过2个ISERDESE2级联出10bit,14bit位宽数据接口。
1.3.2串并转换后的数据顺序
如上图所示,可以看到数据的大小端是反了过来。
1.3.3数据对齐Bitslip用来实现并行数据的边界对齐。串行输入的8bit的数据,经过串并转换后,数据之间可能会错位,这是串并转换无法识别的,因此Bitslip就专门用来找到用户需要的并行数据边界。 对于SDR模式,Bitslip使能1次,则数据会左移1次,对于8bit并行数据,移动8次完成一个循环,可以这样无止境的循环,直到找到用户定义的并行数据。对于DDR模式,Bitslip工作方式不同,Bitslip使能1次,数据会右移1次或者左移3次,两者交替进行,同样移动8次完成一个循环。
Clock Event 1 第一个字CDAB已被采样到ISERDESE2的输入侧寄存器中。Bitslip引脚未置位,数据没有任何重新对齐。 Clock Event 2 Bitslip引脚被置为有效,这使Bitslip控制器在内部将所有数据位向右移一位。 Bitslip在一个(仅一个)CLKDIV周期内保持高电平。 Clock Event 3 置位Bitslip后的三个CLKDIV周期,Bitslip操作完成,新的移位数据作为BCDA在输出上可用。 Clock Event 4 于ISERDESE2配置为1:4,因此Bitslip最多可以再声明两次。 在第二次移位后(此DDR还剩三个位置),在Q4-Q1上提供了(必需)输出ABCD。 在第三次移位后(右移一个位置),输出DABC在Q4-Q1上可用。 第四次移位(剩下三个位置)之后,原始输出CDAB在Q4-Q1上可用,并且Bitslip已完成所有四个输入组合的循环。 1.3.4时钟
1、网络接口类型时钟要求(NETWORKING Interface Type) CLK 和CLKDIV必须确保相位相同。 CLK由BUFIO驱动,CLKDIV由BUFR驱动 CLK由MMCM或PLL驱动,CLKDIV由同一MMCM或PLL的CLKOUT [0:6]驱动。 当使用MMCM驱动ISERDESE2的CLK和CLKDIV时,不能混用提供ISERDESE2的缓冲器类型。例如,如果CLK由BUFG驱动,则CLKDIV也必须由BUFG驱动。另外,MMCM可以通过BUFIO和BUFR驱动ISERDESE2。 2、内存接口类型时钟要求(MEMORY Interface Type) CLK由BUFIO驱动,OCLK由BUFIO驱动并且CLKDIV由BUFR驱动 CLK由MMCM或者PLL驱动,OCLK由MMCM或者PLL驱动,CLKDIV由相同的MMCM或者PLL驱动 CLK由BUFG驱动,OCLK由BUFG驱动,CLKDIV由不同的BUFG驱动 2 IDELAYE2
2.1 参数描述
2.2 信号描述
2.3 IDELAY Timing DiagramIDELAY_TYPE = VARIABLE, IDELAY_VALUE = 0, and DELAY_SRC = IDATAIN
Clock Event 0: 在LD有效前,CNTVALUEOUT输出为未知值; Clock Event 1: 在C的上升沿,检测到复位(LD为高电平),加载预装值IDELAY_VALUE,数据在抽头(抽头为了形象描述数据的位的延迟位置)tap0上。 Clock Event 2: CE和INC上的脉冲在C的上升沿被捕获。这表示递增操作。输出增加1个tap延迟输出,这个时候抽头在tap1上。 Clock Event 3 CE和INC不再有效, 输出会保持在tap1上,直到LD,CE或INC引脚上有进一步的动作。也就是后面的数据都会由1个tap的延迟吗,如果是200M时钟就是78ps IDELAY timing diagram in VAR_LOAD mode
Clock Event 0: 在LD有效前,CNTVALUEOUT输出为未知值; Clock Event 1: 在C的上升沿采样到LD有效,此时DATAOUT延时CNTVALUEIN指定的延时Taps,改变tap Setting到Tap2,CNTVALUEOUT更新到新的Tap值; Clock Event 2: INC和CE有效,此时指定了增量操作,Tap值加1,DATAOUT输出从Tap2更新到Tap3,CNTVALUEOUT更新到新的Tap值; Clock Event 3 LD有效,DATAOUT输出延时更新到Tap10,CNTVALUEOUT更新到新的Tap值。 3 hdmi_rx_phy_wrapper.v此模块负责对数据进行1to10转化,知识点如下。 IBUFDS将差分数据转化成单端数据
- // IBUFDS实例,用于将差分输入转换为单端信号
- IBUFDS #(
- .DIFF_TERM ("TRUE"), // 启用差分终端电阻
- .IBUF_LOW_PWR("FALSE"), // 高性能模式
- .IOSTANDARD ("TMDS_33") // 设置I/O标准为TMDS_33
- ) IBUFDS_inst_d0 (
- .O (I_hdmi_rx_data), // 单端输出信号
- .I (TMDS_RX_P), // 差分正相位输入信号
- .IB(TMDS_RX_N) // 差分负相位输入信号
- );
复制代码
IDELAYE2负责对数据进行延迟的调整,使用的是VARIABLE模式,允许我们可以动态的调整数据的延迟值,调整的规则如下表:
初始延迟值我们设置的是0,但是注意,设置为0并不是代表延迟值就是0,IDELAYE2的默认延迟值为600ps,REFCLK_FREQUENCY值有范围,影响的是一次tap的延迟时间,范围是190.0-210.0或290.0-310.0MHz,选择默认200MHz,同时我们需要给IDELAYCTRL提供200MHz时钟,此处必须保持一致。数据类型我们选择DATA,影响的是编译器对于数据抖动的判断。
- // IDELAYE2实例,用于调整输入数据的延迟
- IDELAYE2 #(
- .CINVCTRL_SEL ("FALSE"), // 禁用动态时钟反转
- .DELAY_SRC ("IDATAIN"), // 使用内部数据输入作为延迟源
- .HIGH_PERFORMANCE_MODE("FALSE"), // 最小化功耗模式
- .IDELAY_TYPE ("VARIABLE"), // 可变延迟类型
- .IDELAY_VALUE (0), // 初始延迟值为0
- .PIPE_SEL ("FALSE"), // 不使用流水线模式
- .REFCLK_FREQUENCY (200.0), // 引用时钟频率为200MHz
- .SIGNAL_PATTERN ("DATA") // 数据信号模式
- ) IDELAYE2_inst (
- .CNTVALUEOUT(O_cnt_value), // 当前延迟计数值输出
- .DATAOUT (w_idelay_o), // 延迟后的数据输出
- .C (I_clk_x1), // 操作时钟输入
- .CE (I_ce), // 计数器控制信号
- .CINVCTRL (1'b0), // 时钟反转控制信号,默认不反转
- .CNTVALUEIN (1'b0), // 计数器初始值输入,默认为0
- .DATAIN (), // 数据输入(未使用)
- .IDATAIN (I_hdmi_rx_data), // 差分输入缓冲后的数据输入
- .INC (I_inc), // 增加或减少延迟的控制信号
- .LD (1'b0), // 加载延迟值到计数器(未使用)
- .LDPIPEEN (1'b0), // 流水线加载使能(未使用)
- .REGRST (~I_rst) // 计数器复位信号,高电平有效
- );
复制代码
IDELAYCTRL注意点为REFCLK时钟必须与IDELAYE2设置参数保持一致。
- // IDELAYCTRL实例,用于初始化和控制IDELAYE2
- IDELAYCTRL IDELAYCTRL_inst (
- .RDY (O_idelay_rdy), // 就绪输出信号
- .REFCLK(I_clk_200), // 引用时钟输入,200MHz
- .RST (~I_rst) // 复位信号,高电平有效
- );
复制代码
ISERDESE2使用级联模式,使用两个ISERDESE2级联,同时使用DDR模式,INTERFACE_TYPE需要配置成NETWORKING,可以同时输出10bit的数据,手册中对于两个ISERDESE2级联的连接方式如下图:
因为使用IDELAYE2负责数据的延迟,所以需要将延迟后的数据从DDLY输入。
- // ISERDESE2实例(主通道),用于将串行数据并行化
- ISERDESE2 #(
- .DATA_RATE ("DDR"), // 双数据速率模式
- .DATA_WIDTH (10), // 并行数据宽度为10位
- .DYN_CLKDIV_INV_EN("FALSE"), // 禁用动态时钟分频反转
- .DYN_CLK_INV_EN ("FALSE"), // 禁用动态时钟反转
- .INTERFACE_TYPE ("NETWORKING"), // 网络接口类型
- .IOBDELAY ("IFD"), // 输入延时
- .NUM_CE (1), // 一个片选信号
- .OFB_USED ("FALSE"), // 不使用反馈路径
- .SERDES_MODE ("MASTER") // 主通道模式
- ) ISERDESE2_u0inst (
- .O (), // 并行输出(未使用)
- .Q1 (O_raw_data[0]), // 第1个并行输出位
- .Q2 (O_raw_data[1]), // 第2个并行输出位
- .Q3 (O_raw_data[2]), // 第3个并行输出位
- .Q4 (O_raw_data[3]), // 第4个并行输出位
- .Q5 (O_raw_data[4]), // 第5个并行输出位
- .Q6 (O_raw_data[5]), // 第6个并行输出位
- .Q7 (O_raw_data[6]), // 第7个并行输出位
- .Q8 (O_raw_data[7]), // 第8个并行输出位
- .SHIFTOUT1 (SHIFTOUT1), // 数据宽度扩展输出1
- .SHIFTOUT2 (SHIFTOUT2), // 数据宽度扩展输出2
- .BITSLIP (1'b0), // 位滑动控制信号,默认不滑动
- .CE1 (1), // 片选信号1,始终启用
- .CE2 (0), // 片选信号2,禁用
- .CLKDIVP (1'b0), // 分频时钟相位选择,默认为0
- .CLK (I_clk_x5), // 主时钟输入
- .CLKB (~I_clk_x5), // 主时钟反相信号
- .CLKDIV (I_clk_x1), // 分频时钟输入
- .OCLK (1'b0), // 输出时钟(未使用)
- .DYNCLKDIVSEL(1'b0), // 动态分频时钟反转选择,默认为0
- .DYNCLKSEL (1'b0), // 动态时钟反转选择,默认为0
- .D (), // 串行数据输入(未使用)
- .DDLY (w_idelay_o), // 延迟后的数据输入
- .OFB (1'b0), // 反馈数据输入(未使用)
- .OCLKB (1'b0), // 输出时钟反相信号(未使用)
- .RST (~I_rst), // 复位信号,高电平有效
- .SHIFTIN1 (), // 数据宽度扩展输入1(未使用)
- .SHIFTIN2 () // 数据宽度扩展输入2(未使用)
- );
-
- // ISERDESE2实例(从通道),用于处理剩余的数据位
- ISERDESE2 #(
- .DATA_RATE ("DDR"), // 双数据速率模式
- .DATA_WIDTH (10), // 并行数据宽度为10位
- .DYN_CLKDIV_INV_EN("FALSE"), // 禁用动态时钟分频反转
- .DYN_CLK_INV_EN ("FALSE"), // 禁用动态时钟反转
- .INTERFACE_TYPE ("NETWORKING"), // 网络接口类型
- .IOBDELAY ("IFD"), // 输入延时
- .NUM_CE (1), // 一个片选信号
- .OFB_USED ("FALSE"), // 不使用反馈路径
- .SERDES_MODE ("SLAVE") // 从通道模式
- ) ISERDESE2_u1inst (
- .O (), // 并行输出(未使用)
- .Q1 (), // 第1个并行输出位(未使用)
- .Q2 (), // 第2个并行输出位(未使用)
- .Q3 (O_raw_data[8]), // 第3个并行输出位
- .Q4 (O_raw_data[9]), // 第4个并行输出位
- .Q5 (), // 第5个并行输出位(未使用)
- .Q6 (), // 第6个并行输出位(未使用)
- .Q7 (), // 第7个并行输出位(未使用)
- .Q8 (), // 第8个并行输出位(未使用)
- .SHIFTOUT1 (), // 数据宽度扩展输出1(未使用)
- .SHIFTOUT2 (), // 数据宽度扩展输出2(未使用)
- .BITSLIP (1'b0), // 位滑动控制信号,默认不滑动
- .CE1 (1), // 片选信号1,始终启用
- .CE2 (0), // 片选信号2,禁用
- .CLKDIVP (1'b0), // 分频时钟相位选择,默认为0
- .CLK (I_clk_x5), // 主时钟输入
- .CLKB (~I_clk_x5), // 主时钟反相信号
- .CLKDIV (I_clk_x1), // 分频时钟输入
- .OCLK (1'b0), // 输出时钟(未使用)
- .DYNCLKDIVSEL(1'b0), // 动态分频时钟反转选择,默认为0
- .DYNCLKSEL (1'b0), // 动态时钟反转选择,默认为0
- .D (1'b0), // 串行数据输入(未使用)
- .DDLY (1'b0), // 延迟后的数据输入(未使用)
- .OFB (1'b0), // 反馈数据输入(未使用)
- .OCLKB (1'b0), // 输出时钟反相信号(未使用)
- .RST (~I_rst), // 复位信号,高电平有效
- .SHIFTIN1 (SHIFTOUT1), // 数据宽度扩展输入1
- .SHIFTIN2 (SHIFTOUT2) // 数据宽度扩展输入2
- );
复制代码
4 hdmi_rx_phy_wapper.v的RTL功能
hdmi_rx_phy_wapper模块的主要功能
1.完成串行数据的延迟,并且能完成延迟控制
2.完成1:10的串行数据转并行数据
3.输出Taps值下面我们观察仿真信号能否达到上述三个需求
如下图所示,可以看出,O_cnt_value输出当前延迟的Taps值,黄线前的Taps值为00,黄线后的Taps值为01。通过自动校准模块控制的ce和inc信号进行控制。通过SHIFTOUT1和SHIFTOUT2信号,进行两个Iserdes2级联,输出10位的数据
。对比延迟前的数据和延迟后的数据,当Taps值为0的时候,看出两个信号之间延迟值为600ps。
对比延迟前的数据和延迟后的数据,当Taps值为1的时候,看出两个信号之间延迟值为678ps。
我们通过上面两种情况可得,一个Taps值大概延迟78ps的时间。通过观察仿真波形图,我们判断该模块满足我们的设计需求。
|