
FPGAの専門家ではない私が、AI駆動開発でGPUを作ってみた
まさか本当に動くとは思わなかった
ここ最近、Tang Nano 9Kという小さなFPGAボードを使って、KAGURA-HDという2D GPUを作っています。
GPUと言っても、NVIDIAのような3D GPUではありません。
640×360・16色のフレームバッファを持ち、矩形描画やBLIT、ダブルバッファ、ハードウェアカーソルを備えた、どちらかというと昔のPCのグラフィックチップに近いものです。
これを1280×720・60HzのHDMIへ出力しています。
ちなみに私はFPGAの専門家ではありません。
組込み開発や電子回路についてはそれなりに経験があるのですが、FPGAのRTL設計、特に配置配線やタイミング収束については専門外です。
なので、今回の開発ではかなりの部分をAIに任せています。
で、何が面白いかというと……。
これが本当にTang Nano 9Kに収まって、実機で動いてしまったんですよね。

そもそものきっかけ
もともとはKAGURA-9という、タイル・スプライト方式の映像システムを作っていました。
RP2040とTang Nano 9Kを組み合わせて、昔のゲーム機のVDPのようなものを実現するプロジェクトです。
ところが、開発を進めているうちにGUIや日本語入力まで動かすようになり、だんだん普通のコンピュータに近づいてきました。
そこで考えたのが、
「これ、フレームバッファ方式にしたらGUIを作るのがもっと楽になるのでは?」
ということです。
640×360・4bppなら1画面115,200バイト。
PSRAMに2画面確保しても230,400バイトです。
内蔵PSRAMからFPGAが自律的に画面を読み出し、RP2040は描画命令を送るだけ。
理屈としては成立しそうです。

ただ、FPGA側のリソースには当然限界があります。
今回使っているGW1NR-9Cは、CLSが4,320個しかありません。
そこにHDMI、Framebuffer、BLIT、FILL、パレット、カーソル、さらには6ch音源とPCMまで入れる。
いや、さすがに無茶では?
と思いながら開発を始めました。
AIにRTLを書かせれば解決する……わけではない
最近のAIはSystemVerilogも普通に書いてくれます。
それどころかテストベンチを作り、Verilatorで検証し、失敗すれば修正するところまでやってくれます。
ただ、ここで厄介なのがFPGAです。
RTLが論理的に正しくても、配置配線が成功するとは限りません。
実際、KAGURA-HDでは何度もタイミング違反が発生しました。
特にPSRAMから表示用ラインRAMへデータを転送する経路は厄介で、当初の設計は大量のレジスタを使っていました。
そこで設計を見直し、512bitの受信データを保持するのではなく、32bit単位で直接ラインRAMへ書き込む方式へ変更。
結果として460bit分のレジスタを削減できました。
これが効いて、ようやく内部STAが収束しました。
ちなみに、ここで重要だったのはAIの提案をそのまま採用することではありません。
独立した設計レビューを行い、複数案を比較して、見込みのない案は捨てています。
AIが書いたRTLだから正しい、なんてことは全然ないんですよね。
そしてPSRAMが壊れる
さて、ようやくFPGAに収まったので実機試験を開始しました。
ところが、4バイトを書き込んで読み戻すだけの試験で失敗します。
期待値:
ED 0F 21 43実測:
0D 23 21 43先頭2バイトが化けています。
最初は当然PSRAM側を疑いました。
クロック位相か、READ latencyか、それともHyperRAMのアドレス生成か。
実際、32バイト境界を跨ぐとデータが回り込むという別の問題も見つかっていたので、ますますPSRAMが怪しく見えます。
しかし、調査を進めていくと妙なことが分かりました。
PSRAMを一切使わない診断回路でもデータが化ける。
えっ、そっち?
ということで、Hostから受信したデータをFPGA内部で追跡する診断回路を作りました。
すると、CSRまでは正しく受信しているのに、upload用Block RAMの出力で値が変わっていることが判明しました。
試しにBlock RAMをFFへ置き換えると正常になります。

