01_VGA 显示器驱动设计与验证
1. VGA
1.1 VGA 简介
1.2 VGA接口及引脚定义
1.3 VGA 显示原理
1.4 VGA 时序标准
1.4.1 Hsync行同步
行扫描周期的单位是像素
1.4.2 Vsync 场同步
场扫描周期得单位是行,一个完整得行扫描周期
1.4.3 重合得部分就是显示部分
1.5 VGA 显示模式及相关参数
640 * 480 @ 60
640:一行 :行扫周期又 640个像素点。
480:每一帧图像有480行。
60:在这个模式下 每秒钟显示60次图像,刷新60次。
时钟频率的计算
2. 实验目标
实验目标:编写 VGA 驱动,使用 FPGA 开发板驱动 VGA 显示器显示十色等宽彩条,VGA 显示模式为 640x480@60。
3. VGA 硬件资源原理
权电阻网络数模转换
RGB 565 r:5 g:6 b:5 16位宽 [15:0]
4. 功能模块
4.1 顶层模块
整个系统的框图
locked 是高电平有效
4.2 时钟生成模块
本实验选用经典 VGA 显示模式 640x480@60,理论时钟频率应为 25.175MHz,为了便于时钟生成,我们使用 25MHz 的时钟代替25.175MHz 的时钟,不会对实验造成影响。本次实验工程中,而板卡晶振传入时钟频率为 50MHz。时钟生成模块的作用就是将 50MHz 晶振时钟分频为25MHz 的 VGA 工作时钟。
4.3 VGA 时序控制模块
产生行场信号,并且把输入的图像信息pix_data,给输出rgb
Pix_x,Pix_y 就是视频图中的坐标 用于生成图像
4.4 图像数据生成模块
5. 波形图
5.1 VGA 时序控制模块
由时序标准可知:行场信号在同步阶段拉高,设置行场信号计数器。
图像有效信号
由上文可知,VGA 只有在有效的显示区域内送入图像数据,图像才会被正确显示,那么在什么时候可以送入图像数据呢?我们可以声明一个有效信号,在图像有效显示区域赋值高电平,在非图像有效显示区域赋值低电平,以此信号为约束条件,控制图像信号的正确输入,定义此信号为图像显示有效信号(rgb_valid)。
当两个计数器计数到图像有效显示区域时,rgb_valid 赋值高电平,否则赋值低电平。 0-34 , 0-144
pix_x
pix_y, 先扫描行,然后在扫描场
rgb
因为本次实验是 VGA 多色彩条的显示,图像数据生成模块 vga_pic 需要以坐标(pix_x,pix_y)为约束条件对 pix_data 信号进行赋值,只能使用时序逻辑的赋值方式,那么pix_data 的赋值时刻会滞后条件满足时刻一个时钟周期,显示图像会出现问题。
为了解决这一问题,我们需要声明新的图像数据请求信号 pix_data_req,该信号要超前
图像显示有效信号(rgb_valid)一个时钟周期,以抵消 pix_data 时序逻辑赋值带来的问题。
5.2 图像数据生成模块
6. RTL
6.1 vga_ctrl
module vga_ctrl(
input wire vga_clk , //输入工作时钟,频率 25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [15:0] pix_data , //输入像素点色彩信息
output wire [9:0] pix_x , //输出有效显示区域像素点 X 轴坐标
output wire [9:0] pix_y , //输出有效显示区域像素点 Y 轴坐标
// pix_x 和 pix_y 目的就是为了 rgb 的有效显示
output wire [15:0] rgb, //输出像素点色彩信息
output wire hsync , //输出行同步信号
output wire vsync //输出场同步信号
);
//parameter define
parameter H_SYNC = 10'd96 , //行同步
H_BACK = 10'd40 , //行时序后沿
H_LEFT = 10'd8 , //行时序左边框
H_VALID = 10'd640 , //行有效数据
H_RIGHT = 10'd8 , //行时序右边框
H_FRONT = 10'd8 , //行时序前沿
H_TOTAL = 10'd800 ; //行扫描周期
parameter V_SYNC = 10'd2 , //场同步
V_BACK = 10'd25 , //场时序后沿
V_TOP = 10'd8 , //场时序上边框
V_VALID = 10'd480 , //场有效数据
V_BOTTOM = 10'd8 , //场时序下边框
V_FRONT = 10'd2 , //场时序前沿
V_TOTAL = 10'd525 ; //场扫描周期
//wire define
wire rgb_valid ; //VGA 有效显示区域
wire pix_data_req ; //像素点色彩信息请求信号
//reg define
reg [9:0] cnt_h ; //行同步信号计数器
reg [9:0] cnt_v ; //场同步信号计数器
//cnt_h:行同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_h <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_h <= 10'd0 ;
else
cnt_h <= cnt_h + 1'd1 ;
//hsync:行同步信号
assign hsync = (cnt_h <= H_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//cnt_v:场同步信号计数器
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
cnt_v <= 10'd0 ;
else if((cnt_v == V_TOTAL - 1'd1) && (cnt_h == H_TOTAL-1'd1))
cnt_v <= 10'd0 ;
else if(cnt_h == H_TOTAL - 1'd1)
cnt_v <= cnt_v + 1'd1 ;
else
cnt_v <= cnt_v ;
//vsync:场同步信号
assign vsync = (cnt_v <= V_SYNC - 1'd1) ? 1'b1 : 1'b0 ;
//rgb_valid:VGA 有效显示区域
assign rgb_valid = (((cnt_h >= H_SYNC + H_BACK + H_LEFT)
&& (cnt_h < H_SYNC + H_BACK + H_LEFT + H_VALID))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_data_req:像素点色彩信息请求信号,超前 rgb_valid 信号一个时钟周期
assign pix_data_req = (((cnt_h >= H_SYNC + H_BACK + H_LEFT - 1'b1)
&& (cnt_h<H_SYNC + H_BACK + H_LEFT + H_VALID - 1'b1))
&&((cnt_v >= V_SYNC + V_BACK + V_TOP)
&& (cnt_v < V_SYNC + V_BACK + V_TOP + V_VALID)))
? 1'b1 : 1'b0;
//pix_x,pix_y:VGA 有效显示区域像素点坐标
assign pix_x = (pix_data_req == 1'b1)
? (cnt_h - (H_SYNC + H_BACK + H_LEFT - 1'b1)) : 10'h3ff;
assign pix_y = (pix_data_req == 1'b1)
? (cnt_v - (V_SYNC + V_BACK + V_TOP)) : 10'h3ff;
//rgb:输出像素点色彩信息
assign rgb = (rgb_valid == 1'b1) ? pix_data : 16'b0 ;
endmodule
6.2 vga_pic
module vga_pic(
input wire vga_clk , //输入工作时钟,频率 25MHz
input wire sys_rst_n , //输入复位信号,低电平有效
input wire [9:0] pix_x , //输入有效显示区域像素点 X 轴坐标
input wire [9:0] pix_y , //输入有效显示区域像素点 Y 轴坐标
output reg [15:0] pix_data //输出像素点色彩信息
);
parameter H_VALID = 10'd640 , //行有效数据
V_VALID = 10'd480 ; //场有效数据
parameter RED = 16'hF800, //红色
ORANGE = 16'hFC00, //橙色
YELLOW = 16'hFFE0, //黄色
GREEN = 16'h07E0, //绿色
CYAN = 16'h07FF, //青色
BLUE = 16'h001F, //蓝色
PURPPLE = 16'hF81F, //紫色
BLACK = 16'h0000, //黑色
WHITE = 16'hFFFF, //白色
GRAY = 16'hD69A; //灰色
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//pix_data:输出像素点色彩信息,根据当前像素点坐标指定当前像素点颜色数据
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= 16'd0;
else if((pix_x >= 0) && (pix_x < (H_VALID/10)*1))
pix_data <= RED;
else if((pix_x >= (H_VALID/10)*1) && (pix_x < (H_VALID/10)*2))
pix_data <= ORANGE;
else if((pix_x >= (H_VALID/10)*2) && (pix_x < (H_VALID/10)*3))
pix_data <= YELLOW;
else if((pix_x >= (H_VALID/10)*3) && (pix_x < (H_VALID/10)*4))
pix_data <= GREEN;
else if((pix_x >= (H_VALID/10)*4) && (pix_x < (H_VALID/10)*5))
pix_data <= CYAN;
else if((pix_x >= (H_VALID/10)*5) && (pix_x < (H_VALID/10)*6))
pix_data <= BLUE;
else if((pix_x >= (H_VALID/10)*6) && (pix_x < (H_VALID/10)*7))
pix_data <= PURPPLE;
else if((pix_x >= (H_VALID/10)*7) && (pix_x < (H_VALID/10)*8))
pix_data <= BLACK;
else if((pix_x >= (H_VALID/10)*8) && (pix_x < (H_VALID/10)*9))
pix_data <= WHITE;
else if((pix_x >= (H_VALID/10)*9) && (pix_x < H_VALID))
pix_data <= GRAY;
else
pix_data <= BLACK;
endmodule
6.3 vga_colorbar
module vga_colorbar(
input wire sys_clk , //输入工作时钟,频率 50MHz
input wire sys_rst_n , //输入复位信号,低电平有效
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素信息
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
wire vga_clk ; //VGA 工作时钟,频率 25MHz
wire locked ; //PLL locked 信号
wire rst_n ; //VGA 模块复位信号
wire [9:0] pix_x ; //VGA 有效显示区域 X 轴坐标
wire [9:0] pix_y ; //VGA 有效显示区域 Y 轴坐标
wire [15:0] pix_data; //VGA 像素点色彩信息
//rst_n:VGA 模块复位信号
assign rst_n = (sys_rst_n & locked);
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- clk_gen_inst -------------
ip_pll ip_pll_inst
(
.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit
.inclk0 (sys_clk ), //输入 50MHz 晶振时钟,1bit
.c0 (vga_clk ), //输出 VGA 工作时钟,频率 25MHz,1bit
.locked (locked ) //输出 pll locked 信号,1bit
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率 25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_data (pix_data ), //输入像素点色彩信息,16bit
.pix_x (pix_x ), //输出 VGA 有效显示区域像素点 X 轴坐标,10bit
.pix_y (pix_y ), //输出 VGA 有效显示区域像素点 Y 轴坐标,10bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素点色彩信息,16bit
);
//------------- vga_pic_inst -------------
vga_pic vga_pic_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率 25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_x (pix_x ), //输入 VGA 有效显示区域像素点 X 轴坐标,10bit
.pix_y (pix_y ), //输入 VGA 有效显示区域像素点 Y 轴坐标,10bit
.pix_data (pix_data ) //输出像素点色彩信息,16bit
);
endmodule
7. Testbench
7.1 tb_vga_ctrl
`timescale 1ns/1ns
module tb_vga_ctrl();
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
wire locked ;
wire rst_n ;
wire vga_clk ;
reg sys_clk ;
reg sys_rst_n ;
reg [15:0] pix_data ;
//********************************************************************//
//**************************** Clk And Rst ***************************//
//********************************************************************//
//sys_clk,sys_rst_n 初始赋值
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//sys_clk:产生时钟
always #10 sys_clk = ~sys_clk;
//rst_n:VGA 模块复位信号
assign rst_n = (sys_rst_n & locked);
//pix_data:输入像素点色彩信息
always@(posedge vga_clk or negedge rst_n)
if(rst_n == 1'b0)
pix_data <= 16'h0000;
else
pix_data <= 16'hffff;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- clk_gen_inst -------------
ip_pll ip_pll_inst
(
.areset (~sys_rst_n ), //输入复位信号,高电平有效,1bit
.inclk0 (sys_clk ), //输入 50MHz 晶振时钟,1bit
.c0 (vga_clk ), //输出 VGA 工作时钟,频率 25MHz,1bit
.locked (locked ) //输出 pll locked 信号,1bit
);
//------------- vga_ctrl_inst -------------
vga_ctrl vga_ctrl_inst
(
.vga_clk (vga_clk ), //输入工作时钟,频率 25MHz,1bit
.sys_rst_n (rst_n ), //输入复位信号,低电平有效,1bit
.pix_data (pix_data ), //输入像素点色彩信息,16bit
.pix_x (pix_x ), //输出 VGA 有效显示区域像素点 X 轴坐标,10bit
.pix_y (pix_y ), //输出 VGA 有效显示区域像素点 Y 轴坐标,10bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出像素点色彩信息,16bit
);
endmodule
7.2 tb_vga_colorbar
`timescale 1ns/1ns
module tb_vga_colorbar();
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//wire define
wire hsync ;
wire [15:0] rgb ;
wire vsync ;
//reg define
reg sys_clk ;
reg sys_rst_n ;
//********************************************************************//
//**************************** Clk And Rst ***************************//
//********************************************************************//
//sys_clk,sys_rst_n 初始赋值
initial
begin
sys_clk = 1'b1;
sys_rst_n <= 1'b0;
#200
sys_rst_n <= 1'b1;
end
//sys_clk:产生时钟
always #10 sys_clk = ~sys_clk ;
//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//
//------------- vga_colorbar_inst -------------
vga_colorbar vga_colorbar_inst
(
.sys_clk (sys_clk ), //输入晶振时钟,频率 50MHz,1bit
.sys_rst_n (sys_rst_n ), //输入复位信号,低电平有效,1bit
.hsync (hsync ), //输出行同步信号,1bit
.vsync (vsync ), //输出场同步信号,1bit
.rgb (rgb ) //输出 RGB 图像信息,16bit
);
endmodule