HDMI dvi_decoder详细解析

代码架构:

bash 复制代码
应用层(Top Module)
    └── hdmi_rx.v
        └── dvi_decoder.v
            ├── tmds_clock.v        (时钟恢复)
            ├── tmds_decoder.v x3   (红、绿、蓝三个通道)
            │   ├── selectio_1_10.v (串并转换)
            │   ├── phasealign.v    (字对齐与相位对齐)
            │   ├── channelbond.v   (通道同步)
            │   └── decoder.v       (8b/10b解码)
            └── (i2c_edid.v)        (EDID读取,独立模块)

1 时钟恢复模块:tmds_clock.v

bash 复制代码
module tmds_clock(

     input      tmds_clk_p,
     input      tmds_clk_n,


     output     pixelclk,
     output     serialclk,
     output     alocked     
    );

//wire define  
wire        clk_in_hdmi_clk;
wire        pixelclk;     
wire        serialclk; 
wire        alocked;
wire        clkfbout_hdmi_clk;
wire        clk_out_5x_hdmi_clk;

//*****************************************************
//**                    main code
//***************************************************** 
  
IBUFDS  # (
  .DIFF_TERM     ("FALSE"),
  .IBUF_LOW_PWR  ("TRUE"),
  .IOSTANDARD    ("TMDS_33")    
) u_IBUFDS(
  .O    (clk_in_hdmi_clk),
  .I    (tmds_clk_p),
  .IB   (tmds_clk_n)     
); 

     
MMCME2_ADV                                                                 
#(.BANDWIDTH            ("OPTIMIZED"),
  .CLKOUT4_CASCADE      ("FALSE"),
  .COMPENSATION         ("ZHOLD"),
  .STARTUP_WAIT         ("FALSE"),
  .DIVCLK_DIVIDE        (1),                //分频系数
  .CLKFBOUT_MULT_F      (5.000),            //反馈时钟的倍数系数
  .CLKFBOUT_PHASE       (0.000),
  .CLKFBOUT_USE_FINE_PS ("FALSE"),
  .CLKOUT0_DIVIDE_F     (1.000),            //分频系数,相位延迟,占空比
  .CLKOUT0_PHASE        (0.000),
  .CLKOUT0_DUTY_CYCLE   (0.500),
  .CLKOUT0_USE_FINE_PS  ("FALSE"),
  .CLKOUT1_DIVIDE       (5),                //分频系数,相位延迟,占空比
  .CLKOUT1_PHASE        (0.000),
  .CLKOUT1_DUTY_CYCLE   (0.500),
  .CLKOUT1_USE_FINE_PS  ("FALSE"),
  .CLKIN1_PERIOD        (6.667))            //输入时钟的周期,单位ns 6.667
                                      
