本帖最后由 FPGA课程 于 2024-9-24 19:17 编辑
软件版本:VIVADO2021.1
操作系统:WIN10 64bit
硬件平台:适用 XILINX A7/K7/Z7/ZU/KU 系列 FPGA
实验平台:米联客-MLK-H3-CZ08-7100开发板
板卡获取平台:https://milianke.tmall.com/
登录“米联客”FPGA社区 http://www.uisrc.com 视频课程、答疑解惑!
1 概述
通过前文中实验的学习,相信读者已经掌握了7SeriesFPGAsTransceiverswizard这个IP的基本使用,本文将使用这个IP以aurora8b10b的方式去实现光口传输HDMI视频数据。
本文实验目的:
1:使用7SeriesFPGAsTransceiverswizard去实现实际传输案例
2:对整个工程的功能进行仿真和测试
2 系统框架
根据实验目的,首先我们设计出基于光口的视频传输实验的系统框图,如下图所示:
本次实验包括以下模块:时钟模块、adv7611驱动模块、视频编码解码模块、数据对齐模块、和HDMI输出模块。从上面的系统框图,我们可以看到,首先时钟模块给adv7611iic驱动模块提供配置时钟,当adv7611芯片被驱动成功,开始接收HDMI输入的视频数据信号并将其传入encode模块进行编码,然后将编码后的数据送给UltrascaleFPGAsTransceiverswizard去传输,最后将rx接收到的数据进行对齐并解码出图像数据,输出到HDMI。
时钟模块:时钟模块是使用官方的 MMCM IP 核实现,这里使用了两个时钟模块,一个时钟模块提供adv7611_iic的配置时钟和光口传输的的drp_clk。另一个时钟模块用来产生1080P的像素时钟(148.5MHZ),用作视频编解码以及视频传输的参考时钟,148.5MHZ(像素时钟)、371.25MHZ(像素时钟的2.5倍频)、742.5MHZ(像素时钟的5倍频)负责驱动HDMI输出模块。
HDMI驱动模块:米联客的HDMI输入方案是使用ADI公司的ADV7611信号接收芯片实现的,需要通过iic对adv7611进行配置,从而驱动芯片正常工作,把接收到的HDMI输入的TMDS信号转换成RGB数据及对应的行场信号。
视频编码模块:7SeriesFPGAsTransceiverswizard的外部数据位宽是32bit,我们需要将每一幅图像的行长信号及对应图像数据编码成32bit的数据流,传输给光口传输模块进行外部回环,再将回环过来的数据进行对齐,
然后通过视频解码模块将对应的图像数据及行场解码出来,给到HDMI输出。
数据对齐模块:在光通信的传输链路中,数据是以串行的方式进行传输的,在接收端就需要对数据进行串并转换。而串行数据必须重新对齐,然后才能用作并行数据,由于无法直接获取串行数据中的每个数据的最高位或者最低位。为了方便校准数据流,发送端会发送一个可识别的码列,通常称为comma码,8B/10B编码中常用的comma是K28.5(16’hbc)。由于comma只能用作控制字符出现,在数据部分不会出现,comma字符还可以用来指示指示帧的开始和结束标志。
视频解码模块:对从rx端接收回来的32bit数据进行解码,将它还原成对应的图像数据以及行场信号。
Vtc时序模块:用来生成对应分辨率的视频时序Videotostream/streamtovideo模块:使用
video_in_to_stream和stream_to_video_out这两个IP是用来检测输入进来的视频时序是否满足相应的行场标准,例如每一行的有效像素的个数以及有效行数是否准确。
HDMI输出模块:根据其他模块提供行、场同步信号、数据有效信号和数据信号,将对应的图像数据转换成TMDS信号去驱动显示器输出。
3 系统工程搭建
Step1:首先按照上一节课的内容,创建一个7SeriesFPGAsTransceiverswizardIP。
在配置这个ip的时候,我们可以配置成单通道的模式,也可以配置成多通道的模式,如下图所示单通道配置方式如下
双通道配置方式如下
Step2:右击打开example工程,这个example工程是xilinx官方提供的一个示例工程,这个示例工程已经完成了光口传输的时序控制,满足了光通信通信需求,用户只需要把顶层的数据接口开放出来,放入需要传输的数据即可。
3.1uiAurora_8b10b_vid.v
- //////////////////////////////////////////////////////////////////////////////////
- // Company: msxbo cz124
- // Engineer: tjy
- //
- // Create Date: 2019/05/10 11:10:06
- // Design Name: 8b10b video loop back
- // Module Name: aurora_top
- // Project Name:
- // Target Devices:
- // Tool Versions:
- // Description:
- //
- // Dependencies:
- //
- // Revision:
- // Revision 0.01 - File Created
- // Additional Comments:
- //
- module uiAurora_8b10b_vid #(
- parameter VID_H = 1920 ,
- parameter VID_V = 1080 ,
- parameter FIFO_VTH = 200
- )
- (
- input clk_100M ,
-
- input vid_in_clk ,
- input vid_in_vs ,
- input vid_in_de ,
- input [15:0] vid_in_data ,
-
- input vid_out_clk ,
- output vid_out_vs ,
- output vid_out_de ,
- output [15:0] vid_out_data ,
-
- input GT_REF_P ,
- input GT_REF_N ,
-
- input [0:0] rxn ,
- input [0:0] rxp ,
- output [0:0] txn ,
- output [0:0] txp ,
- output [0:0] tx_disable
- );
- //gtx
- 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;
- (*mark_debug = "TRUE" *)wire [31:0] rx0_data ;
- (*mark_debug = "TRUE" *)wire [3:0] rx0_kchar ;
- (*mark_debug = "TRUE" *)wire [31:0] rx_data_align ;
- (*mark_debug = "TRUE" *)wire [3:0] rx_ctrl_align ;
- assign tx_disable = 1'b0 ;
- 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 )
- );
- //瀵硅棰戞祦杩涜瑙g爜
- 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 )
- );
- //gtx浼犺緭瑙嗛娴?
- gtx_aurora_exdes u1_gtx_aurora_exdes(
- .Q1_CLK1_GTREFCLK_PAD_N_IN (GT_REF_N ),
- .Q1_CLK1_GTREFCLK_PAD_P_IN (GT_REF_P ),
- .drp_clk (clk_100M ),
- .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 )
- );
- /*ila_0 u1_ila_0 (
- .clk (rx_clk ), // input wire clk
- .probe0 ({tx_data,tx_kchar,rx_data,rx_kchar}) // input wire [99:0] probe0
- ); */
- endmodule
复制代码 3.2video_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发出,到此一行发送结束,以同样的编码将视频流每一行的数据发送给gtx即可。
上述中的同步信号以及穿插的无用信号的高24位皆为自定义,低8位为bc,bc是k28.5的控制字符,这个在IP核中会进行设置。代码中有txctrl[2:0]和rxctrl[3:0]两种控制信号,如下图所示,在8B10B的编码格式下,用户只需要关注rxctrl[0]和txctrl[2],由于我们的外部用户数据只有32位,所以rxctrl[0]和txctrl[2]都只用到了低四位,rxctrl[0]和txctrl[2]每个bit对应coded_data的一个字节,表示发送数据或者接收数据的相应字节为K码,也就是我们所说的bc,由于bc在低8位,因此把4bit中的0位置1。
上述中有一个描述:"当FIFO中有一定量的数据后是"什么意思?对于1080P的视屏,16位为一个像素点,他的时钟是148.5Mhz左右,gtx在此工程中设置为5gbps,在IP核中可以看到,user_clk2为125M,这是相对于32位数据而言,相对于16位数据而言,它相当于250M,也就是说在此模块FIFO的写时钟是148.5,FIFO的读时钟是148.5,但是是32位的读时钟,对于16位数据也就相当于FIFO读时钟是297,250和297基本差距不大,因此我们的FIFO不需要缓存一行再开始读,这个FIFO_VTH的设置和视频输入分辨率和gtx的传输速率相关,不同的情况具体分析。
- module video_encode #(
- parameter VID_H = 1920,
- parameter VID_V = 1080,
- parameter VID_DW = 16 ,
- parameter CODED_DW = 32 ,
- parameter FIFO_VTH = 500
- )
- (
- 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 ;
- //(*mark_debug = "TRUE" *)
- 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];
- assign vs_nege = vid_vs_r[3]&&~vid_vs_r[2];
- 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))
- hcnt <= 'd0;
- else if(rd_en==1'b1)
- 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
- state <= frame_sys0;
- rd_en <= 1'b0;
- end
- else if(vs_nege==1'b1) begin
- 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;
- 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
- 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);
- 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
复制代码 3.3video_decode.vvideo_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;
- (*mark_debug = "TRUE" *) reg [2:0] state ;
- reg [CODED_DW-1:0] coded_data_r;
- (*mark_debug = "TRUE" *) reg vid_vs ;
- (*mark_debug = "TRUE" *) reg vid_de ;
- (*mark_debug = "TRUE" *) 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;
- always@(posedge coded_clk or negedge rst_n)//wr_en_r鎷夐珮涓?琛岀殑闀垮害锛屾妸32浣嶈棰戞暟鎹啓鍏IFO
- if(!rst_n || vid_vs)
- wr_cnt <= 'd0;
- else if((wr_cnt == CODE_H -1'b1))
- wr_cnt <= 'd0;
- else if(wr_en == 1'b1)
- 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)
- hcnt <= 'd0;
- else if(vid_de == 1'b1)
- hcnt <= hcnt + 1'b1;
- always@(posedge vid_clk)//浠嶧IFO涓鍙?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
- 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);
- 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
复制代码
3.4data_align.v
data_align将GTX接收后的数据利用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 ;
- 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;
- always@(posedge rx_clk or negedge rst_n)
- if(!rst_n)
- rx_data_r <= 'd0;
- else
- rx_data_r <= rx_data;
-
- always@(posedge rx_clk or negedge rst_n)
- if(!rst_n)
- rx_ctrl_r <= 4'd0;
- else
- rx_ctrl_r <= rx_ctrl;
-
- 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
- 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
- /* ila_0 u1_ila_1 (
- .clk (rx_clk ), // input wire clk
- .probe0 ({rx_ctrl,align_bit,rx_data,rx_data_r,rx_ctrl_r,rx_data_align,rx_ctrl_align} ) // input wire [99:0] probe0
- ); */
- endmodule
-
复制代码
4 硬件工程搭建
然后添加adv7611的HDMI输入方案,搭建完成后的demo如下图所示:
5 硬件电路分析
5.1FEP扩展接口
MZ7035板卡的外设没有HDMI输入接口,我们需要使用到米联客推出的一款HDMI7611图像采集卡,这个HMDI模块专门针对图像采集应用,最高可实现1080P@60fps视频输入输出。模块输入部分采用ADI公司的ADV7611信号接收芯片实现HDMI视频信号的接收。模块输出部分采用了IO模拟HDMI信号,输出最高可达到1080P@60HZ高清传输,如下图所示:
HDMI扩展卡安装在FEP位置上,下图中左边图纸为MZ7035底板的FEP连接器定义,右边为HDMI7611视频卡上的FEP定义。
5.2参考时钟和HDMI输入输出IO约束
配套工程的FPGA PIN脚定义路径为soc_prj/uisrc/04_pin/fpga_pin.xdc。
6 硬件连接
| 1 | 2 | 3 | 4 | 5 | 6 | 时钟 |
| OFF | OFF | ON | OFF | OFF | OFF | 125M | 对应工程目录下提供了三种测试demo,分别是单通道光口回环传输、双通道光口回环传输、双板间光口传输
Singlelane连接方式
multilanes连接方式
boardtoboard连接方式
7 实验结果
|