これまで、Zynq ZedBoardを利用して、Vivadoで作成したカスタムIPをインテグレートする方法について調査してきた。 この場合、IPは自分でVerilogを記述しAXIに接続した。これはこれで十分有益だ。 しかしせっかくVivado HLSがあるので、C言語で回路を記述し、それをHDLに変換した上でVivado IPとしてまとめ、Zynq ZedBoardのPL階層に接続して利用できるようにしたい。
ここでは、折角なのでC言語の行列乗算器を記述し、それをHWに変換、ARMから制御できるようにしたい。このための調査と試行をしよう。
Vivado HLSを使ってC言語で行列演算関数を実装する
まずはVivado HLSを立ち上げるところから備忘録として取っていく。
プロジェクト名はmatrix_hls
とした。まずは[New Project]でプロジェクトを作成しよう。
最初に追加するソースコード類は、ここでは特に指定しない。
実装ボードとしては、Zynq ZedBoardを指定する。
これでプロジェクトとしては作成完了となる。
ここでは、matrix_hls.cpp
として以下のような関数を記述した。ごく普通の行列積を計算する関数だ。
void matrix_mul (const float matrix_a[8][8], const float matrix_b[8][8], float matrix_c[8][8]) { for (int j = 0; j < 8; j++) { for (int i = 0; i < 8; i++) { matrix_c[j][i] = 0; for (int k = 0; k < 8; k++) { matrix_c[j][i] += matrix_a[j][k] * matrix_b[k][i]; } } } return; }
これをプロジェクトに追加する。
さらに、本関数をテストするためのテストプログラムを作成した。このテストプログラムは、CoSimulationによりC言語でのプログラムでも、RTLに変換後のプログラムでも使えるように工夫してある。 入力としては8×8の行列を2つ用意しており、データ型はfloatとした。この場合の結果の行列積のデータも保持しておき、一致すればPASS、一致しなければFAILとする。
#ifdef C_SIMULATION #include <stdio.h> #endif // C_SIMULATION #include "./matrix_hls.h" int main () { const float matrix_a[8][8] = {{2.00861, 4.96191, 0.03365, 8.49076, 2.05199, 2.35618, 6.64845, 2.48825}, {8.24828, 3.79825, 0.71616, 5.02615, 2.37722, 1.70809, 3.09136, 7.97736}, {0.81262, 4.22766, 6.10453, 1.67064, 1.95103, 7.01496, 0.92316, 4.52642}, {6.44442, 2.59385, 1.32141, 1.64139, 7.25922, 1.16130, 0.29613, 1.15290}, {8.40171, 4.97169, 3.97962, 2.65527, 6.64343, 4.18250, 9.66444, 8.76806}, {6.02191, 0.71815, 2.29678, 5.76173, 4.72776, 9.79415, 6.47138, 7.49650}, {2.11535, 5.73055, 6.17571, 5.24272, 5.13695, 8.90108, 6.35120, 1.16608}, {7.40435, 0.45430, 8.73240, 6.09247, 4.89821, 4.02948, 2.66578, 4.80374}}; const float matrix_b[8][8] = {{6.31683, 7.40894, 4.11552, 5.11706, 3.89267, 2.53873, 0.89988, 3.21347}, {9.67123, 4.80318, 0.46820, 2.54917, 8.17656, 7.38784, 0.98314, 4.25930}, {1.61677, 5.34579, 5.74210, 7.85177, 1.93416, 3.57129, 9.49534, 8.32125}, {5.20353, 1.78412, 9.06261, 6.23715, 5.52162, 4.11266, 2.34838, 4.09494}, {8.49776, 9.95641, 1.99669, 5.11842, 1.71897, 0.96354, 3.33916, 9.04869}, {8.19442, 6.65203, 6.72998, 4.58975, 0.77722, 1.05325, 2.65544, 0.80571}, {6.91818, 5.26098, 8.58867, 7.27429, 4.99427, 2.20705, 0.79829, 0.85794}, {3.72035, 8.24444, 8.54816, 8.66889, 2.99164, 9.01359, 4.28659, 0.49119}}; float matrix_c[8][8]; const float mul_answer[8][8] = {{196.909332, 145.638458, 186.056839, 167.399612, 141.344727, 118.357239, 56.026924, 90.030434}, {201.411331, 209.213669, 196.371002, 200.510941, 137.020523, 155.046509, 78.897285, 98.682167}, {161.872070, 170.204514, 153.244339, 161.424118, 85.720619, 114.073273, 112.057991, 104.578331}, {154.012604, 161.261200, 84.908005, 114.835960, 76.222694, 66.256241, 57.253033, 116.916557}, {311.613464, 329.238220, 283.188660, 302.985901, 184.883301, 194.360352, 135.061783, 168.245651}, {271.772003, 278.695862, 285.540314, 267.828125, 136.055466, 149.216202, 120.559464, 125.021599}, {270.917847, 238.948105, 229.040268, 230.078293, 146.940125, 130.176163, 129.347855, 163.740082}, {207.943314, 243.793289, 236.898621, 250.211197, 122.303810, 136.542236, 154.109879, 175.557037}}; matrix_mul (matrix_a, matrix_b, matrix_c); for (int j = 0; j < 8; j++) { for (int i = 0; i < 8; i++) { if (matrix_c[j][i] != matrix_c[j][i]) { #ifdef C_SIMULATION printf ("matrix_result[%d][%d](%f) != matrix_answer[%d][%d](%f). compare failed\n", j, i, matrix_c[j][i], j, i, mul_answer[j][i]); #endif // C_SIMULATION return -1; } } } #ifdef C_SIMULATION printf ("Matrix verification SUCCEEDED\n"); #endif // C_SIMULATION return 0; }
また、defineとして、C_SIMULATION
を用意した。これはC言語モードのシミュレーションでのみ利用する。すなわち、C simulation時のオプションでのみ-DC_SIMULATION
をあてておけば、RTL Simulationの時にでもこのソースコードを活用できる訳だ。
メニューの[Project]→[Project Settings]として、プロジェクトの設定を開く。ここから[Simulation]タブを開き、matrix_hls.cppの項目で[Edit CFLAGS]をクリックする。
ダイアログに、-DC_SIMULATION
を設定する。
Vivado HLSを使ってC言語で行列演算関数をテストする
まずはC言語での実行だ。ツールバーから[Run C Simulation]をクリックし実行する。ダイアログが出てきたがそのままOKを押した。
C言語での実行なので、特に問題無く終了となる。正常に動作していることが確認できた。コンソールにMatrix verification SUCCEEDED
と表示されているのが確認できた。
次は、RTLへのコンパイルと、C/RTL Cosimulationへと移ろう。