ラッパー S-Function および TLC ファイルの記述
ラッパーの概念を使用して、Simulink® およびコード ジェネレーター製品でシームレスに機能する S-Function を作成します。以下が可能です。
MEX S-Function ラッパー (
) を記述して、Simulink モデル内のアルゴリズム同士を接続します。sfunction
.mexTLC S-Function ラッパー (
) を作成して、コード ジェネレーターでアルゴリズムを生成コードに挿入します。sfunction
.tlc
MEX S-Function ラッパー
S-Function ラッパーを使用して S-Function を作成すると、元の C/C++ 関数をほとんど、あるいはまったく変えることなく、C/C++ コード アルゴリズムを Simulink モデルや生成コードに挿入することができます。MEX S-Function ラッパーは、別のモジュール内にあるコードを呼び出す S-Function です。
メモ
MEX S-Function ラッパーは、そのラッパーが作成された MATLAB® バージョンのみで使用してください。
ファイル my_alg.c
内に my_alg
と呼ばれるアルゴリズム (つまり、C 関数) があるとします。MEX S-Function ラッパー (wrapsfcn.c
など) を作成すると、my_alg
を Simulink モデルに統合できます。Simulink モデルは my_alg
を S-Function ブロックから呼び出すことができます。Simulink S-Function には、Simulink エンジンが API に関連したさまざまな用途で必要とする一連の空の関数があります。たとえば、my_alg
を呼び出すのは mdlOutputs
のみですが、エンジンは、この S-Function ルーチンがアクションを実行しないとしても mdlTerminate
を呼び出します。
TLC S-Function ラッパー (wrapsfcn.tlc
など) を作成すると、生成コードに my_alg
への呼び出しを組み込むことができます。空の関数呼び出しは削除できます。関数 mdlOutputs
実行のオーバーヘッドが回避でき、関数 my_alg
を排除できます。
ラッパー S-Function は、プロシージャ型アルゴリズムの作成時や、レガシ コードの Simulink モデルへの統合時に役立ちます。次のようなコードを作成するとします。
実際には解釈コードである (つまり、動作モードで高度にパラメーター化されている)
大幅に最適化されている (つまり、コードが動作するモードを決定するための正確なテストが存在しない)
この場合、S-Function 用に完全にインライン化された TLC ファイルを作成しなければなりません。
次の図では、ラッパー S-Function の概念を説明します。
S-Function ラッパーを使用して Simulink モデルにアルゴリズムをインポートするということは、S-Function が、C/C++ アルゴリズムを mdlOutputs
から呼び出すインターフェイスの役割を果たすということを意味します。コードを変更しなくても大きいスタンドアロンの C /C++ プログラムをすばやくモデルに統合できます。
次のサンプル モデルには S-Function ラッパーが含まれています。
wrapsfcn
ブロックに関連付けられているファイルは 2 つあります。S-Function ラッパーとアルゴリズムを含んでいる C/C++ コードです。最初の 3 つのステートメントは次のとおりです。
S-Function の名前を定義します (Simulink S-Function ブロック ダイアログ ボックスに入力します)。
S-Function がレベル 2 の形式を使用するように指定します。
SimStruct
データ構造体にアクセスできるようにします。SimStruct
にはシミュレーションおよびコード生成時に使用されるデータへのポインターが含まれており、SimStruct
にデータを保存してデータを抽出するマクロを定義します。
#define S_FUNCTION_NAME wrapsfcn #define S_FUNCTION_LEVEL 2 #include "simstruc.h" extern real_T my_alg(real_T u); /* Declare my_alg as extern */ /* * mdlInitializeSizes - initialize the sizes array */ static void mdlInitializeSizes(SimStruct *S) { ssSetNumSFcnParams( S, 0); /*number of input arguments*/ if (!ssSetNumInputPorts(S, 1)) return; ssSetInputPortWidth(S, 0, 1); ssSetInputPortDirectFeedThrough(S, 0, 1); if (!ssSetNumOutputPorts(S,1)) return; ssSetOutputPortWidth(S, 0, 1); ssSetNumSampleTimes( S, 1); } /* * mdlInitializeSampleTimes - indicate that this S-function runs * at the rate of the source (driving block) */ static void mdlInitializeSampleTimes(SimStruct *S) { ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME); ssSetOffsetTime(S, 0, 0.0); } /* * mdlOutputs - compute the outputs by calling my_alg, which * resides in another module, my_alg.c */ static void mdlOutputs(SimStruct *S, int_T tid) { InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); *y = my_alg(*uPtrs[0]); /* Call my_alg in mdlOutputs */ } /* * mdlTerminate - called when the simulation is terminated. */ static void mdlTerminate(SimStruct *S) { } #ifdef MATLAB_MEX_FILE /* Is this file being compiled as a MEX-file? */ #include "simulink.c" /* MEX-file interface mechanism */ #else #include "cg_sfun.h" /* Code generation registration function */ #endif
詳細については、Templates for C S-Functionsを参照してください。
S-Function ルーチン mdlOutputs
には my_alg
の関数呼び出しが格納されています。これは、S-Function が実行するアルゴリズムを含んだ C 関数です。my_alg.c
のコードは次のとおりです。
#ifdef MATLAB_MEX_FILE #include "tmwtypes.h" #else #include "rtwtypes.h" #endif real_T my_alg(real_T u) { return(u * 2.0); }
詳細については、ビルド プロセスのファイルの依存関係の管理を参照してください。
ラッパー S-Function wrapsfcn
は、u * 2.0
を計算する関数 my_alg
を呼び出します。wrapsfcn.mex
をビルドするには、このコマンドを使用します。
mex wrapsfcn.c my_alg.c
TLC S-Function ラッパー
TLC S-Function ラッパーはコード ジェネレーターがコードを呼び出す方法を指定する TLC ファイルです。たとえば、生成コードの mdlOutputs
セクションにある my_alg
への呼び出しをインライン化できます。MEX S-Function ラッパーの例では、my_alg
への呼び出しは次のように mdlOutputs
セクションに組み込まれています。
*y = my_alg(*uPtrs[0]);
TLC S-Function ラッパーを作成する際、その目標は、生成コードに同じ種類の呼び出しを組み込むことです。
コード ジェネレーターによってインラインでない S-Function がどのように実行されるのかを確認します。インラインでない S-Function は、ファイル
が存在しないことと sfunction
.tlc
が存在することで識別されます。インラインでない S-Function のコードを生成する場合、コード ジェネレーターは、この例では関数 sfunction
.mexmy_alg
を呼び出す関数ポインターから関数 mdlOutputs
の呼び出しを生成します。
ラッパーの例には、1 つの S-Function wrapsfcn.mex
があります。追加のモジュールである関数 my_alg
を、生成コードでコンパイルし、リンクしなければなりません。MATLAB コマンド プロンプトで、次のように入力します。
set_param('wrapper/S-Function','SFunctionModules','my_alg')
インラインでない S-Function のコード オーバーヘッド
wrapsfcn.tlc
を 使用せずにシステム ターゲット ファイルとして grt.tlc
を使用したときに生成されるコードを示します。
<Generated code comments for wrapper model with noninlined wrapsfcn S-function> #include <math.h> #include <string.h> #include "wrapper.h" #include "wrapper.prm" /* Start the model */ void mdlStart(void) { /* (start code not required) */ } /* Compute block outputs */ void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions create a SimStruct object and * generate a call to S-function routine mdlOutputs */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid); } /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; } /* Perform model update */ void mdlUpdate(int_T tid) { /* (update code not required) */ } /* Terminate function */ void mdlTerminate(void) { /* Level2 S-Function Block: <Root>/S-Function (wrapsfcn) */ { /* Noninlined S-functions require a SimStruct object and * the call to S-function routine mdlTerminate */ SimStruct *rts = ssGetSFunction(rtS, 0); sfcnTerminate(rts); } } #include "wrapper.reg" /* [EOF] wrapper.c */
wrapper.reg
生成ファイルには、ラッパー S-Function ブロックの SimStruct
の初期化が含まれています。モデルの各 S-Function ブロックには 1 つの子 SimStruct
があります。S-Function に対して TLC ラッパーを作成すると、このオーバーヘッドを大幅に減らせます。
ラッパー S-Function のインライン化
次のコードを使用すると、生成コードによって mdlOutputs
で S-Function wrapsfcn.c
の呼び出しが行われます。
SimStruct *rts = ssGetSFunction(rtS, 0); sfcnOutputs(rts, tid);
この呼び出しには、呼び出しに関連付けられている計算オーバーヘッドがあります。Simulink エンジンは S-Function ブロックに SimStruct
データ構造体を作成します。コード ジェネレーターが関数 mdlOutputs
を実行するための関数ポインターから呼び出しを作成すると、関数 mdlOutputs
が関数 my_alg
を呼び出します。C/C++ アルゴリズム my_alg
の呼び出しをインライン化することによって、SimStruct
と他の関数呼び出しの両方が取り除かれ、効率が向上して生成コードのサイズを抑えることができます。
ラッパー S-Function をインライン化するには、S-Function の
ファイルが必要です。TLC ファイルは、sfunction
.tlcmy_alg
の関数呼び出しを含んでいなければなりません。次の図では、アルゴリズム、ラッパー S-Function および
ファイル間の関係を説明します。sfunction
.tlc
my_alg
への呼び出しをインライン化するには、関数呼び出しを S-Function と同じ名前 (この例では wrapsfcn.tlc
) で
ファイルに配置します。Target Language Compiler は、生成コードの S-Function に呼び出しを配置する既定の方法をオーバーライドします。 sfunction
.tlc
このコードは wrapsfcn.c
をインライン化する TLC ファイル wrapsfcn.tlc
です。
%% File : wrapsfcn.tlc %% Abstract: %% Example inlined tlc file for S-function wrapsfcn.c %% %implements "wrapsfcn" "C" %% Function: BlockTypeSetup ==================================================== %% Abstract: %% Create function prototype in model.h as: %% "extern real_T my_alg(real_T u);" %% %function BlockTypeSetup(block, system) void %openfile buffer extern real_T my_alg(real_T u); /* This line is placed in wrapper.h */ %closefile buffer %<LibCacheFunctionPrototype(buffer)> %endfunction %% BlockTypeSetup %% Function: Outputs =========================================================== %% Abstract: %% y = my_alg( u ); %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign u = LibBlockInputSignal(0, "", "", 0) %assign y = LibBlockOutputSignal(0, "", "", 0) %% PROVIDE THE CALLING STATEMENT FOR "algorithm" %% The following line is expanded and placed in mdlOutputs within wrapper.c %<y> = my_alg(%<u>); %endfunction %% Outputs
このコードの最初のセクションでは、wrapsfcn
S-Function ブロックがインライン化され、C でコードが生成されます。
%implements "wrapsfcn" "C"
次のタスクでは、モデル内にあるすべての wrapsfcn
S-Function ブロックについて、生成された wrapper.h
ファイルでルーチン my_alg
を外部として宣言しなければならないことをコード ジェネレーターに通知します。関数 BlockTypeSetup
を使用して、すべての wrapsfcn
S-Function ブロックに対してこの宣言を 1 度行います。この関数で、wrapper.h
生成ヘッダー ファイルの extern
のように、バッファーを作成して my_alg
をキャッシュに格納するように Target Language Compiler に指定します。
最後の手順で、関数 my_alg
の呼び出しをインライン化します。関数 Outputs
は呼び出しをインライン化します。この関数では、ブロックの入出力にアクセスして my_alg
の直接呼び出しを配置します。呼び出しは wrapper.c
に組み込まれます。
インライン化コード
ラッパー S-Function をインライン化するときに生成されるコードは、既定の生成コードに似ています。関数 mdlTerminate
には空の関数の呼び出しは含まれず、関数 mdlOutputs
は関数 my_alg
を直接呼び出します。
void mdlOutputs(int_T tid) { /* Sin Block: <Root>/Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase); /* S-Function Block: <Root>/S-Function */ rtB.S_Function = my_alg(rtB.Sin); /* Inlined call to my_alg */ /* Outport Block: <Root>/Out */ rtY.Out = rtB.S_Function; }
生成コードは関数 my_alg
を直接呼び出すため、wrapper.reg
は S-Function の子 SimStruct
を作成せず、メモリ使用量が 1 KB 以上削減されます。