バージョン: Vitis 2024.1
要約
パート 1a では、AI エンジンを使って、IIR フィルターの 2 次セクションの 8 つの出力を並列に計算しました。
このセクションでは、6 次楕円ローパス フィルターの ADF グラフとテストベンチを修正する方法を示します。
Julia スクリプト
aie_iir_1b.jl
は、任意の IIR フィルターに必要な SIMD 係数をデザインおよび生成できる Julia スクリプトです。
初期状態では、ユーザー ラメーターは次のようになります。
first_set = true; # true: 1st set; false: 2nd set
if first_set
fp = 10.0e6 # passband frequency
coeff_file_suffix = "a" # file suffix to distinguish different coefficient sets
# with the same architecture
else
fp = 20.0e6 # passband frequency
coeff_file_suffix = "b" # 2nd set of coefficients
end # if first_set
fs = 100.0e6 # sampling frequency
p = 6 # no. of poles
rp = 0.1 # passband ripple (dB)
rs = 80.0 # stopband attenuation (dB)
N = 256 # no. of samples for impulse response
show_plots = true # display plots?
write_cmatrix = true # write C matrix to files?
write_impulse = true # write impulse response?
Julia を起動し、次のコマンドでスクリプトを実行します。
julia> cd("{specify_path_to_aie_iir_1b.jl}")
julia> include("aie_iir_1b.jl")
これはフィルター特性のプロットを作成し、フィルターには 6 つのポール (極) があるため、3 つの係数ファイル (C[1~3]a.h) を生成します。
カーネル コードは変わりませんが、ADF グラフとテストベンチは、追加されたセクションに対応するために少し修正する必要があります。
生成された *.dat
ファイルを data
ディレクトリに、生成された *.h
ファイル (係数ファイル) を src
ディレクトリに移動します。
適応型データフロー (ADF) グラフ
修正した ADF グラフは、次のようになります。
graph.hpp
#ifndef __GRAPH_H__ // include guard to prevent multiple inclusion
#define __GRAPH_H__
#include <adf.h> // Adaptive DataFlow header
#include "kernel.hpp"
using namespace adf;
// dataflow graph declaration
class the_graph : public graph { // inherit all properties of the adaptive dataflow graph
private:
kernel section1;
kernel section2;
kernel section3;
public:
input_plio in; // input port for data to enter the kernel
input_port cmtx1; // input port for SIMD matrix coefficients
input_port cmtx2;
input_port cmtx3;
output_plio out; // output port for data to leave the kernel
// constructor
the_graph() {
// associate the kernel with the function to be executed
section1 = kernel::create(SecondOrderSection<1>);
section2 = kernel::create(SecondOrderSection<2>);
section3 = kernel::create(SecondOrderSection<3>);
// declare data widths and files for simulation
#ifndef RTP_SWITCH
in = input_plio::create(plio_32_bits, "data/input.dat");
#else
in = input_plio::create(plio_32_bits, "data/two_freqs.dat");
#endif // RTP_SWITCH
out = output_plio::create(plio_32_bits, "output.dat");
const unsigned num_samples = 8;
// declare buffer sizes
dimensions(section1.in[0]) = {num_samples};
dimensions(section1.out[0]) = {num_samples};
dimensions(section2.in[0]) = {num_samples};
dimensions(section2.out[0]) = {num_samples};
dimensions(section3.in[0]) = {num_samples};
dimensions(section3.out[0]) = {num_samples};
// establish connections
connect(in.out[0], section1.in[0]);
connect<parameter>(cmtx1, adf::async(section1.in[1]));
connect(section1.out[0], section2.in[0]);
connect<parameter>(cmtx2, adf::async(section2.in[1]));
connect(section2.out[0], section3.in[0]);
connect<parameter>(cmtx3, adf::async(section3.in[1]));
connect(section3.out[0], out.in[0]);
// specify which source code file contains the kernel function
source(section1) = "kernel.cpp";
source(section2) = "kernel.cpp";
source(section3) = "kernel.cpp";
// !!! temporary value: assumes this kernel dominates the AI Engine tile !!!
runtime<ratio>(section1) = 1.0;
runtime<ratio>(section2) = 1.0;
runtime<ratio>(section3) = 1.0;
} // end the_graph()
}; // end class the_graph
#endif // __GRAPH_H__
注記:
2 つのカーネル (セクション) 追加されます。
新規セクションの係数に対して 2 つの
input_port
宣言が追加されます。2 つの
kernel::create()
文が追加されます。ネットワーク トポロジは、3 つのセクションがカスケード接続されるように変更されます。
追加で
source()
およびruntime<ratio>()
文が追加されます。
テストベンチ コード
テストベンチは、次のようになります。
tb.cpp
//#define RTP_SWITCH // comment out to check impulse response
#include "kernel.hpp"
#include "graph.hpp"
#include "C1a.h"
#include "C2a.h"
#include "C3a.h"
#ifdef RTP_SWITCH
#include "C1b.h"
#include "C2b.h"
#include "C3b.h"
#endif // RTP_SWITCH
using namespace std;
using namespace adf;
// specify the DFG
the_graph my_graph;
const unsigned num_pts = 256; // number of sample points in "input.dat"
#ifndef RTP_SWITCH
const unsigned num_iterations = num_pts/8; // number of iterations to run
#else
const unsigned num_iterations = num_pts/8/2; // number of iterations to run
#endif // RTP_SWITCH
// main simulation program
int main() {
my_graph.init(); // load the DFG into the AI Engine array, establish connectivity, etc.
my_graph.update(my_graph.cmtx1, C1a, 96);
my_graph.update(my_graph.cmtx2, C2a, 96);
my_graph.update(my_graph.cmtx3, C3a, 96);
#ifndef RTP_SWITCH
my_graph.run(num_iterations); // run the DFG for the specified number of iterations
#else
// run with set "a" for 1st half
my_graph.run(num_iterations); // run the DFG for the specified number of iterations
my_graph.wait(); // wait for all iterations to finish before loading new coefficients
// load set "b"
my_graph.update(my_graph.cmtx1, C1b, 96);
my_graph.update(my_graph.cmtx2, C2b, 96);
my_graph.update(my_graph.cmtx3, C3b, 96);
my_graph.run(num_iterations); // run the DFG for the specified number of iterations
#endif // RTP_SWITCH
my_graph.end(); // housekeeping
return (0);
} // end main()
注記:
#define RTP_SWITCH
をコメントアウトすると、1 組の係数だけが読み込まれ、以前と同じようにインパルス応答をチェックすできます。ほかのセクションについては、追加の係数セットが 1 つ含まれます。
その他のセクションについては、
my_graph.update()
文が追加されます。
デザインのビルドおよび実行
デザインの機能性を検証するには、Emulation-SW
を使用します。パート 1a と同様、Julia を使用して、リファレンスと生成されたインパルス応答の間の絶対誤差の最大値を計算できます (check1.jl
を実行)。この 3 段階デザインのインパルス応答誤差は、次のとおりです。
ランタイム中の係数の変更
aie_iir_1b.jl
の first_set
を false
に設定して、通過帯域 20 MHz の LPF の別の係数セットを生成します。
first_set = false; # true: 1st set; false: 2nd set
...
20 MHz の通過帯域を持つ周波数特性は、次のとおりです。
生成された *.h
(係数ファイル) を src
に、impresponse_b.dat
を data
に移動します。
two_freqs.jl
を使用し、2 つの周波数 (f1 = 2 MHz, f2 = 18 MHz) の入力信号 (two_freqs.dat
) を生成し、係数切り替えの機能をテストします。信号の時間プロットと周波数領域プロットは、次のとおりです。
生成された two_freqs.dat
を data
に移動します。
テストベンチ (tb.cpp
) では、#define RTP_SWITCH
のコメントをはずして、2 つ目の係数セットを含めます。
注記: 新しい係数セットを読み込む前に、指定された反復回数を完了させるには、wait()
文が必要です。
AI エンジンの出力は、次のようになります (check2.jl
を使用します)。
プロットの前半では、10 MHz フィルターの係数を使用しています。このため、ノイズの多い 2 MHz のコンポーネントだけが通過し、18 MHz のコンポーネントはかなり減衰します。
後半では、係数が 20 MHz のフィルター用です。このため、出力には 2 MHz と 18 MHz の両方のコンポーネントが含まれます。
完全なデザインは、data
と src
ディレクトリに含まれています。AMD Vitis™ デザインをゼロから構築することに慣れていない場合は、aie_exp/Part1 チュートリアルを参照してください。
まとめ
3 つの 2 次セクションで構成される 6 次 IIR フィルターが、ADF グラフの中でどのようにカスケードされるかを示しました。
また、非同期のランタイム パラメーターを使用して、ランタイム時に係数を変更可能なことも示しました。
パート 1 では、Emulation-SW
を使用した機能検証にのみ焦点を当てました。このあとは、デザインを解析し、スループットに対して最適化します。
サポート
GitHub 問題は、リクエストやバグの追跡に使用します。質問については、forums.xilinx.com を参照してください。
Copyright © 2020-2024 Advanced Micro Devices, Inc