Chiselを使って行列積を計算するハードウェアを開発してきたが、これには弱点がある。 小さな行列に焦点を当てたので、大きな行列(16行以上)に対応できないことだ。
しかしこの方法には利点もある。16行に限定したため内部にレジスタキャッシュを持っており、同じアドレスをフェッチする場合はフェッチリクエストを省略し、高速化した。 しかしより大きな行列を計算する場合はそうもいかない。例えば数千行の行列を計算するのにすべて内部レジスタに保持しておくわけにもいかないので、別の方法を考える必要がある。
今回より行列サイズを一般化して、レジスタファイルキャッシュを省略し、どのような行列サイズでも実現できるアクセラレータを作ってみる。
アイデアとしては、これまで行と列を順番にフェッチしてきたが、これを編み込む。
これにより、最初にフェッチした行を全て記憶しておく必要が無いので、いくらでも行列サイズを大きくすることが出来る。 この場合L1の局所性が心配になる。改善策としてはある程度の塊で一気に取ってくる方法があるが、まあ列方向だとそれは効きそうにないので、今回は省略。
で、とりあえず今回はアイデア出しと、簡単な実装のみ実現した。
命令としてはカスタム命令に対してFunct=0, Funct=1の2種類を用意した。
- Funct=0、行列サイズ(行列Aの縦方向 = 行列Bの横方向)を指定する。
- Funct=1、メモリをフェッチしDot Productを実行する
の2種類を作った。最初にFunct=0はレジスタ戻り値を使わないので適当に返していたところRocket Coreが固まってしまい原因を突き止めるのに相当な時間がかかってしまった。
when (io.cmd.fire() && setLength) { printf("MatrixMulTwoRequester: SetLength Request. %x\n", io.cmd.bits.rs1) r_matrix_max := io.cmd.bits.rs1 r_recv_state := s_recv_finish } // たとえ使わなくてもきちんと初期化して返すこと! when (io.cmd.fire()) { r_total := UInt(0) r_resp_rd := io.cmd.bits.inst.rd } when (io.cmd.fire() && doCalc) { printf("MatrixMulTwoRequester: DoCalc Received. %x, %x\n", io.cmd.bits.rs1, io.cmd.bits.rs2) r_v_addr := io.cmd.bits.rs2 r_h_addr := io.cmd.bits.rs1 r_recv_count := UInt(0) r_cmd_count := UInt(0) r_tag := UInt(0) r_cmd_state := s_mem_fetch r_recv_state := s_mem_recv }
とりあえず今日はここまで。まだ答えはあっていないが、ステートマシンは動作しているみたいだ。きちんとデバッグしていこう。