02_VGA 显示器字符显示
1. 实验目标
实验目标:在 VGA 显示器中心位置显示金色“野火科技”四个汉字,字符外的背景颜色为黑色。每个汉字大小为 5656,字模点阵为 6464,VGA 显示模式为 640*480@60。
2. 模块框图
2.1 顶层模块
整个系统的框图
locked 是高电平有效
2.2 时钟生成模块
本实验选用经典 VGA 显示模式 640x480@60,理论时钟频率应为 25.175MHz,为了便于时钟生成,我们使用 25MHz 的时钟代替25.175MHz 的时钟,不会对实验造成影响。本次实验工程中,而板卡晶振传入时钟频率为 50MHz。时钟生成模块的作用就是将 50MHz 晶振时钟分频为25MHz 的 VGA 工作时钟。
2.3 VGA 时序控制模块
产生行场信号,并且把输入的图像信息pix_data,给输出rgb
Pix_x,Pix_y 就是视频图中的坐标 用于生成图像
2.4 图像数据生成模块
3. 波形图
3.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 时序逻辑赋值带来的问题。
3.2 字符数据生成模块
4. RTL
4.1 vga_pic
`timescale 1ns/1ns
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 and Internal Signal *******************//
//********************************************************************//
//parameter define
parameter CHAR_B_H= 10'd192 , //字符开始X轴坐标
CHAR_B_V= 10'd208 ; //字符开始Y轴坐标
parameter CHAR_W = 10'd256 , //字符宽度
CHAR_H = 10'd64 ; //字符高度
parameter BLACK = 16'h0000, //黑色
WHITE = 16'hFFFF, //白色
GOLDEN = 16'hFEC0; //金色
//wire define
wire [9:0] char_x ; //字符显示X轴坐标
wire [9:0] char_y ; //字符显示Y轴坐标
//reg define
reg [255:0] char [63:0] ; //字符数据
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//字符显示坐标
assign char_x = (((pix_x >= CHAR_B_H) && (pix_x < (CHAR_B_H + CHAR_W)))
&& ((pix_y >= CHAR_B_V) && (pix_y < (CHAR_B_V + CHAR_H))))
? (pix_x - CHAR_B_H) : 10'h3FF;
assign char_y = (((pix_x >= CHAR_B_H) && (pix_x < (CHAR_B_H + CHAR_W)))
&& ((pix_y >= CHAR_B_V) && (pix_y < (CHAR_B_V + CHAR_H))))
? (pix_y - CHAR_B_V) : 10'h3FF;
//char:字符数据
always@(posedge vga_clk)
begin
// char[y][x]
// char[场][行]
char[0] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[1] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[2] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[3] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[4] <= 256'h00000000003C0000000000000000000000000000000070000000000000000000;
char[5] <= 256'h0000000E003E00000000000000000000000000000000F0000000000000000000;
char[6] <= 256'h000000FF001F00000000000000000000000000000001F0000000000000400000;
char[7] <= 256'h000007FF000F00000000000000000000000010000001F8000000000000E00000;
char[8] <= 256'h00007FFE000F000000000000000000000003FE000001F8000000000000F00000;
char[9] <= 256'h0000FE7E003F00000000000000000000000FFF800001F80000003E0000F00000;
char[10] <= 256'h0000E07C01F8000000000000000000000007FF81F801FC0000003F0000F00000;
char[11] <= 256'h0000E0F80380000000000000000000000003FF80FE00780000003F0001E00000;
char[12] <= 256'h0000E0F80380000000000000000000000001FF80FF00780000003F0001E00000;
char[13] <= 256'h0780E1F003FC000000000000000000000000FF80FF80780000003E0001E00000;
char[14] <= 256'h07C0FFF003FE0000000000020000000000003F007F80780000003E0001E00000;
char[15] <= 256'h07E0FFE001FE0000000000070000000000000E007F00780000003C0001FF0000;
char[16] <= 256'h07F3FFE0000E00000000000700000000000000007E00F80000003C0001FF0000;
char[17] <= 256'h07F9FFC0000E00000000000F0000000000003E00CC01F80000007C0003FE0000;
char[18] <= 256'h03F9FFC0001C03E00000000F8000000000003F010001F80000007E0003F80000;
char[19] <= 256'h03F9FF8000781FF80000001F8000000000007F038001F8000000FF0007F00000;
char[20] <= 256'h03F9FF8001F07FF80000001F878000000000FE078001F8000000FF000FE00000;
char[21] <= 256'h03FDFF0007C3FFF00000003FFFC000000001FC07C001F8000001FF003FE00000;
char[22] <= 256'h03FDFF000F9F8FF00001C03FFFC000000007F807F001F8000007FE003FC00000;
char[23] <= 256'h03FFFE001FFF8FE00001E07FFFC00000001FF807FC01F800000FFC0003C00000;
char[24] <= 256'h01FFF0007FFF8FC00003E07FFC000000067FF007FE01FC00001FF80003800000;
char[25] <= 256'h01FFC000FFEF9F800003E0FE000000000FFFF003FC01FE00007FF00007800000;
char[26] <= 256'h01FF8001FF8F9F000007E0FE000000000FFFF041F803FC0000FFE00007800000;
char[27] <= 256'h01FF8007FC1F9E00001FE0FE000000000FFFF180F00FFC000001E00007000000;
char[28] <= 256'h00FF8007F01F3C00003FE1FC00000000007FF601E01FF8000001E000071C0000;
char[29] <= 256'h00FF8007C01FFC00003FE1FC00000000001FFC03C0FFF8000001E0000F3E0000;
char[30] <= 256'h00030002001FF800003FC3FC00000000003FF807C3FFF8000001E7800FFE0000;
char[31] <= 256'h00070000001FF000007FC3F800000000003FF80F9FFFF0000001FF000FFE0000;
char[32] <= 256'h0007F000001FE000007F83F800000000007FF01FFFC3F0000001FE000FFE0000;
char[33] <= 256'h001FF000001FC000003F83F00000000000FFF01FFE03F0000001FC001FFE0000;
char[34] <= 256'h007FE000001F8000003F07F00000000000FFE03FF003F0000003F8001F3E0000;
char[35] <= 256'h007FC000001F8000001807F00000000001FFE03F8003F0000003F0001F3C0000;
char[36] <= 256'h00078600001F000000000FF80000000003FFC07E0003F0000003F0001E3C0000;
char[37] <= 256'h000F1E00001F000000000FFC0000000007FFC0180003F0000007E003843C0000;
char[38] <= 256'h003FFC00001F800000001FDE0000000007FFC0000003F000000FF001E03C0000;
char[39] <= 256'h007FF800001F800000003FDF8000000007C7C0000003F00000FFF000F83C0000;
char[40] <= 256'h00FFF000001F800000007F9FF00000000787C0000003F00007FFF0007F3C0000;
char[41] <= 256'h01FFE000001F80000000FF0FFE0000000707C0000003F0001FF1F8003FFC0000;
char[42] <= 256'h03FF8000001F80000001FE0FFFE000000003C0000003F0003F81F8001FFC0000;
char[43] <= 256'h07FF0000003F80000003FC07FFFE0000000300000003F0003E01FC000FFE0000;
char[44] <= 256'h07FC0000003F80000007F803FFFFE000000000000003F0001F83FC0007FFF000;
char[45] <= 256'h07F80000003F8000000FF001FFFFFF80000000000003F0000FFFFC0003FFFF80;
char[46] <= 256'h03E00000007F8000001FC0007FFFFFC0000000000003F00003FFFE0007FFFFF0;
char[47] <= 256'h0000000000FF8000007F80001FFFFFE0000000000003F000007FFE003FBFFFF8;
char[48] <= 256'h0000000001FF800000FE000007FFFFE0000000000003E000000FFC03FE0FFFF8;
char[49] <= 256'h0000000007FF800001F8000001FFFFF0000000000003E0000000F8FFC003FFF8;
char[50] <= 256'h000000007FFF000003E00000001FFFC0000000000003C0000000000000007FF8;
char[51] <= 256'h00000007FFFE0000070000000001FFC0000000000003C0000000000000000FF0;
char[52] <= 256'h0000000200F800000000000000000F8000000000000300000000000000000060;
char[53] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[54] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[55] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[56] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[57] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[58] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[59] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[60] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[61] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[62] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
char[63] <= 256'h0000000000000000000000000000000000000000000000000000000000000000;
end
//pix_data:输出像素点色彩信息,根据当前像素点坐标指定当前像素点颜色数据
always@(posedge vga_clk or negedge sys_rst_n)
if(sys_rst_n == 1'b0)
pix_data <= BLACK;
else if((((pix_x >= (CHAR_B_H - 1'b1))
&& (pix_x < (CHAR_B_H + CHAR_W -1'b1)))
&& ((pix_y >= CHAR_B_V) && (pix_y < (CHAR_B_V + CHAR_H))))
&& (char[char_y][10'd255 - char_x] == 1'b1))
// char[char_y][10'd255 - char_x] == 1'b1
pix_data <= GOLDEN;
else
pix_data <= BLACK;
endmodule
4.2 vga_ctrl
`timescale 1ns/1ns
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 , //输出VGA有效显示区域像素点X轴坐标
output wire [9:0] pix_y , //输出VGA有效显示区域像素点Y轴坐标
output wire hsync , //输出行同步信号
output wire vsync , //输出场同步信号
output wire [15:0] rgb //输出像素点色彩信息
);
//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//
//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 ; //场同步信号计数器
//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//
//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
4.3 vga_char
`timescale 1ns/1ns
module vga_char(
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
5. Testbench
5.1 tb_vga_char
`timescale 1ns/1ns
//
module tb_vga_char();
//********************************************************************//
//****************** 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_char_inst -------------
vga_char vga_char_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