FPGA 推論アクセラレータ / 中間報告

CPU からファブリックへ

小さな言語モデルを FPGA で速く動かす、という計画の現在地。 なぜ CPU では遅いのか、FPGA に移すと何が変わるのか、そして昨日それを初めて実測できた話。

2026-09-05 実測: PYNQ-Z1 (XC7Z020) 前段: ADRFT1 (MPFS095T)
続きがあります — 2026-09-08 追記

この資料は 2026-09-05 時点の理解です。3日後に測り直したところ、 ここに書いた 「PL は DDR の 74%」という読みが誤りだったことが分かりました。 74% という数字は DDR ではなく、その手前にあるもっと低い天井に張り付いていたものです。 結論の向きは変わらず、正しい値は 93.6%(1.97 GB/s)でした。

経緯を残すため、この資料はそのままにしてあります。何がどう変わったかは 「二つの天井」を参照してください。 本文中の該当箇所にも印を付けてあります。

この計画が目指していること

やりたいのは 「量子化した小さめの推論モデルを、FPGA で作った自作の演算回路で動かす」ことです。 GPU を買えば速いのは分かっていて、目的は自分で作ることのほうにあります。

ただ、ここまでの調査と実測で、当初の見立てが2回ひっくり返りました。 この資料は、いま何が分かっていて、次に何をするのかを図で共有するためのものです。

図1 — そもそもこのチップは何なのか

1つのチップの中 PS(プロセッサ側) Cortex-A9 ×2 650 MHz Linux が動いている 出荷時から完成している PL(ファブリック) LUT 53,200 / DSP 220個 中身は空っぽ 回路を自分で作って入れる GP0 PLを制御する細い道 DDR3 メモリ 512 MB 道幅の上限 2.1 GB/s(理論値) CPU のメモリアクセス HP0 = 昨日開通させた道
PS と PL は同じチップに同居し、同じ DDR メモリを共有している。 PS は完成品の CPU。PL は「空っぽの回路の土地」で、そこに何を建てるかは自分で決める。 PL から DDR へ行く道が HP ポート(High Performance)で、昨日その道を1本開通させて、 どれだけの太さがあるのかを測った。

ここで大事なのは PL は最初から空っぽ だということです。DSP が 220 個載っていますが、 配線されていないので何もしません。何をする回路にするかを設計して、 ビットストリーム(回路の設計図を焼いたファイル)を作って流し込む、という手順を踏みます。

そして PL は勝手に DDR にアクセスできません。HP ポートという門が用意されていて、 そこを通る回路を自分で組んで初めてメモリに手が届きます。 ADRFT1 で行き詰まったのは、まさにこの門が塞がれていたからでした。

図2 — ファブリックの素材、LUT

入力 6 ビット LUT 64個の答えを持つ早見表 中身は書き換えられる 出力 1 ビット 表の中身を変えれば別の回路になる(2入力の例) AND 0 0 → 0 0 1 → 0 1 0 → 0 1 1 → 1 OR 0 0 → 0 0 1 → 1 1 0 → 1 1 1 → 1 XOR 0 0 → 0 0 1 → 1 1 0 → 1 1 1 → 0 回路そのものは同じ。違うのは表に書かれた答えだけ。 6入力なら行数が 64 になるだけで、理屈は変わらない。 この LUT が 53,200 個、フリップフロップ(1ビットの記憶素子)が 106,400 個。これがファブリックの素材。
LUT は「書き換えられる早見表」で、表の中身しだいで何の論理回路にもなる。 入力 6 ビットに対する答えを 64 個ぶん持っていて、その 64 ビットを書き換えるだけで AND にも XOR にも加算器にもなる。 ビットストリームとは、全 LUT の表の中身と全配線の指定を焼いたファイルのこと。 これを読み込ませた瞬間に、空っぽだったファブリックが目的の回路になる。

実例として、昨日作った回路が実際にどれだけ使ったかを載せます。

資源使用全体割合役割
LUT4,36053,2008.2%制御・判定・アドレス計算など何でも
フリップフロップ6,199106,4005.8%値を1クロック覚える
Block RAM11.51408.2%まとまったデータの一時置き場
DSP02200.0%掛け算専用。今回は計算をしていないので未使用

DSP がちょうど 0 なのが分かりやすいと思います。あの回路はデータを運ぶだけで計算をしないので、 掛け算器を1個も使っていません。LUT は何でも屋、DSP は掛け算専門、という分業になっています。

掛け算は LUT でも作れますが、まともな幅の掛け算器を LUT で組むと数百個を食って遅くなります。 だから最初から焼き込んである専用回路が用意されている、というわけです。 このチップの DSP は 25ビット × 18ビットの符号付き掛け算器に 48 ビットの累算器が付いたもので、 INT8 を扱うにはかなり大きすぎます。

三値化すると DSP は要らなくなる

重みが +1 / 0 / −1 の三値になると、掛け算は「足す・何もしない・引く」の3択に変わります。 これは掛け算ではないので DSP ではなく LUT で作ります。 つまり三値の道に進むと、効いてくる予算は DSP の 220 個ではなく LUT の 53,200 個のほうです。 ざっと見積もって必要なのは 1,000〜1,500 個程度なので、LUT も余ります。 結局、足りないのは帯域だけ、という結論は変わりません。

