本帖最后由 UT发布 于 2025-3-25 18:57 编辑
1 HDMI输入上面几讲,我们讲清了SerDes IO使用,讲清了TMDS编解码,还有EDID,本讲我们就需要通过控制SerDes IO中的IDELAYE接口,实现自动控制输入信号延迟,完成通道的延迟锁定功能。 1.1auto_delay_ctl.v 1.1.1流程图
1.1.2状态机介绍
1.1.2.1状态机状态定义
- localparam IDLE = 4'h00; // 初始状态
- localparam ADD_DELAY_DEON_CHARGE = 4'h01; // 延迟校验状态
- localparam ADD_DELAY_START = 4'h02; // 延迟增加状态
- localparam ADD_DELAY_END = 4'h03; // 延迟增加结束
- localparam DE_DELAY = 4'h04; // 延迟减少状态
- localparam DE_DELAY_END = 4'h05; // 延迟减少结束
- localparam DOUBLE_CHARGE_DONE = 4'h06; // 二次校验完成
复制代码
1.1.4中心采样
使用ISERDESE进行数据采样时,需要具备中心采样思想,中心采样分成如下步骤: 1、进行Taps移动时,先找到第一次边缘采样成功点。 2、第一次边缘采样完成后,继续增加Taps点,使采样点从边缘往中心移动。 3、增加至已经无法采集到正确的数据时,认为已经到达了采样的另一个边缘点。 4、两个边缘采样点的中间值,就是中心采样点。 - //*****************************开始调整至中心采样模块***********************************//
- //中心采样开始打一拍
- always @(posedge I_clk_x1) begin
- r_middle_charge_start_r <= r_middle_charge_start;
- end
-
- //记录taps的减少次数
- always @(posedge I_clk_x1 or negedge I_rst) begin
- if (~I_rst) r_DETAPS_CUNT <= 5'b0;
- else if (w_ce && ~w_inc && w_detaps_flag) r_DETAPS_CUNT <= r_DETAPS_CUNT + 1'b1;
- else if (~w_detaps_flag) r_DETAPS_CUNT <= 5'b0;
- else r_DETAPS_CUNT <= r_DETAPS_CUNT;
- end
-
- //记录开始正常采样时延迟的taps移动次数
- always @(posedge I_clk_x1 or negedge I_rst) begin
- if (~I_rst) begin
- r_intaps_cunt_begin <= 8'b0;
- end else if (MS_R == ADD_DELAY_DEON_CHARGE && charge_done && ~r_middle_charge_start)//未开始中心采样步骤时,边缘第一次对齐
- r_intaps_cunt_begin <= O_cnt_value_2;
- end
- //记录结束正常采样时延迟的taps移动次数
- always @(posedge I_clk_x1 or negedge I_rst) begin
- if (~I_rst) begin
- r_intaps_cunt_end <= 8'b0;
- end else if (MS_R == ADD_DELAY_DEON_CHARGE && data_charge_fail && r_middle_charge_start)//开始中心采样时,第一次丢失正确数据,发生过采样错位
- r_intaps_cunt_end <= O_cnt_value_2;
- end
-
- //边缘采样完成,进入记录中心采样点步骤
- always @(posedge I_clk_x1 or negedge I_rst) begin
- if (~I_rst) r_middle_charge_start <= 1'b0;
- else if (charge_done && ~r_middle_charge_start && ~r_double_charge) r_middle_charge_start <= 1;
- else if (r_middle_charge_start && ~r_double_charge && data_charge_fail)
- r_middle_charge_start <= 0;
- else r_middle_charge_start <= r_middle_charge_start;
- end
复制代码
1.1.5二次校验为了防止程序的错误锁定,增加二次校验尤为重要。二次校验触发的情况为,当数据初步校验已经通过,并且完成中心采样调整后,对下一行数据再进行一次校验,此行数据的校验能满足校验条件,则视为二次校验通过,后续锁定此状态,进行数据传输。 - //二次校验,当状态机进入DE_DELAY_END状态时,开始二次检验,要求下一行数据检验依旧符合校验要求,目的是排除在场消隐阶段发生误判情况
- always @(posedge I_clk_x1 or negedge I_rst) begin
- if (~I_rst) r_double_charge <= 1'b0;
- else if (MS_R == DE_DELAY_END && r_double_charge == 1'b0) r_double_charge <= 1'b1;
- else if (w_double_charge_fail && r_double_charge == 1'b1)
- r_double_charge <= 1'b0;//二次校验失败,恢复r_double_charge状态,等待下次再次校验,检验成功不需要恢复,防止再次开始校验
- else r_double_charge <= r_double_charge;
- end
复制代码
1.2 auto_delay_ctl.v的RTL仿真1.2.1校验步骤自动延迟检测模块最重要的就是校验部分,校验部分一共有三轮校验,当第一次校验通过后,后续由r_middle_charge_start信号和r_double_charge信号控制,如下图所示。
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信号来判断我们期望的数值。如下图所示:
由上图可以看出,如果某行是符合我们需求的,w_delay_charge_done就在较高的范围保持高电平,如果错误,则较窄。这时我们通过行计数器r_delay_charge_cunt[10:0]来计数一整行的数据,包括行消隐部分,计数完一整行就传递一个时钟脉冲信号w_data_charge_over。当我们判断w_delay_charge_done信号和w_data_charge_over信号重合时,本行信号正确。如下图
当不重合时,则判断该行数据错误。如下图:
2 HDMI输出HDMI的输出并没有输入需要校准的步骤,所以大大简化了我们的设计的步骤,我们仅仅需要参考ISERDESE2的级联方法,将OSERDESE2进行两组级联,实现10to1的数据传输即可,这部分内容比较简单,不重复介绍。 oserdese2_10to1.v
- module oserdese2_10to1
- (
- input [9:0]txdata,
- input pclk,
- input clkdiv2,
- input txrst,
- output tx_p,
- output tx_n
- );
-
- wire [13:0] tx_data;
- wire cascade_do, cascade_di, cascade_to, cascade_ti;
- reg int_rst;
- wire dai;
-
- assign tx_data = {4'd0,txdata[9:0]};
-
- //----------------------------------------------------------------------------------
- //-- Reset should be asserted asynchronously an de-asserted synchronously
- //----------------------------------------------------------------------------------
- always @(*)
- if (txrst == 1'b1)
- int_rst <= 1'b1;
- else if(pclk)
- int_rst <= 1'b0;
- else
- int_rst <= int_rst;
-
-
- OBUFDS #(
- .IOSTANDARD("DEFAULT"), // Specify the output I/O standard
- .SLEW("SLOW") // Specify the output slew rate
- ) OBUFDS_inst (
- .O(tx_p), // Diff_p output (connect directly to top-level port)
- .OB(tx_n), // Diff_n output (connect directly to top-level port)
- .I(dai) // Buffer input
- );
-
- //----------------------------------------------------------------------------------
- //-- Cascaded OSERDES for 10:1 ratio (DDR)
- //----------------------------------------------------------------------------------
- OSERDESE2 #(
- .DATA_RATE_OQ("DDR"), // DDR, SDR
- .DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
- .DATA_WIDTH(10), // Parallel data width (2-8,10,14)
- .SERDES_MODE("MASTER"), // MASTER, SLAVE
- .TRISTATE_WIDTH(1) // 3-state converter width (1,4)
- )
- oserdese2_master (
- // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
- .D1(tx_data[0]),
- .D2(tx_data[1]),
- .D3(tx_data[2]),
- .D4(tx_data[3]),
- .D5(tx_data[4]),
- .D6(tx_data[5]),
- .D7(tx_data[6]),
- .D8(tx_data[7]),
- // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
- .T1(1'b0),
- .T2(1'b0),
- .T3(1'b0),
- .T4(1'b0),
- // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
- .SHIFTIN1(cascade_di),
- .SHIFTIN2(cascade_ti),
- // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
- .SHIFTOUT1(),
- .SHIFTOUT2(),
- .OCE(1'b1), // 1-bit input: Output data clock enable
- .CLK(clkdiv2), // 1-bit input: High speed clock
- .CLKDIV(pclk), // 1-bit input: Divided clock
- .OQ(dai), // 1-bit output: Data path output
- .TQ(), // 1-bit output: 3-state control
- .OFB(), // 1-bit output: Feedback path for data
- .TBYTEIN(1'b0), // 1-bit input: Byte group tristate
- .TBYTEOUT(), // 1-bit output: Byte group tristate
- .TFB(), // 1-bit output: 3-state control
- .TCE(1'b0), // 1-bit input: 3-state clock enable
- .RST(int_rst) // 1-bit input: Reset
- );
-
- OSERDESE2 #(
- .DATA_RATE_OQ("DDR"), // DDR, SDR
- .DATA_RATE_TQ("SDR"), // DDR, BUF, SDR
- .DATA_WIDTH(10), // Parallel data width (2-8,10,14)
- .SERDES_MODE("SLAVE"), // MASTER, SLAVE
- .TRISTATE_WIDTH(1) // 3-state converter width (1,4)
- )
- oserdese2_slave (
- // D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
- .D1(1'b0),
- .D2(1'b0),
- .D3(tx_data[8]),
- .D4(tx_data[9]),
- .D5(tx_data[10]),
- .D6(tx_data[11]),
- .D7(tx_data[12]),
- .D8(tx_data[13]),
- // T1 - T4: 1-bit (each) input: Parallel 3-state inputs
- .T1(1'b0),
- .T2(1'b0),
- .T3(1'b0),
- .T4(1'b0),
- // SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
- .SHIFTOUT1(cascade_di),
- .SHIFTOUT2(cascade_ti),
- // SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
- .SHIFTIN1(1'b0),
- .SHIFTIN2(1'b0),
- .OCE(1'b1), // 1-bit input: Output data clock enable
- .CLK(clkdiv2), // 1-bit input: High speed clock
- .CLKDIV(pclk), // 1-bit input: Divided clock
- .OQ(), // 1-bit output: Data path output
- .TQ(), // 1-bit output: 3-state control
- .OFB(), // 1-bit output: Feedback path for data
- .TFB(), // 1-bit output: 3-state control
- .TBYTEIN(1'b0), // 1-bit input: Byte group tristate
- .TBYTEOUT(), // 1-bit output: Byte group tristate
- .TCE(1'b0), // 1-bit input: 3-state clock enable
- .RST(int_rst) // 1-bit input: Reset
- );
-
- endmodule
复制代码
3 硬件接线使用 MLK-F9-CA01-100T开发板和FEP-HDMI扩展子卡作为方案演示,HDMI A和HDMI B均可以作为输入和输出接口使用,仅需要调整一下管脚约束。
4 测试结果
|