Ryuz's tech blog

FPGAなどの技術ブログ

回路図のお供に Pandas 便利そう

データプロセッシングのアクセラレータ―としてではなく、IoTデバイスとしてFPGAを使ったことのある方は Excel でピン配置表を作って VLOOKUP 関数で頑張った経験がある方も多いのではないでしょうか?(古い人だけかもしれんないですが)。

FPGAマイコンと違って、プログラムから操作できるピンが圧倒的に上に、ピンごとに用途が決まっているわけではなく汎用性が極めて高いため、表で管理しないとやってられなくなります。

また、興味の範囲が FPGA に収まり切れずに KiCAD なんかで基板を作り始めちゃう人は、いろんな部品のデータシートとにらめっこしながら、こちらもピン配置表を作りつつ部品のライブラリを作ったりしていることでしょう。

回路図はネットリストになりますので、ネットリストを Python とかのスクリプトで解析して、ピン配置表を作るなんてことも出来てしまいます。

で、話は FPGA に戻って、最近 Kria KV260 で遊んでいるときに SOM(System On Module) に載っているデバイスのピン配置までたどり着くのがおっくうで表を作り始めました。

いいものができれば、公開したいところなのですが、この時に参照する xdc ファイルが、他のデータシートと違って、ユーザー登録してダウンロード時の同意をしないと入手できない仕組みになっています。

想像ですが、 AMD(Xilinx)さんは、おそらく将来的にLSIバイス単位ではなく、SOMを丸ごと合成対象のデバイスとして扱いたいのではないかと想像しています。Vivado のツールに直接 SOM のコネクタピンが指定できれば使う側としてもかなり楽になりそうです(ただし、残念ながらまだそうはなっていません)。

ですので、私がピン配置テーブルを作って公開するのはいろいろと問題がありそうなので xdc ファイルからテーブルを作るツールとして下記のようなものを作ってみました。

github.com

現時点で K26 SOM を回路図を見ながら使うには、暫定的ですが活用できればと思います。

で、ここからが本題なのですが、やってみて

うわ、Pandas 便利だ!

って、話です。

SQL まではいかないのかもしれませんが、Excel に比べるとかなりちゃんとしたデータベース環境です(まあ私は SQL も Pandas も Excel も中途半端な知識しかないのですが)。

こういうネットリスト的な、信号名とかピン番号とかを経由していろんなものに繋がっていくものの処理に極めて便利だと感じました。

信号名もピン番号もそれぞれ特定の範囲でユニークである必要がありますので、リレーショナルデータベースのKEYとしてよく機能します。 むしろ間違って重複していたらエラーに気づきやすい点で VLOOKUP よりはるかに優秀です。

過去に、検図のお供に、信号が浮いてたり、繋がってなかったりを探すのに PerlPythonスクリプト書いたりしたことがあるのですが、リレーショナルデータベースとして扱うととても便利そうです。

FPGA の ピンの外を制御するバリバリの IoT プログラマの皆さん。Python + Pandas でピン配置テーブル管理するのおススメです。

Zynq MP SoC のチュートリアルを考えてみる

はじめに

個人的には Zynq UltraScale+ MPSoC (以下、ZynqMP) は 現時点では最強の IoT プロセッサなのではないかと思っています。

そして多分、「自分はわりとマニアックな偏った使い方をしている方なのではなかろうか」とも思っています。

一方巷で、

  • FPGA 興味あるけど何から始めたらいいかわからない
  • ZynqMP やることになったけど機能が膨大すぎてどこから手を付けていいかわからない

などの声も聞くことがあります。

ここでよいアドバイスができれば(偏った)仲間増やすチャンスなのですが、なかなか良い回答を思いつけない事が多いです。

そこで、少し真面目に考えてみよう、というのが今回の記事となります。

過去に書いた L チカ記事など

やはり IoT の Hello World といえば LED チカチカ だと考えます。

過去に Lチカまでの環境構築や、手順を書いてみた記事を掘り返してみます。

Ultra96用のLチカ

振り返ってみるとUltra96用には結構記事書いてました。

KV260用のLチカ

最近 KV260 向けにいくつか書いています。

ZynqMP とか RTL の話

最近はこんな記事も書いています。

考えてみたチュートリアルの流れ

既にFPGA以外のマイコンでなら C/C++ などで Lチカやったことあるような人々をターゲットに Lチカから順にステップしながら ZynqMP を覚えてもらえる流れを考えてみました。

  1. ボード買ってきて Linux(Ubuntu/Debianなど) を SDカードに書く
  2. とりあえず起動して Linux で遊んでみる
  3. PLのみでLチカするbitstream作って Linux から動かしてみる
  4. LinuxアプリからPLにアクセスしてLチカしてみる
  5. このへんで、シミュレータとILAデバッグもやってみる
  6. 外部SDRAM経由でPLとPSのデータやり取りをやってみる
  7. 非同期クロック間のデータ交換とかPLLとかを理解する
  8. IP Catalog とか DSPとか BRAM とかPLをいろいろやる
  9. MIOとか、RPUとか、割り込みとかPSもいろいろやる
  10. ZynqMP エキスパートのできあがり

みたいな感じはどうでしょうか?

案外、 1~6 ぐらいまでは過去にいろいろ書いていたものを再整理していけばよいような気もします。

おわりに

ここまで書いて、Vitis 使ってないことに気が付いたりもしています。

他:「Xilinx の開発は Vitis というツールを使うと聞いたんだけど、どう使えばいいの?」 私:「使ったことないのでわかりません」

というやり取りも過去に何度かあったような...

とはいえ、IoT を Lチカ を軸足に話を進めるとどうしても Vivado が中心のフローになっちゃうのですよね。 IPコア 作るのに Vitis HLS は使うにしても、どうしてもデザインフローの中心が Vivado になってしまう。

HLS は使うにしても IoT な人には Vitis 要らないんじゃないかとも思い始めていたりいなかったり。

FPGAは1命令を繰り返すVLIWプロセッサ?

ちょっとしたジョーク的な駄文ですが、ふと思いついたのでメモしておきます。

こちらで少し、RTLプログラミングを普通の逐次処理型のプログラミングモデルと比較したりしていましたが、ふと「 FPGA って実はとてもたくさんの命令をパッキングした、たった1命令を繰り返し実行している VLEW(Very Long Instruction Word)プロセッサと言えるのでは?」 と思いいたってブログにしている次第です。

今時の FPGA は、内部に FF も LUT も 何万個も持っており、DSPDRAMだって数百とか数千持っていたりしますが、すべてクロックに合わせて毎サイクルプログラムされた通りに計算を行います。

なので、

  • レジスタはFF数が足りる限り任意長のものを好きな数だけ定義できる(r0~r31までの汎用レジスタなんてケチなことは言わない)
  • 演算リソースが足りる限り何千という命令を一度に実行できるVLIW
  • 命令オペコードの演算内容は always 文のなかで自由に定義(プログラム)できる
  • 複数の命令はディスティネーションレジスタが衝突しなければ何命令あってもOK
  • 命令のオペランドはいくつレジスタを参照してもOK
  • ただし命令メモリは1番地しかなく、同じ命令を繰り返すことしかできない
  • すべての命令はレイテンシ1で完了し、一切ストールはしない
  • あえて言うならクロックイネーブルで一時停止したり、リセットで初期化したりもできる

というような捉え方もでき、極端な話、単一クロックの同期回路であれば、 bitstream を1つの命令として繰り返し実行する VLIW プロセッサと言えなくもない気がします。

こういう見方をすると Verilog の always 文の単位で VLIW のなかの 1命令要素を記述しているというような捉え方もできそうです。

こういうモデルでとらえたとき、これは「命令セットのプログラミング」に他ならないわけです。

逐次処理モデルのC++のような言語仕様を使ってHLSを書くのも便利ですが、RTL言語を命令セット定義言語として使うのも文字通り Register Transfer Level のプログラムで面白いかなと、思った次第です。