図3 — なぜ「帯域」が壁になるのか

DDR 重み 1億3500万個 1トークンごとに全部読む 0.77 GB/s この太さが上限 PL の掛け算器(DSP)220個 ■ = 実際に働けるぶん(約8個) 残り 212 個は待っているだけ INT8 なら、重み 1 バイトにつき掛け算は 1 回きり → 掛け算器を増やしても、パイプが細ければ何も速くならない
推論の速度を決めているのは、演算器の数ではなくメモリからの読み出し速度。 1トークン作るのに全部の重みを1回ずつ読む必要があり、INT8 では読んだ 1 バイトに対して掛け算は 1 回しか発生しない。 つまり「1秒間に何バイト読めるか」がそのまま「1秒間に何回計算できるか」の上限になる。 220 個の DSP のうち、この帯域で養えるのは約 8 個分しかない。 (2026-09-08 追記: 帯域が 1.97 GB/s と確定したので、この数は約 14 個に更新された。 全体の 6%。「演算器は桁で余る」という話の向きは変わらない。)

これが、この計画で最初にひっくり返った見立てです。 当初は「DSP が何百個も空いているから速くなるはず」と考えていました。実際には DSP はまったく足りていて、足りないのはメモリからの道幅のほうでした。

ここから出てくる結論

速くしたければ、重み1個あたりのバイト数を減らすのが一番効きます。 INT8(1バイト/重み)を三値化(約0.2バイト/重み)にすれば、同じ道幅で 5 倍の重みを流せます。 しかも三値なら掛け算が足し算・引き算で済むので、DSP すら要らなくなります。 BitNet b1.58 のようなモデルがこのボードに向いている理由がここにあります。

図4 — 昨日測ったのは「道をどれだけ使えるか」

DDR の道幅(理論上の上限)= 2.1 GB/s 2.1 CPU 2コア 読み出しの実測 0.70 GB/s = 33% PL(昨日) DMA の実測 1.54 GB/s = 74% 参考: ADRFT1 CPU 4コアで 6.4 GB/s の道の 13% しか使えていなかった
同じボード、同じ DDR に対して、CPU と PL がそれぞれどれだけ引き出せたか。 CPU は 2 コア総出で 33%。PL は 1 本の DMA だけで 74%。 74% という数字は、リフレッシュやバンク切り替えの損失を考えるとほぼ実用上の上限で、 PL 側は DDR を使い切れているといえる。

「PL は同じことを 74% でやります」というのは、こういう意味でした。 同じメモリに対して、CPU は3割しか引き出せないが、PL は7割強を引き出せる。 ハードウェアで直接メモリを叩く回路のほうが、汎用 CPU よりずっと効率よくメモリを使えるということです。

2026-09-08 追記 — この節の 74% は誤りでした

1.54 GB/s という測定値そのものは正しいのですが、その解釈が違いました。 これは DDR の 2.1 GB/s に対する 74% ではなく、PL 側の「道幅」0.80 GB/s に対する 96%です。 AXI の経路は 64 ビット幅=1クロックあたり 8 バイトしか運べないので、 100 MHz では 8 × 100,000,000 = 0.80 GB/s が上限になります。 読みの 0.77 も書きの 0.77 も、DDR ではなくこちらの天井に当たっていました。

読み専用の回路に作り替えて測り直した結果、PL が DDR から読み出せる本当の上限は 1.97 GB/s(DDR の 93.6%)でした。CPU の 33% に対して 2.8 倍で、 「PL のほうが DDR を引き出せる」という結論はむしろ強まっています。 ただし HP ポートを2本使うことが条件で、1本では 53.7% までしか届きません。 詳細は 「二つの天井」へ。

そして ADRFT1 では、CPU が 13% しか引き出せていませんでした。 あのボードで「遅い」と感じていたものの正体は、DDR の性能ではなく CPU がその性能を引き出せていなかったことだったわけです。 「ファブリックに移す価値がある」という仮説が実測で裏付けられたというのは、この差を指しています。

図5 — 昨日作った回路は「何もしない回路」

PS(CPU) AXI DMA データを運ぶ係 FIFO ただの通り道 GP0 出て 戻る DDR メモリ 上限 2.1 GB/s HP0(PLとDDRをつなぐ門) 読み 0.77 GB/s 書き 0.77 GB/s 合計 1.54 GB/s = 上限の 74%
DDR から読んで、PL の中を一周させて、DDR に書き戻すだけの回路。 計算は何もしていない。目的は工場を建てることではなく、工場に材料を運ぶ道の太さを測ることだった。 読みと書きが同じ門を通るので、DDR から見た仕事量は 0.77 の 2 倍で 1.54 GB/s になる。

「何もしない回路をわざわざ作った」のには理由があります。 演算器を先に作り込んでしまうと、遅かったときに「回路が下手なのか、道が細いのか」が切り分けられません。 先に道の太さだけを確定させておけば、次に作る演算回路は「その道を食い切れるだけの規模」に設計すればよく、 過剰にも過少にもなりません。

