问答 店铺
热搜: ZYNQ FPGA discuz

QQ登录

只需一步,快速开始

微信登录

微信扫码,快速开始

微信扫一扫 分享朋友圈

已有 127 人浏览分享

开启左侧

HDMI项目-主讲hdmi输入输出-连载四

[复制链接]
127 0
本帖最后由 UT发布 于 2025-3-25 18:57 编辑

1 HDMI输入

上面几讲,我们讲清了SerDes IO使用,讲清了TMDS编解码,还有EDID,本讲我们就需要通过控制SerDes IO中的IDELAYE接口,实现自动控制输入信号延迟,完成通道的延迟锁定功能。

1.1auto_delay_ctl.v
1.1.1流程图
e534a9b77daa665401ecb4646a032c5d.jpg
1.1.2状态机介绍
1.1.2.1状态机状态定义
  1.   localparam IDLE = 4'h00;  // 初始状态
  2.   localparam ADD_DELAY_DEON_CHARGE = 4'h01;  // 延迟校验状态
  3.   localparam ADD_DELAY_START = 4'h02;  // 延迟增加状态
  4.   localparam ADD_DELAY_END = 4'h03;  // 延迟增加结束
  5.   localparam DE_DELAY = 4'h04;  // 延迟减少状态
  6.   localparam DE_DELAY_END = 4'h05;  // 延迟减少结束
  7.   localparam DOUBLE_CHARGE_DONE = 4'h06;  // 二次校验完成
复制代码
1.1.4中心采样

使用ISERDESE进行数据采样时,需要具备中心采样思想,中心采样分成如下步骤:

1、进行Taps移动时,先找到第一次边缘采样成功点。

2、第一次边缘采样完成后,继续增加Taps点,使采样点从边缘往中心移动。

3、增加至已经无法采集到正确的数据时,认为已经到达了采样的另一个边缘点。

4、两个边缘采样点的中间值,就是中心采样点。

  1.   //*****************************开始调整至中心采样模块***********************************//
  2.   //中心采样开始打一拍
  3.   always @(posedge I_clk_x1) begin
  4.     r_middle_charge_start_r <= r_middle_charge_start;
  5.   end
  6.   //记录taps的减少次数
  7.   always @(posedge I_clk_x1 or negedge I_rst) begin
  8.     if (~I_rst) r_DETAPS_CUNT <= 5'b0;
  9.     else if (w_ce && ~w_inc && w_detaps_flag) r_DETAPS_CUNT <= r_DETAPS_CUNT + 1'b1;
  10.     else if (~w_detaps_flag) r_DETAPS_CUNT <= 5'b0;
  11.     else r_DETAPS_CUNT <= r_DETAPS_CUNT;
  12.   end
  13.   //记录开始正常采样时延迟的taps移动次数
  14.   always @(posedge I_clk_x1 or negedge I_rst) begin
  15.     if (~I_rst) begin
  16.       r_intaps_cunt_begin <= 8'b0;
  17.     end else if (MS_R == ADD_DELAY_DEON_CHARGE && charge_done && ~r_middle_charge_start)//未开始中心采样步骤时,边缘第一次对齐
  18.       r_intaps_cunt_begin <= O_cnt_value_2;
  19.   end
  20.   //记录结束正常采样时延迟的taps移动次数
  21.   always @(posedge I_clk_x1 or negedge I_rst) begin
  22.     if (~I_rst) begin
  23.       r_intaps_cunt_end <= 8'b0;
  24.     end else if (MS_R == ADD_DELAY_DEON_CHARGE && data_charge_fail && r_middle_charge_start)//开始中心采样时,第一次丢失正确数据,发生过采样错位
  25.       r_intaps_cunt_end <= O_cnt_value_2;
  26.   end
  27.   //边缘采样完成,进入记录中心采样点步骤
  28.   always @(posedge I_clk_x1 or negedge I_rst) begin
  29.     if (~I_rst) r_middle_charge_start <= 1'b0;
  30.     else if (charge_done && ~r_middle_charge_start && ~r_double_charge) r_middle_charge_start <= 1;
  31.     else if (r_middle_charge_start && ~r_double_charge && data_charge_fail)
  32.       r_middle_charge_start <= 0;
  33.     else r_middle_charge_start <= r_middle_charge_start;
  34.   end
复制代码
1.1.5二次校验