BinaryBrain で HLS をやってみる (ver 4.2.4 リリース記念)

はじめに

先ほど BinaryBrain 4.2.4 をリリースしましたので少しだけ時事ネタ的にプログを書こうと思います。

github.com

もともと FPGA の基本構成要素である LUT を LUT 構成のまま誤差逆伝搬で学習させてしまおうというのが、以前発表したLUT-Netの趣旨ですので、そこから C言語を出力するのは逆コンパイルしてまたコンパイルするような話にはなってしまうのですが、今回少しトライしてみましたので使い方などを少しご紹介できればと思います。

HLSチャレンジがきっかけ

第10回ACRiウェビナーを視聴させて頂いていたところ、HLSチャレンジに新しい問題が追加されるとの事を知りました。

さっそく見てみるとHLSでCNNに取り組むのに大変よい良問が設定されていて、私も頭をひねっている途中なのですが、なんとこの中にフリー部門が1問追加されていて「MNIST を 90% 以上の認識率で達成できれば何をやっていい」という、なかなか野心的でわくわくするものが設定されておりました。

当然いろんなやり方があると予想されますが BinaryBrain の HLS 対応を考えるのに十分なモチベーションになりそうでしたので、ここぞとばかりにやってみました(他の方が本気になるとすぐ抜かれそうではありますので(笑))。

結果から言うとまだ誰も提出していない部門に一番乗りしたがゆえに一時的に一位です(当たり前ですが)。 ただし、文字列制限などもあり、BinaryBrain の出力をかなり変則対応しての提出となってしまいました。ですので、最終的にリリース版の BinaryBrain に実装した機能では HLSチャレンジにはそのまま提出できないのですが、良くも悪くも「BinaryBrain で HLS が使えるか試してみるきっかけになった」 ということでご容赦ください。ひとまず簡単ながら HLS 出力機能が付きました。

当初なかなかHLSに LUT を推論してもらえずに困っていたのですが、試行錯誤の末どう書けば意図通り解釈してもらえるか見えてきたのはHLSの良い勉強になりました。

BinaryBrain の HLS 用コード出力機能を使ってみる

セットアップ

こんかいは Windows11 (22H2) の WSL2 で Ubuntu 20.04.4 LTS を 使います。NVIDIAGPUボード と CUDA Toolkit 11.6 が準備されている前提とします。 ちなにみ私の使っているのは NVIDIA GeForce GTX 1660 SUPER です。

以下は私の環境なので、随時ご自身の環境に読み替えてください。

画像データの準備に Torchvision を使いますので PyTorch も入れております。

# とりあえず仮想環境作る
pyenv local 3.9.12
python -m venv .venv 
source .venv/bin/activate

# 必要なものを入れる
pip install numpy
pip install tqdm
pip install pybind11
pip install ipykernel
pip install jupyterlab
pip3 install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu116
pip install binarybrain==4.2.4

# リポジトリゲット
git clone -b ver4.2.4 --recursive https://github.com/ryuz/BinaryBrain.git

# Jupyter Lab に仮想環境を追加して起動
python -m ipykernel install --user --name=bb4.2.4 --display-name=bb4.2.4
jupyter lab

# 以降ブラウザにて BinaryBrain/samples/python/mnist/MnistDifferentiableLutHls.ipynb を開く

学習実施

BinaryBrain/samples/python/mnist/MnistDifferentiableLutHls.ipynb を開いた後に、環境を先ほど作った bb4.2.4 に切り替えて実行すれば、学習が始まります。

下記のように進んでいきます。

学習風景

最後まで流れると

  • BinaryBrain/samples/hls/mnist/simple/src/MnistDifferentiableLutHls.h
  • BinaryBrain/samples/hls/mnist/simple/testbench/mnist_test_data.h

の 2つのファイルが生成されます。

詳細は MnistDifferentiableLutHls.ipynb の中を見ていただければと思いますが、今回は CNN ではなく単純な多層パーセプトロンもどきの構成にしており、HLSチャレンジのフリー課題にかなり近いサンプルの構成になっております。

