将硬件描述语言直接翻译为游戏内电路网络,这一想法看似天马行空,却由 Ben C. 开发的 verilog2factorio 项目变为现实。该工具链成功将完整的 RV32IM RISC-V 处理器编译为 Factorio 2.0 中的可运行电路,为硬件编译与游戏引擎的交叉领域提供了极具参考价值的工程实践。
编译管线总体架构
verilog2factorio 的编译管线采用两阶段架构,前端以 Yosys 为核心完成 Verilog 到逻辑网的转换,后端则由 Rust 实现因子化布局布线。整个流程接收标准 Verilog 文件,输出可直接导入游戏的 JSON 蓝图字符串。
第一阶段是前端编译。Yosys 作为开源综合工具,负责将 Verilog 代码解析为带有多位宽信号的 RTLIL 中间表示。这一阶段输出的并非细粒度比特级网络,而是混合了细粒度逻辑与粗粒度字级表示的混合网表。Yosys 的脚本系统允许设计者干预综合过程,通过修改 rtl.ys 和 mapping.ys 脚本调整综合策略,这对于优化 RISC-V 核心的乘法器和除法器单元尤为关键。
第二阶段是后端处理。Rust 程序读取 Yosys 输出的 JSON 格式 MappedDesign,进行图划分、布局规划与物理实现。最终产物是 Factorio 蓝图,可通过游戏的导入功能直接加载到工厂中运行。
前端综合:Yosys 与 Verilog 解析
Yosys 在前端承担着核心的综合任务。标准综合流程首先对 Verilog 进行词法分析、语法解析与语义检查,生成抽象语法树。随后进行寄存器传输级综合,将行为级描述转换为由触发器、逻辑门与多路选择器组成的网表。
对于 RISC-V 处理器这类复杂设计,Yosys 的综合结果直接影响后端实现的效率。项目作者在 RISC-V 核心的实践中发现,原始设计来自 Ultraembedded 的 RV32IM 实现,但原始的乘除法单元直接综合效率不佳。通过调整 Yosys 综合脚本,对乘法器采用移位加算法重构、对除法器采用试商法实现,最终生成了更适用于 Factorio 电路结构的网表。
综合脚本的典型调用流程如下:先执行 v2f 生成 rtl.ys 和 mapping.ys 脚本,然后分别运行 yosys -s rtl.ys 进行 RTL 综合,yosys -s mapping.ys 进行工艺映射,最后将生成的 * rtl_map.json 回传给 v2f 完成后端布局。这一分离设计允许开发者干预综合过程,平衡面积与性能。
图划分与布局策略
将综合后的逻辑网表映射到 Factorio 的组合器网络,面临两个核心挑战:如何将大规模逻辑图划分为适合游戏渲染的分区,以及如何在二维网格上高效放置组合器并完成信号布线。
图划分采用基于 Metis 的层次化划分算法。综合产生的网表被建模为图结构,顶点代表组合器或触发器,边代表信号连接。划分目标是最小化分区间的信号穿越,同时保持各分区的规模在可接受范围内。对于 RISC-V 处理器这样的设计,划分结果直接影响游戏中的可读性与布线难度。作者在实践中发现,RISC-V 核心仅需手动放置少量变电站即可覆盖全部分区。
物理布局采用启发式图遍历算法。遍历过程中需要同时优化两个目标:最小化铜线使用数量、确保统一的电力分配网络。Factorio 的电力系统通过变电站与电线杆覆盖区域供电,布局算法首先检查每个组合器是否被电力覆盖,对未覆盖区域放置新的变电站以最大化覆盖范围。随后进行基于启发式的图遍历,尝试在满足单电力网络约束的前提下最小化布线长度。这一过程中可能需要人工干预调整,但对于 RISC-V 核心,手动调整的变电站数量仅有个位数。
仿真与验证机制
在导入游戏前验证设计正确性,是编译管线的重要环节。verilog2factorio 提供了内置仿真器,支持在代码环境中快速迭代,而无需反复导入游戏进行调试。
仿真器采用周期精确模型,对每个时钟边沿计算组合逻辑输出与寄存器更新。仿真结果可生成波形文件,与传统硬件仿真流程一致。测试用例覆盖基本逻辑门、触发器、寄存器等基本构建块。对于 RISC-V 处理器,仿真验证确保了指令取指、译码、执行、访存、写回各阶段的正确性,随后才导入游戏运行 hello_world 程序。
仿真结果还可与物理布局结合,生成带有时序标注的 SVG 渲染图。每个组合器在 SVG 中标注输入输出信号,开发者悬停即可查看具体数值,这种可视化方式极大提升了调试效率。
交叉编译与程序加载
RISC-V 处理器在 Factorio 中运行,需要配套的软件工具链支持。项目采用 GNU RISC-V 交叉编译器,构建 freestanding RV32IM 目标代码。交叉编译环境通过 riscv-gnu-toolchain 标准安装,Makefile 与构建脚本位于 top_v2f 子目录。
程序加载通过内存映射实现。Factorio 中的 RAM 被建模为可读写组合器阵列,通过地址译码与数据选择器连接到 RISC-V 核心的访存接口。系统设备定义见 sys_device.h,包括 UART 输出、显示缓冲区等外设。hello_world 程序通过 UART 输出字符,在游戏的组合器网络中显示为可见信号。
处理器频率受限于 Factorio 游戏的更新周期。每个游戏刻 ticks per second,组合器逻辑在每刻计算一次。对于复杂 RISC-V 核心,实际运行频率远低于传统 FPGA 或 ASIC,但足以演示完整处理器功能。
工程参数与优化建议
基于项目实践,以下参数与策略对成功编译具有重要参考价值:
综合阶段,建议保持 Verilog 可综合子集,避免使用延迟控制、强度指定等不可综合特性。对于 RISC-V 乘除法单元,手工优化综合脚本优于依赖自动综合。
图划分阶段,分区规模宜控制在数百个组合器以内。过大的分区增加布局复杂度,过小的分区则导致分区间接口过多。
布局阶段,电力覆盖检查应作为首要步骤。变电站覆盖半径约为 10 格,布局前需预估整体规模并预留变电站放置空间。
仿真阶段,建议先在小规模模块验证逻辑正确性,再逐步集成到完整系统。仿真速度远快于游戏内调试,是首选的验证手段。
小结
verilog2factorio 项目展示了硬件描述语言编译到游戏引擎的完整技术路径。通过 Yosys 完成前端综合、Rust 实现后端布局、Factorio 组合器网络承载逻辑,成功在游戏中运行完整 RISC-V 处理器。这一工程实践为硬件编译器开发、游戏引擎扩展、异构计算探索提供了独特的参考样本。
资料来源:GitHub 仓库 ben-j-c/verilog2factorio 及相关项目文档。