ランタイム グラフ制御 API

この章では、グラフを外部コントローラーから初期化、実行、アップデート、および制御するのに使用可能な制御 API について説明します。また、カーネル内でのデータ処理に影響し、全体的なグラフの制御フローを同期または非同期に変更する入力グラフ仕様を使用して、ランタイム パラメーター (RTP) を指定する方法も説明します。

グラフ実行の制御

AI エンジンを含む Versal™ ACAP では、AI エンジン アレイで実行されるグラフをプロセッシング システム (PS) を使用して動的にロード、監視、および制御できます。AI エンジン グラフが 1 つのビットストリーム イメージとして読み込まれた場合でも、グラフの実行ステートの監視およびランタイム パラメーターの変更に、PS プログラムを使用できます。

グラフの初期化および実行の制御に main プログラムで使用可能な多数の API メソッドは、graph ベース クラスにより提供されています。グラフのシミュレーションに使用するユーザー main アプリケーションでは、argc および argv パラメーターはサポートされません。詳細は、適応型データフロー グラフ仕様のリファレンスを参照してください。

グラフの基本的な反復実行

次のグラフ制御 API は、グラフ API を使用して、グラフの初期化、実行、待機、および終了を特定の回数だけ反復実行する方法の例を示しています。グラフ オブジェクト mygraph は、simpleGraph という定義済みのグラフ クラスを使用して宣言されています。その後、main アプリケーションでこのグラフ オブジェクトが初期化および実行されます。init() メソッドは、事前に指定された AI エンジン タイルの AI エンジン アレイにグラフを読み込みます。これには、各 AI エンジンの ELF バイナリの読み込み、配線用のストリーム スイッチの設定、I/O の DMA の設定が含まれます。プロセッサはディスエーブル ステートに保持されます。run() メソッドは、プロセッサをイネーブルにしてグラフの実行を開始します。run API の実行時に正の整数引数を指定すると、グラフを指定の回数だけ反復実行できます。これは、グラフ実行のデバッグに便利です。

#include "project.h" 
simpleGraph mygraph; 

int main(void) { 
  mygraph.init(); 
  mygraph.run(3); // run 3 iterations 
  mygraph.wait(); // wait for 3 iterations to finish 
  mygraph.run(10); // run 10 iterations 
  mygraph.end(); // wait for 10 iterations to finish 
  return 0; 
}

wait() API は、1 回目の実行の終了を待ってから 2 回目の実行を開始するために使用します。wait には end と同じブロッキング効果がありますが、グラフを再初期化せずに再実行できる点が異なります。run API はグラフのアクティブ プロセッサのループ境界を変更するため、wait を挿入せずに run を連続実行すると、予期されない影響が見られることがあります。

グラフの有限実行

グラフの有限実行では、グラフのステートは graph.run(n) の前後で保持されます。graph.run(n) の実行後、AI エンジンは再初期化されず、メモリの内容はクリアされません。次のコード例では、run を最初に 3 回の呼び出した後、core-main ラッパー コードのステートはそのまま保持され、カーネルは次の実行 (10 回の反復) が pong バッファーで開始します。ピンポン バッファー セレクターのステートはそのままになります。graph.end() は、グラフのステートはクリア (グローバル変数を再初期化) せず、ストリーム スイッチの設定もクリアしません。core-main を終了するだけです。グラフを再実行するには、PDI/XCLBIN を再度読み込む必要があります。
重要: graph.wait() の後、graph.end() の前に graph.run() または graph.resume() のいずれかが必要です。そうしないと、グラフが無限に待機状態になり、graph.end() が実行されません。これらの API の詳細は、グラフ オブジェクト を参照してください。
#include "project.h" 
simpleGraph mygraph; 

int main(void) { 
  mygraph.init(); 
  mygraph.run(3); // run 3 iterations 
  mygraph.wait(); // wait for 3 iterations to finish 
  mygraph.run(10); // run 10 iterations 
  mygraph.end(); // wait for 10 iterations to finish 
  return 0; 
}

グラフの無限実行

次のグラフ制御 API は、グラフを無限に実行する方法を示します。

#include "project.h"
simpleGraph mygraph;

int main(void) {
  mygraph.init();  // load the graph
  mygraph.run();   // start the graph
  return 0;
}