全部 LUT層だと HLS の意味が薄いので、最後に INT8 での Depthwise Dense Affine 層を1層だけ入れております。

----------------------------------------------------------------------
[Sequential] 
 input  shape : [1, 28, 28] output shape : [10]
  --------------------------------------------------------------------
  [Binarize] 
   input  shape : {1, 28, 28} output shape : {1, 28, 28}
  --------------------------------------------------------------------
  [DifferentiableLut6] 
   input  shape : {1, 28, 28} output shape : {256}
   binary : 1   batch_norm : 1
  --------------------------------------------------------------------
  [DifferentiableLut6] 
   input  shape : {256} output shape : {128}
   binary : 1   batch_norm : 1
  --------------------------------------------------------------------
  [DifferentiableLut6] 
   input  shape : {128} output shape : {10, 64}
   binary : 1   batch_norm : 1
  --------------------------------------------------------------------
  [DepthwiseDenseAffineQuantize] 
   input  shape : {10, 64} output shape : {10}
   input(64, 10) output(1, 10)
----------------------------------------------------------------------

HLS を試す

HLS のサンプルディレクトリに移動して、Vitis HLS をセットアップします。

cd BinaryBrain/samples/hls/mnist/simple/
source /tools/Xilinx/Vitis/2021.2/settings64.sh 

src/mnist_sample.cpp がメインのコードで、testbench/tb_mnist.cpp がテストベンチになっています。

先ほど学習で作ったソースコードを include してコンパイルするようになっています。

csim を試す

ここで

make csim

と打ち込むと C言語シミュレーションが走ります。

今回は下記のようになりました。90%程度合えばOKなのですが今回運よく全問正解しています。

out[0]=7 exp:7  ok
out[1]=2 exp:2  ok
out[2]=1 exp:1  ok
out[3]=0 exp:0  ok
out[4]=4 exp:4  ok
out[5]=1 exp:1  ok
out[6]=4 exp:4  ok
out[7]=9 exp:9  ok
out[8]=5 exp:5  ok
out[9]=9 exp:9  ok
out[10]=0 exp:0  ok
out[11]=6 exp:6  ok
out[12]=9 exp:9  ok
out[13]=0 exp:0  ok
out[14]=1 exp:1  ok
out[15]=5 exp:5  ok
out[16]=9 exp:9  ok
out[17]=7 exp:7  ok
out[18]=3 exp:3  ok
out[19]=4 exp:4  ok
accuracy = 20/20

合成してみる

単に

make

と打つと論理合成が走ります。結構時間がかかりますが

solution_1/impl /export.zip

に、Vivado に取り込み可能な IP が出来上がりました。

合成のレポートを見ると下記のような感じでした。

合成レポート

HLSで書いた乗算を行う DenseAffine はやや大きいですが、LUT層は十分小さくできています。

コシミュレーションしてみる

最後に

make cosim

とすると、コシミュレーションが走ります。

デフォルトで 波形表示に GUI を起動するオプションにしていますので、シミュレーションが終わると Vivado が起動し、以下のように動作が確認できます。

コシミュレーションの波形確認

おわりに

如何だったでしょうか? そういえば BinaryBrain を作ってから、このような一連の使い方をブログに書くのは初めてのような気がします。

チュートリアルであればマニュアルの方に書くのが筋ですから、今回、HLSチャレンジという時事ネタで、少しマニュアルとは違う記事にできてよかったと思います。

おまけ

全問正解もびっくりだったのであとからもう少し学習を進めてみました。今度は20個中2個間違えて概ね 90% という理屈通りの結果となりました。 4と9 や 8と3 の間違いという、なんとなく似た文字として間違えており、深層学習っぽさが少し出たかなと思います。

