FPGA中级项目6------VGA 2part
VGA板级验证
在上篇文章中,我们讲解了VGA的Verilog设计与显示原理。
这次的第一个任务便是进行VGA的板级验证 ,主要是验证以下三个方面:
1. 能够正确的全屏点亮,显示稳定
2. 能否正确的显示颜色,也就是按照用户需求来实现需要显示的目标颜色(赤橙黄绿青蓝紫)
**3.**能否正确的定位坐标,也就是在对应的屏幕位置显示对应的数据
同样为了更好的显示验证效果,我们创建Verilog代码来实行。
其中必须要事先说明的是;RGB三色分量可以组成不同的颜色,当某一个分量全满或全0,可以组成相应的8种颜色。例如:R分量和B分量全满,G分量全0时,便构成了紫色。相应代码表示为24'hFF00FF。

其次要分割相应区域,来定义不同的颜色像素点坐标范围 ,如下所示,然后将相应区域上显示想要显示颜色即可。
在实际的FPGA开发板上的VGA测试文件代码如下:
//定义输入输出端口
module VGA_TEXT(
clk_50M,
reset_n,
VGA_HS,//行同步信号
VGA_VS,//场同步信号
VGA_BLK,//数据输出时间段
VGA_CLK,
data_out
);
input clk_50M;
input reset_n;
output VGA_HS;
output VGA_VS;
output VGA_BLK;
output VGA_CLK;
output [23:0]data_out;
//定义相关信号
reg [23:0]data_in;
wire locked;
wire clk;
wire [11:0]hcount;
wire [11:0]vcount;
//定义计数器counter0
reg [31:0]counter0;
always@(posedge clk_50M or negedge reset_n)
if(!reset_n)
counter0 <= 0;
else if(counter0 == 99_999_999)
counter0 <= 0;
else
counter0 <= counter0 + 1'd1;
//定义使能信号
reg en;
always@(posedge clk_50M or negedge reset_n)
if(!reset_n)
en <= 0;
else if(counter0 >= 49_999_999)
en <= 1;
else
en <= 0;
//定义颜色代码
localparam black = 24'h000000,
blue = 24'h0000FF,
red = 24'hFF0000,
purpple = 24'hFF00FF,
green = 24'h00FF00,
cyan = 24'h00FFFF,
yellow = 24'hFFFF00,
white = 24'hFFFFFF;
//例化时钟IP核 33M
clk clk_33M
(
// Clock out ports
.clk_out1(clk), // output clk_out1
// Status and control signals
.reset(!reset_n), // input reset
.locked(locked), // output locked
// Clock in ports
.clk_50M(clk_50M));
//例化已经写好的VGA模块
VGA(
.clk(clk),
.reset_n(reset_n),
.data_in(data_in),//用户输入数据
.VGA_HS(VGA_HS),//行同步信号
.VGA_VS(VGA_VS),//场同步信号
.hcount(hcount),//行扫描位置
.vcount(vcount),//场扫描位置
.VGA_BLK(VGA_BLK),//数据输出时间段
.VGA_CLK(VGA_CLK),
.data_out(data_out)
);
//定义扫描像素块
wire c0_act = hcount >= 0 && hcount <400;//扫描第0列
wire c1_act = hcount >= 400 && hcount <800;//扫描第1列
wire r0_act = vcount >= 0 && vcount < 120;//扫描第0行
wire r1_act = vcount >= 120 && vcount < 240;//扫描第1行
wire r2_act = vcount >= 240 && vcount < 360;//扫描第2行
wire r3_act = vcount >= 360 && vcount < 480;//扫描第3行
wire r0_c0_act = r0_act & c0_act;//定位第0列,第0行像素块扫描
wire r0_c1_act = r0_act & c1_act;//定位第0列,第1行像素块扫描
wire r1_c0_act = r1_act & c0_act;//定位第1列,第0行像素块扫描
wire r1_c1_act = r1_act & c1_act;//定位第1列,第1行像素块扫描
wire r2_c0_act = r2_act & c0_act;//定位第2列,第0行像素块扫描
wire r2_c1_act = r2_act & c1_act;//定位第2列,第1行像素块扫描
wire r3_c0_act = r3_act & c0_act;//定位第3列,第0行像素块扫描
wire r3_c1_act = r3_act & c1_act;//定位第3列,第1行像素块扫描
//定义像素块显示颜色
always@(posedge clk)
if(en == 1) begin
case({r3_c1_act, r3_c0_act,r2_c1_act,r2_c0_act,r1_c1_act,r1_c0_act,r0_c1_act,r0_c0_act})
8'b0000_0001 : data_in <= black;
8'b0000_0010 : data_in <= blue;
8'b0000_0100 : data_in <= red;
8'b0000_1000 : data_in <= purpple;
8'b0001_0000 : data_in <= green;
8'b0010_0000 : data_in <= cyan;
8'b0100_0000 : data_in <= yellow;
8'b1000_0000 : data_in <= white;
endcase
end
else
data_in <= 24'h000000;
endmodule
在上述代码中,我们还定义了一个33M的时钟IP核 ,主要是由于由于分辨率不同导致时钟(像素时钟)不同。例如我们本项目所要显示的800*480的分辨率,扫描完成一幅图像所用时间为(在1秒之内,时钟震动33M次)恰好完成一幅图像的扫描。实现1056*525(*60hz)像素点(存在水平和垂直消隐期,因此像素点会扩大)的刷新。
刷新率 60Hz 表示每秒更新 60 帧图像,每帧需要传输完整的像素数据。
多分辨率VGA控制器设计
为了满足不同分辨率的需求,使得应用于不同的应用场景 。我们需要对已经设计好的VGA模块进行优化。在进行这第二个任务之前,我们首先要补充一种新的语法:条件编译
条件编译简介
1. ifdef、else 和 endif
ifdef指令用来检查某个宏是否已经被定义。若宏已定义,就编译 ifdef 和 else(如果存在)之间的代码;若未定义,则编译 else 和 endif 之间的代码。
`define DEBUG // 定义一个宏
module test_module;
initial begin
`ifdef DEBUG
$display("Debug mode is enabled.");
`else
$display("Debug mode is disabled.");
`endif
end
endmodule
在这个例子中,由于定义了 DEBUG
宏,所以会打印出 "Debug mode is enabled."。要是去掉 define DEBUG
这一行,就会打印 "Debug mode is disabled."。
2. ifndef
ifndef 指令和 ifdef 相反,它检查某个宏是否未被定义。若宏未定义,就编译 ifndef 和 else(如果存在)之间的代码;若已定义,则编译 else 和 endif 之间的代码。
// 不定义宏
// `define RELEASE
module test_module;
initial begin
`ifndef RELEASE
$display("This is not a release version.");
`else
$display("This is a release version.");
`endif
end
end
因为没有定义 RELEASE
宏,所以会打印 "This is not a release version."。
3. ``define 和 undef
define 指令用于定义一个宏,而 undef 指令用于取消定义一个宏。
`define VERSION 2
module test_module;
initial begin
`ifdef VERSION
if (`VERSION == 1) begin
$display("Version 1 is selected.");
end else if (`VERSION == 2) begin
$display("Version 2 is selected.");
end
`endif
end
endmodule
// 在其他地方取消定义
`undef VERSION
在这个例子中,由于定义了 VERSION
宏且其值为 2,所以会打印 "Version 2 is selected."。之后使用 undef VERSION
取消了宏的定义。
4. elsif
elsif 指令用于在 ifdef 或者 ifndef 块里添加更多的条件判断。
`define SIMULATION
module test_module;
initial begin
`ifdef SIMULATION
$display("Simulation mode is enabled.");
`elsif SYNTHESIS
$display("Synthesis mode is enabled.");
`else
$display("Unknown mode.");
`endif
end
endmodule
在这个例子中,由于定义了 SIMULATION
宏,所以会打印 "Simulation mode is enabled."。若定义了 SYNTHESIS
宏,就会打印 "Synthesis mode is enabled.";若两个宏都未定义,就会打印 "Unknown mode."。
代码展示
我们可以对VGA时序中的必要参数进行定义,列举所有情况,类似原先的parameter和if语句综合。这样便可方便的修改参数。如以下VGA代码所示:
//定义不同的分辨率
`define resolution_480x272 1//时钟为9hz
`define resolution_640x480 1//时钟为25hz
`define resolution_800x480 1//时钟为33hz
`define resolution_800x600 1//时钟为40hz
`define resolution_1024x600 1//时钟为51hz
`define resolution_1024x768 1//时钟为65hz
`define resolution_1280x720 1//时钟为74.25hz
`define resolution_1920x1080 1//时钟为148.5hz
`ifdef resolution_480x272
`define h_right_border 0
`define h_front_porch 2
`define h_sync_time 41
`define h_back_porch 2
`define h_left_border 0
`define h_data_time 480
`define h_total_time 525
`define v_bottom_border 0
`define v_front_porch 2
`define v_sync_time 10
`define v_back_porch 2
`define v_top_border 0
`define v_data_time 272
`define v_total_time 286
`elsif resolution_1280x720
`define h_right_border 12'd0
`define h_front_porch 12'd110
`define h_sync_time 12'd40
`define h_back_porch 12'd220
`define h_left_border 12'd0
`define h_data_time 12'd1280
`define h_total_time 12'd1650
`define v_bottom_border 12'd0
`define v_front_porch 12'd5
`define v_sync_time 12'd5
`define v_back_porch 12'd36
`define v_top_border 12'd0
`define v_data_time 12'd720
`define v_total_time 12'd750
`elsif resolution_1920x1080
`define h_right_border 12'd0
`define h_front_porch 12'd88
`define h_sync_time 12'd44
`define h_back_porch 12'd148
`define h_left_border 12'd0
`define h_data_time 12'd1920
`define h_total_time 12'd2200
`define v_bottom_border 12'd0
`define v_front_porch 12'd4
`define v_sync_time 12'd5
`define v_back_porch 12'd36
`define v_top_border 12'd0
`define v_data_time 12'd1080
`define v_total_time 12'd1125
//仅输入部分例子,其余可查询手册或VGA标准自行计算
`endif
同时在原先的顶层设计VGA中,也要进行相应的参数定义! 以达到一致便于分辨率适配。
//定义时序中相关信号
//parameter VGA_HS_end = 11'd127;
//parameter hdat_begin = 11'd216;//行数据开始输出位置
//parameter hdat_end = 11'd1016;//行数据停止输出位置
//parameter hpixel_end = 11'd1055;//行扫描的最大位置处
//parameter VGA_VS_end = 11'd1;
//parameter vdat_begin = 11'd35;
//parameter vdat_end = 11'd515;
//parameter vline_end = 11'd524;
//将上述的parameter定义改为参数定义,便于适配
parameter VGA_HS_end = `h_sync_time - 1,
hdat_begin = `h_sync_time + `h_back_porch + `h_left_border,
hdat_end = `h_sync_time + `h_back_porch + `h_left_border + `h_data_time,
hpixel_end = `h_total_time - 1,
VGA_VS_end = `v_sync_time - 1,
vdat_begin = `v_sync_time + `v_back_porch + `v_top_border,
vdat_end = `v_sync_time + `v_back_porch + `v_top_border + `v_data_time,
vpixel_end = `v_total_time - 1;
到此,我们的VGA控制显示便生成!!!可生成自己想要实现的图像,例如:
