はじめに
私はこれまで ZYBO(Zynq-7000) や KV260(ZynqMP) の Linux 環境を活用してセルフコンパイル環境をメインに活用してきました。
Linux では gcc も rust も動きますので、OpenCV などの C++ や Rust などネイティブコンパイラでバージョン依存の強いものも、その場でコンパイルすれば問題が起こりにくいですし、何より FPGA のハードウェアというナマモノを扱うのにセルフ環境からいろいろできるのは便利だったからです。
また、VS Code Remote Development もこれらの環境に ssh で繋ぐだけで使えるため、大変便利でした。
一方で、AI の台頭で Vibe Coding みたいなものが当たり前になってくると、GitHub Copilot などが動くのに慣れ切ってくるわけですが、ZYBO の 512MByte のメモリ環境だと Copilot を有効にすると固まることも増えてきました。
また、4GByte ある KV260 であっても、Copilot と rust-analyzer などを使って、OpenCV や tonic を使うのはさすがに辛くなってきました(主に rust-analyzer が原因?)。
そこで、意を決して、PC からのクロス開発環境の整備に踏み出しました、というのが今回のお話です。
リモートから DeviceTree Overlay を行う FPGA-Server と FPGA-Loader
まず、セルフ開発でない場合に一番面倒なのがこれです。 セルフ開発なら dtb と bitstream を用意して sudo で DeviceTree Overlay できますし、xmutil や dfx-mgr-client を使う手もあります。
root 権限が必要と言うのも何とも面倒で、であれば サービスとして立ち上げてしまえ という安直な解に走ったのが下記の2つのプロジェクトです(PYNQも同じ?)。 このサービスはクロス環境だけでなく、セルフ環境でも便利です。なお、ローカルな利用のみ想定しているので、認証などはまだ入れていません。
その際、これまた cargo install もコンパイル時間長いので、バイナリインストールしたいなという事で、 後で述べるクロスコンパイル環境使って GitHub Actions でクロスコンパイルして自動でバイナリファイルをデプロイできるようにしました。
加えて cargo-binstall というとても便利なものを AI に教えてもらい導入しました。
結果、 サーバー側となる ZYBO や KV260 側では
curl -LsSf https://raw.githubusercontent.com/ryuz/jelly-fpga-server/master/binst.sh | sudo bash
とすればサービス登録が完了し、
クライアントを使う PC や ZYBO や KV260 のユーザー環境では
cargo-binstall --git https://github.com/ryuz/jelly-fpga-loader.git jelly-fpga-loader
とすればよいところまできて、サクッとバイナリインストールができるようになりました。
なお、自動デプロイする .github/workflows/release.yml などは、GitHub Copilot がサクッと書いてくれました。AI便利ですね。
クロスコンパイル環境を作る
今回、目指したのは各ボード環境で
apt install libopencv-dev
して作ったお手軽 OpenCV のネイティブ環境で動くバイナリをクロスコンパイル環境で作る事です。
Python などのインタプリタ言語なら悩むことは無いのですが、FPGA 制御にはまだまだポインタを扱える C++や Rust などのネイティブコードを出力するコンパイラ言語は手放せません。
ここで有効な技術となったのが
- Ubuntu/Debian のマルチアーキテクチャ
- Rust の cross や VS-Code DevContainer
です。
まずわかったのが、x86 であっても arm ターゲットボードと同じ Distro で、例えば
dpkg --add-architecture armhf
apt-get update
apt-get install crossbuild-essential-armhf
apt-get install libopencv-dev:armhf
などのようにして、armhf アーキテクチャの libopencv-dev と クロスコンパイラを入れて、おけば、ZYBO で動くバイナリが作れることがわかってきました。
そこで、C++ の為に DevContainer 用の Dokcerイメージ と Rust の cross 用の Docker イメージをそれぞれ用意しました。
さらに、devcontainer.json で、
"features": {
"ghcr.io/devcontainers/features/docker-in-docker:2": {}
},
のようなことをすれば Docke in Docker の機能で DevContainer 内でも cross が使えることがわかりました。
ひとまず、これらを組み合わせて、セルフ環境でもクロス環境でもビルドできる Makefile が書けるようになってきました。
また、bitstream のダウンロードも、セルフ環境でもクロス環境でも先の jelly-fpga-loader で行えますので、あとはビルドしたバイナリを scp なりで送りつけて ssh で実行すればいいことになります。 何らかの方法で、ターゲットボードとクロス開発環境でファイルシステムをマウントしてしまうのも良いかもしれません。
また、Docker イメージは、GitHub Actions でも使えますので、新しいバージョンタグを打つと同時に必要な各種バイナリも自動デプロイする環境が作れてしまうわけで、いろいろ快適になってきたように思います。
おわりに
そのうち、実際のプログラム例が整備出来てきたら、もうちょっとちゃんとした記事にしようと思いますが、ここしばらくこれらに嵌っていたのがようやく落ち着いてきたので、記事にしてみました。