グラフ オブジェクト mygraph は、simpleGraph という定義済みのグラフ クラスを使用して宣言されます。その後、main アプリケーションでこのグラフ オブジェクトが初期化および実行されます。init() メソッドは、事前に指定された AI エンジン タイルの AI エンジン アレイにグラフを読み込みます。これには、各 AI エンジンの ELF バイナリの読み込み、配線用のストリーム スイッチの設定、I/O の DMA の設定が含まれます。プロセッサはディスエーブル ステートに保持されます。run() メソッドは、プロセッサをイネーブルにしてグラフの実行を開始します。run() メソッドに実行の反復回数が指定されていないので、このグラフは無限に実行されます。

graph::run() の引数を指定しない場合、AI エンジン カーネルは以前に指定された回数だけ反復実行されます。グラフを引数なしで実行した場合は、デフォルトで無限実行されます。グラフを有限の反復回数で実行した場合 (例: mygraph.run(3);mygraph.run())、2 回目の実行呼び出しでも 3 回実行されます。前の反復回数の設定にかかわらずグラフを無限に実行するには、graph::run(-1) を使用します。

グラフの並列実行

前述の API メソッドでは、wait() および end() メソッドのみがブロッキング操作で、main アプリケーションを無限にブロックできます。そのため、最上位に複数のグラフを宣言する場合は、次に示すように API を交互に実行して、グラフを並列実行する必要があります。

#include "project.h"
simpleGraph g1, g2, g3;

int main(void) {
  g1.init(); g2.init(); g3.init();
  g1.run(<num-iter>); g2.run(<num-iter>); g3.run(<num-iter>);
  g1.end(); g2.end(); g3.end();
  return 0;
}
注記: 各グラフは、初期化 (init) した後にのみ開始 (run) できます。また、並列実行するには、すべてのグラフを開始 (run) してから終了 (end) を待機する必要があります。

時間を指定した実行

マルチレート グラフでは、すべてのカーネルが同じ反復回数実行される必要はありません。その場合、テストするのに時間を指定した実行モデルの方が適しています。wait および end API には、サイクル タイムアウトを指定する正の整数を使用するバリエーションがあります。これは、プロセッサをディスエーブルにして戻すまでに API 呼び出しがブロックする AI エンジンのサイクル数です。ブロッキング条件は、グラフの終了イベントに依存しません。タイムアウト時のグラフのステートは、どのステートでもかまいません。

#include "project.h"
simpleGraph mygraph;

int main(void) {
  mygraph.init();
  mygraph.run();
  mygraph.wait(10000);  // wait for 10000 AI Engine cycles
  mygraph.resume();     // continue executing 
  mygraph.end(15000);   // wait for another 15000 cycles and terminate
}
注記: resume() API は、最初のタイムアウトの後、実行を停止した時点から再開するのに使用します。resume は、タイマーをリセットし、AI エンジンをイネーブルにするだけです。AI エンジンの実行が終了した後に resume を呼び出しても、何も起こりません。

ランタイム パラメーターの指定

ここまでで示したデータフロー グラフは、完全にスタティックに定義されていました。実際の状況では、動的な状況またはイベントに基づいてグラフの動作を変更する必要がある場合があります。必要な変更は、動作モードの変更など処理するデータに対してであったり、条件実行やグラフを別のグラフで動的にリコンフィギュレーションするなどグラフの制御フローに対してであったりします。これには、ランタイム タイミング パラメーター (RTP) が便利です。カーネルまたはグラフのどちらも、パラメーターを使用して実行するように定義できます。グラフの実行中にこれらのパラメーター値をアップデートまたは読み出すため、追加のグラフ API が提供されています。

2 種類のランタイム パラメーターがサポートされています。1 つ目は非同期 (スティッキー) パラメーターで、プロセッシング システム (PS) などの制御プロセッサまたは別の AI エンジン カーネルでいつでも変更できます。カーネルが起動されるたびに、特定の同期化なしで読み出されます。これらのタイプのパラメーターは、たとえば変更が頻繁ではないフィルター係数として使用できます。

サポートされるランタイム パラメーターのもう 1 つのタイプは、同期 (トリガー) パラメーターです。トリガー パラメーターを必要とするカーネルは、これらのパラメーターが制御プロセッサにより書き込まれるまで実行されません。書き込まれるとカーネルが 1 回実行され、アップデートされた新しい値が読み出されます。これが完了すると、パラメーターが再びアップデートされるまでカーネルは実行されません。これにより、通常のストリーミング モデルから異なるタイプの実行モデルを使用できるようになり、同期化をブロックすることが重要な特定のアップデート操作に役立つことがあります。

