软件版本: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概述
2 VTC控制器设计
2.1系统框图
2.2时序设计
3 VTC源码
4 RTL仿真
4.1仿真激励文件
4.2仿真结果
5分辨率以及时钟计算方法
6常用系统分辨率
1概述
Video Timing Controller 缩写VTC是我们在所有涉及FPGA图像、FPGA视频类方案中经常用到的一种用于产生视频时序的控制器。
2 VTC控制器设计
2.1系统框图
VTC控制器通过hcnt和vcnt计数器实现hs时序vs时序以及de时序。
2.2时序设计
视频数据在H_AcitiveSize和V_AcitiveSize同时有效时候有效,显示屏上的显示效果如下图所示。
3 VTC源码
`timescale 1ns / 1ns //仿真时间刻度/精度
module uivtc#
(
parameter H_ActiveSize = 1980, //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素
parameter H_FrameSize = 1920+88+44+148, //视频时间参数,行视频信号,一行视频信号总计占用的时钟数
parameter H_SyncStart = 1920+88, //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
parameter H_SyncEnd = 1920+88+44, //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行有效数据部分
parameter V_ActiveSize = 1080, //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
parameter V_FrameSize = 1080+4+5+36, //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
parameter V_SyncStart = 1080+4, //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
parameter V_SyncEnd = 1080+4+5 //视频时间参数,场同步结束,即多少场数后停止产生场同步信号,之后就是场有效数据部分
)
(
input I_vtc_rstn, //系统复位
input I_vtc_sysclk_p,
input I_vtc_sysclk_n,//系统时钟
output O_vtc_vs, //场同步输出
output O_vtc_hs, //行同步输出
output O_vtc_de_valid, //视频数据有效
output O_vtc_user, //满足stream时序产生 user 信号,用于帧同步
output O_vtc_last //满足stream时序产生 later 信号,用于每行结束
);
wire I_clk;
IBUFGDS CLK_U(
.I(I_vtc_sysclk_p),
.IB(I_vtc_sysclk_n),
.O(I_clk)
);
reg [11:0] hcnt = 12'd0; //视频水平方向,列计数器,寄存器
reg [11:0] vcnt = 12'd0; //视频垂直方向,行计数器,寄存器
reg [2 :0] rst_cnt = 3'd0; //复位计数器,寄存器
wire rst_sync = rst_cnt[2]; //同步复位
always @(posedge I_clk or negedge I_vtc_rstn)begin //通过计数器产生同步复位
if(I_vtc_rstn == 1'b0)
rst_cnt <= 3'd0;
else if(rst_cnt[2] == 1'b0)
rst_cnt <= rst_cnt + 1'b1;
end
//视频水平方向,列计数器
always @(posedge I_clk)begin
if(rst_sync == 1'b0) //复位
hcnt <= 12'd0;
else if(hcnt < (H_FrameSize - 1'b1))//计数范围从0 ~ H_FrameSize-1
hcnt <= hcnt + 1'b1;
else
hcnt <= 12'd0;
end
//视频垂直方向,行计数器,用于计数已经完成的行视频信号
always @(posedge I_clk)begin
if(rst_sync == 1'b0)
vcnt <= 12'd0;
else if(hcnt == (H_ActiveSize - 1'b1)) begin//视频水平方向,是否一行结束
vcnt <= (vcnt == (V_FrameSize - 1'b1)) ? 12'd0 : vcnt + 1'b1;//视频垂直方向,行计数器加1,计数范围0~V_FrameSize - 1
end
end
wire hs_valid = hcnt < H_ActiveSize; //行信号有效像素部分
wire vs_valid = vcnt < V_ActiveSize; //场信号有效像素部分
wire vtc_hs = (hcnt >= H_SyncStart && hcnt < H_SyncEnd);//产生hs,行同步信号
wire vtc_vs = (vcnt > V_SyncStart && vcnt <= V_SyncEnd);//产生vs,场同步信号
wire vtc_de = hs_valid && vs_valid;//只有当视频水平方向,列有效和视频垂直方向,行同时有效,视频数据部分才是有效
//********************** video stream video rgb ***************************
//如果是输入RGB时序,那么转为stream时序
reg vtc_vs_r1 ;
reg vtc_hs_r1 ;
reg vtc_de_r1 ;
reg vtc_user_r1 ,vtc_user_r2;
reg vtc_valid_r1,vtc_valid_r2;
reg vtc_last_r2;
reg vs_start;
always @(posedge I_clk )begin
if(rst_sync == 1'b0) //复位
vs_start <= 1'b0;
else if(vtc_user_r1)//清除VS帧同步
vs_start <= 1'b0;
else if(vtc_vs && vtc_vs_r1==1'b0)//当vtc_vs发生上升沿跳变代表一帧开始
vs_start <= 1'b1;
end
always @(posedge I_clk )begin
vtc_vs_r1 <= vtc_vs;
vtc_hs_r1 <= vtc_hs;
vtc_user_r1 <= ~vtc_user_r1 & vs_start & vtc_de;//vtc_user延迟1拍
vtc_last_r2 <= ~vtc_de & vtc_valid_r1; //产生stream video last 延迟于数据输入2拍
vtc_valid_r1 <= vtc_de;//vtc_valid延迟1拍
vtc_valid_r2 <= vtc_valid_r1;//vtc_valid对输入信号延迟2拍,以和vtc_last_r2信号配套同步
vtc_user_r2 <= vtc_user_r1; //vtc_user 对输入信号延迟2拍,以和vtc_last_r2信号配套同步
end
assign O_vtc_vs = vtc_vs_r1;
assign O_vtc_hs = vtc_hs_r1;
assign O_vtc_de_valid = vtc_valid_r2;
assign O_vtc_user = vtc_user_r2;
assign O_vtc_last = vtc_last_r2;
endmodule
4 RTL仿真
4.1仿真激励文件
为简化仿真,这里模拟的视频格式为320*5 即一行的有效数据为350个像素,一帧数据有5行像素数据。
/*************视频时序测试仿真文件****************************************
*********************************************************************/
`timescale 1ns / 1ns //仿真时间刻度/精度
module vtc_tb;
localparam SYS_TIME = 10;//系统时钟周期10ns
reg I_vid_rstn,I_vid_sysclk_p,I_vid_sysclk_n;
wire O_vid_vs,O_vid_hs,O_vid_de,O_vtc_user,O_vtc_last;
//例化视频时序产生模块
uivtc#
(
.H_ActiveSize(320), //视频时间参数,行视频信号,一行有效(需要显示的部分)像素所占的时钟数,一个时钟对应一个有效像素,设置320个像素
.H_FrameSize(320+88+44+239),//视频时间参数,行视频信号,一行视频信号总计占用的时钟数
.H_SyncStart(320+88), //视频时间参数,行同步开始,即多少时钟数后开始产生行同步信号
.H_SyncEnd(320+88+44), //视频时间参数,行同步结束,即多少时钟数后停止产生行同步信号,之后就是行数据有效数据部分
.V_ActiveSize(5), //视频时间参数,场视频信号,一帧图像所占用的有效(需要显示的部分)行数量,通常说的视频分辨率即H_ActiveSize*V_ActiveSize
.V_FrameSize(5+4+5+28), //视频时间参数,场视频信号,一帧视频信号总计占用的行数量
.V_SyncStart(5+4), //视频时间参数,场同步开始,即多少行数后开始产生场同步信号
.V_SyncEnd (5+4+5) //视频时间参数,场同步结束,即多少行数后停止产生场同步信号,之后就是场有效数据部分
)
uivtc_inst
(
.I_vtc_sysclk_p(I_vid_sysclk_p),
.I_vtc_sysclk_n(I_vid_sysclk_n), //系统时钟
.I_vtc_rstn(I_vid_rstn),//系统复位
.O_vtc_vs(O_vid_vs), //图形的vs信号
.O_vtc_hs(O_vid_hs), //图形的hs信号
.O_vtc_de_valid(O_vid_de), //de数据有效信号
.O_vtc_user(O_vtc_user),
.O_vtc_last(O_vtc_last)
);
initial begin
I_vid_sysclk_p = 1'b0;
I_vid_sysclk_n = 1'b1;
I_vid_rstn = 1'b0;
#100;
I_vid_rstn = 1'b1;
end
always #(SYS_TIME/2) I_vid_sysclk_p= ~I_vid_sysclk_p;
always #(SYS_TIME/2) I_vid_sysclk_n= ~I_vid_sysclk_n;
endmodule
4.2仿真结果
5分辨率以及时钟计算方法
如果是标准分辨率,建议大家以标准分辨率参数直接填入,如果不是标准分辨率则根据实际情况可以用接近标准分辨率的参数去修改相关参数。
比如对于标准分辨率1080*1920*60的分辨率通常采用148.5MHZ的像素时钟。此时我们可以给出以下参数设置:
H_ActiveSize =1920
H_SyncStart =1920+88
H_SyncEnd =1920+88+44
H_FrameSize =1920+88+44+148
V_ActiveSize =1080
V_SyncStart =1080+4
V_SyncEnd =1080+4+5
V_FrameSize =1080+4+5+36
计算分辨率和时钟的推断可以用一个关键公式 像素时钟= H_FrameSize* V_FrameSize*帧率,但是通常我们只会知道,H_ActiveSize、V_ActiveSize以及帧率,而像素时钟和其他参数都需要我们自己设置。
可以先以以上标准1080*1920*60的分辨率计算下:
H_FrameSize* V_FrameSize*帧率 = 2,200* 1,125*60= 148,500,000,在时钟晶振足够精确的情况下,可以确保帧率以恒定60fps输出。
但是很多时候如果某个参数不能满足要求,比如系统时钟只能提供140MHZ的情况,那么我们也可以重新调整参数,尽量来接近标准分辨率。
比如当以140MHZ输出1080*1920*60fps的视频时,我们给出以下参数:
H_ActiveSize =1920
H_SyncStart =1920+88
H_SyncEnd =1920+88+44
H_FrameSize =1920+88+44+71
V_ActiveSize =1080
V_SyncStart =1080+4
V_SyncEnd =1080+4+5
V_FrameSize =1080+4+5+16
H_FrameSize* V_FrameSize *帧率=2,123*1099*60=139,990,620 这样每一帧的精确度误差为0.000067。对于140MHZ的时钟输出帧率会稍微快点。
6常用系统分辨率
对于148.5MHZ像素时钟,输出1080*1920*60
H_ActiveSize =1920
H_SyncStart =1920+88
H_SyncEnd =1920+88+44
H_FrameSize =1920+88+44+148
V_ActiveSize =1080
V_SyncStart =1080+4
V_SyncEnd =1080+4+5
V_FrameSize =1080+4+5+36
对于74.25MHZ像素时钟,输出1280*720*60
H_ActiveSize =1280
H_SyncStart =1280+110
H_SyncEnd =1280+110+ 40
H_FrameSize =1280+110+ 40+220
V_ActiveSize =720
V_SyncStart =720+4
V_SyncEnd =720+4+5
V_FrameSize =720+4+5+21
对于42MHZ像素时钟,输出640*480*60
H_ActiveSize =640
H_SyncStart =640+16
H_SyncEnd =640+16+96
H_FrameSize =640+16+96+48
V_ActiveSize =480
V_SyncStart =480+9
V_SyncEnd =480+9+2
V_FrameSize =480+9+2+34
对于非标准分辨率,用户可以以上面介绍的方法计算相近参数。
比如对于非标准140MHZ像素时钟,输出1080*1920*60
H_ActiveSize =1920
H_SyncStart =1920+88
H_SyncEnd =1920+88+44
H_FrameSize =1920+88+44+71
V_ActiveSize =1080
V_SyncStart =1080+4
V_SyncEnd =1080+4+5
V_FrameSize =1080+4+5+16
比如对于非标准75MHZ像素时钟,输出1280*720*60
H_ActiveSize =1280
H_SyncStart =1280+88
H_SyncEnd =1280+88+44
H_FrameSize =1280+88+44+239
V_ActiveSize =720
V_SyncStart =720+4
V_SyncEnd =720+4+5
V_FrameSize =720+4+5+28