大俠好,歡迎來到FPGA技術江湖,江湖偌大,相見即是緣分。大俠可以關注FPGA技術江湖,在“闖蕩江湖”、"行俠仗義"欄里獲取其他感興趣的資源,或者一起煮酒言歡。
今天給大俠帶來基于FPGA的VGA/LCD顯示控制器設計,由于篇幅較長,分三篇。今天帶來第三篇,下篇,程序的仿真與測試以及總結,話不多說,上貨。
前兩篇和之前推送過關于VGA顯示相關的文章,這里給個超鏈接,給各位大俠作個參考。
VGA (Video Graphics Array) 即視頻圖形陣列,是IBM于1987年隨PS/2機(PersonalSystem 2)一起推出的使用模擬信號的一種視頻傳輸標準。這個標準對于現(xiàn)今的個人電腦市場已經十分過時。但在當時具有分辨率高、顯示速率快、顏色豐富等優(yōu)點,在彩色顯示器領域取得了廣泛的應用,是眾多制造商所共同支持的一個低標準。
LCD ( Liquid Crystal Display 的簡稱)液晶顯示器。LCD 的構造是在兩片平行的玻璃基板當中放置液晶盒,下基板玻璃上設置TFT(薄膜晶體管),上基板玻璃上設置彩色濾光片,通過TFT上的信號與電壓改變來控制液晶分子的轉動方向,從而達到控制每個像素點偏振光出射與否而達到顯示目的。按照背光源的不同,LCD可以分為CCFL顯示器和LED顯示器兩種。LCD已經替代CRT成為主流,價格也已經下降了很多,并已充分普及。
在之前的文章中介紹了如何獲取、處理攝像頭提供的視頻信號,在實際應用中還需要將經過處理的信號顯示在顯示器上。這個過程與信號處理中的過程上是相反的,將數(shù)字信號按照電視信號的制式組成合乎時序、格式要求的信號,并加入用于控制的各種同步信號。本篇將通過 FPGA實現(xiàn)一個 VGA/LCD 顯示控制器的實例,并詳細介紹實現(xiàn)過程。
第三篇內容摘要:本篇會介紹程序的仿真與測試以及總結等相關內容。
四、程序的仿真與測試
為了檢驗程序是否實現(xiàn)預先設定的功能,需要編寫仿真程序。仿真程序的主要代碼如下:
module test;
//寄存器
reg clk;
reg rst;
//參數(shù)
parameter LINE_FIFO_AWIDTH = 7;
//wire 申明
wire int;
wire [31:0] wb_addr_o;
wire [31:0] wb_data_i;
wire [31:0] wb_data_o;
wire [3:0] wb_sel_o;
wire wb_we_o;
wire wb_stb_o;
wire wb_cyc_o;
wire [2:0] wb_cti_o;
wire [1:0] wb_bte_o;
wire wb_ack_i;
wire wb_err_i;
wire [31:0] wb_addr_i;
wire [31:0] wbm_data_i;
wire [3:0] wb_sel_i;
wire wb_we_i;
wire wb_stb_i;
wire wb_cyc_i;
wire wb_ack_o;
wire wb_rty_o;
wire wb_err_o;
reg pclk_i;
wire pclk;
wire hsync;
wire vsync;
wire csync;
wire blanc;
wire [7:0] red;
wire [7:0] green;
wire [7:0] blue;
wire dvi_pclk_p_o;
wire dvi_pclk_m_o;
wire dvi_hsync_o;
wire dvi_vsync_o;
wire dvi_de_o;
wire [11:0] dvi_d_o;
wire vga_stb_i;
wire clut_stb_i;
reg scen;
// 測試程序變量
integer wd_cnt;
integer error_cnt;
reg [31:0] data;
reg [31:0] pattern;
reg int_warn;
integer n;
integer mode;
reg [7:0] thsync, thgdel;
reg [15:0] thgate, thlen;
reg [7:0] tvsync, tvgdel;
reg [15:0] tvgate, tvlen;
reg hpol;
reg vpol;
reg cpol;
reg bpol;
integer p, l;
reg [31:0] pn;
reg [31:0] pra, paa, tmp;
reg [23:0] pd;
reg [1:0] cd;
reg pc;
reg [31:0] vbase;
reg [31:0] cbase;
reg [31:0] vbara;
reg [31:0] vbarb;
reg [7:0] bank;
// 常量定義
`define CTRL 32'h0000_0000
`define STAT 32'h0000_0004
`define HTIM 32'h0000_0008
`define VTIM 32'h0000_000c
`define HVLEN 32'h0000_0010
`define VBARA 32'h0000_0014
`define VBARB 32'h0000_0018
`define USE_VC 1
parameter PCLK_C = 20;
//測試內容
initial
begin
$timeformat (-9, 1, " ns", 12);
$display("nn");
$display("******************************************************");
$display("*VGA/LCD Controller Simulation started ... *");
$display("******************************************************");
$display("n");
`ifdef WAVES
$shm_open("waves");
$shm_probe("AS",test,"AS");
$display("INFO: Signal dump enabled ...nn");
`endif
scen = 0;
error_cnt = 0;
clk = 0;
pclk_i = 0;
rst = 0;
int_warn=1;
repeat(20) @(posedge clk);
rst = 1;
repeat(20) @(posedge clk);
if(0)
begin
end
else
if(1)
begin
`ifdef VGA_12BIT_DVI
dvi_pd_test;
`endif
end
else
begin
// 測試區(qū)域
$display("nn");
$display("*****************************************************");
$display("*** XXX Test ***");
$display("*****************************************************n");
s0.fill_mem(1);
repeat(10) @(posedge clk);
//參數(shù)設置
vbara = 32'h0000_0000;
vbarb = 32'h0001_0000;
m0.wb_wr1( `VBARA, 4'hf, vbara );
m0.wb_wr1( `VBARB, 4'hf, vbarb );
thsync = 0;
thgdel = 0;
thgate = 340;
thlen = 345;
tvsync = 0;
tvgdel = 0;
tvgate = 240;
tvlen = 245;
/*
thsync = 0;
thgdel = 0;
thgate = 63;
thlen = 70;
tvsync = 0;
tvgdel = 0;
tvgate = 32;
tvlen = 36;
*/
hpol = 0;
vpol = 0;
cpol = 0;
bpol = 0;
m0.wb_wr1( `HTIM, 4'hf, {thsync, thgdel, thgate} );
m0.wb_wr1( `VTIM, 4'hf, {tvsync, tvgdel, tvgate} );
m0.wb_wr1( `HVLEN, 4'hf, {thlen, tvlen} );
mode = 2;
for(bank=0;bank<3;bank=bank + 1)
begin
case(mode)
0:
begin
cd = 2'h2;
pc = 1'b0;
end
1:
begin
cd = 2'h0;
pc = 1'b0;
end
2:
begin
cd = 2'h0;
pc = 1'b1;
end
3:
begin
cd = 2'h1;
pc = 1'b0;
end
endcase
m0.wb_wr1( `CTRL, 4'hf, {
16'h0, // Reserved
bpol, cpol,
vpol, hpol,
pc, // 1'b0, // PC
cd, // 2'h2, // CD
2'h0, // VBL
1'b0, // Reserved
1'b1, // CBSWE
1'b1, // VBSWE
1'b0, // BSIE
1'b0, // HIE
1'b0, // VIE
1'b1 // Video Enable
});
$display("Mode: %0d Screen: %0d", mode, bank);
//repeat(2) @(posedge vsync);
@(posedge vsync);
// 每一行數(shù)據
for(l=0;l<tvgate+1;l=l+1)
// For each Pixel
for(p=0;p<thgate+1;p=p+1)
begin
while(blanc) @(posedge pclk);
if(bank[0]) vbase = vbarb[31:2];
else vbase = vbara[31:2];
if(bank[0]) cbase = 32'h0000_0c00;
else cbase = 32'h0000_0800;
// 各種顯示模式
//像素數(shù)目 = 行數(shù)* (thgate + 1) + p
pn = l * (thgate + 1) + p;
case(mode)
0: // 24 位模式
begin
pra = pn[31:2] * 3;
paa = pra + vbase; // 像素決定地址
// 像素數(shù)據
case(pn[1:0])
0:
begin
tmp = s0.mem[paa];
pd = tmp[31:8];
end
1:
begin
tmp = s0.mem[paa];
pd[23:16] = tmp[7:0];
tmp = s0.mem[paa+1];
pd[15:0] = tmp[31:16];
end
2:
begin
tmp = s0.mem[paa+1];
pd[23:8] = tmp[15:0];
tmp = s0.mem[paa+2];
pd[7:0] = tmp[31:24];
end
3:
begin
tmp = s0.mem[paa+2];
pd = tmp[23:0];
end
endcase
end
1: // 8 位灰度模式
begin
pra = pn[31:2]; // 像素相對地址
paa = pra + vbase; // 像素絕對地址
case(pn[1:0])
0:
begin
tmp = s0.mem[paa];
pd = { tmp[31:24], tmp[31:24], tmp[31:24] };
end
1:
begin
tmp = s0.mem[paa];
pd = { tmp[23:16], tmp[23:16], tmp[23:16] };
end
2:
begin
tmp = s0.mem[paa];
pd = { tmp[15:8], tmp[15:8], tmp[15:8] };
end
3:
begin
tmp = s0.mem[paa];
pd = { tmp[7:0], tmp[7:0], tmp[7:0] };
end
endcase
end
2: // 8 位偽彩色模式
begin
pra = pn[31:2]; //像素相對地址
paa = pra + vbase; //像素絕對地址
case(pn[1:0])
0:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[31:24]];
pd = tmp[23:0];
end
1:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[23:16]];
pd = tmp[23:0];
end
2:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[15:8]];
pd = tmp[23:0];
end
3:
begin
tmp = s0.mem[paa];
tmp = s0.mem[cbase[31:2] + tmp[7:0]];
pd = tmp[23:0];
end
endcase
end
3: // 16 位模式
begin
pra = pn[31:1]; //像素相對地址
paa = pra + vbase; //像素絕對地址
case(pn[0])
0:
begin
tmp = s0.mem[paa];
tmp[15:0] = tmp[31:16];
pd = {tmp[15:11], 3'h0, tmp[10:5], 2'h0, tmp[4:0], 3'h0};
end
1:
begin
tmp = s0.mem[paa];
pd = {tmp[15:11], 3'h0, tmp[10:5], 2'h0, tmp[4:0], 3'h0};
end
endcase
end
endcase
??????
if(pd !== {red, green, blue} )
begin
$display("ERROR: Pixel Data Mismatch: Expected: %h, Got: %h %h %h",
pd, red, green, blue);
$display(" pixel=%0d, line=%0d, (%0t)",p,l,$time);
error_cnt = error_cnt + 1;
end
@(posedge pclk);
end
end
show_errors;
$display("*****************************************************");
$display("*** Test DONE ... ***");
$display("*****************************************************nn");
end
repeat(10) @(posedge clk);
$finish;
end
//同步監(jiān)視
`ifdef VGA_12BIT_DVI
sync_check #(PCLK_C*2) ucheck(
`else
sync_check #(PCLK_C) ucheck(
`endif
.pclk( pclk ),
.rst( rst ),
.enable( scen ),
.hsync( hsync ),
.vsync( vsync ),
.csync( csync ),
.blanc( blanc ),
.hpol( hpol ),
.vpol( vpol ),
.cpol( cpol ),
.bpol( bpol ),
.thsync( thsync ),
.thgdel( thgdel ),
.thgate( thgate ),
.thlen( thlen ),
.tvsync( tvsync ),
.tvgdel( tvgdel ),
.tvgate( tvgate ),
.tvlen( tvlen ) );
// 視頻數(shù)據監(jiān)視
wb_b3_check u_wb_check (
.clk_i ( clk ),
.cyc_i ( wb_cyc_o ),
.stb_i ( wb_stb_o ),
.cti_i ( wb_cti_o ),
.bte_i ( wb_bte_o ),
.we_i ( wb_we_o ),
.ack_i ( wb_ack_i ),
.err_i ( wb_err_i ),
.rty_i ( 1'b0 ) );
//看門狗計數(shù)器
always @(posedge clk)
if(wb_cyc_i | wb_cyc_o | wb_ack_i | wb_ack_o | hsync)
wd_cnt <= #1 0;
else
wd_cnt <= #1 wd_cnt + 1;
always @(wd_cnt)
if(wd_cnt>9000)
begin
$display("nn*************************************n");
$display("ERROR: Watch Dog Counter Expiredn");
$display("*************************************nnn");
$finish;
end
always @(posedge int)
if(int_warn)
begin
$display("nn*************************************n");
$display("WARNING: Recieved Interrupt (%0t)", $time);
$display("*************************************nnn");
end
always #2.5 clk = ~clk;
always #(PCLK_C/2) pclk_i = ~pclk_i;
//模塊原型
vga_enh_top #(1'b0, LINE_FIFO_AWIDTH) u0 (
.wb_clk_i ( clk ),
.wb_rst_i ( 1'b0 ),
.rst_i ( rst ),
.wb_inta_o ( int ),
//從信號
.wbs_adr_i ( wb_addr_i[11:0] ),
.wbs_dat_i ( wb_data_i ),
.wbs_dat_o ( wb_data_o ),
.wbs_sel_i ( wb_sel_i ),
.wbs_we_i ( wb_we_i ),
.wbs_stb_i ( wb_stb_i ),
.wbs_cyc_i ( wb_cyc_i ),
.wbs_ack_o ( wb_ack_o ),
.wbs_rty_o ( wb_rty_o ),
.wbs_err_o ( wb_err_o ),
//主信號
.wbm_adr_o ( wb_addr_o[31:0] ),
.wbm_dat_i ( wbm_data_i ),
.wbm_sel_o ( wb_sel_o ),
.wbm_we_o ( wb_we_o ),
.wbm_stb_o ( wb_stb_o ),
.wbm_cyc_o ( wb_cyc_o ),
.wbm_cti_o ( wb_cti_o ),
.wbm_bte_o ( wb_bte_o ),
.wbm_ack_i ( wb_ack_i ),
.wbm_err_i ( wb_err_i ),
//VGA 信號
.clk_p_i ( pclk_i ),
`ifdef VGA_12BIT_DVI
.dvi_pclk_p_o ( dvi_pclk_p_o ),
.dvi_pclk_m_o ( dvi_pclk_m_o ),
.dvi_hsync_o ( dvi_hsync_o ),
.dvi_vsync_o ( dvi_vsync_o ),
.dvi_de_o ( dvi_de_o ),
.dvi_d_o ( dvi_d_o ),
`endif
.clk_p_o ( pclk ),
.hsync_pad_o ( hsync ),
.vsync_pad_o ( vsync ),
.csync_pad_o ( csync ),
.blank_pad_o ( blanc ),
.r_pad_o ( red ),
.g_pad_o ( green ),
.b_pad_o ( blue )
);
wb_mast m0( .clk( clk ),
.rst( rst ),
.adr( wb_addr_i ),
.din( wb_data_o ),
.dout( wb_data_i ),
.cyc( wb_cyc_i ),
.stb( wb_stb_i ),
.sel( wb_sel_i ),
.we( wb_we_i ),
.ack( wb_ack_o ),
.err( wb_err_o ),
.rty( 1'b0 )
);
wb_slv #(24) s0(.clk( clk ),
.rst( rst ),
.adr( {1'b0, wb_addr_o[30:0]} ),
.din( 32'h0 ),
.dout( wbm_data_i ),
.cyc( wb_cyc_o ),
.stb( wb_stb_o ),
.sel( wb_sel_o ),
.we( wb_we_o ),
.ack( wb_ack_i ),
.err( wb_err_i ),
.rty( )
);
`include "tests.v"
endmodule
五、總結
本篇介紹了一個 VGA/LCD 顯示控制器的實例。首先介紹了 VGA/LCD 顯示的相關知識,然后介紹了程序的主要結構和主要功能模塊的實現(xiàn)過程。最后用一個測試程序驗證程序的功能是否滿足要求。本章為各位大俠設計自己的 VGA/LCD 顯示控制器提供了一個可以使用的方案。
本篇到此結束,各位大俠,有緣再見!