ランタイム パラメーターは、スカラー値または配列値で指定できます。制御プロセッサ (PS など) がアップデートを実行する場合、graph.update() API を使用する必要があります。

ランタイム データ パラメーターの指定

パラメーターの推論

カーネル関数の仮引数に整数スカラー値が表示される場合、パラメーターはランタイム パラメーターになります。次の例では、引数 select はランタイム パラメーターです。

#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
 
  void simple_param(input_window_cint16 * in, output_window_cint16 * out, int select);

#endif
ランタイム パラメーターは、ストリームおよびウィンドウで生成されたパラメーターと共にポートとして処理されます。スカラーおよびスカラー配列のデータ型は、いずれもランタイム パラメーターとして渡すことができます。サポートされるスカラー データ型は、int8int16int32int64uint8uint16uint32uint64cint16cint32floatcfloat です。
注記: 構造体とポインターは、ランタイム パラメーターとして渡すことはできません。各 AI エンジンにはそれぞれメモリ空間があるので、ポインターが AI エンジン間で渡されると、一貫性が失われます。また、PS と AI エンジンではポインターの表現が異なります。structs にはメンバーとしてポインターが含まれることがあるため、structs をランタイム パラメーターとして渡すことはできません。
次の例では、32 個の整数の配列がパラメーターとして filter_with_array_param 関数に渡されます。
#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H

  void filter_with_array_param(input_window_cint16 * in, output_window_cint16 * out, const int32 (&coefficients)[32]);

#endif

関数引数 (配列パラメーターを含む) の各パラメーターにはポートが推論されます。次の表に、各関数引数に推論されるポートのタイプを示します。

表 1. パラメーター別のポートのタイプ
仮引数 ポート クラス
T 入力
const T 入力
T & 入出力
const T & 入力
const T (&)[ …] 入力
T(&)[…] 入出力

この表から、AI エンジンで関数パラメーターに対して外部から見える変更を加えられない場合、入力ポートが推論されることがわかります。仮引数が値渡しの場合、コピーが作成されるため、そのコピーに対する変更は外部から見えません。パラメーターが const 修飾子付きで渡される場合、パラメーターに書き込めないため、これらも入力ポートとして扱われます。

AI エンジン カーネルにパラメーター参照が渡され、カーネルがそれを変更できる場合、入出力ポートが推論されるので、これを AI エンジン カーネル間でのパラメーター渡し、または制御プロセッサからの結果の読み出しに使用できます。

注記: 入出力ポートは、カーネル自体が読み出しまたは書き込みを実行できるポートです。ただし、グラフから見ると、入出力ポートは出力ポートとしてのみ動作します。そのため、入出力ポートは、入力 RTP ポートに接続するか、または graph::read() で読み出すことのみが可能です。入出力ポートは、graph::update() でアップデートすることはできません
注記: カーネルが入力ランタイム パラメーターを受け入れ、その値を変更し、変更した値を出力ランタイム パラメーターとして返す場合、変数は arg リストに 2 回 (1 回は入力として、もう 1 回は入出力として) 現れる必要があります (例: kernel_function(int32 foo_in, int32 &foo_out))。

パラメーターの接続

入力および入出力ランタイム パラメーター ポートは、それらを含むグラフ内の対応する階層的ポートに接続できます。このメカニズムにより、パラメーターはランタイムに変更するためアクセスできるようになります。次のグラフでは、事前に定義された simple_param カーネルのインスタンスが作成されます。このカーネルには、2 つの入力ポートと 1 つの出力ポートがあります。引数リストに現れる最初の引数 in[0] は入力ウィンドウです。2 番目の引数は出力ウィンドウです。3 番目の引数はランタイム パラメーター (ウィンドウ型でもストリーム型でもない) であり、値渡しであるため、入力パラメーター in[1] として推論されます。

次のグラフ定義では、simple_param カーネルがインスタンシエートされ、ウィンドウは in[0] および out[0] (カーネルの入力ウィンドウと出力ウィンドウ) に接続されます。ランタイム パラメーターは、グラフの入力ポート select_value に接続されます。

class parameterGraph : public graph {
private:
  kernel first;
  
public:
  input_port select_value;
  input_port in;
  output_port out;
  parameterGraph() {
    first = kernel::create(simple_param);

    connect< window <32> >(in, first.in[0]);
    connect< window <32> >(first.out[0], out);
    connect<parameter>(select_value, first.in[1]);
  }
};

配列パラメーターも同じ方法で接続できます。配列データには、このカーネルがマップされるプロセッサからアクセスできるように、空間が自動的に割り当てられます。

