Back to Home

FPGA VGA card: Verilog and timings

The article describes the implementation of a VGA video card on FPGA based on Ben Eater's principles. Covers Verilog modules for clock divider, counters, sync signals, and ROM with 2-bit color from BMP. Includes C++ image converter code.

VGA on FPGA: from Verilog to screen
Advertisement 728x90

Implementation of a VGA Graphics Card on FPGA Following Ben Eater's Principles

Developers familiar with HDL will appreciate the shift from simulations in Turing Complete to real hardware implementation. Drawing on Ben Eater's ideas from his video courses on discrete logic, this project builds a simple graphics card on an FPGA. It uses the Altera EP4CE15F23C8N chip with 15,000 logic elements, connected to VGA via standard 800x600@60Hz timings. The 50 MHz system clock is divided by 5 to produce 10 MHz, matching the original project.

The ClockDivider module handles the clock division:

module ClockDivider
( 
	input wire clk,
	output reg clk_out
);

	reg [2:0] DATA;

	always @(posedge clk) 
	begin 
		if(DATA == 3'd4)
		begin
			DATA <= 3'd0; 
			clk_out <= 1'd1;
		end
		else
		begin
			clk_out <= 1'd0;
			DATA <= DATA + 1'd1;
		end
	end
endmodule 

Verilog Features for FPGA Development

Verilog stands out from imperative languages due to its parallel execution. Variables fall into two categories: reg (which hold state) and wire (simple wires without storage). Assignment operators produce different hardware circuits:

Google AdInline article slot
  • assign for combinational logic:
wire a,b,c;
assign c = a & b;
  • always @(...) = (blocking) for sequential execution within a block.
  • always @(posedge clk) <= (non-blocking) for clocked registers:
reg A;
always @(posedge clk) 
begin
	A <= ~A;
end

Module parameters are defined using #() with default values and passed by name during instantiation. The `define directive is discouraged due to preprocessor issues.

Counters for VGA Timings

The horizontal counter tracks pixels in a line:

module HorizontalCounter
#(
	parameter ENDSTRING = 0
)
( 
	input wire clk,
	output reg [8:0] DATA
);

	always @(posedge clk ) 
	begin 
		if(DATA == ENDSTRING) DATA <= 9'd0;
		else DATA <= DATA + 1'd1; 
	end
endmodule 

The vertical counter increments at the end of each line:

Google AdInline article slot
module VerticalCounter
#(
	parameter ENDFRAME = 0,
	parameter ENDSTRING = 0
)
( 
	input wire clk, 
	input reg [8:0] Hor_count,
	output reg [10:0] DATA
);

	always @(posedge clk ) 
	begin 
		if(DATA == ENDFRAME) DATA <= 11'd0;
		else if (Hor_count == ENDSTRING)  DATA <= DATA + 1'd1; 
	end
endmodu

Signals like Hsync, Vsync, Hblank, and Vblank are asserted based on timings from tinyvga.com (front and back porch values are specified in case statements for H_count and V_count).

Generating Video Data

The visible area has a resolution of 100x75 pixels with 2-bit color depth (4 levels per RGB channel, packed into 6 bits). Images are prepared in Photoshop: resize to 100x75, convert to Indexed Color with a custom palette (0x00/0x55/0xaa/0xff), and export as 8-bit BMP.

A C++ utility converts the BMP into a Verilog memory array mem[]:

Google AdInline article slot
#include <windows.h>
#include <iostream>
// ... (full code parsera BMP with SetPixCollor for mappinga palitry in 6-bitnye values)

The SetPixCollor function encodes RGB values into bits (R2R1 G2G1 B2B1). The output mem_block.txt is loaded into an FPGA ROM, addressed using Hor_count[6:0] and V_count[6:0].

  • Load BMP and validate headers (bfType=0x4D42, biBitCount=8).
  • Read the RGBQUAD[256] palette.
  • Parse pixels and map them to 6-bit values.
  • Generate Verilog code along with an ASCII visualization.

Integration and Debugging

Assembly is done in Quartus Prime: modules are instantiated with parameters like ENDSTRING=799 and ENDFRAME=624. USB Blaster needs custom firmware for reliable operation. The Quartus simulator stands in for traditional debuggers, showing oscillograms of signals like clk_out, H_count, and RGB.

Key points:

  • Verilog's parallelism calls for non-blocking assignments when working with registers.
  • VGA timings are critical: 10 MHz pixel clock for 800x600@60Hz.
  • 2-bit color keeps the ROM simple (7500x6 bits) while maintaining gradients.
  • FPGA setup is economical: a minimal board with no extras + standard VGA cables.
  • Debugging happens entirely in simulation, avoiding runtime issues.

— Editorial Team

Advertisement 728x90

Read Next