新千葉 ガーベージ・コレクション

FPGA マガジンやインターフェースで書けなかったこと等をちょぼちょぼ書いてます。@ryos36

ラズパイ カメラ V2.1 と FPGA

Zybo Z7 とラズパイ・カメラ V2.1 を購入。
忘れないようにメモ(備忘録)

https://github.com/allwinner-zh/linux-3.4-sunxi/blob/master/drivers/media/video/sunxi-vfe/device/imx219.c
android_kernel_asus_ze55xml/imx219.h at master · jerdog/android_kernel_asus_ze55xml · GitHub

どうやら 0x30eb の記述。このなぞの 6 ワード(6byte) で起動がかかるみたい。そのまえに GPIO を操作して起動しないとダメだけど。

0x6620 の隠し機能(?)に書いてるコードもある。
nuttx/camera_ext_mhb_imx219_pi.c at master · MotorolaMobilityLLC/nuttx · GitHub

この場合、起動シーケンスがちょっと変わる。たぶん、高解像度の時かなんかに必要なシーケンス(そしてアンドキュメント)だと思われる。imx219 の pdf の資料には書いてないから。

Zybo Z7 では CAM 用の GPIO に CAM_CLK と CAM_GPIO がある。CAM_CLK のピンは以前のラズパイカメラ(V1.3) では単に LED に繋がっていた。V2.1 はどうも LED がないらしい。今のところ CAM_CLK は on/off しようが I2C とのやりとりはできる。重要なのはどうやら CAM_GPIO のようだ。

参考までにラズパイではそれぞれ GPIO5 と GPIO21 に繋がっている。GPIO5 の使い方の例は次のところで見つけた。
https://www.raspberrypi-spy.co.uk/2013/05/how-to-disable-the-red-led-on-the-pi-camera-module/

試してないし、V2.1 で有効かはわからない。
私が実験した結果では 電源オン後、CAM_CLK を0のまま、CAP_GPIO を 1 にしその後 I2C の通信をすれば、すくなくととも I2C は動くことがわかっている(映像までは確認していないので CAM_CLK がなんらかの影響がある可能性はある)。

下がその I2C をキャプチャした画面。
f:id:ryos36:20180221121339p:plain

そして UART に 0219 を出力。
f:id:ryos36:20180221121420p:plain

デザインはひっちゃかめっちゃか(死語)だが、正しい。
f:id:ryos36:20180221121500p:plain

やりたいことは WindowsTeraTerm から Zynq の PS の UART を通って Zynq PL の UART との通信が一つ。
これは Zynq の PS の UART はただの橋渡し役。いろいろ調べたが、私のやりたいように、うまく接続できないことがわかっている。
Zynq の PL の UART まできたら、Polyphony で解釈して I2C と直接話す。途中までできた。
vio である Virtual な GPIO を on にすると、I2C のプログラムが走り出し imx219 と通信して ID をとってくる。そして、その16進の値をストリングに直して UART へ送る。すると、WindowsTeraTerm で情報が見れる。というところまでは出来た。ソフトウェアはほとんど関与しておらず、Polyphony (Python による高位合成)で達成している。

I2C の確認

AXI の I2C で I2C のプロトコルの確認をした。I2C は MSB からビットを送るので注意が必要という事がわかった。
毎度のことだが FPGA の部屋の記事が役に立つ。
FPGAの部屋 Vivado 2013.4でAXI IIC v2.0 を使用した時のIOBUF

f:id:ryos36:20180216133044p:plain

まずはデザイン ILA のデバッグマークを付けている。

ソフトが必要なのでプログラムを書く。

int main()
{
    int rv;
    uint8_t recv_data[2];
    uint8_t send_data[2];

    init_platform();

    print("Hello World\n\r");
    iic_config0 = XIic_LookupConfig(XPAR_AXI_IIC_0_DEVICE_ID);
    rv = XIic_CfgInitialize(&iic0, iic_config0, iic_config0->BaseAddress);

    XIic_Reset(&iic0);
    XIic_DynamicInitialize(&iic0);

    send_data[0] = 4;
    rv = XIic_DynRecv(iic_config0->BaseAddress, ZC706_I2C_MUX_ADDR, recv_data, 1);
    printf("%d\n", rv);
    rv = XIic_DynSend(iic_config0->BaseAddress, ZC706_I2C_MUX_ADDR, send_data, 1, XIIC_STOP);
    printf("%d\n", rv);
    rv = XIic_DynRecv(iic_config0->BaseAddress, ZC706_I2C_MUX_ADDR, recv_data, 1);

    printf("xrecv_data = %d\n", recv_data[0]);

    cleanup_platform();
    return 0;
}

