Main Content

ラッパー S-Function および TLC ファイルの記述

ラッパーの概念を使用して、Simulink® およびコード ジェネレーター製品でシームレスに機能する S-Function を作成します。以下が可能です。

  • MEX S-Function ラッパー (sfunction.mex) を記述して、Simulink モデル内のアルゴリズム同士を接続します。

  • TLC 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 つのステートメントは次のとおりです。

  1. S-Function の名前を定義します (Simulink S-Function ブロック ダイアログ ボックスに入力します)。

  2. S-Function がレベル 2 の形式を使用するように指定します。

  3. 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 が存在しないことと sfunction.mex が存在することで識別されます。インラインでない S-Function のコードを生成する場合、コード ジェネレーターは、この例では関数 my_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 の sfunction.tlc ファイルが必要です。TLC ファイルは、my_alg の関数呼び出しを含んでいなければなりません。次の図では、アルゴリズム、ラッパー S-Function および sfunction.tlc ファイル間の関係を説明します。

my_alg への呼び出しをインライン化するには、関数呼び出しを S-Function と同じ名前 (この例では wrapsfcn.tlc) で sfunction.tlc ファイルに配置します。Target Language Compiler は、生成コードの S-Function に呼び出しを配置する既定の方法をオーバーライドします。

このコードは 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 以上削減されます。

関連するトピック