転送後に元データと結果を1バイトずつ照合して完全一致を確認しています。 速いが壊れている、では意味がないので。

図6 — Mac 1台で回るようになった

Mac (M4) Docker + Rosetta 2 = x86 の Linux Vivado 2024.1 設計図 → 回路に変換 7分半 .bit scp で転送 LAN 経由 PYNQ-Z1 Python で 読み込んで実測 結果を見て設計を直す ― 一周およそ 15 分
Apple Silicon の Mac だけで、FPGA の設計から実測までが一周する。 Vivado は x86 の Linux でしか動かないが、Docker と Rosetta 2 で動かせた。 JTAG のような書き込み器材は不要で、生成したファイルをネットワークで送って Python から読み込むだけ。

これは地味に見えて、この計画で一番大きな成果かもしれません。 ADRFT1 では最後までこの一周が作れませんでした。 FPGA を書き換えるのに専用の器材(3万円以上)が必要で、しかも失敗すると復旧できない、という状態だったからです。 引き出しから出てきた PYNQ-Z1 では、初日にこれが回りました。

ここまでの経緯

8/29–30
ADRFT1 が不安定な原因を特定 デバイスツリーの記述ミスで、CPU が使えないメモリ領域を普通の RAM として掴んでいた。 起動スクリプトに2行足す回避策で完全に解消。2台とも適用済みで、今も安定して動いている。
9/3
メーカーは対応しないと回答 不具合は認識しているが直す予定はない、との返信。回避策は暫定ではなく恒久的な構成になった。
9/4
帯域を実測して、見立てが変わった ADRFT1 の CPU は DDR の 13% しか引き出せていなかった。「DSP が余っている」ではなく 「メモリの道が使えていない」が問題だと判明。あわせて FPGA の書き換え手段が塞がっていることも確定し、 ADRFT1 での FPGA 計画は中止を決めた。
9/5
PYNQ-Z1 で一周させた 引き出しから出てきた 2017 年のボードをセットアップ。Mac 上に Vivado 環境を構築し、 自作のビットストリームを生成してボードに流し込み、PL→DDR の帯域 1.54 GB/s(上限の 74%)を実測。
9/8
その 74% が DDR の話ではなかったと判明 書き戻しをやめた「読み専用」の回路を作り、クロックを変えて測り直したところ、 実測は毎回「道幅」の 99% に張り付いた。犯人は DDR ではなく AXI の経路幅のほうだった。 HP ポートを2本に増やして初めて DDR 側が飽和し、本当の上限 1.97 GB/s(DDR の 93.6%)を確定。 → 「二つの天井」

ちなみに、KV260($199、性能は PYNQ-Z1 のおよそ 10 倍)への移行を検討していましたが、 PYNQ-Z1 が思ったより使えることが分かったので、判断は保留でよさそうです。 三値化した 135M クラスのモデルなら 512MB のメモリに収まるので、このボードで一度最後まで通せる可能性があります。

次にやること

段階やること何が分かるか
済 読み専用の帯域を測る 2026-09-08 に実施。1.97 GB/s(DDR の 93.6%)。 予想どおり読み専用のほうが出たが、理由は想定と違った—— 伸びしろは書き戻しの分ではなく道幅にあった。 → 「二つの天井」
次 INT8 の掛け算器を並べる 道の太さに合わせた規模で作る。220個は要らない。 確定した設計値は 142.86 MHz で 14 個(全体の 6%)
本命 三値(BitNet b1.58)化 重み1個あたり 1.0 → 0.2 バイト。同じ道で 5 倍流せる。 1.97 GB/s なら毎秒 9.8 G 個の重み相当

見込みとしては、三値化まで進めば 135M クラスで CPU の 10〜20 倍、 毎秒 30 トークン前後は狙えるはずです。ただしこれは今回測った帯域からの試算で、 実際に動かしてみるまでは分かりません。

用語

PS
Processing System。チップに載っている完成品の CPU 側。Linux が動いている。
PL
Programmable Logic。空っぽの回路の土地。ここに自分の回路を建てる。「ファブリック」とほぼ同義。
ファブリック
PL の中身、書き換え可能な回路の海。布地(fabric)に喩えた呼び方。
ビットストリーム
回路の設計図をチップに流し込める形式にしたファイル(.bit)。これを読み込ませると PL がその回路になる。
DSP
ファブリックに埋め込まれている掛け算専用の小さな回路。このチップには 220 個ある。
HP ポート
High Performance port。PL から DDR メモリへ行くための門。これを通る回路を作らないと PL はメモリに触れない。
AXI
チップ内部の部品どうしをつなぐ規格。道路の規格のようなもの。
DMA
Direct Memory Access。CPU を介さずにメモリとデータをやりとりする運搬係の回路。
帯域
1秒間に運べるバイト数。この計画では一貫してこれが速度を決めている。
量子化
モデルの重みを粗い数値で表して小さくすること。INT8 は1バイト、三値は約0.2バイト。