out[0]=7 exp:7  ok
out[1]=2 exp:2  ok
out[2]=1 exp:1  ok
out[3]=0 exp:0  ok
out[4]=4 exp:4  ok
out[5]=1 exp:1  ok
out[6]=4 exp:4  ok
out[7]=4 exp:9  miss
out[8]=5 exp:5  ok
out[9]=9 exp:9  ok
out[10]=0 exp:0  ok
out[11]=6 exp:6  ok
out[12]=9 exp:9  ok
out[13]=0 exp:0  ok
out[14]=1 exp:1  ok
out[15]=5 exp:5  ok
out[16]=9 exp:9  ok
out[17]=7 exp:7  ok
out[18]=8 exp:3  miss
out[19]=4 exp:4  ok
accuracy = 18/20

件のHLSチャレンジのMNISTフリー部門ですが、どうやら一番乗りだったらしく 2022/11/21 現在、暫定1位となっています(ほかに誰もいないので当たり前ですが)。きっとすぐに抜かれる気がするので、記念スナップショットです。

HLSチャレンジ暫定一位?

恐らくある程度ランダムに設定されていると思われる本番時の画像入力ですが、90% クリアすればパスできるようですので、決定木の類とかで攻めていけばかなり好スコアが出るのではなかろうかと予想しつつ、ガチ勢の方々の参加を恐々と見守っております。一応 DeepLearning っぽいもので攻めた記念ということで(あわよくばこのまま逃げ切れないかなとも期待しつつ)。

ハードマクロの稼働率について考えてみる

はじめに

FPGAに限らずCPUやGPUなどすべての計算機に言えることなのですが、ハードマクロの演算リソースは使ってなくてもそこに居座り続ける(要するに無駄)という問題があります。

筆者は普段主に ZynqMP を使っておりますので、APUやRPUやOCM、PL内部のLUTやDSPやBRAM、そのほかにも USB や Ether や DDR4のコントローラ、など様々なハードマクロが初めから配置されており、これらは使わなくてもトランジスタリソースを消費してそこに居座り続けることになります。

ある程度は、品番選択によって無駄のスリム化はできますが限界はあります。

なのでこれらハードマクロとどう向かい合えばいいのだろうというのを今日は考えてみます。

多少非効率でもハードマクロを使うのはお得

ハードマクロはその計算専用に作られた回路ですので、多くの場合PLでLUTを使って同じ回路を構成するより圧倒的に高速かつ低消費電力です。

例えば、16bit の乗算がしたい場合、27x18 のDSP を使うのはもったいなからと言ってDSPが余ってるのにLUTで乗算回路を組んだりなんてことは通常しません。明らかに27x18 のDSPを多少の無駄は承知で使った方が電力も速度も良貨しますし、合成器も自動でDSPに割り当てます。

DSPやBRAMの場合はある程度合成器が自動で割り当ててくれますが、APUやRPUとなるとプログラマが明示的にシステム設計しないと割り当てを変えることは難しいです。

ハードマクロは数にも限りがあるし、できることも決まっているので、どこに何をやらせればいいの? というのは重要な問題な気がします。

CPU/GPUに至ってはもっとすごい話で、例えば整数演算しかない大量のデータを処理しているときなど、お高いCPU/GPUに搭載されたリッチな倍精度実数のSIMD演算器は大量のトランジスタ面積を占めたままお休みしております。INT4などに量子化されたニューラルネットなどがCPUではなくFPGAで極めて効率がいいのはこういう事情なのかと思います。

例えば浮動小数点演算

最近は HLS の普及もあり PL で浮動小数点演算を行うことも増えてきたように思います。ところでZynqMPの場合APU(Cortex-A53)にNEON、RPU(Cortex-R5)にVFPを抱いており、ハードマクロの浮動小数点演算器ががっつりいるので、こちらが使えるなら使った方がお得に思います。周波数もCPUは高速ですので、並列化困難で応答性を上げたければ周波数頼りになるような処理にも適していると思います。

そうはいっても、

  • PL⇔PS の同期や通信が大変
  • 将来 PS のないFPGAに移植したいとき困る

とか、諸事情はあるかとは思いますが、多少の無駄を加味しても十分元が取れるケースはありそうに思えます。

全体を考えた最初のシステム設計が大事