为了防止程序的错误锁定,增加二次校验尤为重要。二次校验触发的情况为,当数据初步校验已经通过,并且完成中心采样调整后,对下一行数据再进行一次校验,此行数据的校验能满足校验条件,则视为二次校验通过,后续锁定此状态,进行数据传输。

  1. //二次校验,当状态机进入DE_DELAY_END状态时,开始二次检验,要求下一行数据检验依旧符合校验要求,目的是排除在场消隐阶段发生误判情况
  2.   always @(posedge I_clk_x1 or negedge I_rst) begin
  3.     if (~I_rst) r_double_charge <= 1'b0;
  4.     else if (MS_R == DE_DELAY_END && r_double_charge == 1'b0) r_double_charge <= 1'b1;
  5.     else if (w_double_charge_fail && r_double_charge == 1'b1)
  6.       r_double_charge <= 1'b0;//二次校验失败,恢复r_double_charge状态,等待下次再次校验,检验成功不需要恢复,防止再次开始校验
  7.     else r_double_charge <= r_double_charge;
  8.   end
复制代码
1.2 auto_delay_ctl.v的RTL仿真1.2.1校验步骤

自动延迟检测模块最重要的就是校验部分,校验部分一共有三轮校验,当第一次校验通过后,后续由r_middle_charge_start信号和r_double_charge信号控制,如下图所示。

640?wx_fmt=png&from=appmsg

r_middle_charge_start信号阶段是中心采样步骤,r_double_charge信号阶段是二次校验步骤。

1.2.2校验判断原理

校验步骤从仿真图中也可以很明显的体现。

校验通过三个计数器控制,分别是两个编码计数器,和一个行计数器。

编码计数器是r_delay_charge1_done_cunt[8:0]和r_delay_charge0_done_cunt[8:0]。当判断出符合要求的编码时,这两个计数器就分别开始计数。然后由w_delay_charge_done信号来判断我们期望的数值。如下图所示:

640?wx_fmt=png&from=appmsg

由上图可以看出,如果某行是符合我们需求的,w_delay_charge_done就在较高的范围保持高电平,如果错误,则较窄。这时我们通过行计数器r_delay_charge_cunt[10:0]来计数一整行的数据,包括行消隐部分,计数完一整行就传递一个时钟脉冲信号w_data_charge_over。当我们判断w_delay_charge_done信号和w_data_charge_over信号重合时,本行信号正确。如下图

640?wx_fmt=png&from=appmsg
当不重合时,则判断该行数据错误。如下图:
640?wx_fmt=png&from=appmsg
2 HDMI输出

HDMI的输出并没有输入需要校准的步骤,所以大大简化了我们的设计的步骤,我们仅仅需要参考ISERDESE2的级联方法,将OSERDESE2进行两组级联,实现10to1的数据传输即可,这部分内容比较简单,不重复介绍。

oserdese2_10to1.v
  1. module oserdese2_10to1
  2. (
  3. input [9:0]txdata,
  4. input pclk,
  5. input clkdiv2,
  6. input txrst,
  7. output tx_p,
  8. output tx_n
  9. );
  10. wire [13:0] tx_data;
  11. wire cascade_do, cascade_di, cascade_to, cascade_ti;
  12. reg int_rst;
  13. wire dai;
  14. assign tx_data = {4'd0,txdata[9:0]};
  15. //----------------------------------------------------------------------------------
  16. //-- Reset should be asserted asynchronously an de-asserted synchronously
  17. //----------------------------------------------------------------------------------
  18. always @(*)
  19.    if (txrst == 1'b1)
  20.       int_rst <= 1'b1;
  21.    else if(pclk)
  22.       int_rst <= 1'b0;
  23.     else
  24.         int_rst <= int_rst;
  25.    
  26. OBUFDS #(
  27.       .IOSTANDARD("DEFAULT"), // Specify the output I/O standard
  28.       .SLEW("SLOW")           // Specify the output slew rate
  29.    ) OBUFDS_inst (
  30.       .O(tx_p),     // Diff_p output (connect directly to top-level port)
  31.       .OB(tx_n),   // Diff_n output (connect directly to top-level port)
  32.       .I(dai)      // Buffer input
  33.    );
  34. //----------------------------------------------------------------------------------
  35. //-- Cascaded OSERDES for 10:1 ratio (DDR)
  36. //----------------------------------------------------------------------------------
  37. OSERDESE2 #(
  38.       .DATA_RATE_OQ("DDR"),   // DDR, SDR
  39.       .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
  40.       .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
  41.       .SERDES_MODE("MASTER"), // MASTER, SLAVE
  42.       .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
  43.    )
  44.    oserdese2_master (
  45.       // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
  46.       .D1(tx_data[0]),
  47.       .D2(tx_data[1]),
  48.       .D3(tx_data[2]),
  49.       .D4(tx_data[3]),
  50.       .D5(tx_data[4]),
  51.       .D6(tx_data[5]),
  52.       .D7(tx_data[6]),
  53.       .D8(tx_data[7]),  
  54.       // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
  55.       .T1(1'b0),
  56.       .T2(1'b0),
  57.       .T3(1'b0),
  58.       .T4(1'b0),
  59.       // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
  60.       .SHIFTIN1(cascade_di),
  61.       .SHIFTIN2(cascade_ti),
  62.       // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
  63.       .SHIFTOUT1(),
  64.       .SHIFTOUT2(),   
  65.       .OCE(1'b1),             // 1-bit input: Output data clock enable   
  66.       .CLK(clkdiv2),        // 1-bit input: High speed clock
  67.       .CLKDIV(pclk),     // 1-bit input: Divided clock                       
  68.       .OQ(dai),               // 1-bit output: Data path output
  69.       .TQ(),               // 1-bit output: 3-state control
  70.       .OFB(),             // 1-bit output: Feedback path for data
  71.       .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate      
  72.       .TBYTEOUT(),   // 1-bit output: Byte group tristate
  73.       .TFB(),             // 1-bit output: 3-state control
  74.       .TCE(1'b0),          // 1-bit input: 3-state clock enable      
  75.       .RST(int_rst)             // 1-bit input: Reset
  76.    );
  77. OSERDESE2 #(
  78.       .DATA_RATE_OQ("DDR"),   // DDR, SDR
  79.       .DATA_RATE_TQ("SDR"),   // DDR, BUF, SDR
  80.       .DATA_WIDTH(10),         // Parallel data width (2-8,10,14)
  81.       .SERDES_MODE("SLAVE"), // MASTER, SLAVE
  82.       .TRISTATE_WIDTH(1)      // 3-state converter width (1,4)
  83.    )
  84.    oserdese2_slave (
  85.       // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
  86.       .D1(1'b0),
  87.       .D2(1'b0),
  88.       .D3(tx_data[8]),
  89.       .D4(tx_data[9]),
  90.       .D5(tx_data[10]),
  91.       .D6(tx_data[11]),
  92.       .D7(tx_data[12]),
  93.       .D8(tx_data[13]),  
  94.       // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
  95.       .T1(1'b0),
  96.       .T2(1'b0),
  97.       .T3(1'b0),
  98.       .T4(1'b0),
  99.       // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
  100.       .SHIFTOUT1(cascade_di),
  101.       .SHIFTOUT2(cascade_ti),         
  102.       // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
  103.       .SHIFTIN1(1'b0),
  104.       .SHIFTIN2(1'b0),
  105.       .OCE(1'b1),             // 1-bit input: Output data clock enable   
  106.       .CLK(clkdiv2),        // 1-bit input: High speed clock
  107.       .CLKDIV(pclk),     // 1-bit input: Divided clock                       
  108.       .OQ(),               // 1-bit output: Data path output
  109.       .TQ(),               // 1-bit output: 3-state control
  110.       .OFB(),             // 1-bit output: Feedback path for data
  111.       .TFB(),             // 1-bit output: 3-state control
  112.       .TBYTEIN(1'b0),     // 1-bit input: Byte group tristate      
  113.       .TBYTEOUT(),   // 1-bit output: Byte group tristate
  114.       .TCE(1'b0),          // 1-bit input: 3-state clock enable      
  115.       .RST(int_rst)             // 1-bit input: Reset
  116.    );
  117.    
  118. endmodule
复制代码
3 硬件接线

使用 MLK-F9-CA01-100T开发板和FEP-HDMI扩展子卡作为方案演示,HDMI A和HDMI B均可以作为输入和输出接口使用,仅需要调整一下管脚约束。

640?wx_fmt=jpeg&from=appmsg
4 测试结果
640?wx_fmt=jpeg&from=appmsg

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

253

主题
精彩推荐
热门资讯
网友晒图
图文推荐

  • 微信公众平台

  • 扫描访问手机版