名作ゲーム「DOOM」を、市販のCPUではなく、自分たちで設計したCPUで動かした——。そんなプロジェクトを、ArmaanさんとLiamさんの2人組が公開しています。CPUは論理ゲート(AND・ORといった回路の最小部品)のレベルから自分たちで設計したもの。それをFPGAという「中身の回路を書き換えられるチップ」に載せ、キーボードや映像出力までそろえて、実際にゲームをプレイできるところまで持っていきました。
最初に動いたときの速度は、わずか0.7fps。1コマ描くのに1.4秒かかる、紙芝居のような状態でした。そこから改良を重ねて15〜20fpsまで引き上げ、いまでは「普通に遊べて、けっこう楽しい」と本人が言うレベルに到達しています。動作の様子を投稿した9秒の動画は、200万回以上再生されたそうです。

「DOOMを動かす」というハッカーの伝統
DOOMは1993年にid Softwareが発売したシューティングゲームで、現代のFPS(一人称視点のシューティング)の原型を作った作品です。そして発売から30年以上たった今、この古いゲームには少し変わった役割があります。「DOOMは何でも動く」という合言葉のもと、電子工作の世界では、ありとあらゆる機械にDOOMを移植するのが伝統芸のようになっているのです。これまでにマイコンやプリンターはもちろん、キッチンタイマーのような妙なものでも動かされてきました。
ただ、今回のプロジェクトはひと味違います。「変わった機械の上で動かした」のではなく、動かすためのCPUそのものを自分たちで作ったからです。
論理ゲートから設計した自作CPU
2人が作ったのは、RISC-V(リスクファイブ)という公開されている命令セットに沿ったCPUです。命令セットとは「このCPUはこういう命令を理解します」という取り決めのことで、RISC-Vは誰でも無料で使える仕様として公開されています。ただし仕様が公開されているのは「何をすべきか」だけで、「どう作るか」は自分で考えるしかありません。2人はその中身を、論理ゲートの組み合わせのレベルから設計しました。
設計したCPUは、FPGAに書き込んで動かします。FPGAは内部の配線を何度でも書き換えられるチップで、自分で設計した回路を実際のハードウェアとして動かせるのが特徴です。ソフトウェアでCPUの動きを真似る「エミュレーター」とは違い、設計した回路そのものが電気信号で動きます。
いきなりDOOMに挑んだわけではありません。まずはPong(1970年代の卓球ゲーム)や、マンデルブロ集合(複雑な模様を描き出す数学の図形)を表示する自作プログラムで、CPUが正しく動くことを確かめました。そのうえで、「みんなが何にでも移植するあのゲーム」に照準を合わせたわけです。
立ちはだかったメモリの壁
DOOMを動かすうえで最大の問題はメモリでした。それまでの設計では、FPGAに内蔵された高速メモリ(BRAM)だけを使っていて、その容量は1メガバイト未満。ところがDOOMの無料版のデータファイルだけで14メガバイトあります。プログラムを動かすための作業領域を含めれば、まったく足りません。
そこでArmaanさんは、ボードに載っているDDR3メモリ(パソコンのメインメモリと同じ種類の外付けメモリ)をCPUにつなぐ作業に取り組みました。これが想像以上の難物でした。内蔵メモリは「読みたい」と言えば必ず1サイクル後に答えが返ってくる素直な相手ですが、DDR3は返事が遅いうえに、待たされる時間が毎回違います。そのまま使うとCPU全体がメモリ待ちで止まってしまうため、よく使うデータを内蔵メモリに写しておく「キャッシュ」を、命令用とデータ用の2系統作りました。さらに、2つのキャッシュが同時にメモリを使おうとしたときに交通整理をする回路も必要になりました。
シミュレーション上では完璧に動くのに、実機ではなぜかDOOMだけが動かない——そんな不可解なバグにも悩まされています。原因は、DOOM側が「使う前のメモリは0で埋まっている」と想定していたこと。シミュレーションではたまたまその通りでしたが、実機のメモリの初期状態はでたらめな値です。シミュレーション側の条件を実機に寄せてみて、ようやく正体を突き止めたそうです。
キーボードとHDMIも手作りの周辺回路
CPUが動いても、それだけではゲームはできません。画面に映す、キー入力を受け取る、時間を計る——そうした周辺の仕組みも一式必要です。映像は、もともと作ってあった映像出力回路を4096色に拡張してHDMI端子につなぎました。ゲーム内の時間管理に使うハードウェアタイマーも実装しています。
意外と苦戦したのがキーボードでした。当初はFPGAボードのUSB端子にキーボードをつなぐ計画でしたが、ボードのUSB端子が電力を供給しない仕様だったらしく、キーボードが動きません。最終的には、ノートパソコンで押されたキーをシリアル通信でFPGAに転送するという方法で切り抜けました。本人いわく「完璧ではないけれど、動く」。工作あるあるの、実に正直な着地です。
0.7fpsから20fpsへの最適化
こうしてDOOMは動きました。ただし0.7fpsで。ここで「目標は達成した」と締めるつもりだったそうですが、線形代数の勉強をしようと机に向かったはずが、気づけばCPUの最適化を始めていたとのこと。ここからの改善の積み重ねが、このプロジェクトのもうひとつの見どころです。
まずCPUの動作クロックを100MHzから125MHzに引き上げ、命令を読み込む回路の無駄(1命令に2回問い合わせていた)を直して約2.5fpsに。次に掛け算を専用回路で処理する命令を追加して3.5fpsへ。さらに、画面データの受け渡しでメモリを圧迫していた処理を、映像回路のメモリへ直接書き込む方式に変え、キャッシュの応答も1サイクル速くして6.7fpsまで来ました。
最後の一押しはハードウェアではなく、コンパイラー(プログラムをCPU向けの命令に翻訳するソフト)の設定でした。それまでDOOMは最適化を一切しない設定で翻訳されていて、最適化を有効にすれば速くなるはずが、なぜか毎回クラッシュ。何時間もの調査の末に分かった原因は、タイマーを読み取る部分のプログラムに「この値は勝手に変わるので省略しないで」という指定(volatile)を付け忘れていたことでした。コンパイラーが「誰も書き込んでいない値だから読む必要なし」と判断して、タイマーを読む処理ごと削ってしまっていたのです。これを直して最適化を効かせた結果、15〜20fpsに到達。0.7fpsから約25倍の高速化です。