実行結果
f:id:ryos36:20180216133529p:plain

追伸

情報としては
I2Cについて
ここが正確なようだ。

当初、picfun com というサイトをみていたのだが、
LSB から送信しているように見える図。NACK に関しての記述がない。などから不正確なことが分かった。

BNN-PYNQ の走り書き

アドレスはハードコーディング

library/driver/platform-xlnk.cpp:               platform = new XlnkDriver(0x43c00000, 64 * 1024);

FoldedMVInit でメモリアロケーションしている。accelBufIn と AccelBufOut は thePlatform->allocAccelBuffer で取得している。これは最終的に sds_mmap を呼ぶ。sds つかってなくてもね。sds_mmap は UIO の一部を使っているか使ってないかを無視してマップするので危険。使っているか使っていないかはドライバやアプリしだいなので、つかってくれるな~~とお願いしながら使うことになる(と思う。改善されてるかもしれない。)

で IP にこの物理アドレスを教えてあげる。

    thePlatform->write64BitJamRegAddr(0x10, (AccelDblReg) accelBufIn);
    thePlatform->write64BitJamRegAddr(0x1c, (AccelDblReg) accelBufOut);
    thePlatform->writeJamRegAddr(0x28, 0);

0x28 へのアクセスは // disable weight loading mode ということみたい。

writeJamRegAddr は最初の 32 個のシステム用のアドレスをスキップして(つまり 32xsizeof(uint32_t) = 0x80) てアクセスする。この場合 0x43c00000 + 0x80 + 0x28 だね。64bit の場合はリトルエンディアンで 2回 writeJamRegAddr を呼ぶ。

testPrebinarized_nolabel で実際の処理。1つのワードが 64bit ( 8x8 )で、これに切り上げられる。例えば、この場合 28x28 = 784 だけど、832 ビットになる。この LFC ネットワークの MNIST はグレースケールを二値化(0 と 0xff) になっていることを前提としていて、それをされに 0 と 1 に落とし込む。ので 832ビット。

参考までに書くとゼロから作る DL の本では 784 バイトでグレースケールを考慮している。 隠れが1層で 784(バイト) => (50x100) => 10だった?BNN-PYNQ は 832bit => 1000x1000x1000 => 10 。方や float で方や二値。たぶん、精度はゼロから作るの方がいいのか微妙。BNN-PYNQ で私の手書きの9は7になってしまった。

さて、ドライバに戻る。

立ち上げた瞬間はまだネットワークのウェイトが設定されていない。load_parameter で設定する。

extern "C" void load_parameters(const char* path)
{
#include "config.h"
FoldedMVInit("lfc-pynq");
network<mse, adagrad> nn;
makeNetwork(nn);
        cout << "Setting network weights and thresholds in accelerator..." << endl;
        FoldedMVLoadLayerMem(path, 0, L0_PE, L0_WMEM, L0_TMEM);
        FoldedMVLoadLayerMem(path, 1, L1_PE, L1_WMEM, L1_TMEM);
        FoldedMVLoadLayerMem(path, 2, L2_PE, L2_WMEM, L2_TMEM);
        FoldedMVLoadLayerMem(path, 3, L3_PE, L3_WMEM, L3_TMEM);
}

L0_PE = 32, L0_WMEM=416, L0_TMEM=32 う~ん。PE(Processor Entity?)とかSIMD とかよくわからんが、、、各レイヤーのパラメタを設定する。
L0_WMEM が 416 で L0_SIMD が 64 なので 416 x (64 / 32) = 832 bit が入力。
L0_TMEM が 32 で LO_PE が 32 なので 1024 bit が出力。
L1 は 入力 L1_SIMD / 32 * L1_WMEM => 32 / 32 * 512 => 512(あれ?)出力は 64 * 16 = 1024。てな具合にやっていくのでしょう。なんか計算が合わないけど。
HLS 側のソース見ると

