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)