class arrayParameterGraph : public graph {
private:
  kernel first;
  
public:
  input_port coeffs;
  input_port in;
  output_port out;
  arrayParameterGraph() {
    first = kernel::create(filter_with_array_param);

    connect< window <32> >(in, first.in[0]);
    connect< window <32> >(first.out[0], out);
    connect<parameter>(coeffs, first.in[1]);
  }
};

入力パラメーターの同期

入力ランタイム パラメーター ポートのデフォルト動作は、動作をトリガーすることです。つまり、このパラメーターは、カーネルをいつ実行できるかを決定する規則の一部として使用されます。このグラフの例では、カーネルは次の 3 つの条件が満たされたときにのみ実行されます。

  • 32 バイトの入力データの有効なウィンドウが使用可能
  • 出力データ用に 32 バイトの空のウィンドウが使用可能
  • 入力パラメーターへの書き込みが発生

トリガー モードでは、入力パラメーターへの 1 回の書き込みでカーネルを 1 回実行でき、カーネルが呼び出されるたびに入力パラメーターの値が設定されます。

入力カーネル パラメーターを非同期で設定できるモードもあります。パラメーターを非同期でアップデートするよう指定するには、ポートの接続時に async 修飾子を使用します。

connect<parameter>(param_port, async(first.in[1]));

カーネル ポートが非同期と指定されている場合、カーネルの起動規則の一部としては使用されません。パラメーターに一度値が書き込まれると、それ以降のカーネル起動にその値が使用されます。PS は、いつでもランタイム パラメーターに新しい値を書き込むことができます。その値は、それ以降のカーネル起動に使用されます。

入出力パラメーターの同期

入出力ランタイム パラメーター ポートのデフォルト動作は、非同期動作です。つまり、パラメーターは制御プロセッサまたはほかのカーネルで読み出すことができますが、プロデューサー カーネルの実行には影響しません。入出力パラメーターからの同期動作で、カーネルの各呼び出しでパラメーター値が読み出されるまでカーネルがブロックされる場合は、次のように、それらを含むグラフで入出力ポートを接続する際に sync 修飾子を使用できます。

connect<parameter>(sync(first.out[1]), param_port);

ランタイム パラメーターのアップデート/読み出しメカニズム

このセクションでは、ランタイム パラメーターのアップデートと読み出しのメカニズムについて説明します。これらのタイプのアプリケーションでは、通常はコンパイル時の反復回数の上限を指定せずにコアを無制限に実行し、パラメーターの変更の影響をモニターすることを推奨します。

グラフ API を使用したパラメーターのアップデート/読み出し

デフォルトのコンパイル モードでは、main アプリケーションは個別の制御スレッドとしてコンパイルされます。この制御スレッドは、AI エンジン アレイ上で実行されるグラフと並行して PS 上で実行する必要があります。main アプリケーションは、アップデート API および読み出し API を使用して、グラフ内で宣言されたランタイム パラメーターに任意のレベルでアクセスできます。このセクションでは、これらの API を例を使用して説明します。

同期アップデート/読み出し

次のコードは、ランタイム データ パラメーターの指定 に説明されている simple_param グラフの main アプリケーションを示します。

#include "param.h"
parameterGraph mygraph;

int main(void) {
  mygraph.init();
  mygraph.run(2);
  
  mygraph.update(mygraph.select_value, 23);
  mygraph.update(mygraph.select_value, 45);

  mygraph.end();
  return 0;
}

この例では、グラフ mygraph が初期化された後に反復が 2 回実行されます。これにより、受信カーネルの実行ごとに新しい値で更新する必要がある入力パラメーター ポート select_value がトリガーされます。API の最初の引数 update はアップデートするポートを示し、2 番目の引数はその値を示します。ポートの方向、そのデータ型、およびスカラー パラメーターか配列パラメーターかに基づいて、ほかにもいくつかのアップデート API がサポートされています。適応型データフロー グラフ仕様のリファレンス を参照してください。

プログラムが固定された回数のテスト反復を使用してコンパイルされている場合、トリガーされたパラメーターに対して、main プログラム内のアップデート API 呼び出しの数がテスト反復回数と一致している必要があります。一致していない場合、シミュレーションが追加のアップデートを待機し続けることになる可能性があります。非同期パラメーターの場合、アップデートはグラフの実行とは非同期に実行されれ、アップデートされなかった場合はカーネルは以前の値を使用します。