static ap_uint<L0_SIMD> weightMem0[L0_PE][L0_WMEM];
static ap_fixed<24, 16> thresMem0[L0_PE][L0_TMEM];
static ap_uint<L1_SIMD> weightMem1[L1_PE][L1_WMEM];
static ap_uint<16> thresMem1[L1_PE][L1_TMEM];
static ap_uint<L2_SIMD> weightMem2[L2_PE][L2_WMEM];
static ap_uint<16> thresMem2[L2_PE][L2_TMEM];
static ap_uint<L3_SIMD> weightMem3[L3_PE][L3_WMEM];
static ap_uint<16> thresMem3[L3_PE][L3_TMEM];
static ap_uint<L4_SIMD> weightMem4[L4_PE][L4_WMEM];
static ap_uint<16> thresMem4[L4_PE][L4_TMEM];
static ap_uint<L5_SIMD> weightMem5[L5_PE][L5_WMEM];
static ap_uint<16> thresMem5[L5_PE][L5_TMEM];
static ap_uint<L6_SIMD> weightMem6[L6_PE][L6_WMEM];
static ap_uint<16> thresMem6[L6_PE][L6_TMEM];
static ap_uint<L7_SIMD> weightMem7[L7_PE][L7_WMEM];
static ap_uint<16> thresMem7[L7_PE][L7_TMEM];
static ap_uint<L8_SIMD> weightMem8[L8_PE][L8_WMEM];

