2020年1月3日金曜日

TensorFlow lite for micro で RISC-Vターゲットのサンプルをビルドする

目的

  • TensorFlow Lite for microをやってみる。
  • はじめのサンプル"Hello world"をビルドして、ビルド方法やビルドしているものをなんとなく見てみる。
  • ターゲットはRISC-V、最終的にはRISC-Vプロセッサで動かしたい。


動機


去年よりTensorFlow Lit for microをやってみたいと思ってた。また、RISC-Vにも興味がある。


ビルドする前に


2019.1.3時点で、TensorFlowのmaster(commit 68cbb78)では、RISC-V("Hifive1" SiFive FE310 development board)向けのビルド手順がREADMEから削除されている。いつの時点からなのか、理由は不明。このあたりまでは、手順があった。experimentalが外れたときに削除されたのかも?

また、現時点ではRISC-Vのターゲットではビルドができない。PRはあるがまだ取り込まれていない。
#35302はレビュー待ちの状態なので、そのうち取り込まれると思われる。
#33972はコンフリクトが発生(microがexperimentalでなくなったため)しているため、いつ取り込まれるかは不明。。。

この2つはRISC-Vターゲットの固有のものなので、少し内容をおさえておく。

#35302


RISC-Vターゲットでのみビルドエラーが発生する。C++11にはstd::roundがあるが、RISC-V toolchainには何故か存在せず、::roundが使える(おそらく、toolcainのバグ?)。このため、tflite::TfLiteRound を使うことにする。tflite::TfLiteRoundはstd::roundが定義されていない場合は、::roundを使用するようになっているだけである。

std::roundが使えず、::roundが使える理由はおそらく、このtweetのリンク先と同じだろう(RISC-V toolchainのことではないが)。



#33972


もとのissueはこちら。2つの問題がある。
  • __wrap__funsのundefined referenceが発生する。
    lite/micro/tools/make/targets/mcu_riscv_makefile.incにある変数が上書きされてしまうためである。このため、example/xxxx配下に変数が上書きされないよう、makfile.incを作ることで回避。
  • __dso_handleが定義されていない。
    RISC-V toolcainには__dso_handleが定義されていない。lite/experimental/micro/arduino/abi.cc に定義してビルドすることで回避している(fno-use-cxa-atexitオプションで回避できるのだろうか?)。


ビルド


ようやく本題。

手順は古いREADMEを参照しながら。#35302、#33972については自分のリポジトリに取り込んだ。このため、この手順は公式のものでなく、今後変更される可能性は大。


まずは、githubからリポジトリをclone。ブランチは"micro_riscv"。

$ git clone -b micro_riscv https://github.com/NobuoTsukamoto/tensorflow.git

ビルド用のDocker imageをビルドし、コンテナを立ち上げる。

$ sudo docker build -t riscv_build -f ./tensorflow/tensorflow/lite/micro/testing/Dockerfile.riscv ./tensorflow/tensorflow/lite/micro/testing/
$ sudo docker run -it -v ./tensorflow/micro/tensorflow:/workspace riscv_build:latest bash

コンテナ内でhello worldのサンプルのビルドを行う。RISC-V toolchainはビルド時にダウンロードする(ダウンロードするtoolchainはここを参照)。toolchainへのパスを通しておく。

# export PATH=${PATH}:/workspace/tensorflow/lite/micro/tools/make/downloads/riscv_toolchain/bin/
# cd /workspace
# make -f tensorflow/lite/micro/tools/make/Makefile TARGET=riscv32_mcu hello_world_bin

ビルド時に気になるメッセージがあるが、バイナリが生成されているので気にしない(たぶん、問題ない?)。