さらに、前のグラフが同期入出力パラメーターを使用してコンパイルされている場合は、次の例に示すように、アップデート呼び出しと読み出し呼び出しを交互に実行する必要があります。

#include "param.h"
parameterGraph mygraph;

int main(void) {
  int result0, result1;
  mygraph.init();
  mygraph.run(2);

  mygraph.update(mygraph.select_value, 23);
  mygraph.read(mygraph.result_out, result0);
  mygraph.update(mygraph.select_value, 45);
  mygraph.read(mygraph.result_out, result1);

  mygraph.end();
  return 0;
}

この例では、グラフは反復ごとに入出力ポート result_out を介してスカラー結果を生成すると想定されています。read API は、各反復後にこのポートの値を同期的に読み出すために使用されます。read API の最初の引数はリードバックするグラフの入出力ポートを示し、2 番目の引数はその値を保存する場所を示します (参照により渡す)。

同期プロトコルにより、グラフで値が生成されてから読み出し操作用に値がサンプリングされ、グラフの値が読み出されてから次の反復に進むようになります。このため、updateread を交互に実行することが重要です。

非同期アップデート/読み出し

入力パラメーターが非同期プロトコルで指定されている場合、最初のアップデートが実行されてパラメーターが初期化されてからカーネルが実行されます。ただし、次のアップデートの前に、カーネルが任意の回数実行される可能性があります。これは通常、アプリケーション運用時に非同期にアップデートされることを意図しています。デバッグでは、次の例に示すように、次のアップデートの前に、反復を決まった回数実行するために wait API を使用できます。

#include "param.h"
asyncGraph mygraph;

int main(void) {
  int result0, result1;
  mygraph.init();

  mygraph.update(mygraph.select_value, 23);
  mygraph.run(5);
  mygraph.wait();
  mygraph.update(mygraph.select_value, 45);
  mygraph.run(15);
  mygraph.end();
  return 0;
}

上記の例では、最初のアップデート後に反復が 5 回実行され、その後に別のアップデートが実行されてから、反復が 15 回実行されます。グラフに非同期入出力ポートがある場合は、待機 (または終了) の直後にデータをリードバックすることもできます。

非同期アップデートの別のテンプレートでは、次の例に示すように、wait API でタイムアウトを使用します。

#include "param.h"
asyncGraph mygraph;

int main(void) {
  int result0, result1;
  mygraph.init();
  mygraph.run();
  mygraph.update(mygraph.select_value, 23);
  mygraph.wait(10000);
  mygraph.update(mygraph.select_value, 45);
  mygraph.resume();
  mygraph.end(15000);
  return 0;
}

この例では、グラフは無限に実行されるよう設定されています。ただし、run API が呼び出された後、最初のアップデートでパラメーターが初期化されるまで実行がブロックされます。その後約 10,000 サイクル実行されてから、制御スレッドで別のアップデートが実行できるようになります。新しいアップデートは、次のカーネル呼び出し境界で有効になります。その後、グラフがさらに 15,000 サイクル実行されてから終了します。

AI エンジン カーネル間でのチェーン アップデート

上記のランタイム パラメーターの例は、制御プロセッサからランタイム パラメーターをアップデートする機能を示しています。パラメーターのアップデートを AI エンジン 間で伝搬することも可能です。カーネルの入出力ポートがほかのカーネルの入力ポートに接続されている場合、複数の AI エンジンを介してアップデートのチェーンをトリガーできます。次のコードで定義される 2 つのカーネルがあるとします。プロデューサー カーネルには、スカラー整数を読み出す入力ポートと、32 個の整数からなる配列の読み出しと書き込みを実行する入出力ポートがあります。コンシューマー カーネルには、係数の配列を読み出す入力ポートと、データのウィンドウを書き込む出力ポートがあります。

#ifndef FUNCTION_KERNELS_H
#define FUNCTION_KERNELS_H
 
  void producer(const int32 &, int32 (&)[32] );
  void consumer(const int32 (&)[32], output_window_cint16 *);

#endif

次のグラフに示すように、PS はプロデューサー カーネルのスカラー入力をアップデートします。プロデューサー カーネルが実行されると、コンシューマーの実行が自動的にトリガーされます (出力データ用にバッファーが使用可能な場合)。

#include <adf.h>
#include "kernels.h"

using namespace adf;

class chainedGraph : public graph {
private:
  kernel first;
  kernel second;
  
public:
  input_port select_value; 
  output_port out;