mmcm_adv_inst                                                         
  // Output clocks                                                    
 (                                                                    
  .CLKFBOUT            (clkfbout_hdmi_clk),                           
  .CLKFBOUTB           (),                            
  .CLKOUT0             (clk_out_5x_hdmi_clk),                         
  .CLKOUT0B            (),                             
  .CLKOUT1             (clk_out_1x_hdmi_clk),                              
  .CLKOUT1B            (),                             
  .CLKOUT2             (),                              
  .CLKOUT2B            (),                             
  .CLKOUT3             (),                              
  .CLKOUT3B            (),                             
  .CLKOUT4             (),                              
  .CLKOUT5             (),                              
  .CLKOUT6             (),                              
   // Input clock control                                             
  .CLKFBIN             (clkfbout_hdmi_clk),                           
  .CLKIN1              (clk_in_hdmi_clk),                             
  .CLKIN2              (1'b0),                                        
   // Tied to always select the primary input clock                   
  .CLKINSEL            (1'b1),                                        
  // Ports for dynamic reconfiguration                                
  .DADDR               (7'h0),                                        
  .DCLK                (1'b0),                                        
  .DEN                 (1'b0),                                        
  .DI                  (16'h0),                                       
  .DO                  (),                                   
  .DRDY                (),                                 
  .DWE                 (1'b0),                                        
  // Ports for dynamic phase shift                                    
  .PSCLK               (1'b0),                                        
  .PSEN                (1'b0),                                        
  .PSINCDEC            (1'b0),                                        
  .PSDONE              (),                               
  // Other control and status signals                                 
  .LOCKED              (alocked),                                
  .CLKINSTOPPED        (),                         
  .CLKFBSTOPPED        (),                         
  .PWRDWN              (1'b0),                                        
  .RST                 (0));                           
 
// 5x fast serial clock
BUFG u_BUFG(
      .O (serialclk), // 1-bit output: Clock output (connect to I/O clock loads).
      .I (clk_out_5x_hdmi_clk)  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
   );   
   
BUFG u_BUFG_0(
      .O (pixelclk), // 1-bit output: Clock output (connect to I/O clock loads).
      .I (clk_out_1x_hdmi_clk)  // 1-bit input: Clock input (connect to an IBUF or BUFMR).
   );      
 
 
endmodule

像素时钟 (pixelclk):视频像素速率时钟(如74.25MHz、148.5MHz等)

串行时钟 (serialclk):像素时钟的5倍频(用于串并转换)

锁定状态 (alocked):PLL锁定指示

bash 复制代码
module tmds_clock(
    // 差分时钟输入(来自HDMI接口)
    input      tmds_clk_p,
    input      tmds_clk_n,

    // 恢复时钟输出
    output     pixelclk,    // 1x像素时钟
    output     serialclk,   // 5x串行时钟(用于ISERDES)
    output     alocked      // PLL锁定状态
);
bash 复制代码
// 核心参数
.DIVCLK_DIVIDE        (1),        // 分频系数 = 1(不分频)
.CLKFBOUT_MULT_F      (5.000),    // 反馈时钟倍频系数 = 5
.CLKOUT0_DIVIDE_F     (1.000),    // CLKOUT0分频系数 = 1
.CLKOUT1_DIVIDE       (5),        // CLKOUT1分频系数 = 5
.CLKIN1_PERIOD        (6.667)     // 输入时钟周期 = 6.667ns(≈150MHz)
bash 复制代码
输入时钟周期 = 6.667ns → 频率 ≈ 150MHz(实际由HDMI源决定)

反馈路径:
  VCO频率 = 输入频率 × CLKFBOUT_MULT_F / DIVCLK_DIVIDE
         = 150MHz × 5 / 1 = 750MHz

输出时钟:
  serialclk(CLKOUT0) = VCO / CLKOUT0_DIVIDE_F
                     = 750MHz / 1 = 750MHz(5x像素时钟)
  
  pixelclk(CLKOUT1) = VCO / CLKOUT1_DIVIDE
                    = 750MHz / 5 = 150MHz(1x像素时钟)
  1. IBUFDS - 差分输入缓冲器
bash 复制代码
 IBUFDS #(
  .DIFF_TERM     ("FALSE"),     // 不启用板载差分终端
  .IBUF_LOW_PWR  ("TRUE"),      // 低功耗模式
  .IOSTANDARD    ("TMDS_33")    // TMDS 3.3V电平标准
) u_IBUFDS(
  .O    (clk_in_hdmi_clk),      // 单端时钟输出
  .I    (tmds_clk_p),           // 正端输入
  .IB   (tmds_clk_n)            // 负端输入
);

将差分时钟转换为单端时钟

提供合适的输入阻抗匹配

支持TMDS电平标准

  1. BUFG - 全局时钟缓冲器

  2. // 5x时钟缓冲

bash 复制代码
BUFG u_BUFG(
  .O (serialclk),              // 缓冲后的5x时钟
  .I (clk_out_5x_hdmi_clk)     // 来自MMCM的5x时钟
);

// 1x时钟缓冲  
BUFG u_BUFG_0(
  .O (pixelclk),               // 缓冲后的1x时钟
  .I (clk_out_1x_hdmi_clk)     // 来自MMCM的1x时钟
);

提供低偏斜的全局时钟分布

驱动FPGA全局时钟网络

确保时钟到达各个模块的时间一致性

3 时钟拓扑结构

bash 复制代码
tmds_clk_p/n(差分,来自HDMI)
        ↓ IBUFDS
clk_in_hdmi_clk(单端)
        ↓ MMCME2_ADV
        ├── clk_out_5x_hdmi_clk(5x,未缓冲)
        │       ↓ BUFG
        │       serialclk(5x,全局)
        │
        └── clk_out_1x_hdmi_clk(1x,未缓冲)
                ↓ BUFG
                pixelclk(1x,全局)
        ↑反馈
clkfbout_hdmi_clk

2 selectio_1_10.v - 串并转换模块

selectio_1_10.v 模块实现:

差分输入:接收TMDS差分数据

可调延迟:通过IDELAYE2调整数据采样相位

串并转换:将1位高速串行数据转换为10位并行数据

字对齐支持:提供bitslip控制接口

bash 复制代码
`timescale 1ps/1ps

module selectio_1_10
   // width of the data for the system
 #(parameter SYS_W = 1,
   // width of the data for the device
   parameter DEV_W = 10)
 (
  // From the system into the device
  input  [SYS_W-1:0]   data_in_from_pins_p,   //差分数据输入
  input  [SYS_W-1:0]   data_in_from_pins_n,
  output [DEV_W-1:0]   data_in_to_device,     //10bit并行数据输出
  input                in_delay_ld,           //加载寄存器的延迟值
  input  [SYS_W -1 :0] in_delay_data_ce,      //调整延迟值的有效使能
  input  [SYS_W -1 :0] in_delay_data_inc,     //增减延迟值
 
  input                ref_clock,             //200M参考时钟
  input  [SYS_W-1:0]   bitslip,               //字对齐调整信号
  output  [4:0]        in_delay_data_cnt,     //当前延迟值
                                   
  input                clk_in,                //5倍像素时钟
  input                clk_div_in,            //1倍像素时钟
  input                io_reset               //io的复位
  );    
  
  localparam         num_serial_bits = DEV_W/SYS_W;


  wire [SYS_W-1:0]  data_in_from_pins_int;
  wire [SYS_W-1:0]  data_in_from_pins_delay;
  wire [SYS_W-1:0]  delay_data_busy;
  wire [SYS_W-1:0]  in_delay_ce;
  wire [SYS_W-1:0]  in_delay_inc_dec;
  wire 				      ref_clock_bufg;
  wire [SYS_W-1:0]  iserdes_q[0:13];   
  
  assign in_delay_ce = { in_delay_data_ce[0]};
  assign in_delay_inc_dec = { in_delay_data_inc[0]};

  // We have multiple bits- step over every bit, instantiating the required elements
  genvar pin_count;
  genvar slice_count;
  generate for (pin_count = 0; pin_count < SYS_W; pin_count = pin_count + 1) begin: pins
    // Instantiate the buffers
    ////------------------------------
    // Instantiate a buffer for every bit of the data bus
    IBUFDS
      #(.DIFF_TERM  ("FALSE"),             // Differential termination
        .IOSTANDARD ("TMDS_33"))
     ibufds_inst
       (.I          (data_in_from_pins_p  [pin_count]),
        .IB         (data_in_from_pins_n  [pin_count]),
        .O          (data_in_from_pins_int[pin_count]));

    // Instantiate the delay primitive
    ////-------------------------------

     (* IODELAY_GROUP = "selectio_wiz_0_group" *)
     IDELAYE2
       # (
         .CINVCTRL_SEL           ("FALSE"),                            // TRUE, FALSE
         .DELAY_SRC              ("IDATAIN"),                          // IDATAIN, DATAIN
         .HIGH_PERFORMANCE_MODE  ("FALSE"),                            // TRUE, FALSE
         .IDELAY_TYPE            ("VARIABLE"),                         // FIXED, VARIABLE, or VAR_LOADABLE
         .IDELAY_VALUE           (0),                                  // 0 to 31
         .REFCLK_FREQUENCY       (200.0),
         .PIPE_SEL               ("FALSE"),
         .SIGNAL_PATTERN         ("DATA"))                             // CLOCK, DATA
       idelaye2_bus
           (
         .DATAOUT                (data_in_from_pins_delay[pin_count]),
         .DATAIN                 (1'b0),                               
         .C                      (clk_div_in),
         .CE                     (in_delay_ce[pin_count]), 
         .INC                    (in_delay_inc_dec[pin_count]), 
         .IDATAIN                (data_in_from_pins_int  [pin_count]), 
         .LD                     (in_delay_ld),
         .REGRST                 (io_reset),
         .LDPIPEEN               (1'b0),
         .CNTVALUEIN             (5'b00000),
         .CNTVALUEOUT            (in_delay_data_cnt),
         .CINVCTRL               (1'b0)
         );

     // local wire only for use in this generate loop
     wire cascade_shift;
     wire [SYS_W-1:0] icascade1;
     wire [SYS_W-1:0] icascade2;
     wire clk_in_int_inv;

     assign clk_in_int_inv = ~ clk_in;

     // declare the iserdes
     ISERDESE2
       # (
         .DATA_RATE         ("DDR"),
         .DATA_WIDTH        (10),
         .INTERFACE_TYPE    ("NETWORKING"), 
         .DYN_CLKDIV_INV_EN ("FALSE"),
         .DYN_CLK_INV_EN    ("FALSE"),
         .NUM_CE            (2),
         .OFB_USED          ("FALSE"),
         .IOBDELAY          ("IFD"),                               
         .SERDES_MODE       ("MASTER"))
       iserdese2_master (
         .Q1                (iserdes_q[0][pin_count]),
         .Q2                (iserdes_q[1][pin_count]),
         .Q3                (iserdes_q[2][pin_count]),
         .Q4                (iserdes_q[3][pin_count]),
         .Q5                (iserdes_q[4][pin_count]),
         .Q6                (iserdes_q[5][pin_count]),
         .Q7                (iserdes_q[6][pin_count]),
         .Q8                (iserdes_q[7][pin_count]),
         .SHIFTOUT1         (icascade1[pin_count]),               
         .SHIFTOUT2         (icascade2[pin_count]),               
         .BITSLIP           (bitslip[pin_count]),                 
                                                                  
         .CE1               (1'b1),                       
         .CE2               (1'b1),                       
         .CLK               (clk_in),                             
         .CLKB              (clk_in_int_inv),                     
         .CLKDIV            (clk_div_in),                         
         .CLKDIVP           (1'b0),
         .D                 (1'b0),                               
         .DDLY              (data_in_from_pins_delay[pin_count]), 
         .RST               (io_reset),                           
         .SHIFTIN1          (1'b0),
         .SHIFTIN2          (1'b0),
    // unused connections
         .DYNCLKDIVSEL      (1'b0),
         .DYNCLKSEL         (1'b0),
         .OFB               (1'b0),
         .OCLK              (1'b0),
         .OCLKB             (1'b0),
         .O                 ());                                  

     ISERDESE2
       # (
         .DATA_RATE         ("DDR"),
         .DATA_WIDTH        (10),
         .INTERFACE_TYPE    ("NETWORKING"),
         .DYN_CLKDIV_INV_EN ("FALSE"),
         .DYN_CLK_INV_EN    ("FALSE"),
         .NUM_CE            (2),
         .OFB_USED          ("FALSE"),
         .IOBDELAY          ("IFD"),                
         .SERDES_MODE       ("SLAVE"))
       iserdese2_slave (
         .Q1                (),
         .Q2                (),
         .Q3                (iserdes_q[8][pin_count]),
         .Q4                (iserdes_q[9][pin_count]),
         .Q5                (iserdes_q[10][pin_count]),
         .Q6                (iserdes_q[11][pin_count]),
         .Q7                (iserdes_q[12][pin_count]),
         .Q8                (iserdes_q[13][pin_count]),
         .SHIFTOUT1         (),
         .SHIFTOUT2         (),
         .SHIFTIN1          (icascade1[pin_count]),  
         .SHIFTIN2          (icascade2[pin_count]),  
         .BITSLIP           (bitslip[pin_count]),    
                                                     
         .CE1               (1'b1),         
         .CE2               (1'b1),         
         .CLK               (clk_in),                
         .CLKB              (clk_in_int_inv),        
         .CLKDIV            (clk_div_in),            
         .CLKDIVP           (1'b0),
         .D                 (1'b0),                  
         .DDLY              (1'b0),
         .RST               (io_reset),              
   // unused connections
         .DYNCLKDIVSEL      (1'b0),
         .DYNCLKSEL         (1'b0),
         .OFB               (1'b0),
         .OCLK              (1'b0),
         .OCLKB             (1'b0),
         .O                 ());              
     ////---------------------------------------------------------
     for (slice_count = 0; slice_count < num_serial_bits; slice_count = slice_count + 1) begin: in_slices
        assign data_in_to_device[slice_count] =
          iserdes_q[num_serial_bits-slice_count-1];
     end
  end
  endgenerate
  
// IDELAYCTRL is needed for calibration
(* IODELAY_GROUP = "selectio_wiz_0_group" *)
  IDELAYCTRL
    delayctrl (
     .RDY    (delay_locked),
     .REFCLK (ref_clock),
     .RST    (io_reset));

endmodule
bash 复制代码
物理层 → 差分输入 → 可调延迟 → 串并转换 → 并行输出
            ↓            ↓           ↓
         IBUFDS      IDELAYE2    ISERDESE2
  1. IBUFDS - 差分输入缓冲器
bash 复制代码
IBUFDS #(
  .DIFF_TERM     ("FALSE"),          // 禁用板载差分终端
  .IOSTANDARD    ("TMDS_33")         // TMDS 3.3V电平标准
) ibufds_inst (
  .I  (data_in_from_pins_p[pin_count]),    // 正端输入
  .IB (data_in_from_pins_n[pin_count]),    // 负端输入  
  .O  (data_in_from_pins_int[pin_count])   // 单端输出
);

电平标准:TMDS_33,支持3.3V TMDS信号

终端电阻:FALSE(通常由HDMI源端提供终端)

传输延迟:约200-300ps

  1. IDELAYE2 - 精密可调输入延迟单元
bash 复制代码
(* IODELAY_GROUP = "selectio_wiz_0_group" *)
IDELAYE2 #(
  .CINVCTRL_SEL           ("FALSE"),         // 不使用时钟反相控制
  .DELAY_SRC              ("IDATAIN"),       // 延迟数据输入路径
  .HIGH_PERFORMANCE_MODE  ("FALSE"),         // 标准性能模式
  .IDELAY_TYPE            ("VARIABLE"),      // 可变延迟模式
  .IDELAY_VALUE           (0),               // 初始延迟值
  .REFCLK_FREQUENCY       (200.0),           // 参考时钟频率(MHz)
  .PIPE_SEL               ("FALSE"),         // 不使用流水线模式
  .SIGNAL_PATTERN         ("DATA")           // 信号类型为数据
) idelaye2_bus (
  .DATAOUT    (data_in_from_pins_delay[pin_count]), // 延迟后输出
  .DATAIN     (1'b0),                         // 不使用DATAIN路径
  .C          (clk_div_in),                   // 控制时钟(像素时钟)
  .CE         (in_delay_ce[pin_count]),       // 延迟调整使能
  .INC        (in_delay_inc_dec[pin_count]),  // 增加(1)/减少(0)控制
  .IDATAIN    (data_in_from_pins_int[pin_count]), // 输入数据
  .LD         (in_delay_ld),                  // 加载延迟值
  .REGRST     (io_reset),                     // 复位延迟寄存器
  .CNTVALUEOUT(in_delay_data_cnt),            // 当前延迟计数值
  .CNTVALUEIN (5'b00000)                      // 加载值(未使用)
);
bash 复制代码
输入信号 → IDELAYE2 → 延迟后输出
                    ↑控制
               clk_div_in + CE/INC
bash 复制代码
延迟步长 = 1/(REFCLK_FREQUENCY × 32 × 2)
        = 1/(200MHz × 64)
        ≈ 78.125ps

每个IDELAYE2:31个tap(0-30)

总延迟范围:0-30 × 78.125ps ≈ 0-2.34ns

覆盖约0.46个UI@148.5MHz(6.73ns周期)

bash 复制代码
// 增加延迟
if (CE=1, INC=1) CNTVALUE <= CNTVALUE + 1
// 减少延迟  
if (CE=1, INC=0) CNTVALUE <= CNTVALUE - 1
// 加载预设值
if (LD=1) CNTVALUE <= CNTVALUEIN
// 复位
if (REGRST=1) CNTVALUE <= IDELAY_VALUE

参考学习:详细解释xilinx源语的使用:IDELAYE2和IDELAYE3

  1. ISERDESE2 - 高速串并转换器

模块包含主从级联的两个ISERDESE2:

bash 复制代码
ISERDESE2 #(
  .DATA_RATE         ("DDR"),           // 双倍数据率
  .DATA_WIDTH        (10),              // 10:1串并转换
  .INTERFACE_TYPE    ("NETWORKING"),    // 网络接口模式
  .NUM_CE            (2),               // 使用2个时钟使能
  .OFB_USED          ("FALSE"),         // 不使用OFB反馈
  .IOBDELAY          ("IFD"),           // 延迟在输入寄存器前
  .SERDES_MODE       ("MASTER")         // 主模式
) iserdese2_master (
  // 并行数据输出(Q1-Q8)
  .Q1        (iserdes_q[0][pin_count]),  // 最早的数据位
  .Q2        (iserdes_q[1][pin_count]),
  .Q3        (iserdes_q[2][pin_count]),
  .Q4        (iserdes_q[3][pin_count]),
  .Q5        (iserdes_q[4][pin_count]),
  .Q6        (iserdes_q[5][pin_count]),
  .Q7        (iserdes_q[6][pin_count]),
  .Q8        (iserdes_q[7][pin_count]),
  
  // 级联输出(到从ISERDESE2)
  .SHIFTOUT1 (icascade1[pin_count]),      // 级联数据1
  .SHIFTOUT2 (icascade2[pin_count]),      // 级联数据2
  
  // 控制信号
  .BITSLIP   (bitslip[pin_count]),        // 字对齐控制
  .CE1       (1'b1),                      // 时钟使能1(常开)
  .CE2       (1'b1),                      // 时钟使能2(常开)
  .CLK       (clk_in),                    // 高速串行时钟(5x像素时钟)
  .CLKB      (clk_in_int_inv),            // 反相高速时钟
  .CLKDIV    (clk_div_in),                // 并行时钟(像素时钟)
  .DDLY      (data_in_from_pins_delay[pin_count]), // 延迟后数据
  .RST       (io_reset)                   // 复位
);
bash 复制代码
ISERDESE2 #(
  .SERDES_MODE       ("SLAVE")          // 从模式
) iserdese2_slave (
  // 并行数据输出(Q3-Q8,实际使用Q3-Q6)
  .Q3        (iserdes_q[8][pin_count]),   // 对应主ISERDES的Q9
  .Q4        (iserdes_q[9][pin_count]),   // 对应主ISERDES的Q10
  .Q5        (iserdes_q[10][pin_count]),
  .Q6        (iserdes_q[11][pin_count]),
  .Q7        (iserdes_q[12][pin_count]),
  .Q8        (iserdes_q[13][pin_count]),
  
  // 级联输入(来自主ISERDESE2)
  .SHIFTIN1  (icascade1[pin_count]),      // 级联数据1
  .SHIFTIN2  (icascade2[pin_count]),      // 级联数据2
);

4 数据位重映射

bash 复制代码
generate
  for (slice_count = 0; slice_count < num_serial_bits; slice_count = slice_count + 1) begin: in_slices
    // 重新排列数据位序
    assign data_in_to_device[slice_count] =
      iserdes_q[num_serial_bits-slice_count-1];
  end
endgenerate
bash 复制代码
ISERDES输出:  Q1 Q2 Q3 Q4 Q5 Q6 Q7 Q8 Q3' Q4'
对应TMDS位:  D0 D1 D2 D3 D4 D5 D6 D7 D8 D9
重映射后:    D9 D8 D7 D6 D5 D4 D3 D2 D1 D0

参考学习:Xilinx LVDS ISERDESE2

  1. IDELAYCTRL - 延迟校准控制器
bash 复制代码
(* IODELAY_GROUP = "selectio_wiz_0_group" *)
IDELAYCTRL delayctrl (
  .RDY    (delay_locked),      // 延迟模块就绪
  .REFCLK (ref_clock),         // 200MHz参考时钟
  .RST    (io_reset)           // 复位
);
bash 复制代码
校准IDELAYE2的tap延迟
补偿工艺、电压、温度(PVT)变化
确保每个tap延迟≈78.125ps
每个bank需要一个IDELAYCTRL

参考学习:详细解释xilinx源语的使用:IDELAYCTRL

(https://blog.csdn.net/baidu_34971492/article/details/157582262)

时序关系图

bash 复制代码
时序图(DDR模式,10位数据):
clk_in     _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
clk_in_inv ‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
串行数据    D9 D8 D7 D6 D5 D4 D3 D2 D1 D0 ...
采样点        ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑   ↑
            (clk_in上升沿和下降沿交替采样)
            
clk_div_in _|‾‾‾‾‾‾‾‾‾‾|_____|‾‾‾‾‾‾‾‾‾‾|_____|
并行输出          Q1=D0              Q1=D1
               Q2=D1              Q2=D2
               ...                ...
            (每个clk_div_in周期输出2位)

ISERDESE2内部移位寄存器

bash 复制代码
时间    | clk_div_in周期0       | clk_div_in周期1
--------|----------------------|----------------------
clk_in  | _|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_|‾|_
串行数据| D9 D8 D7 D6 D5 D4 D3 D2 D1 D0
--------|----------------------|----------------------
主ISERDES:
  Q1    | D0                   | D1
  Q2    | D1                   | D2
  Q3    | D2                   | D3
  Q4    | D3                   | D4
从ISERDES:
  Q3    | D8                   | D9
  Q4    | D9                   | (下一数据D8)

Bitslip操作机制

Bitslip用于调整10位数据的边界对齐:

bash 复制代码
.BITSLIP (bitslip[pin_count])  // 字对齐控制
bash 复制代码
初始状态(错位):
  ISERDES输出: | X | D9| D8| D7| D6| D5| D4| D3| D2| D1| 
  实际数据:      | D9| D8| D7| D6| D5| D4| D3| D2| D1| D0|
  
应用bitslip后:
  ISERDES输出: | D9| D8| D7| D6| D5| D4| D3| D2| D1| D0| ✓

Bitslip信号必须在clk_div_in上升沿有效

需要保持至少1个clk_div_in周期

Bitslip后需要3个clk_div_in周期稳定(kBitslipDelay=3)

延迟控制状态机

bash 复制代码
// phasealign.v中的控制
always @ (posedge pixelclk) begin
    if(pstate == DlyIncSt) begin
        pidly_inc <= 1;      // 增加延迟
        pidly_ce <= 1;       // 使能调整
    end    
    else if(pstate == DlyDecSt) begin
        pidly_inc <= 0;      // 减少延迟
        pidly_ce <= 1;       // 使能调整
    end
    else begin
        pidly_ce <= 0;       // 禁止调整
    end
end
相关推荐
忙什么果16 小时前
上位机、下位机、FPGA、算法放在哪层合适?
算法·fpga开发
博览鸿蒙21 小时前
从迷茫自学到稳定入行:我的 FPGA 上岸全过程
fpga开发
芯门1 天前
FPGA商用级ISP(二):镜头阴影校正(LSC)的网格增益插值与并行硬件架构实现
图像处理·fpga开发·isp
Felven1 天前
corundum 40G开源网卡测试结果
fpga开发·性能测试·dds·开源网卡·mqnic
顾知行1 天前
ABB PC D230 3BHE022291R0101 励磁CCM测量板
fpga开发·abb·abb励磁
芯门1 天前
FPGA商用级ISP:动态坏点校正(DPCC)的滑窗架构与并行判决实现
图像处理·fpga开发·isp
碎碎思2 天前
双管齐下筑优势 AMD 扩容中端 FPGA 阵营并延至 2045 + 长期供货
fpga开发
绿算技术2 天前
【无标题】
数据库·人工智能·科技·算法·fpga开发·硬件架构
FPGA技术联盟2 天前
DO‑254 物理测试到底在“测什么”?
fpga开发
FPGA小c鸡2 天前
FPGA中的DSP与AI融合:从硬件加速到推理部署完全指南(附实战案例)
人工智能·fpga开发