2人は次の目標として、命令を順番どおりでなく効率よく並べ替えて実行する「アウトオブオーダー実行」という、市販のCPUにも使われている本格的な仕組みの実装を進めています。これで30fps超えを狙い、その先には簡単なGPUを作って「Quake 2」を動かす構想もあるそうです。
「ゼロから」の意味と留意点
元記事のタイトルには「built from scratch(ゼロから作った)」とありますが、ここは正確に補足しておきます。2人が作ったのは、FPGAの上で動くCPUの設計です。トランジスタを並べて物理的なチップを製造したわけではなく、既製品のFPGAチップに自分たちの回路設計を書き込んでいます。実際、元記事のコメント欄でも「FPGAならゼロからとは言えないのでは」という指摘が出ていました。
とはいえ、命令の取り込みから実行、メモリ管理、周辺機器とのやり取りまで、CPUの論理をすべて自分たちで設計したのは事実です。市販のCPUコアの設計データを流用したわけでもありません。「物理的なチップ製造以外は全部自作」と捉えるのが実態に近いでしょう。
また、fpsの数値は製作者本人の報告に基づくもので、測定条件の詳細までは公開されていません。今後の30fps達成も、あくまで目標段階です。
出典・もっと知りたい人へ
今回の話は、Hackadayの記事と、製作者Armaan Gomesさん本人のブログをもとにしています。本人のブログには、キャッシュの設計やデバッグの経緯がさらに詳しく書かれていて、実際にプレイしている動画へのリンクもあります。CPU設計に興味がある人には、前段のプロジェクト(単純な構成のCPUから段階的に育てていく過程)の記事もおすすめです。


コメント