  chainedGraph() {
      first =  kernel::create(producer);
      second = kernel::create(consumer);

      connect< window <32> >(second.out[0], out);
      connect<parameter>(select_value, first.in[0]);
      connect<parameter>(first.inout[0], second.in[0]);
  }
};

ストリームの連続処理に使用される値を一度だけアップデートすることが目的である場合は、継続的に実行されることを確実にするため、コンシューマー カーネルのパラメーター入力ポートに async 修飾子を使用できます (パラメーターが指定されている場合)。

制御パラメーターによるランタイム グラフ リコンフィギュレーション

ランタイム パラメーターを使用して、グラフ内でデータのフローを切り替え、代替処理ルートを動的に提供できます。カーネル バイパスは、このタイプの処理で最も基本的であり、ランタイム パラメーターに基づいてデータの処理またはパススルーを許可します (カーネル バイパス 参照)。この機能は、たとえば、マルチモード アプリケーションでモードの切り替え時にカーネルをバイパスする必要がある場合に便利です。

ランタイム パラメーターによるバイパス制御

次の図に、2つのチャネルの信号データをサポートするアプリケーションを示します。1 つのチャネルは 2 つの低帯域幅チャネルに分割され、もう 1 つのチャネルは影響を受けずに動作を続けます。このタイプの動的リコンフィギュレーションは、ワイヤレス アプリケーションによく見られます。

1: 2 つの LTE20 チャネルを 1 つの LTE20 チャネルと 2 つの LTE10 チャネルに分割する動的リコンフィギュレーション

この図で、最初のチャネルは LTE20 データを変更せずに処理しますが、中間のチャネルは 2 つの LTE10 チャネルに動的に分割されます。carrier configuration RTP という制御パラメーターを使用して、ブロック境界上でデータ処理を分割します。中間のチャネルが 1 つの LTE20 チャネルとして動作しているときは、11 タップのハーフバンド カーネルはバイパスされます。ただし、中間のチャネルの帯域幅がそれ自体と 3 番目のチャネルの間で分割されて 2 つの LTE10 チャネルを形成するときは、両方のデータをミキシングする前に、両チャネルに 3 段フィルター チェーンが必要です。これを実現するには、11 タップのハーフバンド フィルターを切り替えてフローに戻し、2 つのデータ ストリームの代わりに 3 つのデータ ストリームを処理するようにミキサーを再設定します。

ヒント: LTE20 信号と LTE10 信号をミキシングするときにサンプル遅延のバランスがとれるように、遅延アライメント カーネルが必要です。2 つの信号は動的にスイッチングされるため、これらのカーネルを制御フローの一部にする必要があります。

次のコードに、上記のアプリケーションの最上位の入力グラフ仕様を示します。

class lte_reconfig : public graph {
  private:
    kernel demux;
    kernel cf[3];
    kernel interp0[3];
    kernel interp1[2];
    bypass bphb11;
    kernel delay ;
    kernel delay_byp ;
    bypass bpdelay ;
    kernel mixer ;
  public:
    input_port in;
    input_port  fromPS;
    output_port out ;