SoC上でやらせたい処理を考えるとき、アプリケーションの処理や、解きたい問題のアルゴリズム全体を考え、どこをどこに割り当てると効率的に計算できるのかのシステム設計を行うのはとても重要な作業に思います。

  1. システム全体の要件(応答性/処理量)
  2. 全体アルゴリズムとデータフローの検討
  3. 各演算処理の演算場所のアサイ
  4. 必要機能の実装
  5. テストやデバッグ

のような比較的ありきたりな手順で設計は進むかとは思いますが、私は必要に応じて2まで戻って反復的に設計を進めることが重要に思います。特に設計フェーズで利用するSoCのどこにどういう資源があるのかしっかりと把握したうえで、2と3 の反復を十分やってシステム最適化しておくことが、全体性能を上げてくれるように思います。

しばし 4と5 あたりの反復が増えがちな気がしますが、実装してみてデータフローそのものを変えた方がよさそうなボトルネックが見つかったら迷わず2まで戻るべきかとは思います(まあ、スケジュールが許さず強行するケースは多いのでしょうけど)。

おわりに

SoC の中を見渡すと結構いろんなところにいろんな演算リソースがハードマクロとして存在しているように思います。

設計初期の段階からそれらに目を配り、活用できないか考えてみるのは、案外思いもしなかったアーキテクチャでの目的達成につながるかもしれません。

今後、Versal ACAP とかになるともっといろんなハードマクロが入ってきそうですし、視野の広い設計を楽しめるとよいなと思います。

リアルタイム処理を再考してみる

はじめに

最近、github連携が便利でZennを使い始めましたが、あくまでこちらはQiitaの乗り換え色が濃いので、引き続き、結論のない試行錯誤中の感想とか、ポエム系はこちらに書こうかなと思います。

今回はまたあらためてプロセッサのリソース観点からリアルタイム処理を考えてみたいと思います(何度目だろう?)。

扱うのはリアルタイムシステムですので、デッドラインまでの時間制約を満たすという条件を守りつつ、より少ないリソースや電力での実現とか、よりリアルタイム制約のきついところに対応できるようにするには、とかの効率についてあれこれ駄文を書いている次第です。

TSSと優先度順スケジュールの違い

今回は話をシンプルにするために、CPUがまだシングルコアが当たり前だった時代のプロセッサ、つまり近年のCPUのようなSMT(Simultaneous Multi-Threading)など持たない、一度に一つの処理しかできない原始的なプロセッサを単位として話を進めようと思います(それでも組み込みの世界では十分な話にはなるかとは思います)。

普段使っているWindowsLinuxなどのユーザーフレンドリーなOSは TSS(Time Sharing System)と呼ばれるスケジューリング方式が多く、これらはタイマーを用い、例えば10ミリ秒ぐらいの人間からすると感知できないような短い時間でタスクを切り替えることで、まるで2つ以上のタスク(ここではスレッド/プロセス/ジョブなどを含んだ広い意味での実行単位のことをタスクと呼んでおきますね)が同時に動いているように見せることができます。

これはプロセッサが1つしかなかった時代に、複数人で使ったり、一人であっても複数のタスクを実行させるときに大変便利な仕組みでした。プロセッサが時分割で複数のタスクの処理を一度に進めてくれるので、まるでプロセッサ複数あるかのような振る舞いをしてくれ、ユーザーは複数のアプリを動かすことが可能でした(UNIX文化では当初からこの機能は導入されておりますが、一般に有名なところだとWindows95の登場からユーザーがTSSの恩恵を受けることが増えたのではないかと思います。当時はコンシューマ用途ではシングルプロセッサが普通でした)。

一方で、タスクを沢山実行すると、どんどんタスクの処理が終わるまでの時間が遅くなってしまいます。そうすると例えば通信の接続がタイムアウトしてしまったりと、制限時間のある処理に支障が出始めます。特に組み込み機器などですと「危険を感知してからブレーキを掛けるのが間に合わない」などと言う致命的なことも起こりかねません。

そこで活躍するのが優先度順のスケジューリングであり、特にその機能に特化して設計されたのが RTOS(Realtime-OS)と呼ばれるものです。

