AI エンジンは、複数レーンの MAC 演算をサポートします。FIR アプリケーションのバリエーションでは、複数レーンの乗算 - sliding_mul に説明されている aie::sliding_mul*
クラスと関数を使用できます。
このセクションでは、
aie::sliding_mul
および aie::sliding_mac
関数を Lanes=8
および Points=8
に設定して使用します。データと係数のステップ サイズは 1 (デフォルト) です。たとえば、acc =
aie::sliding_mac<8,8>(acc,coe[1],0,buff,8);
は次を実行します。Lane 0: acc[0]=acc[0]+coe[1][0]*buff[8]+coe[1][1]*buff[9]+...+coe[1][7]*buff[15];
Lane 1: acc[1]=acc[1]+coe[1][0]*buff[9]+coe[1][1]*buff[10]+...+coe[1][7]*buff[16];
...
Lane 7: acc[7]=acc[7]+coe[1][0]*buff[15]+coe[1][1]*buff[16]+...+coe[1][7]*buff[22];
データ buff
は異なるレーンの異なるインデックスから開始します。実行には、8 つ以上のサンプル (buff[8]
から buff[22]
) が必要です。
32 タップあるので、8 レーンの出力を計算するには、FIR に 1 つの aie::sliding_mul<8,8>
演算と 3 つの aie::sliding_mac<8,8>
演算が必要です。データ バッファーは、buff.insert
によりストリーム ポートからアップデートされます。
ベクター化されたカーネルのコードは次のとおりです。
//keep margin data between different executions of graph
static aie::vector<cint16,32> delay_line;
alignas(aie::vector_decl_align) static cint16 eq_coef[32]={{1,2},{3,4},...};
__attribute__((noinline)) void fir_32tap_vector(input_stream<cint16> * sig_in, output_stream<cint16> * sig_out){
const int LSIZE=(SAMPLES/32);
aie::accum<cacc48,8> acc;
const aie::vector<cint16,8> coe[4] = {aie::load_v<8>(eq_coef),aie::load_v<8>(eq_coef+8),aie::load_v<8>(eq_coef+16),aie::load_v<8>(eq_coef+24)};
aie::vector<cint16,32> buff=delay_line;
for(int i=0;i<LSIZE;i++){
//1st 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,0);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,8);
buff.insert(0,readincr_v<4>(sig_in));
buff.insert(1,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,16);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,24);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//2nd 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,8);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,16);
buff.insert(2,readincr_v<4>(sig_in));
buff.insert(3,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,24);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,0);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//3rd 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,16);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,24);
buff.insert(4,readincr_v<4>(sig_in));
buff.insert(5,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,0);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,8);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
//4th 8 samples
acc = aie::sliding_mul<8,8>(coe[0],0,buff,24);
acc = aie::sliding_mac<8,8>(acc,coe[1],0,buff,0);
buff.insert(6,readincr_v<4>(sig_in));
buff.insert(7,readincr_v<4>(sig_in));
acc = aie::sliding_mac<8,8>(acc,coe[2],0,buff,8);
acc = aie::sliding_mac<8,8>(acc,coe[3],0,buff,16);
writeincr(sig_out,acc.to_vector<cint16>(SHIFT));
}
delay_line=buff;
}
void fir_32tap_vector_init()
{
//initialize data
for (int i=0;i<8;i++){
aie::vector<int16,8> tmp=get_wss(0);
delay_line.insert(i,tmp.cast_to<cint16>());
}
};
注記:
-
alignas(aie::vector_decl_align)
を使用して、データがベクター ロードおよびストア用に揃えられていることを確認できます。 - メイン ループの各反復で、複数のサンプルが計算されます。この結果、ループ カウントが削減されます。
- データのアップデート、計算、データの書き込みは、コードで交互に実行されます。データ バッファー
buff
のどの部分を読み出すかの判断は、aie::sliding_mul
のdata_start
を使用して制御します。 -
aie::sliding_mul
にサポートされるデータ型およびレーン数は、 『AI エンジン API ユーザー ガイド』 (UG1529) を参照してください。
メイン ループの開始間隔 (II) を特定する必要があります。ループの開始間隔を特定するには、次の手順に従います。
-
aiecompiler
に-v
オプションを追加し、カーネル コンパイルの詳細レポートを出力します。 - カーネルのコンパイル ログ (
Work/aie/<COL_ROW>/<COL_ROW>.log
など) を開きます。 - ログで
do-loop
などのキーワードを検索し、ループの開始間隔を特定します。次に、結果の例を示します。説明:HW do-loop #2821 in ".../fir_32tap_vector.cc", line 21: (loop #3) : critical cycle of length 130 : ... minimum length due to resources: 128 scheduling HW do-loop #2821 (algo 2) -> # cycles: ...... NOTE: automatically decreased the number of used priority functions to 3 to reduce runtime -> # cycles: .....183 (exceeds -k 110) -> no folding: 183 -> HW do-loop #2821 in ".../Vitis/2023.2/aietools/include/adf/stream/me/stream_utils.h", line 870: (loop #3) : 183 cycles
- ループの開始間隔は 183 です。つまり、サンプルは約 183/32 ~= 6 サイクルです。
- メッセージ
(exceeds -k 110) -> no folding
は、ループのサイクル カウントが制限を超えているため、スケジューラでソフトウェアのパイプライン処理が試行されていないことを示します。
- ループ サイクル制限をオーバーライドするには、
--Xchess="fir_32tap_vector:backend.mist2.maxfoldk=200"
などのユーザー制約をaiecompiler
に追加します。次に、結果の例を示します。
scheduling HW do-loop #2821 (algo 2) -> # cycles: ...... NOTE: automatically decreased the number of used priority functions to 3 to reduce runtime -> # cycles: .....183 (modulo) -> # cycles: ... ok (required budget ratio: 2) ... (resume algo) -> after folding: 161 (folded over 1 iterations) -> HW do-loop #2821 in ".../Vitis/2023.2/aietools/include/adf/stream/me/stream_utils.h", line 870: (loop #3) : 161 cycles
ソフトウェアでサンプルを生成するために、約 161/32 ~= 5 サイクルが必要です。