两名开发者从逻辑门开始,设计了一颗五级流水线CPU,再把它部署到FPGA上跑。他们给这颗处理器接上DDR3内存、自制的指令缓存和数据缓存,又装了显示、键盘、定时器等外设,最后把1993年发布的《DOOM》移植了上去。两周前,这台“玩具处理器”第一次把游戏画面吐了出来。作者自己的说法是“crawled”——勉强爬起来,不是流畅运行。

这不是一次追求性能的突破。DOOM能在微控制器、烤面包机上运行早就不是新闻。这次真正的看点是:两人验证了自己从零搭建的CPU、内存系统和工具链,第一次扛住了一款成熟的商业级软件,这比自己写个Pong难得多。

从Pong到DOOM,考验的是系统全不全

在这次移植之前,这颗CPU只跑过作者自己写的小程序:Pong、曼德勃罗集分形。这些程序体积以几十KB计,逻辑简单,几乎碰不到边界情况。

DOOM不一样。它由id Software在1993年发布,基础共享版体积约14MB,涉及文件系统、渲染管线和大量分支跳转。这颗CPU原本可用的FPGA片上BRAM却不到1MB。

对比项此前:Pong/曼德勃罗现在:DOOM
程序体积几十KB约14MB(基础共享版)
逻辑复杂度简单,少分支完整渲染管线、文件系统、大量跳转
片上BRAM是否够用完全够用不到1MB,远远不够
从自写小程序到完整游戏 此前:自写程序 Pong 曼德勃罗集分形 几十KB 程序体积量级 现在:移植DOOM id Software · 1993年发布 14MB 基础共享版体积 原可用BRAM不足1MB

两个开发者把问题拆开做。一人研究乱序执行,想给流水线补上并行度;另一人扎进内存系统,解决“14MB的游戏怎么塞进不到1MB片上内存”这个问题。这不是多接一块内存芯片那么简单。

缺的不是缓存,是一整套内存系统

FPGA自带的BRAM延迟固定为1个周期,读写按字寻址。原来的流水线设计就是照着这个假设搭的。

DDR3完全不是这么回事。延迟可变,单次访问动辄要30到100个周期。如果每条指令都直接找DDR3要数据,CPU基本瘫痪。

为什么必须自建缓存 BRAM 直连 1 周期 缓存命中 约 2 周期 DDR3 直接访问 约 30–100 周期,视命中率与仲裁排队而定
存储层级访问延迟说明
BRAM直连1周期原设计假设,地址位宽固定
自制缓存命中约2周期单路直接映射,指令/数据缓存各2048行
DDR3直接访问30-100周期需仲裁排队,不能挂在每条指令上

团队自己造了两套缓存,单路直接映射,各挂2048行。取指和执行两个流水级可能同一周期抢内存,团队加了一个仲裁器排队处理,优先让数据缓存过队,避免死锁。

DDR3接口本身很难对付,团队借用了Xilinx的MIG(内存接口生成器),还要处理MIG时钟域和主处理器时钟域不同步的问题,靠时钟域交叉和FIFO缓冲接起来。原来那块板子的MIG始终跑不通,团队中途换成了朋友的64位总线板子继续做。

外设也是将就出来的。显示靠自制VGA控制器接HDMI;键盘本该走板子上的USB主机芯片,结果那个USB口不供电,插键盘没反应,最后改成用笔记本通过UART转发按键。

游戏移植借助了开源项目doomgeneric做适配层,省了不少事。真正麻烦的是调试——程序出错时分不清是DOOM代码的问题,还是CPU本身有毛病。

团队把游戏拆成一段段小程序逐步验证,才找出死循环、乱跳转、写回请求被吞、数据缓存覆盖指令数据等一堆错误。其中一些错误可以追溯到他们更早的单周期处理器,只是此前从没被真正踩到过。能暴露出潜伏多年的CPU错误,恰恰说明简单程序测试不够用,系统完整性只能靠足够复杂的软件来验证。

能跑起来,不等于能用

作者自己承认,这套单路直接映射缓存设计低效。缓存行只有4字宽,是为了迁就早期那块128位MIG接口,换板子后就成了历史包袱,没来得及重新优化。文中也没有给出主频、帧率这类可验证的性能数据,“crawled”这个词已经说明了跑起来的状态。

对CPU和FPGA学习者来说,这条路线值得照着走一遍:流水线、自制缓存、内存仲裁、时钟域交叉、外设集成,每一步都比单纯读教材有用。

对普通玩家来说,不用惦记这套东西能不能玩——键盘靠笔记本转发,USB供电都没解决,这是一个验证平台,不是一台游戏机。

接下来值得盯的,是团队提到的乱序执行能不能真正合入流水线,以及这套单路缓存会不会被重新设计成组相联结构,提高命中率。