Zurück zur Startseite

FPGA VGA-Karte: Verilog und Timings

Der Artikel beschreibt die Implementierung einer VGA-Videokarte auf FPGA basierend auf Ben Eaters Prinzipien. Umfasst Verilog-Module für Clock Divider, Zähler, Synchronsignale und ROM mit 2-Bit-Farbe aus BMP. Enthält C++-Code für den Bildkonverter.

VGA auf FPGA: von Verilog zum Bildschirm
Advertisement 728x90

# Implementierung einer VGA-Grafikkarte auf einem FPGA nach Ben Eaters Prinzipien

Entwickler, die mit HDL vertraut sind, werden den Wechsel von Simulationen in Turing Complete zu einer realen Hardware-Implementierung zu schätzen wissen. Basierend auf Ben Eaters Ideen aus seinen Videokursen zu diskreter Logik baut dieses Projekt eine einfache Grafikkarte auf einem FPGA auf. Es verwendet den Altera EP4CE15F23C8N-Chip mit 15.000 Logikelementen, der über Standard-VGA-Timings 800x600@60Hz mit VGA verbunden ist. Die 50-MHz-Systemtaktfrequenz wird durch 5 geteilt, um 10 MHz zu erzeugen, passend zum Originalprojekt.

Das ClockDivider-Modul übernimmt die Taktteilung:

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 für die FPGA-Entwicklung

Verilog unterscheidet sich von imperativen Sprachen durch seine parallele Ausführung. Variablen gibt es in zwei Kategorien: reg (die Zustände speichern) und wire (einfache Leitungen ohne Speicher). Zuweisungsoperatoren erzeugen unterschiedliche Hardware-Schaltungen:

Google AdInline article slot
  • assign für kombinierende Logik:
wire a,b,c;
assign c = a & b;
  • always @(...) = (blockierend) für sequenzielle Ausführung innerhalb eines Blocks.
  • always @(posedge clk) <= (nicht-blockierend) für getaktete Register:
reg A;
always @(posedge clk) 
begin
	A <= ~A;
end

Modulparameter werden mit #() inklusive Standardwerten definiert und bei der Instanziierung namentlich übergeben. Die `define-Direktive wird aufgrund von Preprocessor-Problemen abgeraten.

Zähler für VGA-Timings

Der horizontale Zähler zählt die Pixel in einer Zeile:

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 

Der vertikale Zähler inkrementiert am Ende jeder Zeile:

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

Signale wie Hsync, Vsync, Hblank und Vblank werden basierend auf Timings von tinyvga.com assertiert (Front- und Back-Porch-Werte sind in Case-Statements für H_count und V_count festgelegt).

Erzeugung der Videodaten

Der sichtbare Bereich hat eine Auflösung von 100x75 Pixeln mit 2-Bit-Farbtiefe (4 Stufen pro RGB-Kanal, gepackt in 6 Bits). Bilder werden in Photoshop vorbereitet: auf 100x75 vergrößern, in Indizierten Farben mit benutzerdefinierter Palette (0x00/0x55/0xaa/0xff) umwandeln und als 8-Bit-BMP exportieren.

Ein C++-Tool konvertiert das BMP in ein Verilog-Speicherarray mem[]:

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

Die SetPixCollor-Funktion kodiert RGB-Werte in Bits (R2R1 G2G1 B2B1). Die Ausgabe mem_block.txt wird in ein FPGA-ROM geladen, adressiert mit Hor_count[6:0] und V_count[6:0].

  • BMP laden und Header validieren (bfType=0x4D42, biBitCount=8).
  • RGBQUAD[256]-Palette lesen.
  • Pixel parsen und auf 6-Bit-Werte mappen.
  • Verilog-Code zusammen mit einer ASCII-Visualisierung erzeugen.

Integration und Debugging

Die Montage erfolgt in Quartus Prime: Module werden mit Parametern wie ENDSTRING=799 und ENDFRAME=624 instanziiert. USB Blaster benötigt eine angepasste Firmware für zuverlässigen Betrieb. Der Quartus-Simulator ersetzt herkömmliche Debugger und zeigt Oszillogramme von Signalen wie clk_out, H_count und RGB.

Wichtige Punkte:

  • Verilogs Parallelität erfordert nicht-blockierende Zuweisungen bei der Arbeit mit Registern.
  • VGA-Timings sind entscheidend: 10-MHz-Pixel-Takt für 800x600@60Hz.
  • 2-Bit-Farbe hält das ROM einfach (7500x6 Bits), bei Erhalt von Verläufen.
  • FPGA-Setup ist günstig: Minimalboard ohne Extras + Standard-VGA-Kabel.
  • Debugging erfolgt vollständig in der Simulation, ohne Laufzeitprobleme.

— Editorial Team

Advertisement 728x90

Weiterlesen