홈으로 돌아가기

FPGA VGA 카드: Verilog과 타이밍

이 기사는 Ben Eater의 원칙에 기반한 FPGA VGA 비디오 카드 구현을 설명합니다. 클럭 디바이더, 카운터, 동기 신호, BMP에서 2비트 컬러 ROM을 위한 Verilog 모듈을 다룹니다. C++ 이미지 변환기 코드 포함.

FPGA의 VGA: Verilog에서 화면까지
Advertisement 728x90

Ben Eater의 원리를 따른 FPGA 상 VGA 그래픽 카드 구현

HDL에 익숙한 개발자들은 Turing Complete 시뮬레이션에서 실제 하드웨어 구현으로의 전환을 높이 평가할 것입니다. Ben Eater의 이산 논리 비디오 강의 아이디어를 바탕으로 이 프로젝트는 FPGA에 간단한 그래픽 카드를 구축합니다. 15,000개의 로직 요소를 가진 Altera EP4CE15F23C8N 칩을 사용하며, 표준 800x600@60Hz 타이밍을 통해 VGA에 연결됩니다. 50 MHz 시스템 클럭을 5로 나누어 10 MHz를 생성하며, 원본 프로젝트와 맞춥니다.

ClockDivider 모듈이 클럭 분주를 처리합니다:

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 

FPGA 개발을 위한 Verilog 특징

Verilog는 병렬 실행으로 인해 명령형 언어와 차별화됩니다. 변수는 상태를 유지하는 reg와 저장 기능 없는 단순 wire 두 가지로 나뉩니다. 대입 연산자는 서로 다른 하드웨어 회로를 생성합니다:

Google AdInline article slot
  • assign은 조합 논리를 위한 것:
wire a,b,c;
assign c = a & b;
  • always @(...) = (블로킹)은 블록 내 순차 실행을 위한 것.
  • always @(posedge clk) <= (논블로킹)은 클럭드 레지스터를 위한 것:
reg A;
always @(posedge clk) 
begin
	A <= ~A;
end

모듈 파라미터는 #()를 사용해 기본값과 함께 정의되며, 인스턴스화 시 이름으로 전달됩니다. 전처리기 문제로 `define 지시어는 권장되지 않습니다.

VGA 타이밍을 위한 카운터

수평 카운터는 한 줄의 픽셀을 추적합니다:

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 

수직 카운터는 각 줄 끝에서 증가합니다:

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

Hsync, Vsync, Hblank, Vblank 등의 신호는 tinyvga.com의 타이밍에 기반해 단언되며 (H_count와 V_count의 case 문에서 front 및 back porch 값이 지정됩니다).

비디오 데이터 생성

가시 영역은 100x75 픽셀 해상도에 2비트 컬러 깊이(RGB 채널당 4레벨, 6비트로 패킹)를 가집니다. 이미지는 Photoshop에서 준비합니다: 100x75로 리사이즈, 사용자 팔레트(0x00/0x55/0xaa/0xff)로 Indexed Color 변환 후 8비트 BMP로 내보내기.

C++ 유틸리티가 BMP를 Verilog 메모리 배열 mem[]로 변환합니다:

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

SetPixCollor 함수가 RGB 값을 비트(R2R1 G2G1 B2B1)로 인코딩합니다. 출력 mem_block.txt가 FPGA ROM으로 로드되며, Hor_count[6:0]과 V_count[6:0]으로 어드레싱됩니다.

  • BMP 로드하고 헤더 검증 (bfType=0x4D42, biBitCount=8).
  • RGBQUAD[256] 팔레트 읽기.
  • 픽셀 파싱하고 6비트 값으로 매핑.
  • Verilog 코드와 ASCII 시각화 생성.

통합 및 디버깅

Quartus Prime에서 어셈블리: ENDSTRING=799, ENDFRAME=624 등의 파라미터로 모듈 인스턴스화. 안정적 동작을 위해 USB Blaster에 커스텀 펌웨어가 필요합니다. Quartus 시뮬레이터가 전통 디버거 역할을 하며, clk_out, H_count, RGB 등의 신호 오실로그램을 보여줍니다.

주요 포인트:

  • 레지스터 작업 시 Verilog의 병렬성은 논블로킹 대입을 요구합니다.
  • VGA 타이밍이 핵심: 800x600@60Hz를 위한 10 MHz 픽셀 클럭.
  • 2비트 컬러로 ROM을 단순화(7500x6 비트)하면서 그래데이션 유지.
  • FPGA 설정이 경제적: 최소 보드(추가 부품 없음) + 표준 VGA 케이블.
  • 디버깅은 시뮬레이션에서 완전히 이뤄져 런타임 문제를 피합니다.

— Editorial Team

Advertisement 728x90

다음 읽기