ん?なんで L8 まであるんだ?

    switch (targetLayer) {
    case 0:
        weightMem0[targetMem][targetInd] = val;
        break;

いずれにせよこんな感じで入れている。だから、targetLayerと targetMem と targetInd を設定して val をいれればよい。
バイナリデータがあるので64bit ずつ、

  FoldedMVMemSet(layerNo*2, pe, line, e);

てなかんじ。ファイル名は layerno + pe + "-weights.bin" or "-thres.bin" だ。そうか、sigmoid つかうかわりにスレッショルド使っているのか(予測)。

  // enable weight loading mode
  thePlatform->writeJamRegAddr(0x28, 1);
  // set up init data
  thePlatform->writeJamRegAddr(0x30, targetLayer);
  thePlatform->writeJamRegAddr(0x38, targetMem);
  thePlatform->writeJamRegAddr(0x40, targetInd);
  thePlatform->write64BitJamRegAddr(0x48, (AccelDblReg) val);
  // do write
  ExecAccel();
  // disable weight loading mode
  thePlatform->writeJamRegAddr(0x28, 0);

これで値設定。設定するたびに待っているぞ。

void ExecAccel() {
  // invoke accelerator and wait for result
  thePlatform->writeJamRegAddr(0x00, 1);
  while((thePlatform->readJamRegAddr(0x00) & 0x2) == 0) usleep(1);
}

あとは 28x28 をビットに整形して(binarizeAndPack)、IP がアクセスできる連続領域にさらにコピーして(copyBufferHostToAccel)、実行

   FoldedMVOffloadBinarized(binImages, outLabel, count*psi, count*psl, count);

....
void FoldedMVOffloadBinarized(....

  thePlatform->writeJamRegAddr(0x54, numImages);

  // launch
  ExecAccel();

結果を accelBufOut からもってくる。

FTDI と Vivado の Jtag

Vivado で JTAG 認識しなくなった時のリカバリー方法。hw_manager まではつながるけど、その先。陥った©状態は、USB-UART は動くのに JTAG を認識してくれない。

PYNQ や ARTY-Z7 では USB-UART に FTDI の 2232HQ というのをつかっている。Zybo も同じ。このチップ、USB のインタフェースとして2つもっているみたいだ。UltraZed は 232HQ で微妙に違う。

ここの blog だと
http://www.hmwr-lsi.co.jp/fpga/fpga_1.htm
FT2232D(微妙に違うな)、片方をJTAG、片方を UART にできそうな感じ。

Windows で PYNQ を USB に接続して見るとデバイスマネージャだと USB-UART が動いているので、安心してしまう。でもこれは実はインタフェース1のほうなのだ。JTAG として動くのはインタフェース0 。なので、USB-UART は動くけど、JTAG が動かないという状況があり得る。

おそらく、ちょっと前に FreeScale(じゃなくなったんだっけ) の i.MX6 をデバッグするときに、OpenOCDをつかっていた。 zadig-2.3.exe というツールをつかってドライバを入れ替えたのが今回のトラブルの元の気がする。zadig はよくできていて、インストーラもない(だからダウンロードしたらウィルスチェックすること)。

もどしかたはデバイスドライバを元に戻せばいいのだけど、どういうわけか、JTAG のドライバはデバイスマネージャで見えない。あるかもしれないけど、すぐに探せない。ここはもう Windows 10 の使い方の問題。デバイスとプリンタになぜか現れる。Digilent Adept USB Device。

f:id:ryos36:20171007140326p:plain

うまくうごいている状態の記録しかのこっていないのがお粗末だが、上の絵になれば OK。ここに winusb とかが見えているとだめ。winusb 自身は MS だからちょっと安心したりするけど、JTAG 的にはだめ。ダブルクリックして、プロパティを見るとドライバの状態を見ることが出来る。この時点では”見る”だけなので(管理者権限で動いていない)、設定の変更を更にクリックする。あとはドライバの更新で FTDI の USB Serial Conveter にすればよい。私の場合、バージョンは 2.12.24.0 (現時点の最新は 2.12.28 かな?)

f:id:ryos36:20171007140719p:plain

そうこうしていると USB-UART が動かなくなることがあるので、USB Serial Converter B をダブルクリックして詳細設定の TAG を開くと VCP をロードするがあるので、これをロードすると USB-UART が使える。

f:id:ryos36:20171007140854p:plain

これでひとまず終了なのだが、PC の再起動とかなんとかしていると、また、元に戻ったり、あるいはデバイスドライバに不具合があります。とか出てきて、問題を修正すると、元に戻ってしまったりとすることがある。これは、おそらく inf ファイルが残っているから。

そこでコマンドプロンプトからどんな inf があるかチェックする。pnputil.exe というこまんどがそれ。MS 製でもとからついてきている。-e オプションで表示。-d で削除ができる。libusb に関連する inf を削れば(おそらく)元に戻ったりすることはなくなる(と思う)。

c:\Temp>pnputil -e > inf6.txt

c:\Temp>explorer .

c:\Temp>explorer -d oem55.inf

c:\Temp>pnputil -d oem55.inf
Microsoft PnP ユーティリティ

ドライバー パッケージが正常に削除されました。

c:\Temp>pnputil -d oem141.inf
Microsoft PnP ユーティリティ

ドライバー パッケージが正常に削除されました。

c:\Temp>pnputil -e > inf7.txt

alliance

今やるべき事かどうかわからないが、alliance インストールしている。え!!X window が必要なの?

configure: error: requires the X window system to compile and run.
                  Please do not use the configure option '--without-x'.

つかえねーじゃん。

ubuntu があったので、ここでコンパイルすることに X のライブラリが必要みたい。

 X11/X.h: No such file or directory

sudo apt-get install xorg-dev" 

げー今度は Motif かよ。20年前かよ。

 sudo apt-get install libmotif-dev 

これでどうよ。configure は通ったよ。yacc がねー。bison いれればいいみたい。

sudo apt-get install bison

一瞬 bison++ を入れるという誘惑にかられたけど、安全のために bison にした。

  • lMut がないといわれた。LD_LIBRARY_PATH にまちがいがあったらしく。make install とか設定しなおしていいるうちにコンパイルできるようになった。なんかいろいろ途中でエラーになる。make でなくて

make install とすることが判明。あーぜんぶ install しながらつくるのね。

https://soc-extras.lip6.fr/en/alliance-abstract-en/

まぁこの通りにやればいいということ。またエラー

/.libs/libVrd.so: undefined reference to `vrd_y_in'

bison じゃだめなのか、、、無念コンパイルできず。

あーflex インストールしてなかった。インストールして再挑戦中。あーflex missing 。configure からやりなおし。
なんでこんなことしているかというと ocp がないから。man ocp はできるのに、、、

~/Polyphony/examples/alliance-examples/adder4$ make
MBK_WORK_LIB=.; export MBK_WORK_LIB; MBK_IN_LO=vst; export MBK_IN_LO; MBK_OUT_LO=vst; export MBK_OUT_LO; MBK_CATA_LIB=/usr/share/alliance/cells/sxlib; export MBK_CATA_LIB; MBK_IN_PH=ap; export MBK_IN_PH; MBK_OUT_PH=ap; export MBK_OUT_PH; MBK_CATAL_NAME=CATAL; export MBK_CATAL_NAME; /usr/bin/ocp -v -gnuplot -ioc adder4  adder4 adder4_p
/bin/sh: 1: /usr/bin/ocp: not found
Makefile:212: recipe for target 'adder4_p.ap' failed
make: *** [adder4_p.ap] Error 127

BNN-PYNQ をリビルドする

Linux では簡単

tkat0.hateblo.jp
方法はこちらが詳しい。

Windows ではコンパイル時にエラー

これはどうも clang の 4.5.2(?) のバグらしい。(clang のバージョンなのか g++ のバージョンなのか不明)

c++ - Clang on Cygwin with C++11 - Stack Overflow

これによると、4.7.2 をつかえとなっている。どうやら clang はヘッダーなどを g++ から拝借してきているようだ。g++ の構文では通るが clang では通らないという記述があるらしい。でも、ちゃんと type_info という定義はある。
で、4.7.2 を見てみると、、、どちらでも通る記述に変更されている。

diff exception_ptr.h*
132c132
<       const class type_info*
---
>       const type_info*

これ単純に class ってつけ足せばいいのでは、、、、ビンゴ!!コンパイルが通るようになる。そしてまたエラー。

throw_with_nested という関数が2度定義されていて、どちらにも = 0 で初期値をいれている。これ、2回目の定義の時は初期値を入れてはだめなのでは?

> diff nested_exception.h*
122c122
<     __throw_with_nested(_Ex&& __ex, const nested_exception*)
---
>     __throw_with_nested(_Ex&& __ex, const nested_exception* = 0)

ビンゴ!!エラーがなくなる。これでとりあえずこの問題解決。Vivado HLS の最近のバージョンでも同じ問題が残っているので、いちいちぜんぶに変更しないといけない(もちろん自己責任で)

c:/Xilinx/Vivado_HLS/2016.2/win64/tools/clang/include/c++/4.5.2

とにかくこうすると、うまくいく。

シェルプロで作られているよ

なんだかしらんが、シェルプロで作るようになっている。なので、非常にめんどくさい。なんで tcl で共通化しないんだよ。Linux 文化オンリー?
しかたがないのでシェルプロを見ながらつくりなおし。っていうか Vivado HLS はさっきの修正でうまくいく。問題は Vivado の方。
Vivado HLS でつくった BlackBox Jam なる IP を Vivado にいれこむ。これ、単純にいれて自動配線に任せてよさそう。で、どういうわけか、合成と実装のオプションを変えている。タイミングがメットしなかったんだねきっと。

set_property strategy Flow_PerfOptimized_high [get_runs synth_1]

set_property STEPS.SYNTH_DESIGN.ARGS.DIRECTIVE AlternateRoutability [get_runs synth_1]
set_property STEPS.SYNTH_DESIGN.ARGS.RETIMING true [get_runs synth_1]

set_property strategy Performance_ExtraTimingOpt [get_runs impl_1]
set_property STEPS.OPT_DESIGN.ARGS.DIRECTIVE Explore [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.PHYS_OPT_DESIGN.ARGS.DIRECTIVE AggressiveExplore [get_runs impl_1]
set_property STEPS.POST_ROUTE_PHYS_OPT_DESIGN.IS_ENABLED true [get_runs impl_1]

よくこんな設定見つけたな、、、、とおもいつつ、、、GUI からその通りに修正。(しゅうせいしなくてもいけるのか?誰か人柱)

とりあえず、合成もできたし、自分のデザインに入れ込むこともできた。

f:id:ryos36:20170909232858p:plain

ただし、これ、使い方がよくわからない。

BNN-PYNQ をリビルドする

type_info の unknown type name

error: unknown type name 'type_info'
const type_info*

https://forums.xilinx.com/t5/High-Level-Synthesis-HLS/Building-Xilinx-Binary-Neural-Network-in-HLS-Error-on-Windows/td-p/757041
https://github.com/jingpu/Halide-HLS/issues/5

https://stackoverflow.com/questions/12938663/clang-on-cygwin-with-c11

diff exception_ptr.h*
132c132
<       const class type_info*
---
>       const type_info*
diff nested_exception.h*
122c122
<     __throw_with_nested(_Ex&& __ex, const nested_exception*)
---
>     __throw_with_nested(_Ex&& __ex, const nested_exception* = 0)


qiita.com

github.com

tkat0.hateblo.jp