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
相关推荐
仲南音13 小时前
Xilinx FPGA——时钟IP核MMCM、PLL
fpga开发
I'm a winner1 天前
基于Xilinx FPGA的LVDS高速串行通信系统(三)--数据训练及握手机制【文末源码】
fpga开发
upper20201 天前
coremark移植坑之-nostartfiles -nostdlib参数
学习·fpga开发·coremark移植·riscv设计
Eloudy1 天前
Vivado 纯命令行构建 FPGA 项目教程
fpga开发
无线视频解决方案1 天前
【无标题】
无线图传·hdmi·炬力北方·am8360d·编码芯片·方案商·未领科技
Rambo.xia2 天前
AXI跨时钟域与数据一致性——CDC FIFO深度不够、写后读冒险、多Master仲裁、乱序返回,出了Bug根本查不出来
fpga开发·bug
upper20202 天前
FPGA部署卷积神经网络识别MINST图片
fpga开发·卷积神经网络·minst·minst识别·部署神经网络·神经网络识别图片
你是我的解忧王子2 天前
嵌入式 ADC采样 原始数据分析上层(平均值 波动率 正态分布 线性补偿 系统误差)
fpga开发·数据挖掘·数据分析
9527华安3 天前
FPGA纯verilog代码ISP图像处理培训教程,基于IMX214,提供工程源码+视频教程+FPGA开发板
图像处理·fpga开发·isp·imx214·mipi
千寻xun3 天前
二、实战篇-NVME SSD控制之ZYNQ实现(六)-读写NVME SSD硬盘数据
fpga开发·nvme·nvme ssd