ブロッキング呼び出しでの HLS 関数の動作 - 2021.2 日本語

Vitis Model Composer ユーザー ガイド (UG1483)

Document ID
UG1483
Release Date
2021-10-22
Version
2021.2 日本語

HLS 関数は、何よりもまず関数です。決まった数の入力と出力があり、関数が呼び出されると、入力を消費して決まった数の出力を生成します。xmcImportFunction を使用してインポートした HLS 関数がハングした場合 (無限ループがあるなど)、Simulink もインポートされたブロックからの出力を無限に待つことになるためハングします。これは、xmcImportFunction を使用してインポートされた HLS 関数は Simulink と同じスレッドで実行されるからです。インポートされた関数がハングすると、Simulink もハングします。

無限ループを含む関数はあまりよい例ではありませんが、より現実的な例として、データを生成する AI エンジン カーネルとデータを消費する HLS 関数を考えてみます (次の図を参照)。

図 1. データのプロデューサーとコンシューマー

HLS 関数に、入力を読み出すブロッキング呼び出しがあるとします。次に、この HLS 関数の一部を示します。

疑似コード - ブロッキング呼び出し

void hls_func(hls::stream<ap_axis<64, 0, 0, 0> > & in_sample
              hls::stream<ap_axis<64, 0, 0, 0> > & out_sample) {
    ...
    ap_axis<64, 0, 0, 0> in_sample_x = in_sample.read();
    ...
}

このコードで、read() はブロッキング呼び出しです。ストリームにデータがなければ、この読み出しはブロックされます。データを生成する AI エンジン カーネルは、実行されたときに出力を生成する場合としない場合があります。そのため、Simulink が HLS 関数を呼び出したときに生成ブロックからのデータがない場合、HLS 関数がブロックされ、Simulink がハングします。

注記: 現在の形式では、 xmcImportFunctionhls::stream を含むシグネチャのある関数をインポートできません。そのため、ラッパーが必要です。
注記: xmcImportFucntion を使用して作成されたブロックは、AI エンジン ブロックで生成された可変サイズの信号を受信できないので、AI エンジン ブロックには接続できません。

インポートされた HLS 関数とは異なり、HLS Kernel ブロックは別のスレッドで実行されます。そのため、HLS Kernel がプロデューサー AI エンジン ブロックからの入力を待つなどブロックされた場合でも、Simulink は機能し続けます。この場合、HLS Kernel ブロックの出力はデータを含まない可変サイズの出力になります。

HLS Kernel は IP

HLS 関数をそれだけデザインにインポートしても、HLS 関数はストリーミング ポートを持つ IP としては機能しません。Model Composer で Interface Spec ブロックを使用して、デザインのストリーミング ポートを指定し、HLS IP を生成する必要があります。HLS Kernel は、HLS 関数とは異なり、Vitis™ ソフトウェア プラットフォーム HLS で使用可能な HLS IP であり、直接合成できます。次に、ストリーミング インターフェイスを含む HLS カーネル コードを示します。

hls_kernel.cc:

void hls_kernel_blk(
    hls::stream<ap_axis<64, 0, 0, 0> > & in_sample1,
    hls::stream<ap_axis<64, 0, 0, 0> > & in_sample2,
    hls::stream<ap_axis<64, 0, 0, 0> > & out0_itr1,
    hls::stream<ap_axis<64, 0, 0, 0> > & out1_itr1
)
{
    #pragma HLS PIPELINE II=1
    #pragma HLS INTERFACE ap_ctrl_none port=return
    #pragma HLS INTERFACE axis register both port=out1_itr1
    #pragma HLS INTERFACE axis register both port=out0_itr1
    #pragma HLS INTERFACE axis register both port=in_sample1
    #pragma HLS INTERFACE axis register both port=in_sample2
    ap_int64 in_samp0 ; // Iteration-1: 2 complex samples concatenated to 64-bit
    ap_int64 in_samp1 ; // Iteration-2: 2 complex samples concatenated to 64-bit
...

この例で、関数シグネチャとポートのインターフェイスを指定する HLS プラグマに注目してください。この関数には、HLS IP に必要なすべてのコンストラクトが含まれます。

次に示すように、対応するカーネル関数をヘッダー ファイルで指定のフォーマットで宣言する必要があります。

void hls_kernel_blk(
    adf::dir::in hls::stream<ap_axis<64, 0, 0, 0> > &in_sample1,
    adf::dir::in hls::stream<ap_axis<64, 0, 0, 0> > &in_sample2,
    adf::dir::out hls::stream<ap_axis<64, 0, 0, 0> > &out0_itr1,
    adf::dir::out hls::stream<ap_axis<64, 0, 0, 0> > & out1_itr1
);

上記に示すように、各関数パラメーターには、ポートの方向によって adf::dir::in または adf::dir::out を前に付ける必要があります。