少し、極端な例ですが比較のための例を以下に示します。

TSSなOSとRTOSの違い

1秒かかるタスクが2つ起動された時、TSSでは2秒かけて2つのタスクをこなすのに対して、RTOSではまず先に優先度の高いタスクAを全力でこなし、その後にタスクBを処理します。

このときタスクBの結果が得られる時間は2秒後と変わらず、タスクAの結果が得られる時刻が1秒早まるという応答性観点だけならメリットしかない結果となります。

TSSの場合、さらにタスクCやタスクDなどが同時に起動されたりすると結果が得られるまで3秒、4秒と時間が伸びていきますが、優先度順のスケジューリングだと、さらにタスクCやタスクDなどが同時に起動されたとしても、タスクAに最高優先度が設定されている限り、タスクAは常に「要求されたら1秒後に結果を返す」ということが保証されますので、リアルタイム処理に向いているわけです。

(もちろん最近のOSはRTOSでなくても、優先度実行の機能は持っていたりしますが、プロセッサを完全に握ったったままになるバグがあったりするとシステムがロックする恐れもあり、ユーザー権限では利用できないケースが多いかと思います。また一般的な計算機環境ではキャッシュメモリや分岐予測といった他の実行時間を乱す要因も多いので、ハードウェア的にもこれらの要因がケアされた組み込みなどの特別な環境でRTOSが活躍することが多いです。)

なお別の視点でもう一度TSSを考えてみると、TSSは1個のプロセッサを1/Nの性能のN個のプロセッサがあるように見せる技術とも言えますので、ポラックの法則が機能する範囲においては、折角N2 倍のリソースを投入してN倍にしたプロセッサをN分割してしまう事になります。少し乱暴な考察になりますが、TSSでN個のタスク数を動かすとリソース効率は 1/N に低下するということになります。

シングルプロセッサ性能とマルチプロセッサ性能

ここでポラックの法則と呼ばれる「プロセッサの性能はその複雑性の平方根に比例する」という経験則を持ち出します。この法則自体破れ始めている部分はあるかと思いますが、いったん成り立つ前提に立つという前提で話を進めます。

当初はそもそもユーザーフレンドリーな領域のアプリケーションが十分に並列化されてなったといういう事情もあり、シングルプロセッサの性能がユーザー価値を決めていたためトランジスタはプロセッサ単体の性能向上に使われがちであったなどの事情もありましたが、純粋にリアルタイム処理という観点で見ると下記のようになります。

シングルプロセッサとマルチプロセッサの違い

ここで、全体性能で考えると、どちらもプロセッサは常に100%稼働しており、マルチプロセッサの場合1秒後にすべての処理が完了するにもかかわらず、シングルプロセッサだと1.4秒後の完了になるので明らかにマルチプロセッサの方が性能が良いことになります。

一方で、タスクAが結果を返すまでの応答時間みをリアルタイム性能として取り扱うと、シングルプロセッサの方が0.3秒ほど速くなっており性能が向上していることになります。これはもし 0.7秒以内にブレーキを掛けないとクラッシュする装置の制御に用いる場合は、たとえ全体性能が悪くともシングルプロセッサへのリソース投入を採用せざるを得ないということが起こります。

ここがリアルタイム処理の面白いところかと思います。

FPGA を考える

ここで FPGA を考えてみます。FPGA はある程度設計の頑張り具合で、高い周波数で動くようにしたり、並列度の高い回路構成にしたり、有限なリソースの中でその配置バランスを変えた設計が可能です。

シングルプロセッサとマルチプロセッサで起こるようなことはプログラミングの設計時に織り込んで考えていく必要がある点で、FPGAでリアルタイム処理を考えるのは結構奥深かったりします。

さらに、昨今は ZynqMP のような、RTOS向けに 600MHzで動くRPU(Cortex-R5) などを内蔵した SoC などが存在しており、並列化が難しく周波数が重要な処理をPL(Programable Logic)からプロセッサにオフロードするというような使い方も可能になっており、リアルタイム性能を突き詰めたときの設計選択肢が広がっています。

