文章目录
- 一、前言
- 二、视频时序控制原理
- 三、Verilog实现
- 3.1 代码
- 3.2 仿真以及分析
一、前言
VTC(Video Timing Controller)是一种用于产生视频时序的控制器,在FPGA图像领域经常用到。Xilinx Vivado 也有专门用于生成视频时序的 IP,叫做 Video Timing Controller 核,简称为 VTC,官方的说明在 PG016,本文使用Verilog实现VTC的功能模块。
二、视频时序控制原理
在《VGA接口时序以及FPGA实现》一问中,我们产生了VGA的视频时序并且成功在显示器上实现。VTC原理和VGA类似:
显示器显示图像主要由行同步信号和场同步信号构成,只要用正确的时钟产生正确的行场同步信号就能正确的输出画面时序。
不同分辨率所对应的时序表如下:
显示模式 | 时钟/Mhz | 行同步hsync | 行后沿 | 行有效 | 行前沿 | 行总共 | 场同步vsync | 场后沿 | 场有效 | 场前沿 | 场总共 |
---|---|---|---|---|---|---|---|---|---|---|---|
640×480@60Hz | 25.2 | 96 | 48 | 640 | 16 | 800 | 2 | 33 | 480 | 10 | 525 |
800×600@60Hz | 40 | 128 | 88 | 800 | 40 | 1056 | 4 | 23 | 600 | 1 | 628 |
1024×768@60Hz | 65 | 136 | 160 | 1024 | 24 | 1344 | 6 | 29 | 768 | 3 | 806 |
1280×720@60Hz | 74.25 | 40 | 220 | 1280 | 110 | 1650 | 5 | 20 | 720 | 5 | 750 |
1280×1024@60Hz | 108 | 112 | 248 | 1280 | 48 | 1688 | 3 | 38 | 1024 | 1 | 1066 |
1920×1080@60Hz | 148.5 | 44 | 148 | 1920 | 88 | 2200 | 5 | 36 | 1080 | 4 | 1125 |
3840×2160@60Hz | 594 | 88 | 296 | 3840 | 196 | 4400 | 10 | 72 | 2160 | 8 | 2250 |
三、Verilog实现
3.1 代码
`timescale 1ns / 1ns
module vtc#
(
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_clk, //系统时钟
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 信号,用于每行结束
);
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_vtc_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_vtc_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_vtc_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 ***************************
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_vtc_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_vtc_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
3.2 仿真以及分析
tb文件编写
`timescale 1ns / 1ns //仿真时间刻度/精度
module vtc_tb;
localparam SYS_TIME = 20;//系统时钟周期20ns
reg I_vtc_rstn,I_vtc_clk;
wire O_vid_vs,O_vid_hs,O_vtc_de_valid,O_vtc_user,O_vtc_last;
//例化视频时序产生模块
vtc#
(
.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_clk(I_vtc_clk), //系统时钟
.I_vtc_rstn(I_vtc_rstn),//系统复位
.O_vtc_vs(O_vid_vs), //图形的vs信号
.O_vtc_hs(O_vid_hs), //图形的hs信号
.O_vtc_de_valid(O_vtc_de_valid), //de数据有效信号
.O_vtc_user(O_vtc_user), //满足stream时序产生 user 信号,用于帧同步
.O_vtc_last(O_vtc_last) //满足stream时序产生 later 信号,用于每行结束
);
initial begin
I_vtc_clk = 1'b0;
I_vtc_rstn = 1'b0;
#100;
I_vtc_rstn = 1'b1;
end
always #(SYS_TIME/2) I_vtc_clk= ~I_vtc_clk;
endmodule
仿真结果如下: