Part1b - 2024.1 日本語 - XD100

Vitis チュートリアル: AI エンジン

Document ID
XD100
Release Date
2024-06-19
Version
2024.1 日本語

バージョン: 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) を生成します。

図 1

カーネル コードは変わりませんが、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 段階デザインのインパルス応答誤差は、次のとおりです。

図 2

ランタイム中の係数の変更

aie_iir_1b.jlfirst_setfalse に設定して、通過帯域 20 MHz の LPF の別の係数セットを生成します。

first_set = false;	# true: 1st set; false: 2nd set
...

20 MHz の通過帯域を持つ周波数特性は、次のとおりです。

図 3

生成された *.h (係数ファイル) を src に、impresponse_b.datdata に移動します。

two_freqs.jl を使用し、2 つの周波数 (f1 = 2 MHz, f2 = 18 MHz) の入力信号 (two_freqs.dat) を生成し、係数切り替えの機能をテストします。信号の時間プロットと周波数領域プロットは、次のとおりです。

図 4

生成された two_freqs.datdata に移動します。

テストベンチ (tb.cpp) では、#define RTP_SWITCH のコメントをはずして、2 つ目の係数セットを含めます。

注記: 新しい係数セットを読み込む前に、指定された反復回数を完了させるには、wait() 文が必要です。

AI エンジンの出力は、次のようになります (check2.jl を使用します)。

図 5

プロットの前半では、10 MHz フィルターの係数を使用しています。このため、ノイズの多い 2 MHz のコンポーネントだけが通過し、18 MHz のコンポーネントはかなり減衰します。

後半では、係数が 20 MHz のフィルター用です。このため、出力には 2 MHz と 18 MHz の両方のコンポーネントが含まれます。

完全なデザインは、datasrc ディレクトリに含まれています。AMD Vitis™ デザインをゼロから構築することに慣れていない場合は、aie_exp/Part1 チュートリアルを参照してください。

まとめ

3 つの 2 次セクションで構成される 6 次 IIR フィルターが、ADF グラフの中でどのようにカスケードされるかを示しました。

また、非同期のランタイム パラメーターを使用して、ランタイム時に係数を変更可能なことも示しました。

パート 1 では、Emulation-SW を使用した機能検証にのみ焦点を当てました。このあとは、デザインを解析し、スループットに対して最適化します。

サポート

GitHub 問題は、リクエストやバグの追跡に使用します。質問については、forums.xilinx.com を参照してください。

Copyright © 2020-2024 Advanced Micro Devices, Inc

Terms and Conditions