FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

RISC-VプロセッサHiFive1で機械学習コードを動作させる(1. コンパイル)

HiFive1ボードはRISC-Vが動作する(おそらく世界で唯一商用の?)プロセッサボードである。このHiFive1ボードの仕様は以下のようになっている。

f:id:msyksphinz:20170325154038p:plain

  • Microcontroller: SiFive Freedom E310 (FE310)
    • SiFive E31 Coreplex
    • 32bit RV32IMAC (整数演算のみ、乗除算ハードウェア回路付属、コンパクト命令付属)
    • Speed: 320+ MHz
    • Performance: 1.61 DMIPs/MHz, 2.73 Coremark/MHz
    • Memory: 16 KB Instruction Cache, 16 KB Data Scratchpad
    • Other Features: Hardware Multiply/Divide, Debug Module, Flexible Clock Generation with on-chip oscillators and PLLs
  • SPI Controllers/HW CS Pins: 1/3
  • External Interrupt Pins: 19
  • External Wakeup Pins: 1
  • Flash Memory: 128 Mbit Off-Chip (ISSI SPI Flash)

というわけで、フラッシュメモリが128Mbitも付属しているのが大きいと個人的に思っている。ここにどうにかして学習データを流し込んで、機械学習のプログラムを動作させたいのだ。

train_twolayernet (MNIST向け機械学習ネットワーク (16bit 固定小数点版))のコンパイル

自作したMNIST動作用の機械学習ネットワークは、固定小数点ライブラリlibfixmathを使うようになっている。

libfixmathをRISC-V向けにコンパイルするためには、オリジナルのlibfixmathに対して以下の変更を加えている。

diff --git a/libfixmath/Makefile b/libfixmath/Makefile
index b284590..27cceb9 100644
--- a/libfixmath/Makefile
+++ b/libfixmath/Makefile
@@ -4,14 +4,19 @@ LIB =
 SRC = .
 INC =

+TARGET=/home/msyksphinz/work/freedom-e-sdk/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/bin/riscv64-unknown-elf-
+
 #Compiler settings
-CPP = gcc
-CC = gcc
-AS = gcc
-LD = gcc
-AR = ar
-CPP_FLAGS = -O2 $(INC) -Wall -Wextra -c
-CC_FLAGS  = -O2 $(INC) -Wall -Wextra -c
+CPP = $(TARGET)gcc
+CC     = $(TARGET)gcc
+AS     = $(TARGET)gcc
+LD     = $(TARGET)gcc
+AR     = $(TARGET)ar
+
+DEFINES += FIXMATH_NO_CACHE
+
+CPP_FLAGS = -O2 $(INC) -Wall -Wextra -c -std=gnu11 -O3 -g  -g -march=rv32imac -mabi=ilp32 -mcmodel=medany $(addprefix -D, $(DEFINES))
+CC_FLAGS  = -O2 $(INC) -Wall -Wextra -c -std=gnu11 -O3 -g  -g -march=rv32imac -mabi=ilp32 -mcmodel=medany $(addprefix -D, $(DEFINES))
 AS_FLAGS  = $(CC_FLAGS) -D_ASSEMBLER_
 LD_FLAGS = -Wall

MNISTの訓練データをRISC-Vバイナリに直接埋め込む方法

HiFive1にはファイルシステムも入っていないしOSも動かないので、これまで使っていたread/write系の関数は動作しない。これを補うために、あらかじめトレーニングデータをRISC-Vの形式に変換して、リンク時に埋め込むという作業が必要になる。

これはバイナリをオブジェクトファイルに変換する必要がある。これにはobjcopyコマンドを使用する。

普通のやつらの下を行け: objcopy で実行ファイルにデータを埋め込む - bkブログ

上記を使用して、トレーニングファイルを以下のようにMakefileを使ってオブジェクトファイルに変換を行った。

%-ubyte.o: %-ubyte
    $(OBJCOPY) -I binary -O elf32-littleriscv -B riscv --rename-section .data=.rodata $(notdir $<) $(notdir $@)

github.com

これをコンパイルすると、デフォルトではファイルの大きさが大きすぎてセクションに入らないので、以下のエラーが出る。

