FPGA实现SDI视频编解码目前有两种方案:
一是使用专用编解码芯片,比如典型的接收器GS2971,发送器GS2972,优点是简单,比如GS2971直接将SDI解码为并行的YCRCB,缺点是成本较高,可以百度一下GS2971的价格;
另一种方案是使用FPGA实现编解码,利用FPGA的GTP/GTX资源实现解串,优点是合理利用了FPGA资源,GTP/GTX资源不用白不用,缺点是操作难度大一些,对FPGA水平要求较高。
本文详细描述了FPGA纯verilog解码SDI视频的实现设计方案,工程代码编译通过后上板调试验证,文章末尾有演示视频,可直接项目移植,适用于在校学生做毕业设计、研究生项目开发,也适用于在职工程师做项目开发,可应用于医疗、军工等行业的数字成像和图像传输领域;
提供完整的、跑通的工程源码和技术支持;
工程源码和技术支持的获取方式放在了文章末尾,请耐心看到最后;
本设计提供两套vivado工程;
一是SDI 1080P@30Hz帧视频输入解码后,无缓存直接HDMI 1080P@30Hz帧输出;
二是SDI 1080P@30Hz帧视频输入解码后,缓存3帧后HDMI 1080P@60Hz帧输出;
关于SDI的理论知识部分,可自行搜索一下,很多大佬讲得很详细,也可以参考我之前写的文章:点击查看:SDI解码详解
我用到的SDI摄像头输出视频分辨率1080P@30Hz;根据不同相机有所区别;
Gv8601a起到均衡 EQ 功能,这里选用Gv8601a是因为抄袭了Xilinx官方的板子,当然也可以用其他型号器件。
GTX负责解串,将原始SDI视频解为20位的并行数据,我的板子是K7,所以用GTX,如果是A7的板子则用GTP,这里使用GTX并没有调用IP,而是直接调用GTXE2_CHANNEL和GTXE2_COMMON源语,这一点可谓将Xilinx的GTX资源用到了极致水平,值得好好品读,其实调用IP无非也就是把调用源语变得界面化而已,直接调用源语或许理解更为深刻,这一点,在市面上的所谓FPGA教程里都学不到。
调用SMPTE-SDI IP核实现,GTX只是将高速串行数据解为了并行,但并没有解析SDI协议,SMPTE-SDI IP核则完成了SDI协议的解码,去掉了SDI协议中的数据包信息和控制信息,解析出有效的视频数据,详细的SMPTE-SDI IP核接口定义请参考官方的使用手册;
此模块的作用就是解码恢复出hs、vs以及de信号,即恢复正常的VGA视频时序;
要恢复正常的VGA视频时序,首先得看懂下面这张图:
根据这张表即可恢复出图像时序,具体看代码,这里一两句话实在讲不清楚,如果要完全讲明白,写5本书都搓搓有余;
这里就简单了,YUV4:4:4转RGB8:8:8,几条公式和几行代码的事儿,属于低端操作;
至此,SDI解码过程就完成了,接下来就是图像输出过程;
图像输出有两种通路
一种是无缓存直接输出,这种方式的优点是没有延迟,适用于做图传的项目,缺点是不能对图像进行其他处理了;
另一种是缓存输出,这种方式的优点是可以平衡和同步后端的接收,比如后端接收不过来时就可以缓存几帧再输出;另外,如果还需要对图像进行其他处理时也需要缓存,适用于做图像处理的项目,缺点是输出有延迟,根据设置可以延迟多帧,本设计是延迟3帧;
既然需求是多样的,所以我们直接做两个工程。
我常用的FDMA数据缓存架构,详情请参考我之前的文章:点击查看:FDMA图像缓存
纯verilog代码实现HDMI发送,输出时序1920x1080P,输出帧率由输入HDMI时钟决定;
HDMI输出顶层接口如下:
module helai_hdmi_out(
input clk_hdmi ,
input clk_hdmix5 ,
input reset_n ,
input i_vga_hs ,
input i_vga_vs ,
input i_vga_de ,
input [23:0] i_vga_rgb ,
output o_hdmi_clk_p ,
output o_hdmi_clk_n ,
output [2: 0] o_hdmi_data_p,
output [2: 0] o_hdmi_data_n
);
开发板:Xilinx Kintex7开发板;
开发环境:Vivado2019.1;
输入:SDI摄像头,分辨率1080p@30帧;
输出:HDMI,分辨率1080p@30帧;
工程代码架构如下:
顶层源码如下:
module helai_sdi_decode_2023(
input I_SDI_2_N ,
input I_SDI_2_P ,
input SDI_GTX_CLK_148_5_N , // MGT REFCLKs 148.5 MHz clock
input SDI_GTX_CLK_148_5_P ,
input SDI_GTX_CLK_148_35_P, // MGT REFCLKs 148.35 MHz clock
input SDI_GTX_CLK_148_35_N,
output HDMI_CLK_P ,
output HDMI_CLK_N ,
output [2:0] HDMI_DATA_P ,
output [2:0] HDMI_DATA_N
);
wire o_vout_clk ;
wire o_vout_hs ;
wire o_vout_vs ;
wire o_vout_de ;
wire [23:0] o_vout_rgb ;
wire clk_hdmi ;
wire clk_hdmix5;
wire hdmi_rstn ;
helai_sdi_driver u_helai_sdi_driver(
.I_SDI_2_N (I_SDI_2_N ),
.I_SDI_2_P (I_SDI_2_P ),
.O_SDI_2_N (),
.O_SDI_2_P (),
.SDI_GTX_CLK_148_5_N (SDI_GTX_CLK_148_5_N ), // MGT REFCLKs 148.5 MHz clock
.SDI_GTX_CLK_148_5_P (SDI_GTX_CLK_148_5_P ),
.SDI_GTX_CLK_148_35_P(SDI_GTX_CLK_148_35_P), // MGT REFCLKs 148.35 MHz clock
.SDI_GTX_CLK_148_35_N(SDI_GTX_CLK_148_35_N),
.o_vout_clk (o_vout_clk ),
.o_vout_hs (o_vout_hs ),
.o_vout_vs (o_vout_vs ),
.o_vout_de (o_vout_de ),
.o_vout_rgb (o_vout_rgb )
);
clk_wiz_1 clk_hdmi (
.clk_hdmi(clk_hdmi), // output clk_hdmi
.clk_hdmi_clkx5(clk_hdmix5), // output clk_hdmi_clkx5
.locked(hdmi_rstn), // output locked
.clk_in1(o_vout_clk) // input clk_in1
);
helai_hdmi_out(
.clk_hdmi (clk_hdmi ),
.clk_hdmix5 (clk_hdmix5 ),
.reset_n (hdmi_rstn ),
.i_vga_hs (o_vout_hs ),
.i_vga_vs (o_vout_vs ),
.i_vga_de (o_vout_de ),
.i_vga_rgb (o_vout_rgb ),
.o_hdmi_clk_p (HDMI_CLK_P ),
.o_hdmi_clk_n (HDMI_CLK_N ),
.o_hdmi_data_p(HDMI_DATA_P),
.o_hdmi_data_n(HDMI_DATA_N)
);
endmodule
开发板:Xilinx Kintex7开发板;
开发环境:Vivado2019.1;
输入:SDI摄像头,分辨率1080p@30帧;
输出:HDMI,分辨率1080p@60帧;
工程BD如下:
工程代码架构如下:
顶层源码如下:
module helai_sdi_decode_2023(
input clk_27m ,
input I_SDI_2_N ,
input I_SDI_2_P ,
input SDI_GTX_CLK_148_5_N , // MGT REFCLKs 148.5 MHz clock
input SDI_GTX_CLK_148_5_P ,
input SDI_GTX_CLK_148_35_P, // MGT REFCLKs 148.35 MHz clock
input SDI_GTX_CLK_148_35_N,
input i_uart_rx ,
output o_uart_tx ,
// DDR3
output [12:0]DDR3_0_addr ,
output [2:0] DDR3_0_ba ,
output DDR3_0_cas_n ,
output [0:0] DDR3_0_ck_n ,
output [0:0] DDR3_0_ck_p ,
output [0:0] DDR3_0_cke ,
output [3:0] DDR3_0_dm ,
inout [31:0] DDR3_0_dq ,
inout [3:0] DDR3_0_dqs_n ,
inout [3:0] DDR3_0_dqs_p ,
output [0:0] DDR3_0_odt ,
output DDR3_0_ras_n ,
output DDR3_0_reset_n ,
output DDR3_0_we_n ,
output ddr3_ok ,
// HDMI_OUT
output HDMI_CLK_P ,
output HDMI_CLK_N ,
output [2:0] HDMI_DATA_P ,
output [2:0] HDMI_DATA_N
);
wire ui_clk_200m;
wire rst_n ;
wire ud_r_0_ud_rclk ;
wire [31:0] ud_r_0_ud_rdata ;
wire ud_r_0_ud_rde ;
wire ud_r_0_ud_rvs ;
wire ud_w_0_ud_wclk ;
wire [31:0] ud_w_0_ud_wdata ;
wire ud_w_0_ud_wde ;
wire ud_w_0_ud_wvs ;
wire o_vout_clk ;
wire o_vout_hs ;
wire o_vout_vs ;
wire o_vout_de ;
wire [23:0] o_vout_rgb ;
wire o_vga_hs ;
wire o_vga_vs ;
wire o_vga_de ;
wire [23:0] o_vga_rgb;
wire [23:0] i_vga_rgb;
wire clk_hdmi ;
wire clk_hdmix5;
wire hdmi_rstn ;
assign ud_r_0_ud_rclk =clk_hdmi ;
assign ud_r_0_ud_rde = o_vga_de ;
assign ud_r_0_ud_rvs = o_vga_vs ;
assign i_vga_rgb =ud_r_0_ud_rdata[23:0];
assign ud_w_0_ud_wclk =o_vout_clk;
assign ud_w_0_ud_wdata =o_vout_rgb;
assign ud_w_0_ud_wde =o_vout_de ;
assign ud_w_0_ud_wvs =o_vout_vs ;
design_1 u_design_1 (
.DDR3_0_addr (DDR3_0_addr ),
.DDR3_0_ba (DDR3_0_ba ),
.DDR3_0_cas_n (DDR3_0_cas_n ),
.DDR3_0_ck_n (DDR3_0_ck_n ),
.DDR3_0_ck_p (DDR3_0_ck_p ),
.DDR3_0_cke (DDR3_0_cke ),
.DDR3_0_dm (DDR3_0_dm ),
.DDR3_0_dq (DDR3_0_dq ),
.DDR3_0_dqs_n (DDR3_0_dqs_n ),
.DDR3_0_dqs_p (DDR3_0_dqs_p ),
.DDR3_0_odt (DDR3_0_odt ),
.DDR3_0_ras_n (DDR3_0_ras_n ),
.DDR3_0_reset_n (DDR3_0_reset_n ),
.DDR3_0_we_n (DDR3_0_we_n ),
.ddr3_ok (ddr3_ok ),
.clk_in1_0 (clk_27m ),
.ui_clk_0 (ui_clk_200m ),
.rst_n (rst_n ),
.ud_r_0_ud_rclk (ud_r_0_ud_rclk ),
.ud_r_0_ud_rdata(ud_r_0_ud_rdata),
.ud_r_0_ud_rde (ud_r_0_ud_rde ),
.ud_r_0_ud_rvs (ud_r_0_ud_rvs ),
.ud_w_0_ud_wclk (ud_w_0_ud_wclk ),
.ud_w_0_ud_wdata(ud_w_0_ud_wdata),
.ud_w_0_ud_wde (ud_w_0_ud_wde ),
.ud_w_0_ud_wvs (ud_w_0_ud_wvs )
);
helai_sdi_driver u_helai_sdi_driver(
.I_SDI_2_N (I_SDI_2_N ),
.I_SDI_2_P (I_SDI_2_P ),
.O_SDI_2_N (),
.O_SDI_2_P (),
.SDI_GTX_CLK_148_5_N (SDI_GTX_CLK_148_5_N ), // MGT REFCLKs 148.5 MHz clock
.SDI_GTX_CLK_148_5_P (SDI_GTX_CLK_148_5_P ),
.SDI_GTX_CLK_148_35_P(SDI_GTX_CLK_148_35_P), // MGT REFCLKs 148.35 MHz clock
.SDI_GTX_CLK_148_35_N(SDI_GTX_CLK_148_35_N),
.o_vout_clk (o_vout_clk ),
.o_vout_hs (o_vout_hs ),
.o_vout_vs (o_vout_vs ),
.o_vout_de (o_vout_de ),
.o_vout_rgb (o_vout_rgb )
);
clk_wiz_1 clk_hdmi (
.clk_hdmi(clk_hdmi), // output clk_hdmi
.clk_hdmi_clkx5(clk_hdmix5), // output clk_hdmi_clkx5
.locked(hdmi_rstn), // output locked
.clk_in1(o_vout_clk) // input clk_in1
);
video_timing_control vga(
.i_clk (clk_hdmi ),
.i_rst_n(hdmi_rstn),
.i_rgb (i_vga_rgb),
.o_hs (o_vga_hs ),
.o_vs (o_vga_vs ),
.o_de (o_vga_de ),
.o_rgb (o_vga_rgb)
);
helai_hdmi_out(
.clk_hdmi (clk_hdmi ),
.clk_hdmix5 (clk_hdmix5 ),
.reset_n (hdmi_rstn ),
.i_vga_hs (o_vga_hs ),
.i_vga_vs (o_vga_vs ),
.i_vga_de (o_vga_de ),
.i_vga_rgb (o_vga_rgb ),
.o_hdmi_clk_p (HDMI_CLK_P ),
.o_hdmi_clk_n (HDMI_CLK_N ),
.o_hdmi_data_p(HDMI_DATA_P),
.o_hdmi_data_n(HDMI_DATA_N)
);
endmodule
FPGA纯verilog解码SDI视频
福利:工程代码的获取
代码太大,无法邮箱发送,以某度网盘链接方式发送,
资料获取方式:私,或者文章末尾的V名片。
网盘资料如下: