Ryuz's tech blog

FPGAなどの技術ブログ

MIPI-CSI2仕様を調べてみる

はじめに

ご承知の通り MIPI 仕様自体は無償公開あれているような代物ではありませんが、多くの FPGA ベンダーが、無償の IP コアを提供していることもあって、趣味レベルであっても FPGA などで画像取り込みができるような状況になっています。

一方で、それらを駆使して波形の観測などしていると、ある程度は自力で仕様が見えてくる部分があります。

そこで、ここ数年かけて私が独自に解析した結果をブログにまとめようかと思ったのですが、既に、私程度の情報はすべて記事になってることに気が付きました(笑)。 自分用備忘録に以下に張っておきます。

【徹底解説】 MIPIなんて難しくない! 新人がゼロから学ぶMIPI規格 ~ 基礎編 第1回 D-PHY とは? ~ - 半導体事業 - マクニカ

www.macnica.co.jp

docs.amd.com

nahitafu.cocolog-nifty.com

私は RAW 10bit の画像を扱う事が多く 8bit にパッキングされているのを並び替えるのがいつも面倒に思ってますが、よく考えると、4画素が 5バイトになっているわけですが、そのまま 8bit 画像として表示しても 5ピクセル周期で縦線が入るだけで、それっぽい絵になるのはデバッグには便利なのかもしれません(8bit RAW として簡易表示するだけなら 5バイト毎に1バイト捨てればよい?)

CRCECC

これは受信側は計算しなくても合っている前提で無視することもできるのですが、いわゆる普通の CRC-16 やハミング符号のようです。 ただし送信側を作る場合は、受信側次第では計算する必要があるのではないかと思います(送受とも自作する場合はどうでもいいですが)。 今だと AI におねだりすれば普通に計算コードを出力してくれたりもします。

昔データをキャプチャして分析したところ、CRCWikipedia などにもある、普通の CRC-16-CCITT のようでした。

ja.wikipedia.org

ECC が苦労した記憶がありますが、一般論やネットの断片的な情報や、無償のIPでいろいろ生成させたりとかで、ある程度のアタリが付いた記憶があります。

emb.macnica.co.jp

www.marubun.co.jp

https://www.eevblog.com/forum/microcontrollers/calculate-ecc-byte-for-mipi-dsi-header/

最近になって

  • 受信側のテストベンチとして送信信号を作りたい
  • 自作グローバルシャッターカメラでオレオレプロトコル以外にMIPIに寄せたものも試しておきたい

など、送信側も書いてみようという動機があり、ECCはおいておいて、少しコードを書きかけたら GitHub Copilot が勝手に ECC 作る function を吐き出してくるなんてびっくりするような事がありました。

いま改めて、ChatGPT さんに聞いてみると、この手の解析しながら調べながら作ってくと結構面倒なところ、いろいろ出力してくれました。

chatgpt.com

もうなんでも AI で出来てしまう時代ですね。

少し作ってみた

あれこれと試行錯誤中のコードを下記に作成中です。

github.com

もちろん、正規のものではなく、独自解析によるものなので嘘が含まれてる可能性も大いにありますが、「FPGAボードにちょっと MIPI カメラ繋いで遊んでみよう」みたいなホビーな人の参考になれば幸いです。

GitHub Copilot を使って 古いブログを引っ越ししてみた話

はじめに

LUT-Network の古い記事が結構ココログの方にあったのですが、急に消えてびっくりしてる人が居たらいけないので(いないとは思いますが)、こちらでも移動先 をアナウンスしておきます。移動しただけで消えてはおりませんのでご心配なく。

LUT-Network や BinaryBrain の黎明期の 2018~2019年 頃の記事がここにあったのですが、当時は http しか使えず、下手に Facebook とかに張ろうとしても弾かれたりとかしていた記憶があります。

現在は https対応しているようですが、2020年ごろから、こちらの hatenaブログに移行したこともあり気づきもせずに http のままにしてしまっておりました。

悪くはなかったのですが、古き良きブログという感じでしたので、当時としては Qiita / Zenn / Hatenaブログなどと比べてしまうと、記事整理などをしようというモチベが起こりにくく、ほったらかしになっておりました。

Niftyさんに関しては、古き Nifty-Serve の時代から使い続けており、当時のメアドも引き続き健在だったりもするのですが、最近、さくらインターネットさんの方で、独自ドメインも取って WordPress で Webページも作り直していますので、心をいれかえて記事を引っ越しして、タグ付けとかして再整理を試みております。

これまで筆者本人が、Google検索なしには自分の過去記事が見つけられない というマヌケな状態だったのですが、今回はカテゴリをLUT-Networkとして検索するなど、である程度さかのぼれるようになってきました。

なお、ゆっくり移行しようと思ったのですが、同じ記事が複数個所にあると Google Sercah Console などがいろいろエラーを出してくるので、今回、潔く引っ越しております。 (ココログの方でまとめて下書きに戻す方法がわからず、記事削除しちゃったというのはここだけの話)。

で、その引っ越し作業で GitHub Copilot が大活躍でした。

AI エージェントに手伝ってもらって引っ越し

あまりブログの引っ越しというのはやったことはなかったのですが、AI さんに聞くと Movable Type形式で移行できるけど、画像やファイルなどは手動で移行しなければならないとの事。

まあ当然100を超えるブログの中を一個づつ手作業で画像移動なんてやってられるわけもないので、自動化しようという話になります。

幸い Movable Type形式というのはテキストファイルのようですので、従来であれば、Python あたりで、エクスポートしたファイルをパースして、URLリストを作って、画像やファイルを一括ダウンロードして、記事内のURLを張りなおすわけですが、今回はその作業を GitHub Copilot の Agent さんに丸投げしてみました。

で、2つの Python ファイルを作ってもらいました。

ファイルダウンロードツール

簡単な指示書をテキストで作って、指示したのですが、大まかな指示内容としては、

  • Movable Type形式を読み込む
  • photos/ で始まる元画像のURLを抽出してディレクトリ構造を保ってDL
  • images/ で始まる表示用リサイズ画像のURLを抽出してディレクトリ構造を保ってDL
  • files/ で始まるダウンロードファイルのURLを抽出してディレクトリ構造を保ってDL

といったもので、わりとサクッと短い python を出力してくれて、ファイルダウンロードは完了しました。

ダウンロードしたファイルを今度は ftp で、サクラのサーバーに転送して準備完了。

インポート用Movable Type形式 の生成

同じく下記のような指示で Agent に Python を作ってもらいました。

  • Movable Type形式を読み込む
  • 旧URLの画像やファイル参照を新URLに書き換える
  • 旧URLで記事間リンクしている部分はリンクを解除して、リンク内容をレポートファイルに書き出し
  • その他、それでも旧 URL が残る箇所があれば警告を出す

といったもので、こちらも初回、なんか photos//photos みたいなへんな URL 吐いたミスはあったもののリトライで完成。

WordPress でインポート

WordPress のバックアップを取ったのちに、インポートするとわりとあっさり記事が移行できました。

例えば、私が初めて MLP 方式ではなく 微分可能LUTモデルで直接的なバックプロパゲーションに成功したときの記念記事なんかも、埋め込み付きでリンクできるようになりました。

rtc-lab.com

後は、タグ付けなどを整理しながらパーマリンク名弄ったりしつつ、先に出力したレポートを見ながら重要な記事中心に手作業で、自分好みに再整理中です。

WordPress だと、クイック編集とかで、記事の一覧を見ながら属性だけを編集していけるので、記事整理は結構捗りました。

これで、過去記事を自力で探せるようになったわけです(笑)。

おわりに

今回は一回きりの使い捨てツールだったので、自分のブログのURLや固有のファイルリンクなど、ソースコード中に埋まっている汎用性のないツールを生成してもらったのですが、こういう使い捨てコードにはAIは特に向いている気はします(何だかんだで品質を気にするコードは中をちゃんと読まないといけないので)。

自分で書いた場合とどちらが速いのかと言われれば、まあまだ微妙だったり、人間がお馬鹿になりそうとかいろいろな話もあるわけですが、良くも悪くも使わないという選択肢はなくなりそうなので、うまくAIと付き合っていければとは思う次第です。

ちなみに Rust や Python を使うときと比べると RTL開発にはまだ人間の利が強そうには感じてますが、いつまで人間が勝てるやら(苦笑)。

Windows版 GOWIN EDA を WSL2のコマンドラインから使う工夫とか

はじめに

GOWIN の FPGA は低価格で買えるものも多いため、夏休みの自由研究的に使う方も多いのではないでしょうか?

GOWIN EDA はユーザー登録を行って、ライセンスファイルを発行してもらわないと使う事が出来ません。 私は、USBを繋いでプログラムを行ったり、埋め込みロジアナ(Gowin Analyzer Oscilloscope)を使うことなどと、WSL2内のMACアドレスが固定されないことなどを考慮して、あまり深く考えずに WindowsMACアドレスでライセンスを申請してしまいました。

一方で、普段の開発は殆ど WSL2 で、しかも極力コマンドラインで済ませようとする派です。

そこで前々から WSL2 から Windows版 の GOWIN EDA を使う工夫をいくつか行っていましたので、一度整理しておこうと思います。

どこかに書いておかないと、主に自分が困るというのが一番の理由ですが(笑)

WSL から Windows 版のコマンドを呼び出せるようにする

コマンドライン派の方が一番よく使うコマンドは gw_sh かと思います。

そこで下記のようなスクリプトを作って、パスの通ったところ(例えば ~/.local/bin とか)に投げ込みます。

#!/usr/bin/bash
/mnt/c/Gowin/Gowin_V1.9.11.03_x64/IDE/bin/gw_sh.exe $@

これで WSL の中から gw_sh コマンドが使えるようになります。alias とかでもいいかもしれません。 もうちょっと頑張れば、環境変数でバージョンを切り替えたりもできそうな気がします。

次に、programmer_cli も同じように使おうとしたのですが、どういうわけか、こちらはディレクトリを移動しないとうまく動いてくれませんでした。

#!/usr/bin/bash
cd /mnt/c/Gowin/Gowin_V1.9.11.03_x64/Programmer/bin/
./programmer_cli.exe $@

上記のままだと、fs ファイルは絶対パスでしてしないといけません。

私は Makefile の中から programmer_cli を呼んでいたので

run_wsl: $(FS_FILE)
    programmer_cli --device GW5AST-138C --run 2 --fsFile '$(shell wslpath -w $(FS_FILE))' --location 11585

のような wslpathWindowsから見た絶対パス に変換してから呼び出すようにしました。

openFPGAloader

zadig によるデバイスドライバの入れ替えが必要なので、GOWIN の programmer と共存できませんが、openFPGAloader も良く使います。

こちらも、USB は Windows 側のまま使う方が楽であるため、Windows版のopenFPGAloader を使う事も多いです。

手っ取り早くはこちら のものをインストールすることなのですが、私は Tang Mega 138K Pro Dock を使うときにより新しいバージョンが必要となってしまい msys2 をインストールして pacman でインストールしました。

当時の入れ方を忘れてしまったのですが、今 Gemini さんに聞くと

pacman -Syu
pacman -S git make mingw-w64-x86_64-gcc mingw-w64-x86_64-libusb

で入るようです。

そして、また同様に WSL2 で

#!/usr/bin/bash
/mnt/c/msys64/mingw64/bin/openFPGALoader.exe $@

というファイルを作りました。

コマンドラインでプロジェクトをビルド

プロジェクトのビルドもコマンドラインだけでやりたくなります。

gw_sh に tcl スクリプトを渡せば、ある程度コントロールできるようです。

私は Makefile から環境変数経由で tcl スクリプトに値を渡して合成するために gowin_build.tclいうファイルを作りました。

使い方はこちらの Makefile などにありますが、WSL2 の場合は WSLENV という環境変数を用意しておけば、WSL から Windows 版の gw_sh に環境変数が渡せます。

絶対パス相対パスに変換

GOWIN EDA を使っていると IP を生成したり、Analyzer Oscilloscope を設定したりするのに、GUI 版のプロジェクトの作成も避けられません。

幸い、WLS のディスク領域(\\wsl.localhost で始まるパス)にプロジェクトを作っても Windows 版の EDA は問題な開いてくれます。

しかしながらプロジェクトファイル内でプロジェクトの外のディレクトリを参照するとファイルのパスが絶対パスになってしまうようです。

私は、いろいろな RTL を異なるプロジェクトで使いまわす関係上、プロジェクトの外のパスを多用しています。

一方で、プロジェクトのなかのソースファイルが埋まっている .gprj ファイルは XML 形式のようです。

そこで、中の絶対パス相対パスに書き換えるツールを AI に作ってもらったのが こちら になります。

この手のものは、日本語で指示するだけで AI が一瞬で作ってくれるので、ホント凄い時代になったものです。

試したところ、開くだけなら問題なく開ける(保存するとまた絶対パスに戻る)ようですので、git commit 前に相対パスにしておけば、別の場所で clone しても大丈夫になりそうです。

おわりに

以上、どこかにメモを書いた気もするのですが検索しても出てこないので、ここにノウハウを纏めておきます。 割と行き当たりばったりで整備してきたので、「もっといい方法あるよ」というのはいろいろご意見ありそうには思いますが。

なお、今更ですが、申請すればフローティングライセンスももらえるという噂を聞きました(笑)。 ただし、仮にそうだとしても「FLEXlm などの設定が面倒」という理由も発生しそうですので、きっと少しは需要がある事でしょう。

GOWINで加算の性能を調べてみる

はじめに

最近 GOWIN を触る機会も多いのですが、他のベンダーさんの FPGA とくらべてあまり深いところまでは触っていません。

また、 AMD の CARRY8 のようなものが無いのではという噂も聞いたのと、LUT4 であるという点なども加味して、大雑把に性能を調べておこうと思って触ってみました。

やったこと

合成に時間がかかるのは嫌なので、 Tang Nano 4k (GW1NSR-LV4CQN48PC6/I5) をターゲットに、カウンタの幅を変えて合成し、レポートの Fmax を調べるという事をやりました。

GOWIN EDA は V1.9.11.02 (64-bit) [Windows版]を使っています。

合成したコードは下記の通りで、WIDTH を変えて実験します。

`default_nettype none

module tang_nano_4k_adder
        #(
            parameter int   WIDTH = 32
        )
        (
            input   var logic           in_reset_n,
            input   var logic           in_clk,     // 27MHz

            output  var logic   [0:0]   led_n
        );
    

    logic   [WIDTH-1:0]  counter;
    always_ff @(posedge in_clk) begin
        if ( !in_reset_n ) begin
            counter <= 0;
        end
        else begin
            counter <= counter + 1;
        end
    end
    assign led_n[0] = ~counter[WIDTH-1];

endmodule


`default_nettype wire

合成結果のレポートで fmax を探すとこのように出てくるようです。

結果

本格的にやるならスクリプト書いても良かったのですが、 手っ取り早く手動で合成を繰り返しました(笑)。

bit数 MHz LUT Regs
1 レポート無し 1 1
2 368.474 3 2
3 322.238 4 3
4 296.991 5 4
5 342.906 7 5
6 378.582 8 6
7 330.006 9 7
8 326.435 10 8
9 318.041 11 9
10 348.501 12 10
11 306.913 13 11
12 335.184 14 12
16 311.387 18 16
32 203.419 34 32
64 148.368 66 64
128 67.424 132 128
256 39.306 262 256

おわりに

なんとなく素直に LUT の数が増えていきながら fmax も順調に下がっていくようですね。ただレジスタは桁数ぴったりですが LUT は時々余分に増えているので、単純な全加算器が連なっていくのではなく、何かしらキャリールックアヘッドみたいなのがあるのでしょうか?

とりあえずRISC-V なんかを作る場合、RV32 はいいけど RV64 は大変そうだという気がしてきます。

また Tang Nano には無縁ですが、最近は FPGA に繋ぐメモリも 4G 以上になるケースも増えていて、DAMのアドレッシングも 64bit じゃ足りなんないなんてこともあるので、大きな数を扱うときは気にしておいた方がいいのかもしれません。

逆に、へんなことせずに 32bit 以下ぐらいで使う分には十分実用的な速度が出ていそうに思いました。 中のアーキテクチャはどうなっているのでしょうね。資料があるならいつかまた時間のある時に見てみたいと思います。

アクティブマルチスペクトルカメラを妄想してみる

今作っている Spartan7 + オンセミ社 PYTHON300 イメージセンサ、やっと絵が出始めました。

お手製のPYTHON300カメラモジュール

で、いつも通りの LUT-Network への応用や、オプティカルフロー計測、その他リアルタイムアクティブセンシングいろいろ考えているのですが、その中の応用例の1つを張っておきます。

しれっと PMOD つけているので外部でLED基板を作ればシャッター同期制御が簡単にできるはずです。

マルチチャネル計測はだいぶ以前から言ってるネタですが、別件で温めていたのでXには書いたけどブログに書いてなかったと思うのですがだいぶ前に書いた絵があるので張るだけ張っておきます。

リアルタイムフィードバック要素よりデータ分析要素が多いので私的にはそこまで重視はしてなかったのですが、こちらの方が需要はありそうなので。

動作例

  • 一応アクティブセンシング
  • 照明とシャッターの同期はグローバルシャッターでしかできない
  • とにかく安くスペクトル方向に何か測れる
  • 照明制御へのフィードバックでいろいろな変化が付けられる
  • 60fps相当のデータレートで16ch撮影できるので、深層学習の素材としても良い
  • 速いので動く物体を対象にできる可能性
  • MIPI なので Raspberry PI にもつながるかも?

などなど?

照明制御は1例ですが、FPGA弄ると外界と同期しながら多数の情報が取れるため、いろいろな応用があるかとは思います。

一方、当方はあくまでリアルタイムコンピュータ推しなので、単なるセンサーにせずに何かもっと即時性のある出力先を探したいところではありますが。

メタステーブルについて考えてみる

はじめに

もう専門の方から一直線にマサカリが飛んできそうなタイトルで怖いんですが、ちょうど今週末は非同期周りを整理していて X でも少し盛り上がったのでネタにしておきます。

組み込みやっていると、チャタリングとかシュミットトリガとかはよく聞くわりに、HDL 書かない限りはあまり聞かない メタステーブル ですが(偏見?)、FPGAでの非同期バグでは鉄板のネタなので触れておきたいと思います。

再現性がないのでやらかすとデバッグがとても厄介なのですよね。

ちなみに私がメタステーブルを最初に勉強したのは定本ASICの論理回路設計です。

メタステーブルとは

フリップフロップ(FF)に値を正しくラッチさせるためには、セットアップ時間、ホールド時間、VIL/VIHなどの電圧範囲を守る必要があります。

これらが守られてない場合、出力が 0 になっても1になっても文句は言えないわけですが、0でも1でもない第3の状態としてFFがメタステーブル状態に突入することがあります。

メタステーブル状態に入ると、1にも0にも確定していない不安定な状態が指数分布に従う時間だけ続いて、やがて0か1に倒れて安定状態になるそうです。

「このようなことが起こると困りますよね。定格はちゃんと守って使いましょう。」で済めばいいのですが、そうはいかないのでこの話になるわけです。

人間がボタンを押すとかの非同期な事象に加え、異なるオシレータからのクロックドメイン同士の通信だとこのメタステーブル状態の発生は基本的に避けられません。

同じ100MHz同士であったとしても、オシレータ は数 ppm 程度の誤差は常に持っていますので、100MHzもあれば1秒後には100サイクルとかのオーダーでずれてしまっているわけで、その間に 100回ぐらいメタステーブルになりうる条件を作ってしまいます。

ただし、メタステーブル状態はずっと続くわけではないので、一定時間で収まり、ダブルラッチなどしておけば、次のクロックまでには高い確率で収まります。

ChatGPT 曰く

メタステーブル状態の発生に関する MTBF(Mean Time Between Failures)の式は以下のようになります:

\text{MTBF} = \frac{1}{f_{\text{clock}} \cdot f_{\text{data}} \cdot T_{\text{window}}} \cdot e^{\frac{T_{\text{res}}}{\tau}}
  •  f_{\text{clock}} : クロック周波数
  •  f_{\text{data}} : データ入力の切り替わり頻度
  •  T_{\text{window}} : メタステーブルが発生する時間幅(セットアップ+ホールド)
  •  T_{\text{res}} : 解決のために使える時間
  •  \tau : フリップフロップ固有の時定数

だそうです。

確率は常にゼロにはなりませんが、通常はダブルラッチするだけで数万年オーダーの MTBF となるそうなので、ちゃんとあしらっておけば、確率的にはECCのないメモリのソフトエラーなどの方がよっぽど心配すべき事象になる程度には下げられるのだと思います。

非同期信号のラッチ

いろんな解釈があると思いますが、入力信号を綺麗にラッチするために、入力クロックの位相をPLLで調整していっていい感じの場所を探すなんてことはよくやるわけです。

で、そんなノリでクロックの方の時間をずらしながら図示してみると、

データとクロックの関係

すべてのラッチ条件が満たせていてスペック通り0や1が取り込める領域と、そうでない領域があります。

とはいえ、「非同期を受けるときはダブルラッチしなさい」とよく訓練されているプログラマだと、とりあえずダブルラッチしているので最終的に0が1に値が倒れるので、早い方か遅い方のどちらかをラッチしたように見えてしまいがちです。

しかし、0と1のどちらかに倒れる話と、メタステーブル状態に突入する話は全く別の話なのでこれは注意しておく必要があります。

FPGA での非同期の扱い

RTL で書いてちゃんと制約する

私は Verilator などの OSS のシミュレータを使う関係上、あまりベンダーの IP は使わずに RTL でいろいろなものを書いてしまいがちです。

その際 AMD の場合、楽に制約を設定するために ASYNC_REG というアトリビュートがあります。 UG912にこのアトリビュートの詳しい説明があります。

UG912の図

これは何者かというと、合成時に関して言えば、メタステーブルが伝搬しにくいように受け側のFFを複数並べてダブルラッチ/トリプルラッチなどを作るときにFF同士を最短経路で繋がるように配置配線を制約してくれるというものです。

折角ダブルラッチ構成にしてもFF同士の距離が離れてしまうと、せかっく元のFFでは1クロック時間たってメタステーブルが収まったのに、取り込む側が配線遅延でまだメタステーブル中だった信号をラッチしてしまい自分もメタステーブル状態に突入する という身もふたもないことが起こってしまいます。

ベンダーの用意したライブラリを使う

AMD の場合、例えば UG974 などを見ると、下記のようなマクロが用意されています。

  • XPM_CDC_ARRAY_SINGLE
  • XPM_CDC_ASYNC_RST
  • XPM_CDC_GRAY
  • XPM_CDC_HANDSHAKE
  • XPM_CDC_PULSE
  • XPM_CDC_SINGLE
  • XPM_CDC_SYNC_RST

これらはクロックドメインをまたいで信号をやり取りするときの、定石的な方法をブラックボックス化してくれているようです。

実は今まで使ったことなかったのですが、使えるときは使った方が安全 かなと心を入れ替えつつ、そうはいっても使えないときに困らないようにRTLで互換部品も書いておこうと、今日この辺りを書きながら勉強してました。

案外普段自分がやってる方法と同じものもあれば違うものもあり勉強になりました。

衝撃だったのが、FFの段数がデフォルトで4なのですね。 トリプルラッチどころじゃないですね。 いつもダブルラッチで済ませているので不安を覚えました。 段数を増やすほどにMTBFは増大していきますので、周波数にもよりますが、品質の問われる用途に応じて増減させるものと思われます。

身もふたもない話

そんなプリミティブなところ弄くらんでも とりあえず非同期FIFO使っておけばOK というのはその通りで、ほぼすべてのFPGAベンダーが非同期FIFOを提供していますね。

AMD も UltraScele 世代だと、IP 生成以外に XPM_FIFO_ASYNC などのマクロも使えるようですね。

逆に 非同期FIFO があまりにもうまく臭いものに蓋をしてくれているせいで、このような話を調べる必要も普段はあんまりなかったとも言えます。

グレイコードに関する疑問

ちなみに IP 使うの避けがちな私は、超昔に書いたグレイコード使うFIFOを未だによく使っていたりします。

とはいえグレイコードには少し疑問も残っていて、複数bitが同時にメタステーブルを起こしてしまう可能性、すなわち、クロック周期からメタステーブルに入る条件のマージン分を引いた値に、全bitの遅延ばらつきを納めておかないと、メタステーブルを起こすのは高々1bitで、それが1に倒れても0に倒れても前後どちらかの符号になるだけ というグレイコードFIFOの前提条件が壊れる件です。

よく

set_max_delay -datapath_only -from [get_clocks clk1] -to [get_clocks clk2]  2.00

のような制約を書くのですが、結局、遅延時間はいくつを書けばいいのかと。

もちろんこれがクロック周期以上ずれるとメタス以前の問題で符号が化けるので、クロック周期より小さい値ではないといけないとは思うのですが、具体的にいくつにするのが良いのかなかなかよくわからず。

もっと言うとバラつきが問題なので set_max_delay というのもそもそもなんか違うんじゃないかと思ってみたり。

XPM_CDC_GRAY の中身にいったいどんな制約が埋め込まれているのか興味津々なのですが、合成結果だけ見ても分からないのですよね。

どう書くのが正解なのでしょうね。いやむしろ XPM_CDC_GRAY を使うべきなのでしょうね。

おわりに

なんとなくとりとめもない記事になってしまいましたが、こんなことをしていた1日でした。

余談(VIH/VIL)の話

今回FPGAの中の話でしたが、FPGAの外だと、Hとみなせる電圧(VIH)、L とみなせる電圧(VIL)に規定があります。

で通常はあまり問題ないのですが、I2Cみたいにプルアップ抵抗からの給電でHレベルを作るようなバスだと、電圧の立ち上がりがとても長いケースがあります。

そうするとFPGAの数百MHzなんかでラッチすると、電位が VIH と VIL の間にいるときに何度もラッチしてしまって、中にはメタステーブル状態に入って 0 や 1 を行き来することがあります。

チャタリングとはまた違う現象で、電圧は単調増加しているのになぜかラッチ後のデジタル波形に髭が出てくるんですね。

対策は、例えば8サイクルに1回だけラッチするとか、通信している仕様にあわせてラッチ周期を間引くことのようです。

間引いてしまえば、偶然1個メタステーブルを起こすようなタイミングに来ても、前後は正しくラッチされますので、0と1どちらに倒れても I2C としてのプロトコルに不整合は起こらなくなるわけですね。

いやはや、非同期系はほんと面倒なことが多い。

リアルタイムコンピューティング的CNN再考

はじめに

当方はFPGAを使ったリアルタイムコンピューティングをメインに活動しております。

流行りの深層学習は私の中では One of them でしかないつもりなのですが、それでも昨今どうしても無視はできないので、リアルタイムコンピューティング観点でCNNを眺めなおしたらどうなるのかというのを再考察してみたいと思います。

まずリアルタイムコンピューティング的には

  • 入力をなるべく低遅延で出力に反映させる
  • 情報は古くなると価値が下がるという前提で、価値が最大化するようアルゴリズムと計算機アーキテクチャをセットで考える

の2点を大事にしています。

世の中は計算している間にどんどん変化していきます。壁にぶつかってからブレーキを踏んでも遅いし、ロボットが流れてくる不良品の隣の良品を廃棄しても困るわけです。

CNNを考えてみる

当方は画像信号処理を扱うことが多いので、無難にCNNの話をします。よくあるこんなやつです。

よくあるCNNのアーキテクチャ

フルサイズで入力された画像を順番に各レイヤーで処理して、途中でプーリング処理を挟んで小さくしながら、でも特徴量を表すチャネルの深さは深くなりながら(次元数を増やしながら)次々と処理していきます。

特にこのプーリング処理で縮小されていくのは重要で、前段では細部を、後段では全体を認識することができます。

画像処理でもよく画像ピラミッドを作りますが、縮小された画像で全体を把握しつつ、オリジナルの解像度に近いところで細かい計測をします。

さて、この段階で、入力から出力までのレイテンシが長そうだというのがなんとなく想像がつくわけです。

リアルタイムコンピューティング的に考えてみる

リアルタイムコンピューティング的には入力をなるべく低遅延で出力に反映させるという事がしたいわけです。

ここで唐突にセマンティックセグメンテーションを考えてみます。

カメラからは左上のピクセルから順に画素が読み出されてきて計算機に入力されるわけですが、1画素新しい情報を得たら、なるべく早くその画素がどういうクラスなのか情報を出力したいわけです。

となると、まあ、下図のようなアーキテクチャに考えが至るわけです。

リアルタイムコンピューティング的CNN(妄想)

まず、何はなくとも、入力されたらなるべくすぐに、赤い線のようにまっすぐと入力画素に対応する部分の出力結果をアップデートしたいわけです。

そうはいってもこれだと、例えば 3x3 の畳み込み層だと遅延は2ライン貯める時間で済んでも、高々周辺の9画素の情報だけで推論しろという無茶な話になります。

