返回首页

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 上构建了一个简单的显卡。它使用 Altera EP4CE15F23C8N 芯片,包含 15,000 个逻辑单元,通过标准的 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 语句中指定)。

生成视频数据

可见区域分辨率为 100x75 像素,颜色深度为 2 位(每个 RGB 通道 4 级,打包成 6 位)。图像在 Photoshop 中准备:调整大小至 100x75,转换为带自定义调色板(0x00/0x55/0xaa/0xff)的索引颜色,并导出为 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

继续阅读