前回の続き。MNISTのハードウェアを作成してRISC-Vに接続し動作させる。 前回はReorderBufferを追加してアウトオブオーダ処理をインオーダ処理に変更し、デバッグを行った。
やはりChiselというか、FPGAでのデバッグは難しい。何が起きているのかわからないし、何度も再合成してFPGAを焼き直した。 SDカードを書き換えるのも面倒だ。
とりあえず、プログラムの書き換えはZedBoardのEthernet経由で書き換えられるようにした。
ZedBoard の EthernetのデフォルトIPアドレスを変更する。
ZedBoardの PetaLinuxでは、デフォルトのIPアドレスが192.168.1.5 になっている。 しかし我が家のネットワークは192.168.11.xで設定されているため、そのままでは繋がらない。 ifconfigを書き換えて、ネットワークが繋がるようにする。 これにより、作業用のPCからリコンパイルしたプログラムをscpで転送できるようになったため、プログラムを書き換えるたびにSDカードを取り出してリブートする必要がなくなった。
ifconfig eth0 192.168.11.5 up
ZedBoard での MNISTハードウェア動作確認
結局、ReorderBufferのタグビットの設定をいくつか間違えていたせいで、動作していなかった。 その辺りを修正し、なんとか無事に動作するようになった。 時々謎の不具合というか、時々結果がずれるのだが、これはとりあえず後回しにする。
- ソフトウェアでの実行結果
root@zynq:~# ./fesvr-zynq train_twolayernet_fix16_full === TestNetwork === === start === 0 : expect=7, actual= 7 CORRECT 1 : expect=2, actual= 2 CORRECT 2 : expect=1, actual= 1 CORRECT 3 : expect=0, actual= 0 CORRECT 4 : expect=4, actual= 4 CORRECT ... 193 : expect=9, actual= 9 CORRECT 194 : expect=0, actual= 0 CORRECT 195 : expect=3, actual= 3 CORRECT 196 : expect=1, actual= 1 CORRECT 197 : expect=6, actual= 6 CORRECT 198 : expect=4, actual= 4 CORRECT 199 : expect=2, actual= 2 CORRECT Final Result : Correct = 185 / 200 Time = 900400921
- ハードウェアでのアクセラレーション結果
root@zynq:~# ./fesvr-zynq train_twolayernet_fix16_hw_full ~ === TestNetwork === === start === 0 : expect=7, actual= 7 CORRECT 1 : expect=2, actual= 2 CORRECT 2 : expect=1, actual= 1 CORRECT 3 : expect=0, actual= 0 CORRECT 4 : expect=4, actual= 4 CORRECT ... 193 : expect=9, actual= 9 CORRECT 194 : expect=0, actual= 0 CORRECT 195 : expect=3, actual= 3 CORRECT 196 : expect=1, actual= 1 CORRECT 197 : expect=6, actual= 6 CORRECT 198 : expect=4, actual= 4 CORRECT 199 : expect=2, actual= 2 CORRECT Final Result : Correct = 184 / 200 Time = 222765286
いいね、ちゃんと動いている。 速度としては、専用ハードウェアを使ったほうが4倍程度高速だ。 もう少し高速化できるんじゃないかというアイデアもあるので、少し改造してみようかな。 今は64ビット幅を半分しか使っていないし、もうすこしやり方があるはずだ。
- 専用ハードウェアでの動作の様子
- ソフトウェアでの動作の様子
性能解析
まず、784x2個のデータをロードするのに要しているサイクル数を計測した。 RoCCアクセラレータを動かす部分にサイクルカウンタを仕込み、動作を確認してみる。
uint64_t rocc_start, rocc_end; rdmcycle(rocc_start); rocc_dot (out[b * output_size + o], &in_data[b * input_size], &wh[o], output_size, input_size); rdmcycle(rocc_end); out[b * output_size + o] = fix16_add (out[b * output_size + o], wb[o]); printf("rocc_dot(input_size=%04d) : %d\n", input_size, rocc_end - rocc_start);
計測結果だ。 784x2個のデータに対して1データあたり15サイクル程度かかっている。これはかかりすぎだろう。もう少し短縮したい。