/home/msyksphinz/work/freedom-e-sdk/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/lib/gcc/riscv64-unknown-elf/7.1.1/../../../../riscv64-unknown-elf/bin/ld: train_twolayernet セク ション `.data' は領域 `ram' 内に入りません
/home/msyksphinz/work/freedom-e-sdk/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/lib/gcc/riscv64-unknown-elf/7.1.1/../../../../riscv64-unknown-elf/bin/ld: section .stack VMA [0000000080003800,0000000080003fff] overlaps section .data VMA [0000000080000000,0000000083468297]
/home/msyksphinz/work/freedom-e-sdk/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/lib/gcc/riscv64-unknown-elf/7.1.1/../../../../riscv64-unknown-elf/bin/ld: 領域 `ram' が 0 バイト 溢れました

objdump -hでセクションを確認すると、.dataが非常に大きいことが分かる。

software/train_twolayernet/train-images-idx3-ubyte.o:     ファイル形式 elf32-littleriscv

セクション:
索引名          サイズ      VMA       LMA       File off  Algn
  0 .data         02cdc610  00000000  00000000  00000034  2**0
                  CONTENTS, ALLOC, LOAD, DATA

.dataセクションはflash.ldsによって以下のように定義されている。

  • ./bsp/env/freedom-e300-hifive1/flash.lds
MEMORY
{
  flash (rxai!w) : ORIGIN = 0x20400000, LENGTH = 512M
  ram (wxa!ri) : ORIGIN = 0x80000000, LENGTH = 16K
}

.dataセクションはramに配置されており、16Kしかない。イメージデータをどうにかしてflashデータにコピーするしかない。これには、あらかじめobjcopyコマンドでrename-sectionを指定する。

%-ubyte.o: %-ubyte
    $(OBJCOPY) -I binary -O elf32-littleriscv -B riscv --rename-section .data=.rodata $(notdir $<) $(notdir $@)

これで、すべての学習データがflashセクションに移動した。

software/train_twolayernet/train_twolayernet:     ファイル形式 elf32-littleriscv

セクション:
索引名          サイズ      VMA       LMA       File off  Algn
  0 .init         0000007e  20400000  20400000  00001000  2**0
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  1 .text         0000dfca  20400080  20400080  00001080  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, CODE
  2 .rodata       034685dc  2040e050  2040e050  0000f050  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  3 .eh_frame     00000068  2387662c  2387662c  0347762c  2**2
                  CONTENTS, ALLOC, LOAD, READONLY, DATA
  4 .lalign       00000000  23876694  23876694  034789f8  2**0
                  CONTENTS
  5 .dalign       00000000  80000000  80000000  034789f8  2**0
                  CONTENTS
  6 .data         000009f8  80000000  23876694  03478000  2**3
                  CONTENTS, ALLOC, LOAD, DATA
  7 .bss          00000040  800009f8  800009f8  00000000  2**2
                  ALLOC
  8 .stack        00000800  80003800  80003800  00000000  2**0
                  ALLOC
  9 .debug_frame  00001258  00000000  00000000  034789f8  2**2
                  CONTENTS, READONLY, DEBUGGING
 10 .debug_info   00010107  00000000  00000000  03479c50  2**0
                  CONTENTS, READONLY, DEBUGGING
 11 .debug_abbrev 00003dbe  00000000  00000000  03489d57  2**0
                  CONTENTS, READONLY, DEBUGGING
 12 .debug_loc    000158a5  00000000  00000000  0348db15  2**0
                  CONTENTS, READONLY, DEBUGGING
 13 .debug_aranges 000004c0  00000000  00000000  034a33c0  2**3
                  CONTENTS, READONLY, DEBUGGING
 14 .debug_ranges 00001ff8  00000000  00000000  034a3880  2**0
                  CONTENTS, READONLY, DEBUGGING
 15 .debug_line   00008d84  00000000  00000000  034a5878  2**0
                  CONTENTS, READONLY, DEBUGGING
 16 .debug_str    000023a6  00000000  00000000  034ae5fc  2**0
                  CONTENTS, READONLY, DEBUGGING
 17 .comment      0000001a  00000000  00000000  034b09a2  2**0
                  CONTENTS, READONLY

ここまでで一応バイナリを作成することができたのだが、アップロード時にHiFive1でエラーが出ている。これは解析する必要があるなあ。

$ sudo make upload PROGRAM=hello
[sudo] msyksphinz のパスワード:
work/build/openocd/prefix/bin/openocd -f bsp/env/freedom-e300-hifive1/openocd.cfg & \
/home/msyksphinz/work/freedom-e-sdk/work/build/riscv-gnu-toolchain/riscv64-unknown-elf/prefix/bin/riscv64-unknown-elf-gdb software/hello/hello --batch -ex "set remotetimeout 240" -ex "target extended-remote localhost:3333" -ex "monitor reset halt" -ex "monitor flash protect 0 64 last off" -ex "load" -ex "monitor resume" -ex "monitor shutdown" -ex "quit" && \
echo "Successfully uploaded 'hello' to freedom-e300-hifive1."
Open On-Chip Debugger 0.10.0-dev (2017-07-17-09:46)
Licensed under GNU GPL v2
For bug reports, read
        http://openocd.org/doc/doxygen/bugs.html
adapter speed: 10000 kHz
Info : auto-selecting first available session transport "jtag". To override use 'transport select <transport>'.
Info : ftdi: if you experience problems at higher adapter clocks, try the command "ftdi_tdo_sample_edge falling"
Info : clock speed 10000 kHz
Info : JTAG tap: riscv.cpu tap/device found: 0x10e31913 (mfg: 0x489 (<unknown>), part: 0x0e31, ver: 0x1)
Info : Examined RISCV core; XLEN=32, misa=0x40001105
Error: couldn't bind gdb to socket: Address already in use