首页 > 其他分享 >03光通信之HDMI视频回环传输

03光通信之HDMI视频回环传输

时间:2023-12-30 17:12:01浏览次数:30  
标签:03 HDMI vid clk rx coded 光通信 rst data

软件版本:VIVADO2021.1

操作系统:WIN10 64bit

硬件平台:适用XILINX A7/K7/Z7/ZU/KU系列FPGA

登录米联客(MiLianKe)FPGA社区-www.uisrc.com观看免费视频课程、在线答疑解惑!

1 概述    

    通过前文中实验的学习,相信读者已经掌握了7 Series FPGAs Transceivers wizard这个IP 的基本使用,本文将使用这个IP以aurora 8b10b的方式去实现光口传输HDMI视频数据。

本文实验目的:

1: 使用7 Series FPGAs Transceivers wizard去实现实际传输案例

2: 对整个工程的功能进行仿真和测试

2 系统框架    

根据实验目的,首先我们设计出基于光口的视频传输实验的系统框图,如下图所示:

本次实验包括以下模块:时钟模块、adv7611驱动模块、视频编码解码模块、数据对齐模块、和HDMI输出模块。。从上面的系统框图,我们可以看到,首先时钟模块给adv7611 iic驱动模块提供配置时钟,当adv7611芯片被驱动成功,开始接收HDMI输入的视频数据信号并将其传入encode模块进行编码,然后将编码后的数据送给Ultrascale FPGAs Transceivers wizard去传输,最后将rx接收到的数据进行对齐并解码出图像数据,输出到HDMI。

时钟模块:时钟模块是使用官方的MMCM IP核实现,这里使用了两个时钟模块,一个时钟模块提供adv7611_iic的配置时钟和光口传输的的drp_clk。另一个时钟模块用来产生1080P的像素时钟(148.5MHZ),用作视频编解码以及视频传输的参考时钟,148.5MHZ(像素时钟)、742.5MHZ(像素时钟的5倍频)负责驱动HDMI输出模块。

HDMI驱动模块:米联客的HDMI输入方案是使用ADI公司的ADV7611信号接收芯片实现的,需要通过iic对adv7611进行配置,从而驱动芯片正常工作,把接收到的HDMI输入的TMDS信号转换成RGB数据及对应的行场信号。

视频编码模块:7 Series FPGAs Transceivers wizard的外部数据位宽是32bit,我们需要将每一幅图像的行长信号及对应图像数据编码成32bit的数据流,传输给光口传输模块进行外部回环,再将回环过来的数据进行对齐,然后通过视频解码模块将对应的图像数据及行场解码出来,给到HDMI输出。

