3dfx Voodoo 显卡在 90 年代中期凭借 Glide API 与硬件加速纹理映射能力统治了消费级 3D 游戏市场。随着 FPGA 技术成熟与开源 RTL 工具链完善,用现代工具重建这一经典硬件成为可能。本文探讨如何利用 Yosys、nextpnr 等开源工具,从 Verilog 代码出发,最终在 FPGA 上实现一个可运行的 Voodoo 风格 3D 渲染流水线。
Voodoo 架构核心组件
3dfx Voodoo Graphics 采用固定功能的渲染流水线,这与现代可编程 GPU 有本质区别。理解其架构是重建工作的第一步。Voodoo 芯片内部主要包含三大功能块:纹理阶段负责从纹理内存中读取 texel 数据并进行过滤;深度缓冲阶段执行 Z 缓冲比较以确定像素可见性;颜色合成阶段则将纹理颜色与顶点光照结果进行混合。这三个阶段顺序执行,形成经典的固定渲染管线。
在寄存器传输级(RTL)实现时,需要将每个功能块建模为独立的时序逻辑模块。以纹理阶段为例,其核心是一个双端口 RAM 用于存储纹理数据,配合一个纹理地址生成单元(TAM)计算当前像素对应的纹理坐标。Voodoo 支持多种纹理格式包括 16 位 RGBA、1555 压缩格式以及 FXT1 硬件压缩格式。重建时可以选择从最基础的 RGBA 纹理格式开始,暂不实现压缩纹理支持以降低实现复杂度。
另一个关键组件是像素混合器,它需要支持 alpha 混合、雾化效果以及基于 alpha 测试的像素丢弃。Voodoo 的混合单元支持多种混合模式,包括源 alpha 混合、目标 alpha 混合以及加法混合。在 Verilog 中实现时,需要为每种模式设计独立的数据通路,并通过配置寄存器选择当前使用的混合模式。
开源 RTL 工具链选型
现代开源 FPGA 工具链已经足够成熟,能够支撑完整的 Voodoo 重建项目。核心工具包括 Yosys 用于 Verilog 合成、nextpnr 用于布局布线、IceStorm 用于比特流生成。这套工具链主要面向 Lattice iCE40 系列 FPGA,虽然资源有限,但足以运行一个简化的 Voodoo 核心。对于需要更大资源的实现,可以考虑使用 Xilinx Vivado 或 Intel Quartus 的免费版本。
选择目标 FPGA 时需要权衡资源与成本。iCE40-HX8K 是一个合理的起点,它提供 8K 逻辑单元、128KB RAM 以及 8 个 DSP 块。纹理存储是资源消耗大户,在 iCE40 上可以配置 512KB 的外部 SRAM 作为纹理内存,配合 FPGA 内部的 Block RAM 作为帧缓冲。对于更复杂的实现,Lattice ECP5 系列提供了更大的资源容量,同时仍然支持开源工具链。
合成过程中的时序约束尤为关键。Voodoo 原始硬件运行在 50MHz 时钟下,现代 FPGA 完全能够满足这一频率要求。但需要注意跨时钟域设计:主机接口可能使用 33MHz PCI 时钟,而内部渲染流水线使用独立的渲染时钟。在 Verilog 中需要使用异步 FIFO 进行跨时钟域数据传递,并在约束文件中明确定义各时钟域的关系。
Glide API 兼容层设计
Glide API 是 Voodoo 成功的关键因素之一,它提供了比 Direct3D 和 OpenGL 更简洁的 3D 渲染接口。重建 Voodoo 时有两种路径实现 API 兼容:一是在主机端实现一个 Glide 到现代 API 的转换层,将游戏调用转发到开源 Glide 实现如 dgVoodoo;二是直接在 FPGA 中实现一个精简的 Glide 前端,将 API 调用转换为内部的渲染命令流。
采用第二种方案时,需要在 FPGA 中实现一个命令解析模块,它能够解析 Glide 的三角形提交函数、纹理状态设置以及渲染状态切换。Glide 的命令格式相对简单,主要包括顶点数据打包、纹理参数设置和渲染模式配置。实现时可以在 FPGA 内部维护一个命令队列,主机通过 PCI 或 PCIe 接口将命令写入队列,后端渲染逻辑从队列中消费命令并执行。
对于不想深入硬件实现的开发者,一个更实用的方案是使用树莓派或类似的单板计算机运行开源 Glide 模拟器,通过高速接口将渲染数据传输到 FPGA 进行实际的光栅化处理。这种软硬件协同的架构可以降低硬件实现的复杂度,同时仍然保留 FPGA 加速的优势。
关键实现参数与监控要点
成功重建 Voodoo 需要关注若干工程化参数。首先是纹理缓存的命中率设计,建议将常用纹理保留在 FPGA 的 Block RAM 中,配置为 16KB 四路组相联缓存,替换策略采用 LRU。其次是渲染流水线的流水线深度,原 Voodoo 使用 4 级流水线,现代实现可以增加到 8 到 12 级以提升时钟频率,但需要增加流水线寄存器并处理 stall 逻辑。
帧缓冲的带宽预算需要仔细规划。假设目标分辨率为 640x480、刷新率 60Hz,每个像素需要 2 字节(16 位彩色),总带宽需求约为 36MB/s。考虑到深度缓冲写入与纹理读取,实际带宽需求会更高。建议使用双缓冲帧缓冲设计,一帧渲染同时显示上一帧,以避免显示撕裂。
比特流生成的监控同样重要。使用 open-source 工具链时,可以通过 Yosys 的统计报告检查资源利用率:逻辑单元使用应低于 85%、Block RAM 使用应低于 70%、DSP 块使用应低于 50%。如果资源接近上限,需要考虑简化功能或迁移到更大容量的 FPGA 设备。
资料来源
- VOGONS Wiki: https://www.vogonswiki.com/index.php/3dfx
- Wikipedia: Glide (API): https://en.wikipedia.org/wiki/Glide_(API)
- Project F FPGA Graphics: https://projectf.io/posts/fpga-graphics/