# Realizacja karty graficznej VGA na FPGA według zasad Bena Eatera
Deweloperzy zaznajomieni z HDL docenią przejście od symulacji w Turing Complete do rzeczywistej implementacji sprzętowej. Na podstawie idei Bena Eatera z jego kursów wideo z logiki dyskretnej powstaje prosta karta graficzna na FPGA. Wykorzystano układ Altera EP4CE15F23C8N z 15 000 elementami logicznymi, podłączony do VGA za pomocą standardowych timingów 800x600@60Hz. Częstotliwość systemowego zegara 50 MHz dzielona jest przez 5, aby uzyskać 10 MHz zgodne z oryginalnym projektem.
Modul ClockDivider zapewnia dzielenie częstotliwości:
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
Cechy Verilog w projektowaniu FPGA
Verilog różni się od języków imperatywnych równoległym wykonywaniem. Zmienne dzielą się na reg (zachowują stan) i wire (przewody bez przechowywania). Operatory przypisania generują różne schematy:
- assign do logiki kombinacyjnej:
wire a,b,c;
assign c = a & b;
- always @(...) = (blokujące) do sekwencyjnego wykonywania wewnątrz bloku.
- always @(posedge clk) <= (nieblokujące) do rejestrów z taktowaniem:
reg A;
always @(posedge clk)
begin
A <= ~A;
end
Parametry modułu ustawia się za pomocą #() z wartościami domyślnymi, przekazywanie przy instancjonowaniu odbywa się nazwowo. Dyrektywa `define nie jest zalecana ze względu na problemy preprocesora.
Liczniki do timingów VGA
Poziomy licznik śledzi piksele w linii:
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
Pionowy licznik inkrementuje się po zakończeniu linii:
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
Sygnały Hsync, Vsync, Hblank, Vblank aktywowane są według timingów z tinyvga.com (front/back porch podane w case dla H_count/V_count).
Generowanie danych wideo
Rozdzielczość obszaru widocznego — 100x75 pikseli, 2-bitowy kolor (4 poziomy na kanał RGB, kodowanie w 6 bitach). Obraz przygotowuje się w Photoshopie: zmiana rozmiaru do 100x75, Indexed Color z niestandardową paletą (0x00/0x55/0xaa/0xff), eksport BMP 8-bit.
Narzędzie w C++ konwertuje BMP na tablicę Verilog mem[]:
#include <windows.h>
#include <iostream>
// ... (pełny kod parsera BMP z SetPixCollor do mapowania palety na wartości 6-bitowe)
Funkcja SetPixCollor koduje RGB w bity (R2R1 G2G1 B2B1). Wyjście mem_block.txt używane jest w ROM FPGA do adresowania według Hor_count[6:0] i V_count[6:0].
- Wczytanie BMP, sprawdzenie nagłówków (bfType=0x4D42, biBitCount=8).
- Odczyt palety RGBQUAD[256].
- Parsowanie pikseli, mapowanie na 6-bit.
- Generowanie kodu Verilog i wizualizacja ASCII.
Integracja i debugowanie
Kompilacja w Quartus Prime: moduły instancjonowane z parametrami (ENDSTRING=799, ENDFRAME=624). USB Blaster wymaga niestandardowego firmware'u dla stabilności. Symulator Quartus zastępuje zwykłe debuggery — oscylogramy sygnałów clk_out, H_count, RGB.
Co ważne:
- Równoległość Verilog wymaga nieblokujących przypisań dla rejestrów.
- Timingi VGA są krytyczne: 10 MHz zegar pikselowy dla 800x600@60Hz.
- 2-bitowy kolor upraszcza ROM (7500x6 bitów), ale zachowuje gradienty.
- Oszczędność FPGA: minimalna płytka bez peryferii + osobne kable VGA.
- Debugowanie przez symulację, bez błędów w runtime.
— Editorial Team
Brak komentarzy.