    lte_reconfig() {
      // demux also handles the control
      demux = kernel::create(demultiplexor);
      connect< window<1536> >(in, demux.in[0]);
      connect< parameter >(fromPS, demux.in[1]);

      runtime<ratio>(demux) = 0.1;
      source(demux) = "kernels/demux.cc";

      // instantiate all channel kernels
      for (int i=0;i<3;i++) {
        cf[i] = kernel::create(fir_89t_sym);
        source(cf[i]) = "kernels/fir_89t_sym.cc";
        runtime<ratio>(cf[i]) = 0.12;
      }
      for (int i=0;i<3;i++) {
        interp0[i] = kernel::create(fir_23t_sym_hb_2i);
        source(interp0[i]) = "kernels/hb23_2i.cc";
        runtime<ratio>(interp0[i]) = 0.1;
      }
      for (int i=0;i<2;i++) {
        interp1[i] = kernel::create(fir_11t_sym_hb_2i);
        source(interp1[i]) = "kernels/hb11_2i.cc";
        runtime<ratio>(interp1[i]) = 0.1;
      }
      bphb11 = bypass::create(interp1[0]);
      mixer =  kernel::create(mixer_dynamic);
      source(mixer) = "kernels/mixer_dynamic.cc";
      runtime<ratio>(mixer) = 0.4;
      delay =  kernel::create(sample_delay);
      source(delay) = "kernels/delay.cc";
      runtime<ratio>(delay) = 0.1;
      delay_byp =  kernel::create(sample_delay);
      source(delay_byp) = "kernels/delay.cc";
      runtime<ratio>(delay_byp) = 0.1;
      bpdelay = bypass::create(delay_byp) ;

      // Graph connections
      for (int i=0; i<3; i++) {
        connect< window<512, 352> >(demux.out[i], cf[i].in[0]);
        connect< parameter >(demux.inout[i], cf[i].in[1]);
      }
      connect< parameter >(demux.inout[3], bphb11.bp);
      connect< parameter >(demux.inout[3], negate(bpdelay.bp)) ;
      for (int i=0;i<3;i++) {
        connect< window<512, 64> >(cf[i].out[0], interp0[i].in[0]);
        connect< parameter >(cf[i].inout[0], interp0[i].in[1]);
      }
       // chan0 is LTE20 and is output right away
      connect< window<1024, 416> >(interp0[0].out[0], delay.in[0]);
      connect< window<1024> >(delay.out[0], mixer.in[0]);
      // chan1 is LTE20/10 and uses bypass
      connect< window<1024, 32> >(interp0[1].out[0], bphb11.in[0]);
      connect< parameter >(interp0[1].inout[0], bphb11.in[1]);
      connect< window<1024, 416> >(bphb11.out[0], bpdelay.in[0]);
      connect< window<1024> >(bpdelay.out[0], mixer.in[1]);
      // chan2 is LTE10 always
      connect< window<512, 32> >(interp0[2].out[0], interp1[1].in[0]);
      connect< parameter >(interp0[2].inout[0], interp1[1].in[1]);
      connect< window<1024> >(interp1[1].out[0], mixer.in[2]);
      //Mixer
      connect< parameter >(demux.inout[3], mixer.in[3]);
      connect< window<1024> >(mixer.out[0], out);
    };
};

バイパス仕様は、バイパスされるカーネル上の特殊なエンキャプスレーターとしてコード化されます。バイパスのポート シグネチャは、そのバイパスがカプセル化するカーネルのポート シグネチャと一致します。このカーネルは、バイパスを制御するランタイム パラメーターも受け取ります (0 はバイパスなし、1 はバイパス)。否定関数を使用して、制御を反転することもできます。

このグラフのバイパス パラメーター ポートは、通常のスカラー ランタイム パラメーターであり、ランタイム パラメーターのアップデート/読み出しメカニズム で説明するインタラクティブまたはスクリプトによるメカニズムを使用して、ほかのカーネルまたは Arm® プロセッサによって駆動できます。このポートは、囲い込むグラフに組み込むことにより、階層的に接続することもできます。

複数のカーネルによるランタイム パラメーターの共有

前述のグラフに含まれるチャネルを切り替えるランタイム パラメーターは、バイパス エンキャプスレーターとミキサー カーネルにより共有されています。両方のエンティティが同じデータ境界で切り替えられた同じ値を検出する必要があります。ランタイム パラメーターを共有している複数のノードが同じ AI エンジンにマップされる場合、同じプロセッサにマップされているすべてのノードが現在のデータ セットを処理してから次のデータ セットを処理するので、パラメーター値の切り替えは同期されます。ただし、共有しているカーネルが異なるプロセッサにマップされる場合は、次の図に示すように、これらのカーネルは異なるデータ セットをパイプライン方式で処理できます。この場合、ランタイム パラメーターはデータと共にパイプライン処理されます。

2: パイプライン処理されるランタイム パラメーター


現在のリリースでは、制御パラメーターを、コンシューマー カーネルの入力パラメーターに接続されているプロデューサー カーネルの入出力パラメーターにして、カーネルを介して制御パラメーターをパイプライン処理する必要があります。複数のプロセッサのパイプライン処理と、1 つのプロセッサ内の 1 対多のブロードキャスト接続を組み合わせて、任意の制御トポロジを作成できます。

ランタイム パラメーターのサポート サマリ

このセクションでは、AI エンジン ランタイム パラメーター (RTP) のサポート ステータスを示します。

表 2. AI エンジン RTP のデフォルトおよびサポート ステータス
AI エンジン RTP

(PS から/PS へ)

入力 出力
同期 非同期 同期 非同期
スカラー デフォルト サポートあり サポートあり デフォルト
配列 デフォルト サポートあり サポートあり デフォルト

PS から/PS への RTP 接続のコード抜粋:

connect<parameter>(fromPS, first.in[0]); //Synchronous RTP, default for input
connect<parameter>(fromPS, sync(first.in[0])); //Synchronous RTP
connect<parameter>(fromPS, async(first.in[0])); //Asynchronous RTP
connect<parameter>(second.inout[0], toPS); //Asynchronous RTP, default for output
connect<parameter>(async(second.inout[0]), toPS); //Asynchronous RTP
connect<parameter>(sync(second.inout[0]), toPS); //Synchronous RTP
表 3. AI エンジン RTP から AI エンジン RTP への接続のサポート ステータス
AI エンジン RTP から AI エンジン RTP デスティネーション
同期 非同期 未指定
ソース 同期 同期 サポートなし 同期
非同期 サポートなし 非同期 非同期
未指定 同期 非同期 同期

AI エンジン間の RTP 接続のコード抜粋:

connect<parameter>(first.inout[0], second.in[0]); //Not specified for output and input. Synchronous RTP from first.inout to second.in
connect<parameter>(sync(first.inout[0]), second.in[0]); //Specify “sync” for output. Synchronous RTP from first.inout to second.in
connect<parameter>(first.inout[0], sync(second.in[0])); //Specify “sync” for input. Synchronous RTP from first.inout to second.in
connect<parameter>(sync(first.inout[0]), sync(second.in[0])); //Specify “sync” for both. Synchronous RTP from first.inout to second.in
connect<parameter>(async(first.inout[0]), async(second.in[0])); //Specify “async” for both. Asynchronous RTP from first.inout to second.in
connect<parameter>(first.inout[0], async(second.in[0])); //Specify “async” for input. Asynchronous RTP from first.inout to second.in
connect<parameter>(async(first.inout[0]), second.in[0]); //Specify “async” for output. Asynchronous RTP from first.inout to second.in
connect<parameter>(async(first.inout[0]), sync(second.in[0])); //Not supported
connect<parameter>(sync(first.inout[0]), async(second.in[0])); //Not supported
注記: AI エンジン RTP から AI エンジン RTP への接続を使用する場合、ソース カーネルとデスティネーション カーネルでデータ通信に共有メモリが使用されます。データ通信には、DMA は使用されません。そのため、RTP 接続を含む AI エンジン カーネルは、同じまたは隣接する AI エンジン タイルに配置する必要があります。
表 4. AI エンジン RTP から複数の AI エンジン RTP デスティネーションへのブロードキャストのサポート ステータス
AI エンジン RTP から AI エンジン RTP デスティネーション
D1: 同期

D2: 同期

D1: 非同期

D2: 非同期

D1: 同期

D2: 非同期

D1: 同期

D2: 未指定

D1: 非同期

D2: 未指定

ソース S1: 同期

S2: 同期

同期 サポートなし サポートなし S1-D1: 同期

S2-D2: 同期

サポートなし
S1: 非同期

S2: 非同期

サポートなし 非同期 サポートなし サポートなし S1-D1: 非同期

S2-D2: 非同期

S1: 同期

S2: 非同期

サポートなし サポートなし S1-D1: 同期

S2-D2: 非同期

S1-D1: 同期

S2-D2: 非同期

サポートなし
S1: 同期

S2: 未指定

サポートなし サポートなし S1-D1: 同期

S2-D2: 非同期

同期 サポートなし
S1: 非同期

S2: 未指定

サポートなし 非同期 サポートなし サポートなし S1-D1: 非同期

S2-D2: 同期

  1. この表の S1 および S2 は、1 番目および 2 番目の接続の RTP ソースを示します。D1 および D2 は、複数のデスティネーション カーネルの RTP ポートを示します。Sync および Async は、グラフで指定されている同期モードおよび非同期モードを示します。S1-D1:Sync は、S1 から D1 への接続が同期モードであることを示します。

AI エンジン RTP から AI エンジン RTP への接続と同様、RTP ブロードキャストのソースおよびデスティネーション カーネルは、同じまたは隣接する AI エンジン タイルに配置する必要があります。コードの抜粋も同様で、複数接続に 1 つのソース RTP ポートが使用されることのみが異なります。RTP バッファーは、ソース カーネルとデスティネーション カーネルで共有されます。

注記:
  • このバージョンでは、AI エンジン カーネルと AI エンジン カーネルが同じ AI エンジン タイルに配置される場合、それらのカーネルの間では同期 RTP のみを使用可能です。
  • このバージョンでは、アレイ RTP ブロードキャストはサポートされません。
  • RTP ブロードキャストでは、シングル バッファーはサポートされません。