/workspace/tensorflow/lite/micro/tools/make/downloads/riscv_toolchain/bin/../lib/gcc/riscv64-unknown-elf/8.1.0/../../../../riscv64-unknown-elf/bin/ld: tensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin/hello_world: section `.gcc_except_table._ZN9__gnu_cxx27__verbose_terminate_handlerEv' can't be allocated in segment 1
LOAD: .data .gcc_except_table._ZN10__cxxabiv111__terminateEPFvvE .gcc_except_table.__gxx_personality_v0 .gcc_except_table.__cxa_call_unexpected .gcc_except_table._ZN9__gnu_cxx27__verbose_terminate_handlerEv

ビルドが終了するとtensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin 配下にELFと.binファイルが生成される。readelfコマンドで見るとRISC-V 32bit向けであることがわかる。

# ls -alh tensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin
drwxr-xr-x. 2 root root 4.0K Jan  3 08:56 .
drwxr-xr-x. 5 root root 4.0K Jan  3 06:14 ..
-rwxr-xr-x. 1 root root 4.0M Jan  3 06:14 hello_world
-rwxr-xr-x. 1 root root 1.5G Jan  3 06:14 hello_world.bin
# readelf -h tensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin/hello_world
ELF Header:
  Magic:   7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 
  Class:                             ELF32
  Data:                              2's complement, little endian
  Version:                           1 (current)
  OS/ABI:                            UNIX - System V
  ABI Version:                       0
  Type:                              EXEC (Executable file)
  Machine:                           RISC-V
  Version:                           0x1
  Entry point address:               0x20400000
  Start of program headers:          52 (bytes into file)
  Start of section headers:          4175496 (bytes into file)
  Flags:                             0x1, RVC, soft-float ABI
  Size of this header:               52 (bytes)
  Size of program headers:           32 (bytes)
  Number of program headers:         3
  Size of section headers:           40 (bytes)
  Number of section headers:         37
  Section header string table index: 36


Renodeによるテスト


Renodeのエミュレータによるバイナリのテストを行う。まず、テスト用バイナリの生成を行う。hello_world_test が生成される。

# make -f tensorflow/lite/micro/tools/make/Makefile TARGET=riscv32_mcu hello_world_test

スクリプトはmicro_speech用なのでhello_worldに書き換える。
/workspace/tensorflow/lite/micro/testing/sifive_fe310.resc ファイルを修正する。10行目のパスの実行ファイルを"micro_speech_test"から"hello_world_test"に変更する。
$bin?=@/workspace/tensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin/hello_world_test

Renodeでバイナリを実行する。~~~ALL TESTS PASSED~~~が出力されればOK。

# renode -P 5000 --disable-xwt -e 's@/workspace/tensorflow/lite/micro/testing/sifive_fe310.resc'
08:56:34.2849 [INFO] Loaded monitor commands from: /opt/renode/./scripts/monitor.py
08:56:34.3117 [INFO] Monitor available in telnet mode on port 5000
08:56:34.6272 [INFO] Including script: /workspace/tensorflow/lite/micro/testing/sifive_fe310.resc
08:56:34.6453 [INFO] System bus created.
08:56:35.1569 [DEBUG] Segment size automatically calculated to value 64KiB (3)
08:56:35.3093 [DEBUG] Segment size automatically calculated to value 16MiB
08:56:35.3691 [WARNING] Previous log file detected and renamed to: /tmp/renode_riscv_log.txt.1
08:56:35.5157 [DEBUG] sysbus: Loading ELF /workspace/tensorflow/lite/micro/tools/make/gen/riscv32_mcu_riscv32_mcu/bin/hello_world_test.
08:56:35.5244 [INFO] sysbus: Loading segment of 135576 bytes length at 0x20400000.
08:56:35.5396 [DEBUG] sysbus: Segment loaded.
08:56:35.5397 [INFO] sysbus: Loading segment of 2152 bytes length at 0x20421198.
08:56:35.5399 [DEBUG] sysbus: Segment loaded.
08:56:35.5784 [INFO] cpu: Setting PC value to 0x20400000.
08:56:35.7980 [INFO] SiFive-FE310: Machine started.
08:56:35.8604 [WARNING] sysbus: [cpu: 0x20400AA8] ReadDoubleWord from non existing peripheral at 0x10000070.
08:56:35.8615 [WARNING] sysbus: [cpu: 0x20400AB6] WriteDoubleWord to non existing peripheral at 0x10000070, value 0x0.
08:56:35.8616 [WARNING] sysbus: [cpu: 0x20400AC2] (tag: 'PRCI_HFROSCCFG') WriteDoubleWord to non existing peripheral at 0x10008000, value 0x40100004.
08:56:35.8618 [WARNING] sysbus: [cpu: 0x20400AC8] (tag: 'PRCI_HFROSCCFG') ReadDoubleWord from non existing peripheral at 0x10008000, returning 0xFFFFFFFF.
08:56:35.8620 [WARNING] sysbus: [cpu: 0x20400ACE] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8622 [WARNING] sysbus: [cpu: 0x20400AD6] (tag: 'PRCI_PLLCFG') WriteDoubleWord to non existing peripheral at 0x10008008, value 0xFFFEFFFF.
08:56:35.8624 [WARNING] sysbus: [cpu: 0x20400AD8] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8625 [WARNING] sysbus: [cpu: 0x20400AE8] (tag: 'PRCI_HFROSCCFG') WriteDoubleWord to non existing peripheral at 0x10008000, value 0x40100004.
08:56:35.8627 [WARNING] sysbus: [cpu: 0x20400AEE] (tag: 'PRCI_HFROSCCFG') ReadDoubleWord from non existing peripheral at 0x10008000, returning 0xFFFFFFFF.
08:56:35.8629 [WARNING] sysbus: [cpu: 0x20400AF4] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8631 [WARNING] sysbus: [cpu: 0x20400AFC] (tag: 'PRCI_PLLCFG') WriteDoubleWord to non existing peripheral at 0x10008008, value 0xFFFEFFFF.
08:56:35.8632 [WARNING] sysbus: [cpu: 0x20400B04] WriteDoubleWord to non existing peripheral at 0x10014000, value 0x8.
08:56:35.8634 [WARNING] sysbus: [cpu: 0x20400B0E] WriteDoubleWord to non existing peripheral at 0x1000800C, value 0x100.
08:56:35.8651 [WARNING] sysbus: [cpu: 0x20400B18] (tag: 'PRCI_PLLCFG') WriteDoubleWord to non existing peripheral at 0x10008008, value 0x405F1.
08:56:35.8652 [WARNING] sysbus: [cpu: 0x20400B1A] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8653 [WARNING] sysbus: [cpu: 0x20400B24] (tag: 'PRCI_PLLCFG') WriteDoubleWord to non existing peripheral at 0x10008008, value 0xFFFBFFFF.
08:56:35.8989 [WARNING] sysbus: [cpu: 0x20400B42] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8990 [WARNING] sysbus: [cpu: 0x20400B4C] (tag: 'PRCI_PLLCFG') ReadDoubleWord from non existing peripheral at 0x10008008, returning 0xFFFFFFFF.
08:56:35.8991 [WARNING] sysbus: [cpu: 0x20400B52] (tag: 'PRCI_PLLCFG') WriteDoubleWord to non existing peripheral at 0x10008008, value 0xFFFFFFFF.
08:56:35.8994 [WARNING] gpioInputs: Unhandled read from offset 0x3C.
08:56:35.8998 [WARNING] gpioInputs: Unhandled write to offset 0x3C, value 0x0.
08:56:35.8998 [WARNING] gpioInputs: Unhandled read from offset 0x38.
08:56:35.8999 [WARNING] gpioInputs: Unhandled write to offset 0x38, value 0x30000.
08:56:36.0392 [DEBUG] uart0: [+0.67s host +0.4ms virt 0.4ms virt from start] core freq at 170833 Hz
08:56:36.0416 [DEBUG] uart0: [+2.47ms host +0s virt 0.4ms virt from start]   Testing LoadModelAndPerformInference
08:56:36.0771 [DEBUG] uart0: [+35.5ms host +0.7ms virt 1.1ms virt from start]   1/1 tests passed
08:56:36.0776 [DEBUG] uart0: [+0.52ms host +0s virt 1.1ms virt from start]   ~~~ALL TESTS PASSED~~~
08:56:36.0777 [DEBUG] uart0: [+83?s host +0s virt 1.1ms virt from start]   
08:56:36.0798 [DEBUG] uart0: [+2.14ms host +0s virt 1.1ms virt from start]   
08:56:36.0808 [DEBUG] uart0: [+0.99ms host +0s virt 1.1ms virt from start]   Progam has exited with code:0x00000000
09:02:45.0257 [INFO] SiFive-FE310: Machine paused.
09:02:45.0330 [DEBUG] SiFive-FE310: Disposing sysbus.cpu.
09:02:45.0352 [DEBUG] SiFive-FE310: Disposing sysbus.
09:02:45.0358 [DEBUG] SiFive-FE310: Disposing sysbus.maskRom.
09:02:45.0360 [DEBUG] SiFive-FE310: Disposing sysbus.otp.
09:02:45.0361 [DEBUG] SiFive-FE310: Disposing sysbus.dtim.
09:02:45.0362 [DEBUG] SiFive-FE310: Disposing sysbus.qspi0Xip.
09:02:45.0371 [INFO] SiFive-FE310: Disposed.


その他


今回、ビルドする際の情報を記載する。

RISC-V toolchainについて


デフォルトで使用しているtoolchainはSiFiveのtoolchain(最新でない)である。別途、riscv-gnu-toolchainでビルドしたものも使用できるはず。なお、Newlib cross-compilerの32bit向けが必要。


ボード向けのBSPなど


これもダウンロードスクリプトでダウンロードしている。Makefileでもこのあたりこのあたり(各サンプルのMakefile)でヘッダや必要なモジュールを加えているので参考になりそう。


Renodeについて


まだ、あまりわかっていない。オープンソースの"virtual development tool "。RISC-Vだけでなくて、その他アーキテクチャもサポートする仮想開発ツール?もう少し調べてみよう。

今回、dockerのコンテナ内でビルドしたのはRenodeを使用するためである。Renodeを使わなければ、dockerは不要。


最後に


TensorFlow Lite for microのRISC-V向けのhello worldのバイナリを作成した。次はこのバイナリを動作させてみたい。

0 件のコメント:

コメントを投稿