数据对齐模块:在光通信的传输链路中,数据是以串行的方式进行传输的,在接收端就需要对数据进行串并转换。而串行数据必须重新对齐,然后才能用作并行数据,由于无法直接获取串行数据中的每个数据的最高位或者最低位。为了方便校准数据流,发送端会发送一个可识别的码列,通常称为comma码,8B/10B编码中常用的comma是K28.5(16'hbc)。由于comma只能用作控制字符出现,在数据部分不会出现,comma字符还可以用来指示指示帧的开始和结束标志。

视频解码模块:对从rx端接收回来的32bit数据进行解码,将它还原成对应的图像数据以及行场信号。

Vtc时序模块:用来生成对应分辨率的视频时序Video to stream/stream to video模块:使用video_in_to_stream和stream_to_video_out这两个IP是用来检测输入进来的视频时序是否满足相应的行场标准,例如每一行的有效像素的个数以及有效行数是否准确。

HDMI输出模块:根据其他模块提供行、场同步信号、数据有效信号和数据信号,将对应的图像数据转换成TMDS信号去驱动显示器输出。

3 GT核设置

Step1:首先按照上一节课的内容,创建一个7 Series FPGAs Transceivers wizard IP。

 

在配置这个ip的时候,我们可以配置成单通道的模式,也可以配置成多通道的模式,如下图所示:

单通道配置方式如下

双通道配置方式如下

 

Step2:右击打开example工程,这个example工程是xilinx官方提供的一个示例工程,这个示例工程已经完成了光口传输的时序控制,满足了光通信通信需求,用户只需要把顶层的数据接口开放出来,放入需要传输的数据即可。

 

Step3:修改example工程

  1. 生成的 example 工程结构如下:一个顶层 exdes 模块,主要由三部分构成,support 是 gtp 收发模块,将来我们只用这个模块,GEN 是测试数据生成模块,CHECK 是接收数据后的检查模块,查看接收是否正确,直接将这两个模块移除,加入我们自己的发送数据编码模块和接收数据解码模块。

  2. 修改 exdes 模块:由于不使用默认的测试代码,所以删除 GEN 模块,删除 CHECK 模块如下图所示:

  3. 将 drp_clk 直接连入 sysclk_in 即可,官方的例子这个时钟是引脚进来的加了 bufg,我们用 PLL 产生即可。

    修改为:assign drpclk_in_i = drp_clk;

     

  4. 对接口处进行修改,修改后如下

    对于使用单光模块进行环路和双头光模块进行环路的设置不一样。

    使用 1 个通道环路测试

    module gt_aurora_exdes

    (

        input wire  Q0_CLK1_GTREFCLK_PAD_N_IN,

        input wire  Q0_CLK1_GTREFCLK_PAD_P_IN,

        input wire            drp_clk  ,

        output                 tx0_clk   ,

        input        [31:0]    tx0_data  ,

        input        [3:0]     tx0_kchar ,

         

        output                 rx0_clk   ,

        output       [31:0]    rx0_data  ,

        output       [3:0]     rx0_kchar ,

        output                 gt0_tx_system_rstn ,

        output                 gt0_rx_system_rstn ,

        input  wire           RXN_IN,

        input  wire           RXP_IN,

        output wire           TXN_OUT,

        output wire           TXP_OUT

    );

     

    使用 2 个通道环路测试

    module gt_aurora_exdes

    (

        input wire  Q0_CLK1_GTREFCLK_PAD_N_IN,

        input wire  Q0_CLK1_GTREFCLK_PAD_P_IN,

        input wire  drp_clk                 ,

     

        output                 tx0_clk       ,

        input        [31:0]    tx0_data      ,

        input        [3:0]     tx0_kchar     ,

        output                 rx0_clk       ,

        output        [31:0]   rx0_data      ,

        output        [3:0]    rx0_kchar     ,

        output                 gt0_tx_system_rstn  ,

        output                 gt0_rx_system_rstn  ,

         

        output                 tx1_clk       ,

        input        [31:0]    tx1_data      ,

        input        [3:0]     tx1_kchar     ,

        output                 rx1_clk       ,

        output        [31:0]   rx1_data      ,

        output        [3:0]    rx1_kchar     ,

        output                 gt1_tx_system_rstn  ,

        output                 gt1_rx_system_rstn  ,

         

        input  wire [1:0]   RXN_IN,

        input  wire [1:0]   RXP_IN,

        output wire [1:0]   TXN_OUT,

        output wire [1:0]   TXP_OUT

    );

     

  5. 添加如下用户代码,这些信号都是此模块的接口信号,直接和 IP 核相连接

    对于 1 个通道环路测试的代码如下:

    //通道0

        assign tx0_clk                 = gt0_txusrclk2_i        ;//用户发送时钟

        assign rx0_clk                 = gt0_rxusrclk2_i        ;//用户接收时钟

        assign gt0_txdata_i            = tx0_data               ;//用户发送数据

        assign gt0_txcharisk_i         = tx0_kchar              ;//用户发送K码

        assign rx0_data                = gt0_rxdata_i           ;//用户接收数据

        assign rx0_kchar               = gt0_rxcharisk_i        ;//用户接收K码

        assign gt0_tx_system_rstn      = gt0_txfsmresetdone_i   ;//gtx初始化完成标志

        assign gt0_rx_system_rstn      = gt0_rxfsmresetdone_i   ;//gtx初始化完成标志

    对于 2 个通道环路测试的代码如下:

    //通道0

                assign tx0_clk                 = gt0_txusrclk2_i        ;//用户发送时钟

                assign rx0_clk                 = gt0_rxusrclk2_i        ;//用户接收时钟

                assign gt0_txdata_i            = tx0_data               ;//用户发送数据

                assign gt0_txcharisk_i         = tx0_kchar              ;//用户发送K码

                assign rx0_data                = gt0_rxdata_i           ;//用户接收数据

                assign rx0_kchar               = gt0_rxcharisk_i        ;//用户接收K码

                assign gt0_tx_system_rstn      = gt0_txfsmresetdone_i   ;//gtx初始化完成标志

                assign gt0_rx_system_rstn      = gt0_rxfsmresetdone_i   ;//gtx初始化完成标志

       

                 

    //通道1    

                assign tx1_clk                 = gt1_txusrclk2_i        ;

                assign rx1_clk                 = gt1_rxusrclk2_i        ;

                assign gt1_txdata_i            = tx1_data               ;

                assign gt1_txcharisk_i         = tx1_kchar              ;

                assign rx1_data                = gt1_rxdata_i           ;

                assign rx1_kchar               = gt1_rxcharisk_i        ;

                assign gt1_tx_system_rstn      = gt1_txfsmresetdone_i   ;//gtx初始化完成标志

                assign gt1_rx_system_rstn      = gt1_rxfsmresetdone_i   ;//gtx初始化完成标志

     

  6. 此处改为

    修改前

    修改后

         gt_aurora_support #

                 (

                     .EXAMPLE_SIM_GTRESET_SPEEDUP    ("FALSE"),

                     .STABLE_CLOCK_PERIOD            (10)

                 )

     

  7. 修改箭头所指处为 1'b1,

    对于使用 2 个通道进行环路测试的需要把 gt0_data_valid_in 和 gt1_data_valid_in 都设置 1'b1,对于只使用 1 个通道进行环路测试的只要设置 gt0_data_valid_in 为 1'b1

    设置 gt0_rxmcommaalignen_in 和 gt0_rxmcommaalignen_in 为 1'b1

    对于使用 2 个通道进行环路测试的,还要设置 gt1_rxmcommaalignen_in 和 gt1_rxmcommaalignen_in 为 1'b1

    4 系统工程搭建

    4.1 uiAurora_8b10b_vid.v

    module uiAurora_8b10b_vid #(

        parameter VID_H =   1920    ,

        parameter VID_V =   1080    ,

        parameter FIFO_VTH =   200

    )

    (

        input               drp_clk ,//DRP参考时钟

         

        input               vid_in_clk  ,//接受端时钟输入

        input               vid_in_vs   ,//接收端vs同步信号

        input               vid_in_de   ,//接受端de数据有效信号

        input     [15:0]    vid_in_data  ,//接受端数据输入

         

        input               vid_out_clk ,//发送端时钟输入              

        output              vid_out_vs  ,//发送端vs同步信号            

        output              vid_out_de  ,//发送端de数据有效信号          

        output    [15:0]    vid_out_data ,//发送端数据输入      

         

        input               GT_REF_P    , //GT 差分参考时钟输入

        input               GT_REF_N    ,

         

        input     [0:0]     rxn         , //GT 收发器通道数据端口

        input     [0:0]     rxp         ,

        output    [0:0]     txn         ,

        output    [0:0]     txp        

    );

     

    //gt    

    wire             tx_clk                 ;

    wire  [31:0]    tx_data                 ;

    wire  [3:0]     tx_kchar                ;

    wire             rx_clk                 ;

    wire  [31:0]    rx_data                 ;

    wire  [3:0]     rx_kchar                ;

     

    wire             tx0_clk                ;

    wire  [31:0]    tx0_data                ;

    wire  [3:0]     tx0_kchar               ;

    wire             rx0_clk                ;

     

    wire            gt0_tx_rstn;

    wire            gt0_rx_rstn;

     

    wire  [31:0]   rx0_data             ;

    wire  [3:0]    rx0_kchar                ;

     

    wire  [31:0]     rx_data_align          ;

    wire  [3:0]      rx_ctrl_align          ;

     

    assign          tx_clk       =  tx0_clk         ;

    assign          tx0_data     =  tx_data         ;

    assign          tx0_kchar    =  tx_kchar        ;

    assign          rx_clk       =  rx0_clk         ;

    assign          rx_data      =  rx0_data        ;

    assign          rx_kchar     =  rx0_kchar       ;

     

    //对视频流进行编码

    video_encode    #(

        .VID_H      (VID_H      ),

        .VID_V      (VID_V      ),

        .VID_DW     (16     ),

        .CODED_DW   (32     ),

        .FIFO_VTH   (FIFO_VTH   )

    )

    u1_video_encode

    (

        .rst_n          (gt0_tx_rstn ),

        .vid_clk        (vid_in_clk  ),

        .vid_vs         (vid_in_vs   ),

        .vid_de         (vid_in_de   ),

        .vid_data       (vid_in_data ),  

         

        .coded_clk      (tx_clk      ),                

        .coded_data     (tx_data     ),

        .coded_ctr      (tx_kchar    )

    );

     

    //对视频流进行解码

    video_decode    #(

        .VID_H      (VID_H   ),

        .VID_V      (VID_V   ),  

        .VID_DW     (16      ),

        .CODED_DW   (32      ),

        .FIFO_VTH   (FIFO_VTH)

    )

    u1_video_decode

    (

        .rst_n          (gt0_rx_rstn    ),

        .vid_clk        (vid_out_clk    ),

        .vid_vs         (vid_out_vs     ),

        .vid_de         (vid_out_de     ),      

        .vid_data       (vid_out_data   ),

         

        .coded_clk      (rx_clk         ),  

        .coded_data     (rx_data_align  ),

        .coded_ctr      (rx_ctrl_align  )

    );

     

    //如果数据错位进行纠正对齐

    data_align  u1_data_align(

        .rst_n              (gt0_rx_rstn    ),

        .rx_clk             (rx_clk         ),

        .rx_data            (rx_data        ),

        .rx_ctrl            (rx_kchar       ),

        .rx_data_align      (rx_data_align  ),

        .rx_ctrl_align      (rx_ctrl_align  )

    );

    //gt传输视频

    gt_aurora_exdes u1_gt_aurora_exdes(  

        .Q0_CLK1_GTREFCLK_PAD_N_IN      (GT_REF_N                   ),

        .Q0_CLK1_GTREFCLK_PAD_P_IN      (GT_REF_P                   ),  

        .drp_clk                        (drp_clk                    ),

        .tx0_clk                        (tx0_clk                    ),

        .tx0_data                       (tx0_data                   ),

        .tx0_kchar                      (tx0_kchar                  ),

        .rx0_clk                        (rx0_clk                    ),

        .rx0_data                       (rx0_data                   ),

        .rx0_kchar                      (rx0_kchar                  ),

        .gt0_tx_system_rstn             (gt0_tx_rstn                ),

        .gt0_rx_system_rstn             (gt0_rx_rstn                ),

        .RXN_IN                         (rxn                        ),

        .RXP_IN                         (rxp                        ),

        .TXN_OUT                        (txn                        ),

        .TXP_OUT                        (txp                        )

    );  

     

    endmodule

    4.2 video_encode.v

    video_encode模块对视频流进行编码,外部HDMI进来的视频流是16位的,首先使用异步FIFO进行缓冲,16位进32位出,以视频vsync信号上升沿时刻算起,当vsync上升沿到来时,将上升沿编码为:32'h55_00_00_bc ,32'h55_00_01_bc,当FIFO中的数据量达不到要求时,发送无效数据:32'h55_00_02_bc,32'h55_00_03_bc交替发送,当FIFO中有一定量的数据后,首先发送1个32位的32'h55_00_04_bc表明一行就要开始传输,然后发送每行数据行号+bc,紧接着将FIFO中的数据依次发出去,发送完一行数据后,发送32'h55_00_05_bc,表明一行数据发送完成,紧接着发送上述的无效数据:32'h55_00_02_bc,32'h55_00_03_bc交替发送,等待vsync下降沿到来,将其编码为32'h55_00_06_bc,32'h55_00_07_bc发出,到此一行发送结束,以同样的编码将视频流每一行的数据发送给GTP即可。

    上述中的同步信号以及穿插的无用信号的高24位皆为自定义,低8位为bc,bc是k28.5 的控制字符,这个在IP核中会进行设置。代码中有txctrl和rxctrl两种控制信号,如下图所示,由于我们的外部用户数据只有32位,所以txctrl和rxctrl都只用到了低四位,txctrl和rxctrl每个bit对应coded_data的一个字节,表示发送数据或者接收数据的相应字节为K码,也就是我们所说的bc,由于bc在低8位,因此把4bit中的0位置1。

    上述中有一个描述:"当FIFO中有一定量的数据后是"什么意思?对于1080P的视屏,16位为一个像素点,他的时钟是148.5Mhz左右,GTP在此工程中设置为5gbps,在IP核中可以看到,user_clk2为125M,这是相对于32位数据而言,相对于16位数据而言,它相当于250M,也就是说在此模块FIFO的写时钟是148.5,FIFO的读时钟是148.5,但是是32位的读时钟,对于16位数据也就相当于FIFO读时钟是297,250和297基本差距不大,因此我们的FIFO不需要缓存一行再开始读,这个FIFO_VTH的设置和视频输入分辨率和GTP的传输速率相关,不同的情况具体分析。

     

    module video_encode #(

        parameter VID_H =   1920,//输入视频行分辨率

        parameter VID_V =   1080,//输入视频场分辨率

        parameter VID_DW    =   16 , //输入视频数据位宽

        parameter CODED_DW  =  32 , //输入视频数据位宽

        parameter FIFO_VTH = 500    //FIFO缓存数据个数

    )

    (

        input                      rst_n    ,

        input                      vid_clk  ,

        input                      coded_clk,

        input                      vid_vs   ,

        input                      vid_de   ,

        input   [VID_DW-1  :0]     vid_data ,              

        output  [CODED_DW-1:0]     coded_data,

        output  [3:0]              coded_ctr    

    );

     

    localparam  CODE_H =  VID_H/2;

    localparam  FIFO_VTH_SET = FIFO_VTH/2;

    //声明视频编码状态

    localparam frame_sys0       = 0,

               frame_sys1       = 1,

               h_data_begin     = 2,

               h_data_index     = 3,

               h_data           = 4,

               h_data_end       = 5,

               unuse_data0      = 6,

               unuse_data1      = 7,

               frame_end0       = 8,

               frame_end1       = 9;

                 

           

    wire                vs_pose           ;

    wire                vs_nege           ;

     

    wire  [CODED_DW-1:0]    dout          ;

    wire                full              ;

    wire                empty             ;

    wire  [10 : 0]      coded_data_count ;

    wire  [11 : 0]      vid_data_count    ;

     

    reg   [31:0]        hcnt              ;

    reg   [31:0]        vcnt              ;

    reg   [3:0]         state             ;

    reg   [3:0]         vid_vs_r          ;

    reg                 rd_en             ;

    reg [CODED_DW-1:0] coded_data         ;

    reg [3:0]          coded_ctr          ;

             

    //设置两个寄存器实现电平前后状态寄存

    always@(posedge coded_clk)  

        if(!rst_n)

            vid_vs_r <= 'd0;

        else

            vid_vs_r <= {vid_vs_r[2:0],vid_vs};

         

    assign vs_pose  =    ~vid_vs_r[3]&&vid_vs_r[2]; //检测vs上升沿

    assign vs_nege  =    vid_vs_r[3]&&~vid_vs_r[2]; //检测vs下降沿

     

    //视频垂直方向,行计数器

    always@(posedge coded_clk )

        if(!rst_n || vs_pose)//复位

            vcnt <= 'd0;

        else if(vcnt == VID_V)

            vcnt <= 'd0;

        else if(coded_data == 32'h55_00_04_bc)//表明一行数据开始传输

            vcnt <= vcnt + 1'b1;

        else

            vcnt <= vcnt;

     

    //视频水平方向,列计数器

    always@(posedge coded_clk)

        if(!rst_n || vs_pose)//复位

            hcnt <= 'd0;

        else if(hcnt == (CODE_H - 11'b1))//计数范围从0 ~ CODE_H-1

            hcnt <= 'd0;

        else if(rd_en==1'b1)//rd_en拉高时计数

            hcnt <= hcnt + 1'b1;

        else

            hcnt <= hcnt;

             

             

    always@(posedge coded_clk)begin

        if(!rst_n) begin

                rd_en       <= 1'b0;

                state       <= unuse_data0;

                coded_data  <= 32'd0;

                coded_ctr   <= 4'd0;

        end

        else if(vs_pose==1'b1) begin               //上升沿到来,进入frame_sys0状态,开始传输    

                state       <= frame_sys0;

                rd_en       <= 1'b0;

        end

        else if(vs_nege==1'b1) begin              //下降沿到来,进入frame_end0状态 ,结束一行传输

                state       <= frame_end0;

                rd_en       <= 1'b0;

        end

        else begin

            case(state)

                frame_sys0:begin                    //发送帧同步信号55_00_00_bc

                    state       <= frame_sys1;

                    coded_data  <= 32'h55_00_00_bc;

                    coded_ctr   <= 4'b0001;

                end

                frame_sys1:begin                    //发送帧同步信号55_00_01_bc

                    state      <= unuse_data0;

                    coded_data <= 32'h55_00_01_bc;

                    coded_ctr  <= 4'b0001;

                end

                unuse_data0:                        //发送无用的信号55_00_02_bc

                    begin

                        state      <= unuse_data1;

                        coded_data  <= 32'h55_00_02_bc;

                        coded_ctr  <= 4'b0001;

                    end

                unuse_data1:                       //发送无用的信号55_00_03_bc

                    begin

                        if(coded_data_count >= FIFO_VTH_SET)   //当FIFO 中有一行数据

                            begin

                                state <= h_data_begin;  

                            end

                        else

                            begin

                                state     <= unuse_data0;//交替发送55_00_02_bc,55_00_03_bc

                                coded_data <= 32'h55_00_03_bc;

                            end

                    end

                h_data_begin:               //发送一行数据开始同步信号

                    begin

                        state       <= h_data_index;

                        coded_data  <= 32'h55_00_04_bc;

                        coded_ctr   <= 4'b0001;

                    end

                h_data_index:               //发送每行数据行号+bc

                    begin

                        state       <= h_data;

                        coded_data  <= {vcnt[23:0],8'hbc};

                        coded_ctr   <= 4'b0001;

                    end

                h_data:               //开始发送FIFO中的一行视频图像数据

                    begin

                        if(hcnt < CODE_H - 1'b1) begin

                                state      <= h_data;

                                coded_data <= dout;

                                coded_ctr  <= 4'b0000;

                                rd_en      <= 1'b1;

                        end                    

                        else begin

                                state      <= h_data_end;

                                coded_data <= dout;

                                rd_en      <= 1'b0;

                        end    

                    end

                h_data_end:           //发送一行数据已接收信号

                    begin

                        state      <= unuse_data0;

                        coded_data <= 32'h55_00_05_bc;

                        coded_ctr  <= 4'b0001;

                    end

                frame_end0:          //发送帧结束信号55_00_06_bc

                    begin

                        state      <= frame_end1;

                        coded_data <= 32'h55_00_06_bc;

                        coded_ctr  <= 4'b0001;

                    end

                frame_end1:         //发送帧结束信号55_00_07_bc

                    begin

                        state      <= unuse_data0;

                        coded_data <= 32'h55_00_07_bc;

                        coded_ctr  <= 4'b0001;

                    end            

                default:;

            endcase

        end

    end

    //FIFO计数器

    reg [7:0] fifo_rst_cnt;

    always@(posedge coded_clk)

        if(vs_pose == 1'b1 )begin

            fifo_rst_cnt <= 8'd50;

        end

        else begin

            if(fifo_rst_cnt >8'd0)

                fifo_rst_cnt<=fifo_rst_cnt-1'b1;

        end

                 

    wire fifo_rst = (fifo_rst_cnt>=8'd10)&&(fifo_rst_cnt<8'd50)||(!rst_n);

    //例化FIFO模块 ,16位数据转化为32位  

    tx_fifo u1_tx_fifo (

      .rst(fifo_rst),

      .wr_clk           (vid_clk        ),

      .rd_clk           (coded_clk      ),

      .din              (vid_data       ),

      .wr_en            (vid_de         ),

      .rd_en            (rd_en          ),

      .dout             (dout           ),

      .full             (full           ),

      .empty            (empty          ),

      .rd_data_count    (coded_data_count),

      .wr_data_count    (vid_data_count )

    );

         

    endmodule

    4.3 video_decode.v

    video_decode是用来将对齐之后的数据进行解码,将对齐的过后的编码数据通过异步FIFO进行缓冲,把32位的编码数据转换成相应的rgb565的图像数据,当检测到帧同步信号(32'h55_00_00_bc、32'h55_00_01_bc)和帧结束信号(32'h55_00_06_bc,32'h55_00_07_bc),会去恢复出vsync信号上升和下降沿。当检测到行传输信号(32'h55_00_04_bc)之后,将hsync信号拉高至行计数结束。当检测到Vsync上升沿,一旦FIFO中有一行数据之后,会去恢复出数据有效de信号。

    module video_decode #(

        parameter VID_H     =   640,

        parameter VID_V     =   480,

        parameter VID_DW      =   16 ,

        parameter CODED_DW    = 32 ,

        parameter FIFO_VTH    =   500

    )

    (

        input                       rst_n       ,

         

        input                       vid_clk     ,  

        output                      vid_vs      ,

        output                      vid_de      ,        

        output     [VID_DW-1 :0]    vid_data    ,

         

        input                       coded_clk   ,

        input      [CODED_DW-1:0]   coded_data  ,

        input      [3:0]            coded_ctr      

    );

     

    parameter  CODE_H = VID_H/2;

     

    localparam  IDLE           = 0,

                READ_LINE      = 1;

     

    reg   [2:0]             state      ;            

    reg   [CODED_DW-1:0]   coded_data_r;

    reg                    vid_vs      ;

    reg                    vid_de      ;    

     

    reg                    wr_en       ;

    reg                     wr_en_r     ;

    reg   [31:0]            wr_cnt      ;

     

    wire                    full        ;

    wire                    empty       ;

    wire  [11 : 0]          rd_data_count;

    wire  [10 : 0]          wr_data_count;

    reg   [31:0]             hcnt       ;

     

    always@(posedge coded_clk or negedge rst_n)

        if(!rst_n)

            coded_data_r <= 'd0;

        else

            coded_data_r <= coded_data;

         

    always@(posedge coded_clk or negedge rst_n)

        if(!rst_n)

            vid_vs <= 1'b0;

        else if(coded_ctr == 4'b0001 && coded_data == 32'h55_00_01_bc&&coded_data_r==32'h55_00_00_bc)//帧上升沿恢复

            vid_vs <= 1'b1;

        else if(coded_ctr == 4'b0001 && coded_data == 32'h55_00_07_bc&&coded_data_r == 32'h55_00_06_bc)//帧下降沿恢复

            vid_vs <= 1'b0;

        else

            vid_vs <= vid_vs;

     

    always@(posedge coded_clk or negedge rst_n)

        if(!rst_n  || vid_vs)

            wr_en <= 1'b0;

        else if((wr_cnt == CODE_H - 1'b1 ))

            wr_en <= 1'b0;

        else if(coded_ctr == 4'b0001 && coded_data == 32'h55_00_04_bc)//检测到行传输信号

            wr_en <= 1'b1;

        else

            wr_en <= wr_en;

             

    always@(posedge coded_clk or negedge rst_n)

        if(!rst_n)

            wr_en_r <= 'd0;

        else

            wr_en_r <= wr_en;//寄存一次wr_en信号

     

    always@(posedge coded_clk or negedge rst_n)//检测到wr_en信号,32位视频数据写入FIFO

        if(!rst_n  || vid_vs)

            wr_cnt <= 'd0;

        else if((wr_cnt == CODE_H -1'b1))//计数范围从0 ~ CODE_H-1

            wr_cnt <= 'd0;

        else if(wr_en == 1'b1)//wr_en拉高时计数

            wr_cnt <= wr_cnt + 1'b1;

        else

            wr_cnt <= wr_cnt;

     

    reg [3:0]vid_vs_r;

    always@(posedge vid_clk )

        vid_vs_r <= {vid_vs_r[2:0],vid_vs};

     

    always@(posedge vid_clk )

        if(!rst_n || vid_vs_r[3])

            hcnt <= 'd0;

        else if(hcnt == VID_H - 1'b1)//计数范围从0 ~ VID_H-1

            hcnt <= 'd0;

        else if(vid_de == 1'b1)//vid_de到时计数

            hcnt <= hcnt + 1'b1;

     

    always@(posedge vid_clk)//从FIFO中读取16位视频数据

        if(!rst_n || vid_vs_r[3]) begin

                state <= IDLE;

                vid_de <= 1'b0;

        end    

        else begin

            case(state)

                IDLE:

                    if(rd_data_count >= FIFO_VTH) begin //FIFO 有一行数据

                        state   <= READ_LINE;

                    end

                READ_LINE:

                    if(hcnt < VID_H - 1'b1) begin //de信号恢复

                        vid_de  <= 1'b1;

                    end

                    else begin

                        state   <= IDLE;

                        vid_de  <= 1'b0;

                    end

                default:state   <= IDLE ;  

            endcase

        end

     

    reg [7:0] fifo_rst_cnt;

    always@(posedge coded_clk)

        if(vid_vs == 1'b1 )begin

            fifo_rst_cnt <= 8'd50;

        end

        else begin

            if(fifo_rst_cnt >8'd0)

                fifo_rst_cnt<=fifo_rst_cnt-1'b1;

        end

                 

    wire fifo_rst = (fifo_rst_cnt>=8'd10)&&(fifo_rst_cnt<8'd50)||(!rst_n);

    //例化FIFO模块 ,32位数据转化为16位

    rx_fifo u1_rx_fifo (

      .rst(fifo_rst),

      .wr_clk       (coded_clk      ),

      .rd_clk       (vid_clk        ),

      .din          (coded_data     ),

      .wr_en        (wr_en_r        ),

      .rd_en        (vid_de         ),

      .dout         (vid_data       ),

      .full         (full           ),

      .empty        (empty          ),

      .rd_data_count(rd_data_count  ),

      .wr_data_count(wr_data_count  )

    );

    endmodule

    4.4 data_align.v

    data_align将GTP接收后的数据利用k码进行对齐,上面已经讲解过k码了,k码可以帮我们查看数据是否错位,如果错位可以将数据对齐。通过逻辑分析仪抓取调试过程中,有时候会出现,发送的32位数据可能出现16位数据移位,也就是上一个32位的数据低16位可能和下一个32位数据的高16位拼接在一起,我们如何知道我们发出的数据,通过光纤传输接收后进行了错位,此时就轮到K码大显威力了,当我们接收的数据错位时,K码也会错位,此前我们发送的0001 ,可能会变为0100,然后我们根据这个0100来把接收到的数据重新组合,就可以得到正确的结果。

    module data_align(

        input                   rst_n           ,

        input                   rx_clk          ,

        input       [31:0]      rx_data         ,

        input       [3:0]       rx_ctrl         ,

        output reg  [31:0]      rx_data_align   ,

        output reg  [3:0]       rx_ctrl_align

    );

     

    reg[31:0] rx_data_r ;

    reg[3:0]  align_bit ;

    reg[3:0]  rx_ctrl_r ;

     

    //检测数据是否为K码的COM码

    always@(posedge rx_clk or negedge rst_n)

        if(!rst_n)

            align_bit <= 4'd0;

        else if(rx_ctrl != 4'd0)

            align_bit <= rx_ctrl;

        else

            align_bit <= align_bit;

             

    //rx_data打拍

    always@(posedge rx_clk or negedge rst_n)

        if(!rst_n)

            rx_data_r <= 'd0;

        else

            rx_data_r <= rx_data;

     

    //rx_ctrl0打拍        

    always@(posedge rx_clk or negedge rst_n)

        if(!rst_n)

            rx_ctrl_r <= 4'd0;

        else

            rx_ctrl_r <= rx_ctrl;

             

    //检测到K码,进行数据校正  对齐          

    always@(posedge rx_clk or negedge rst_n)

        if(!rst_n)

            rx_data_align <= 32'd0;

        else

            case(align_bit)

                4'b0001:

                    rx_data_align <= rx_data;//没错位直接赋值

                4'b0100:

                    rx_data_align <= {rx_data[15:0],rx_data_r[31:16]};//错位进行拼接

                default:

                    rx_data_align <= 32'd0;

            endcase

     

    //K码校正对齐

    always@(posedge rx_clk or negedge rst_n)

        if(!rst_n)

            rx_ctrl_align <= 4'd0;

        else

            case(align_bit)

                4'b0001:

                    rx_ctrl_align <= rx_ctrl;//没错位直接赋值

                4'b0100:

                    rx_ctrl_align <= {rx_ctrl[1:0],rx_ctrl_r[3:2]};//错位进行拼接

                default:

                    rx_ctrl_align <= 4'd0;

            endcase

     

     

     

    endmodule  

    5 工程仿真

    在对应的工程文件夹下,提供了相应的仿真工程

     

    以下是对应的方仿真文件

    `timescale 1ns/1ns

     

    module gt_aurora_example_top_sim ();

     

      wire ch0_gtxn;

      wire ch0_gtxp;

     

      reg GT_REF_P = 1'b0;

      reg I_vid_clk;

      reg I_drp_clk;

      reg O_vid_vs;

      reg O_vid_hs;

      reg O_vid_de;  

      reg I_vid_rstn = 1'b1;  

      reg [15:0] i;

     

      initial begin

        GT_REF_P = 1'b0;

        forever

          GT_REF_P = #4 ~GT_REF_P;

      end

     

      initial begin

        I_vid_clk = 1'b0;

        forever

          I_vid_clk = #3.367 ~I_vid_clk;

      end

      initial begin

        I_drp_clk = 1'b0;

        forever

          I_drp_clk = #5 ~I_drp_clk;

      end

     

    always begin

        if(!I_vid_rstn)

        begin

            O_vid_hs <= 1'b0;

        end

        else begin

            O_vid_hs <= 1'b1;

            repeat(44)@(posedge I_vid_clk);//水平同步信号

             O_vid_hs <= 1'b0;      

            repeat(556)@(posedge I_vid_clk);

        end    

    end  

         

    always begin

        if(!I_vid_rstn)

        begin

            O_vid_vs <= 1'b0;

        end

        else begin

            O_vid_vs <= 1'b1;

            repeat(5)@(posedge O_vid_hs);//垂直同步信号

             O_vid_vs <= 1'b0;      

            repeat(55)@(posedge O_vid_hs);

        end      

    end  

     

    always begin

        if(!I_vid_rstn)

        begin

            O_vid_de <= 1'b0;

        end

        else begin

            O_vid_de <= 1'b0;

            repeat(41)@(posedge O_vid_hs);//垂直消隐后肩

            for(i=0;i<5;i=i+1)

            begin

                repeat(132)@(posedge I_vid_clk);//水平消隐后肩

                O_vid_de <= 1'b1;      

                repeat(320)@(posedge I_vid_clk);//水平有效区

                O_vid_de <= 1'b0;      

                repeat(192)@(posedge I_vid_clk);//水平消隐前肩

        end    

               repeat(9)@(posedge O_vid_hs); //垂直消隐前肩

      end

    end  

     

      system_wrapper system_wrapper_inst(

        .GT_REF_P (GT_REF_P),

        .GT_REF_N (~GT_REF_P),

        .ch0_gtrxn_in (ch0_gtxn),

        .ch0_gtrxp_in (ch0_gtxp),

        .ch0_gttxn_out (ch0_gtxn),

        .ch0_gttxp_out (ch0_gtxp),

        .O_vid_vs (O_vid_vs),

        .O_vid_de (O_vid_de),  

        .O_vid_hs (O_vid_hs),

        .O_rgb (24'hfff),

        .I_vid_clk (I_vid_clk),

        .I_drp_clk (I_drp_clk)

      );

     

    endmodule

     

    由于仿真数据量比较大,需要等待一段时间,这里只仿真一帧数据,从下图可以看到,两个 VSync 之间代表着我们一帧的数据,默认传输的数据是 16'h0fff。

     

    在检测到VS上升沿之后,发送无用数据,等待fifo中有一行数据的时候,发送550004bc、行号以及对应的一行数据。

    在接收到550005bc的时候代表着一帧数据的结束

    这里可以看到rx接收到的数据和刚才tx发送到的数据是一样的,说明光口传输的数据是正确的,对应的vid_out_vs和vid_out_de信号也正常解码出来了。说明仿真是正确的,接下来可以进行实际工程的搭建。

    6 硬件工程搭建

    然后添加adv7611的HDMI输入方案,搭建完成后的demo如下图所示:

     

    7 硬件电路分析

    硬件接口和子卡模块请阅读"附录 1"

    配套工程的 FPGA PIN 脚定义路径为 fpga_prj/uisrc/04_pin/ fpga_pin.xdc。

    8 硬件连接

    对应工程目录下提供了三种测试demo,分别是单通道光口回环传输、双通道光口回环传输、双板间光口传输

     

    Single lane连接方式

    multi lanes连接方式

     

    9 实验结果

标签:03,HDMI,vid,clk,rx,coded,光通信,rst,data
From: https://www.cnblogs.com/milianke/p/17936537.html

相关文章

  • [软件测试] 03 白盒 基本路径测试 习题
    提示:篇幅较长,可以使用Ctrl+F,在页面中快速查询关键词(或者你要找的题目)并跳转到指定的位置。关键词:白盒测试,基本路径测试,独立路径,环复杂度单选题基本路径测试是属于(D)。A.黑盒测试方法且是动态测试B.白盒测试方法且是静态测试C.黑盒测试方法且是静态测试D.白盒测试......
  • [软件测试] 03 白盒 基本路径测试 习题
    提示:篇幅较长,可以使用Ctrl+F,在页面中快速查询关键词(或者你要找的题目)并跳转到指定的位置。关键词:白盒测试,基本路径测试,独立路径,环复杂度单选题基本路径测试是属于(D)。A.黑盒测试方法且是动态测试B.白盒测试方法且是静态测试C.黑盒测试方法且是静态测试D.白盒测试......
  • 03 AXI4总线axi-lite-master
    软件版本:vitis2021.1(vivado2021.1)操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!2.1概述使用XILINX的软件工具VIVADO以及XILINX的7代以上的FPGA或者SOC掌握AXI-4总线结束,并且可以灵活使用AXI-4总线......
  • 03使用fdma读写axi-bram测试
    软件版本:vitis2021.1(vivado2021.1)操作系统:WIN1064bit硬件平台:适用XILINXA7/K7/Z7/ZU/KU系列FPGA登录"米联客"FPGA社区-www.uisrc.com视频课程、答疑解惑!3.1概述    FDMA是米联客基于AXI4总线协议定制的一个DMA控制器。有了这个IP我们可以统一实现用FPGA代码直接读写P......
  • check the manual that corresponds to your MySQL server version for the right syn
    form:{repairstatus:0,name:'',//负责人maintenancetime:newDate().toISOString().split('T')[0],//保修时间equipmentid:'',equipment:'',describe:'',finfishtime:'',repairname:'�......
  • 1-1-03 XILINX JTAG开发工具usb-jtag驱动安装
    1.1概述    一般安装vitis(vivado)的过程中勾选了安装jtagcable驱动就会默认安装好jtag驱动,但是如果vivado无法正确识别到JTAG,那么可以试下重新手动安装驱动1.2准备工作安装驱动前,必须关闭所有的vivado,vitis-sdk并且拔掉USBJTAG以免导致安装失败1.3usb-jtag驱动安装......
  • 代码随想录day03 链表删除 链表类的实现 反转链表
    首先是链表的删除操作热身题这里使用了一个新的头指针这样在删除头指针的时候就不需要进行额外的判断然后是链表类的实现需要一点背诵加上深刻理解有时候理解了但是写起来还是会有些指针边界的小问题应该多写写多记一下就会好了还有就是手写链表要常复习吧学习新......
  • 用python打开文件获取文件属性时,运行报错PermissionError: [Errno 13] Permission den
    一:概述在开发中我们经常会使用python程序去对物理主机平台上的文件进行操作,在这里说明一下,python中获取文件属性时,运行出现的错误。二:具体说明<1>获取文件属性的代码importos#创建一个临时目录temp_dir='temp'os.mkdir(temp_dir)#在临时目录中创建一个文件temp_file=o......
  • OpenEuler22.03安装PostgreSQL15.5并配置一主二从
    环境准备序号IP标识(hostname)CPU/内存配置系统盘数据盘1192.168.8.190pg018C+16G80G500G2192.168.8.191pg028C+16G80G500G3192.168.8.192pg038C+16G80G500G规划的文件夹目录(所有主机):##以下目录为本文规划的目录,可以提前创建,也可以跟着......
  • 【五期李伟平】CCF-B(TFS'23)Consensus Reaching Process With Multiobjective Optimiza
    PengWu,FengenLi,JieZhao,etal.ConsensusReachingProcessWithMultiobjectiveOptimizationforLarge-ScaleGroupDecisionMakingWithCooperativeGame[J].IEEETransactionsonFuzzySystems2023:293-306.  本文针对大规模群体决策(LSGDM)中的共识达成......