どうも、32bit幅のBlock RAMに対するバイト単位書込みの使い方が怪しい。
メーカー資料を確認すると、対象デバイスでその構成が正式にサポートされているという根拠も見つかりません。
シミュレーションでは普通にPASSしていたんですけどね……。
そこで部分書込みをやめ、32bit全体をRead-Modify-Writeする方式へ変更しました。
今度は修正した回路が収まらない
これで解決!
……とはならないのがFPGAの面白いところです。
最初の直接RMW方式は配線に失敗。
次の共有RMW方式は配線できたものの、Setup slackが−2.416ns。
つまりタイミング違反です。
そこで完成した32bit wordをCSR側で保持する方式に変更したところ、ようやく収束しました。
最終結果は以下のとおりです。

いや、よく収まったな……。
しかもFILL、BLIT、ダブルバッファ、カーソル、音声などの機能は基本的に削っていません。
ロジックを増やせばよいわけでも、減らせば必ず速くなるわけでもない。
RTLの構造によって、実際の配置配線結果が大きく変わるわけです。
実機で検証してみた
ここまで来たので、修正したBitstreamを実機で検証しました。
そして、メモリ試験67件がPASS。
かつて失敗していた32バイト境界跨ぎ、64バイト転送、各種オフセット、マスク処理なども通過しています。
独立guardを含めた1,164回、8,736バイトの比較でも不一致はありませんでした。
さらに、Framebufferの描画試験5件もPASS。
FILL、重複BLIT、ページ切替、パレット変更、ハードウェアカーソルが動作しています。
HDMIキャプチャでも確認できました。

つまり、
少なくともFramebuffer GPUとしての主要機能は、実機で動いたわけです。
もっとも、まだ完全ではありません。
音声試験では左右のサンプル一致率が事前の判定基準を満たさず、そこで試験を停止しています。
音声取得装置側の問題なのか、FPGA側の問題なのかはまだ切り分けられていません。
競合負荷や長期安定性も未評価です。
そのため、完成品としてReleaseできる状態ではありません。
それでも、4,320 CLSのFPGAでここまで実現できたのは、個人的にはかなり驚きです。
今回感じたAI駆動開発の可能性
今回の開発を通して感じたのは、AIの価値は単にRTLを生成できることではない、ということです。
むしろ、従来なら探索コストが高すぎて諦めていた設計案を、何度も試せるようになることに価値があるのではないかと思っています。
RTLを変更し、テストベンチを整備し、回帰試験を実施する。
合成・配置配線・STAの結果を確認して、ダメならまた設計を変更する。
さらに実機で問題が起きたら、原因を絞り込むための診断回路を別に作る。
これらの作業は、人間だけでやろうとすると相当な負担です。

AIがそこを担当してくれるなら、設計者はより多くの候補を検討できます。
ただし、重要なのは人間側の判断です。
今回も、見込みのない実装案を捨てる判断や、検証で何を合格条件とするかは非常に重要でした。
AIは普通に間違えますし、シミュレーションがPASSしても実機では動かないことがあります。
だからこそ、数字と実測結果で判断する必要があります。
AIによってFPGAの物理的な限界が変わるわけではない。でも、そこへ到達するために必要な試行錯誤のコストは大きく変えられるかもしれない。
今回のKAGURA-HDは、そのひとつの事例になったのではないかと思います。
おわりに
とりあえず、KAGURA-HDはFramebuffer GPUとして動くところまで来ました。
次は、この上で実際にGUIを動かしたいと思っています。
KAGURA-9で作ったデスクトップ環境を移植するのも面白そうですし、640×360・16色なら昔のPCのようなアプリケーションも作れそうです。
というか、Tang Nano 9Kでここまで出来るなら、もう少しいろいろ試したくなりますね。
FPGAの専門家ではない私が、AIを使ってここまで作れてしまった。
もちろん、専門家ならもっと効率のよい設計ができるのかもしれません。
それでも、以前なら途中で諦めていたかもしれない構成が、実際に動いている。
いやしかし、これ成立するんですね(笑)。