Simulation and testing, as well as a summary, without further ado, let’s get started.
Introduction
VGA (Video Graphics Array) is a video transmission standard using analog signals, launched by IBM in 1987 with the PS/2 (Personal System 2). This standard is now quite outdated in today’s personal computer market. However, at that time, it had advantages such as high resolution, fast display rates, and rich colors, achieving widespread application in the field of color displays, and was a low standard supported by many manufacturers.
LCD (Liquid Crystal Display) is a type of liquid crystal display. The structure of an LCD consists of a liquid crystal box placed between two parallel glass substrates, with a TFT (Thin Film Transistor) set on the lower substrate and a color filter on the upper substrate. By changing the signals and voltages on the TFT, the orientation of the liquid crystal molecules can be controlled, thus controlling whether each pixel emits polarized light for display purposes. Depending on the backlight source, LCDs can be divided into CCFL displays and LED displays. LCDs have replaced CRTs to become mainstream, with prices dropping significantly and becoming widely popular.
In previous articles, we introduced how to acquire and process video signals provided by cameras. In practical applications, it is also necessary to display the processed signals on a monitor. This process is the reverse of the signal processing, where digital signals are composed into signals that meet the timing and format requirements according to television signal standards, along with various synchronization signals for control. This article will implement a VGA/LCD display controller example using FPGA and detail the implementation process.
Summary of the third section: This section will introduce the simulation and testing of the program, as well as related content.

4. Simulation and Testing of the Program

To verify whether the program achieves the pre-set functions, a simulation program needs to be written. The main code of the simulation program is as follows:
moduletest;// Register regclk; regrst;// Parameters parameterLINE_FIFO_AWIDTH = 7;// Wire declaration wireint; wire[31:0] wb_addr_o; wire[31:0] wb_data_i; wire[31:0] wb_data_o; wire[3:0] wb_sel_o; wirewb_we_o; wirewb_stb_o; wirewb_cyc_o; wire[2:0] wb_cti_o; wire[1:0] wb_bte_o; wirewb_ack_i; wirewb_err_i; wire[31:0] wb_addr_i; wire[31:0] wbm_data_i; wire[3:0] wb_sel_i; wirewb_we_i; wirewb_stb_i; wirewb_cyc_i; wirewb_ack_o; wirewb_rty_o; wirewb_err_o; regpclk_i; wirepclk; wirehsync; wirevsync; wirecsync; wireblanc; wire[7:0] red; wire[7:0] green; wire[7:0] blue; wiredvi_pclk_p_o; wiredvi_pclk_m_o; wiredvi_hsync_o; wiredvi_vsync_o; wiredvi_de_o; wire[11:0] dvi_d_o; wirevga_stb_i; wireclut_stb_i; regscen;// Test program variables integerwd_cnt; integererror_cnt; reg[31:0] data; reg[31:0] pattern; regint_warn; integern; integermode; reg[7:0] thsync, thgdel; reg[15:0] thgate, thlen; reg[7:0] tvsync, tvgdel; reg[15:0] tvgate, tvlen; reghpol; regvpol; regcpol; regbpol; integerp, l; reg[31:0] pn; reg[31:0] pra, paa, tmp; reg[23:0] pd; reg[1:0] cd; regpc; reg[31:0] vbase; reg[31:0] cbase; reg[31:0] vbara; reg[31:0] vbarb; reg[7:0] bank;// Constant definitions `defineCTRL 32'h0000_0000`defineSTAT 32'h0000_0004`defineHTIM 32'h0000_0008`defineVTIM 32'h0000_000c`defineHVLEN 32'h0000_0010`defineVBARA 32'h0000_0014`defineVBARB 32'h0000_0018`defineUSE_VC 1parameterPCLK_C = 20;// Test content initial begin $timeformat(-9, 1, " ns", 12); $display("\n\n"); $display("******************************************************"); $display("*VGA/LCDController Simulation started ... *"); $display("******************************************************"); $display("\n"); `ifdefWAVES $shm_open("waves"); $shm_probe("AS",test,"AS"); $display("INFO: Signal dump enabled ...\n\n"); `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 `ifdefVGA_12BIT_DVIdvi_pd_test; `endif end else begin // Test area $display("\n\n"); $display("*****************************************************"); $display("***XXX Test ***"); $display("*****************************************************\n"); s0.fill_mem(1); repeat(10)@(posedge clk); // Parameter settings 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); // Each line of data 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; // Various display modes // Number of pixels = number of lines * (thgate + 1) + ppn = l * (thgate + 1) + p; case(mode) 0: // 24-bit mode begin pra = pn[31:2] * 3; paa = pra + vbase; // Pixel determines address // Pixel data 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-bit grayscale mode begin pra = pn[31:2]; // Relative pixel address paa = pra + vbase; // Absolute pixel address 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-bit pseudo-color mode begin pra = pn[31:2]; // Relative pixel address paa = pra + vbase; // Absolute pixel address 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-bit mode begin pra = pn[31:1]; // Relative pixel address paa = pra + vbase; // Absolute pixel address 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("*****************************************************\n\n"); end repeat(10)@(posedge clk); $finish; end // Synchronization monitoring `ifdefVGA_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 ) ); // Video data monitoring 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 ) ); // Watchdog counter 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("\n\n*************************************\n"); $display("ERROR: Watch Dog Counter Expired\n"); $display("*************************************\n\n\n"); $finish; end always@(posedge int) if(int_warn) begin $display("\n\n*************************************\n"); $display("WARNING: Received Interrupt (%0t)", $time); $display("*************************************\n\n\n"); end always#2.5 clk = ~clk; always#(PCLK_C/2) pclk_i = ~pclk_i; // Module prototype 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 ), // Slave signal .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 ), // Master signal .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 signals .clk_p_i( pclk_i ), `ifdefVGA_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_mastm0( .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

5. Summary

This article introduced an example of a VGA/LCD display controller. First, it presented relevant knowledge about VGA/LCD displays, then described the main structure of the program and the implementation process of the main functional modules. Finally, a test program was used to verify whether the program’s functionality meets the requirements. This chapter provides a usable solution for readers to design their own VGA/LCD display controllers.
*Disclaimer: This article is original by the author. The content reflects the author's personal views, and the reproduction is solely to convey a different perspective, not representing agreement or support for that view. If there are any objections, please feel free to contact us.
‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧ END ‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧‧
Follow the WeChat public account "ZYNQ" and reply "join group" to join the FPGA/ZYNQ technology exchange group. Reply "m" in the background to see more exciting content.