少しまとめ

TSSは

以下、ポラックの法則が成り立ち、かつそれ以上タスクは並列分解できないという条件下で多少乱暴な考察をすると

  • TSSは1個のプロセッサを1/Nの性能のN個のプロセッサがあるように見せる技術であり、リアルタイム性、リソース効率の両者を悪化させる
  • リアルタイム性保証を行うタスクが含まれる範囲においては、速度を1.4倍にするためにリソースを倍掛ける意義があるケースがある
  • リソース効率だけを考える場合、タスクの数までプロセッサを並列化すると全体性能は上がる

一方で1個のプロセッサのシングルスレッド性能は際限なく上昇するわけではなくポラックの法則はどこかで破れていきますし、 下限についても同様で、トランジスタ1個でプロセッサは構成できるわけではないので、ある程度現実的な範囲での議論となります。

とはいえ、この「現実的な範囲」というのはFPGAプログラミングを考える上では案外妥当な範囲に収まってくるので悪くない考え方です。

FPGAのようなプログラミングを考える場合にリソース効率だけを考えるなら、各演算回路が100%近く稼働する前提のもと

  • 並列化可能な処理は事前に並列化し尽くしておく
  • 並列化できた分だけフルに並列エンジンを考える
  • 並列化したタスクの中でそれぞれのリアルタイム応答性に必要な最小限の性能の計算機を最適なリソースで設計する

というのが設計指針になってくるかと思います。

FPGA設計していると、リアルタイム保証や帯域保証の為に「時間当たりの計算量の保証」が必要なケースが多々あるかと思います。そしてしばし普通にやると間に合わないのであらゆる技法を用いて演算効率を高めて保証内で演算が終わるように考えなくてはなりません。この時「並列化」というのは非常によい選択肢であり、特にリアルタイム保証は不要で、帯域保証のみが必要な場合は、設計をN倍に並列化するごとにリソース効率が√N倍に上がっていきますのでより小さなロジック消費で目的が達成できる見積もりになります(まあ実際は理屈通りにはいきませんが)。

ここにリアルタイム保証が加わるともう少し複雑で、分割した各処理のうちデッドラインを持っているものについてはそれを保証できる性能の計算リソースを割り当てていく必要が出てきます。この時、計算リソースの稼働率が100%にならないケースが出てくるので「空いたところに別の処理を入れ込めないか?」という議論が起こってきて、再びRTOSの優先度スケジュールのような話に原点回帰していくことになります。

おわりに

リアルタイム処理を考える場合、実行されるタスクの数だけプロセッサがあると、スケジューリングを考える余地がなくなってしまうため、複数プロセッサを内蔵したマルチコア時代のCPUで RTOS の面白さが低下してきている部分は多少なりあるのではないかと思っております。

一方で、並列度と計算時間の両方を考えないといけないFPGA上でのリアルタイム処理は、まだまだプログラマが考える部分が多数残っている点で、まだまだ面白い分野ではないかと思う次第です。

ちなみに、当サイトは基本的にリアルタイム処理の観点での話が多いのですが、昔に比べるとエッジでもRTOSが使われる比率が下がっておりあまりなじみのない方も増えているように思います。 少し調べてみるととても分かりやすそうなサイトを見つけたので最後に勝手ながらリンクを張らせていただきますね。

hiroyukichishiro.com

試しに Zenn の github 連携を使ってみた

Zenn試してみました

Zennだと github に置いた Markdown と連携できるということを知って、さっそくいくつか最新の試みを試してみました。

イムリーな要素を含むブログと、Tipsとして取り回しよく蓄積したい情報とでうまく使い分けると便利そうには思います。

今回おいてみた記事

とりあえず3本ほど紹介。

zenn.dev

zenn.dev

zenn.dev

今後どうしよう

以前、Qlanch がなくなった時もおいていた記事を慌てて github に回収した経緯もあり、Hatena がなくなるとは思えませんが、長期保存という点では github が適しているので、うまく住み分けできればとは思います。

github.com