ここで、本サイトお得意の1000fpsの高速度カメラなどの話になります。世の中の変化より十分早くオーバーサンプリングしておけば、少し大雑把な周辺情報は1フレーム前の情報でもある程度あてにできるよね? という、撮影対象のダイナミクスよりもサンプリングレートを上げる話に持っていきます。

要するに例えばCNNでありがちな犬猫認識をしつつ動きを追う場合、「猫パンチの手の位置は素早く動くが、1/1000秒後にそこにいた猫が犬に入れ替わったりはすまい」という前提で、「手の動きは最新フレームで追いかけるが、その手が猫のものか犬のものかは少し古い画像も使って認識してもいいよね」というアルゴリズムにするわけです。

で、これを以前やりかけたまま放置してるのが、以前書いた

ミップマップを使ったIIRフィルタライクなRNNを考える - Ryuz's tech blog

です。

まあ、学習させるのがしんどくてそのうち高価なGPU買う機会があったらやろうと放置したまま幾星霜。

しかしまあそういう思想なわけです。

Layer1 で最短経路で出力を更新しつつ、Layer2 以下で Pooling して縮小した画像を処理してさらに下の層へ。一方で、下の層の認識結果も同様にアップコンバートしながら上に戻していくわけです。

各レイヤーでそれぞれのサイズ感での認識ができればな、というのと、過去のデータの更新という意味で RNN 的な要素も取り込めればと思ってしまうわけです。

計算効率をどう上げるか

最近Xのこのへんで書いた話なのですが、画像ピラミッドの縮小に目を付けた話です。

畳み込み層といっても中は IM2COL した後は普通の全結合層が入っているわけですが、全結合層を考えると入力100chで出力100chの時、10000接続あるわけでch数の二乗で演算量とパラメータ数が増えていくわけです。なので画像サイズが1/4になってもチャネルが2倍になると計算量は元と変わらなくなります。

一方、私のやってる LUT-Net の場合、物理制約で 6入力LUTのカスケード数で例えば3段の場合 63=216固定であり、ch数が倍になっても接続は倍にしかなりません。どういうトリックかというと、全結合層の場合、1つのチャネルは全部の出力にもれなく重複して繋がってるところ、LUT-Net ではどんどん重複が減って疎結合になっていく代わりに計算量がリニアな伸びに制限できます。

でまあ、LUT-Net の場合はパラメータ入れ替えできないからこういうことはできないのだけど、仮にDSPとか普通のネットに同じようなポリシーで疎結合化をできたとしたら、単に演算量の話だけすると、1, 1/2, 1/4, 1/8 ・・・ と減っていくよねと。 でほんとにそんなことが出来たら 1 + 1/2 + 1/4 + 1/8 +・・・ = 2 なので、2レイヤー分の演算ユニット入れておけば全部うまいこと回せないかと。低遅延&演算器稼働率100%が目指すところではあるわけです。

演算ユニット2つで無限の深さをやれないか?(妄想)

猫が猫かどうか認識する機能なんて、猫のサイズを同じサイズまでリサイズすれば同じ認識器でいけるはずだし、単純に探索範囲=画像サイズで縮小するほど処理量減っていくはずだし、なんか、なんとかなりそうな気がしなくもないところです。

まあ、実際問題、どうやって学習させるんだとか、精度出るのかとか、いろいろ未知数というかほとんど妄想なのですが、晩酌しながら考えて呟いてしまったわけです。

おわりに

上記のような考えが、まあ半分以上妄想として、じゃあ全部妄想かというとおそらくそんなことは無くて、リアルタイム計算にもっと適したネットワークモデルというのは少なからず、今のCNNより改善できる形はあるであろうし、それを学習させる方法なんかもまだまだ未探索な世界なんじゃないかと思います。

また上記はあくまで「空間認識」において、広い範囲を見るために縮小という概念を時間方向に展開して効率化を試みたわけですが、RNN構造はそもそも「変化に含まれる情報の識別」という意味合いも持っておりますので、学習データ次第ではこの構造で例えばジェスチャー認識のようなもっと動きパターンに起因するものを認識できる可能性なども出てくるかもしれません。

こういったシステムを実際に作ってみて、試してみるというようなことができるのが、FPGAの良いところであり、計算機科学に興味を持つメリットが生まれてくるところです。

どこかの誰かの新しいアイデアに繋がれば幸いです。