Vivado HLSで高位合成を行うと、RTLと共にSDK用のAPIが生成されます。APIの利用方法をベアメタルで確認しました。
確認用のC関数
float_topという名前の関数です。aとbに単精度の浮動小数点形式の値を設定すると、cに乗算結果が格納されます。InterfaceはAXI4-Liteです(別ファイルのDirectiveで指定しています)。
void float_top(float a, float b, float *c) { *c = a * b; }
float_topを高位合成し、そのIPコアを組込んだシステムの詳細はこちらです。
SDKの準備
通常の手順でVivadoのSDKを起動します。具体的な手順は次のとおりです。
まず、Vivado上で次の処理を実行します。
- File->Export->Export Hardwareを実行
- File->Launch SDKを実行
SDK起動後、SDK上で次の処理を実行します。
- File->New->Board Support Packageを実行
- File->New->Application Projectを実行
この時点で、SDKのProject Explorerは次のような構成になります。この例では、testという名前でApplication Projectを作成しています。
IPコアのAPI
APIに必要な処理
関数float_topは、aとbが入力で、cが出力(演算結果)です。このため、Vivado HLSが生成したAPIには次の処理を行う関数が存在するはずです。
- aに値を設定する関数
- bに値を設定する関数
- cの値を読み出す関数
また、ハードウェアを制御するために、次の処理を行うAPIが存在するはずです。
- 演算を開始する関数
- 演算の完了を認識する関数
実際のAPI
IPコアのAPIは、次のディレクトリに格納されています(この例の場合)。
- design_1_wrapper_hw_platform_0->drivers->float_top_v1_0->data->src
ディレクトリには、次のファイルが格納されています。
xfloat_top_hw.h |
xfloat_top_linux.c |
xfloat_top_sinit.c |
xfloat_top.c |
xfloat_top.h |
float_topを制御するAPIを定義したファイルがxfloat_top.hです。また、そのソースコードがxfloat_top.cです。定義されているAPIの意味は、ug902-vivado-high-level-synthesis.pdfに記載されていました。主なAPIの意味は次のとおりです。生成されるAPIは、Directiveの記述などで多少変わるようです。
API | 内容 |
XFloat_top_Config* XFloat_top_LookupConfig(u16 DeviceId); | コンフィギュレーション情報の取得 |
int XFloat_top_CfgInitialize(XFloat_top *InstancePtr, XFloat_top_Config *ConfigPtr); | MMUが使われているシステムで、デバイスを初期化 |
void XFloat_top_Start(XFloat_top *InstancePtr); | 処理を開始 |
u32 XFloat_top_IsDone(XFloat_top *InstancePtr); | 処理が終了 |
u32 XFloat_top_IsIdle(XFloat_top *InstancePtr); | IDLE状態 |
u32 XFloat_top_IsReady(XFloat_top *InstancePtr); | 次のデータを受付可能 |
void XFloat_top_Set_a(XFloat_top *InstancePtr, u32 Data); | aに値を設定 |
u32 XFloat_top_Get_a(XFloat_top *InstancePtr); | aの値を取得 |
void XFloat_top_Set_b(XFloat_top *InstancePtr, u32 Data); | bに値を設定 |
u32 XFloat_top_Get_b(XFloat_top *InstancePtr); | bの値を取得 |
u32 XFloat_top_Get_c(XFloat_top *InstancePtr); | c(演算結果)の値を取得 |
u32 XFloat_top_Get_c_vld(XFloat_top *InstancePtr); | cが有効であることを示す情報 |
テストプログラム
このテストプログラムでは、10回のループで適当なfloatの値a,bを生成し、その乗算結果cを求めています。乗算結果は、IPコアとプログラムの両方で計算し、違いがあるかをチェックしています。floatの値は、32ビットのバイナリに変換してからfloat_topに渡しています。同様に、float_topの演算結果は32ビットのバイナリからfloatに変換する必要があります。
#include <stdio.h> #include "platform.h" #include "xfloat_top.h" // from xparameters.h #define XF_DEVICE_ID XPAR_FLOAT_TOP_0_DEVICE_ID XFloat_top XFT;// Vivado HLSで作成したIPコアに対応したインスタンス // float - unsigned 変換 typedef union _fu_u { unsigned int u; float f; } fu_u; // 期待値モデル float ref_float_top(float a, float b) { return a * b; } int main() { int i,Status; XFloat_top_Config *ConfigPtr; fu_u ref_a, ref_b; fu_u in_a, in_b, out_c; init_platform(); // 初期化 ConfigPtr = XFloat_top_LookupConfig(XF_DEVICE_ID); Status = XFloat_top_CfgInitialize(&XFT, ConfigPtr); if (Status != XST_SUCCESS) { return XST_FAILURE; } Status = XFloat_top_IsIdle(&XFT); for (i=0;i<10;i++) { printf("TEST LOOP %d:\n",i); ref_a.f = (float)i; // 適当 ref_b.f = (float)(i*100 + i); // 適当 while(!XFloat_top_IsReady(&XFT)) ; // Readyを確認 XFloat_top_Set_a(&XFT, ref_a.u); // aに値を設定 XFloat_top_Set_b(&XFT, ref_b.u); // bに値を設定 in_a.u = XFloat_top_Get_a(&XFT); in_b.u = XFloat_top_Get_b(&XFT); printf(" Get value a = %f(%x), b = %f(%x)\n", in_a.f,in_a.u,in_b.f,in_b.u); XFloat_top_Start(&XFT); // 処理を開始 while (!XFloat_top_IsDone(&XFT)) ; // Done待ち out_c.u = XFloat_top_Get_c(&XFT); // cの値を取得 printf(" Get value c = %f(%x)\n",out_c.f,out_c.u); if ( ref_float_top(ref_a.f, ref_b.f) != out_c.f ) // 期待値比較 printf(" ERROR! ref = %f, val = %f\n", ref_float_top(ref_a.f, ref_b.f),out_c.f); } printf("float_top test done.\n"); cleanup_platform(); return 0; }
IDの扱いやXFloat_top_LookupConfig, XFloat_top_CfgInitializeの設定方法は、SDKのサンプルに付属するGPIOなどと同様です。
実行結果
IPコアの処理結果と期待値が完全に一致しました。
TEST LOOP 0: Get value a = 0.000000(0), b = 0.000000(0) Get value c = 0.000000(0) TEST LOOP 1: Get value a = 1.000000(3f800000), b = 101.000000(42ca0000) Get value c = 101.000000(42ca0000) TEST LOOP 2: Get value a = 2.000000(40000000), b = 202.000000(434a0000) Get value c = 404.000000(43ca0000) TEST LOOP 3: Get value a = 3.000000(40400000), b = 303.000000(43978000) Get value c = 909.000000(44634000) TEST LOOP 4: Get value a = 4.000000(40800000), b = 404.000000(43ca0000) Get value c = 1616.000000(44ca0000) TEST LOOP 5: Get value a = 5.000000(40a00000), b = 505.000000(43fc8000) Get value c = 2525.000000(451dd000) TEST LOOP 6: Get value a = 6.000000(40c00000), b = 606.000000(44178000) Get value c = 3636.000000(45634000) TEST LOOP 7: Get value a = 7.000000(40e00000), b = 707.000000(4430c000) Get value c = 4949.000000(459aa800) TEST LOOP 8: Get value a = 8.000000(41000000), b = 808.000000(444a0000) Get value c = 6464.000000(45ca0000) TEST LOOP 9: Get value a = 9.000000(41100000), b = 909.000000(44634000) Get value c = 8181.000000(45ffa800) float_top test done.
まとめ
AXI4-Liteインターフェースの場合、次の手順でIPコアにアクセスできました。
- 初期設定(XDut_LookupConfig,XDut_CfgInitialize)
- ステータスを確認(XDut_IsReady)
- 引数に値を設定(XDut_Set_ARG)
- 処理を開始(XDut_Start)
- 処理終了待ち(XDut_IsDone)
- 処理結果を取得(XDut_Get_ARG)