Main Content

このページの内容は最新ではありません。最新版の英語を参照するには、ここをクリックします。

C MEX S-Function のインライン化

S-Function のインライン化の概要

Simulink® モデルに S-Function が含まれていて、対応する TLC ブロック ターゲット ファイルがその S-Function に対して存在している場合は、コード ジェネレーターは S-Function をインライン化します。S-Function をインライン化すると、S-Function API 層を生成コードから削除して、より効率的なコードを生成できます。

さまざまなタスクを実行できる S-Function では、インライン化すると、ブロックの各インスタンスに対して設定されている現在の操作モードでのみコードを生成できるようになります。この例として、S-Function が任意の信号幅を受け入れ、信号の各要素をループ処理する場合、インライン化されたコードを生成して、信号に 2 つ以上の要素がある場合にはループを使用するが、信号の要素が 1 つだけの場合にはループを使用しない単純な計算を生成するようにすることが可能です。

インライン化されないレベル 1 の C MEX S-Function (S-Function API の古い形式に書き込まれる) では、特定の S-Function のルーチンが空でも、生成コードが該当するすべての関数を呼び出します。

関数目的

mdlInitializeSizes

サイズ配列を初期化する

mdlInitializeSampleTimes

サンプル時間配列を初期化する

mdlInitializeConditions

状態を初期化する

mdlOutputs

出力を計算する

mdlUpdate

離散状態を更新する

mdlDerivatives

連続状態の導関数を計算する

mdlTerminate

シミュレーションの終了時にクリーンアップする

インライン化されないレベル 2 の C MEX S-Function (最新の S-Function API に書き込まれるもの) では、以下の例外を除き、上記の関数を呼び出します。

  • mdlInitializeConditions は、MDL_INITIALIZE_CONDITIONS#define で宣言されている場合にのみ呼び出されます。

  • mdlStart は、MDL_START#define で宣言されている場合にのみ呼び出されます。

  • mdlUpdate は、MDL_UPDATE#define で宣言されている場合にのみ呼び出されます。

  • mdlDerivatives は、MDL_DERIVATIVES#define で宣言されている場合にのみ呼び出されます。

S-Function をインライン化することで、シミュレーション ループで空になる可能性がある関数の呼び出しを削除できます。これにより、生成コードの効率を大幅に高めることができます。

sfunc_name という S-Function をインライン化するには、sfunc_name.tlc というカスタム S-Function ブロック ターゲット ファイルを作成して S-Function MEX ファイルと同じフォルダーに配置します。そうすると、ビルド時に、S-Function .c ファイルへの関数呼び出しの設定を行う代わりに、ターゲット ファイルが実行されます。S-Function ターゲット ファイルは、ターゲット ファイルに定義されているステートメントのみを挿入するように Target Language Compiler に指示して、S-Function を "インライン化" します。

一般的に、S-Function のインライン化は以下の場合に特に役立ちます。

  • S-Function の内容の実行に必要な時間が S-Function の呼び出しに必要なオーバーヘッドと比較して小さい。

  • 特定の S-Function ルーチンが空である (たとえば、mdlUpdate)。

  • S-Function の動作はシミュレーションとコード生成とで変化します。たとえば、デバイス ドライバー I/O S-Function は、シミュレーション時には MATLAB® ワークスペースから読み取る一方で、生成コードでは実際のハードウェア アドレスから読み取ることがあります。

S-Function パラメーター

S-Function は、次の 2 つの異なるタイプのパラメーターを model.rtw ファイルに書き込んで Target Language Compiler ファイルからアクセスできるようにすることが可能です。

  • パラメーター設定: これらは、ssWriteRTWParamSettings を使用して S-Function の mdlRTW メソッドを介して書き込まれる調整不可のパラメーター (通常、マスクされた S-Function でチェック ボックスおよびメニューから設定) に対応します。この場合、S-Function TLC 実装ファイルでは、ブロック内の SFcnParamSettings レコードからこれらのパラメーター設定の値に直接アクセスできます。

  • 調整可能なパラメーター: このクラスのパラメーターは、S-Function 内で実行時パラメーターとして登録するとアクセスできます。なお、このような調整可能なパラメーターは model.rtw ファイルに自動的に書き出されます。S-Function の TLC ファイル内で、ライブラリ関数 LibBlockParameter およびそのバリアントを使用して実行時パラメーターおよびその属性にアクセスできます。

実行時パラメーターの作成および使用方法の詳細については、Create and Update S-Function Run-Time Parametersを参照してください。2 つのクラスのパラメーターを作成して使用する方法については、S-Function の例にある sfcndemo_runtime の例も参照してください。以下は、調べて変更できるソース ファイルの例です。

S-Function のサンプル コード

Gain ブロックを模倣し、1 つの入力、1 つの出力、1 つのスカラー ゲインがある単純な S-Function があるものとします。つまり、y = u * p です。Simulink ブロックの名前が foo で、レベル 2 の S-Function の名前が foogain の場合、C MEX S-Function には以下のコードを含める必要があります。

#define S_FUNCTION_NAME foogain
#define S_FUNCTION_LEVEL 2
#include "simstruc.h"
#define GAIN mxGetPr(ssGetSFcnParam(S,0))[0]

static void mdlInitializeSizes(SimStruct *S)
{
  ssSetNumContStates		(S, 0);
  ssSetNumDiscStates		(S, 0);
  
  if (!ssSetNumInputPorts(S, 1)) return;
  ssSetInputPortWidth            (S, 0, 1);
  ssSetInputPortDirectFeedThrough(S, 0, 1);
  
  if (!ssSetNumOutputPorts(S, 1)) return;
  ssSetOutputPortWidth          (S, 0, 1);
  
  ssSetNumSFcnParams		(S, 1);
  ssSetNumSampleTimes		(S, 0);  
  ssSetNumIWork		(S, 0);
  ssSetNumRWork		(S, 0);
  ssSetNumPWork		(S, 0);
}
 
static void
mdlOutputs(SimStruct *S, int_T tid)
{
  real_T *y = ssGetOutputPortRealSignal(S, 0);
  const InputRealPtrsType u = ssGetInputPortRealSignalPtrs(S, 0);

  y[0] = (*u)[0] * GAIN;
}

static void
mdlInitializeSampleTimes(SimStruct *S){}

static void
mdlTerminate(SimStruct *S) {}

#define MDL_RTW  /* Change to #undef to remove function */
#if defined(MDL_RTW)&&(defined(MATLAB_MEX_FILE)||defined(NRT))
static void
mdlRTW (SimStruct *S)
{
  if (!ssWriteRTWParameters(S, 1,SSWRITE_VALUE_VECT,"Gain","",
                            mxGetPr(ssGetSFcnParam(S,0)),1)) 
  {
    return;
  }
}
#endif

#ifdef  MATLAB_MEX_FILE
#include "simulink.c" 
#else
#include "cg_sfun.h"
#endif

以下の 2 つのセクションでは、S-Function foogain のインライン化されていないバージョンとインライン化されたバージョンを含む model.c の生成コードにおける差異を示します。モデルには他の Simulink ブロックは含まれていません。

これらの S-Function 関連の C ライブラリ関数の詳細については、C/C++ S-Function 機能の構成を参照してください。コードを生成する方法については、モデルの構成とコードの生成およびSimulink モデルから生成されたコードのビルドに使用するアプローチを参照してください。

model.c のインライン化されていないバージョンとインライン化されたバージョンの比較

S-Function の仕様を定義する TLC ファイルがない場合、コード ジェネレーターは S-Function API を使用して MEX ファイル S-Function を呼び出す必要があります。以下のコードは、インライン化されていない S-Function の model.c ファイルです (つまり、対応する TLC ファイルが存在しない)。

インラインでない S-Function

/*
 * model.c
.
.
.
*/
real_T untitled_RGND = 0.0;             /* real_T ground */
/* Start the model */
void MdlStart(void)
{
  /* (no start code required) */
}
/* Compute block outputs */
void MdlOutputs(int_T tid)
{
  /* Level2 S-Function Block: <Root>/S-Function (foogain) */
  {
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnOutputs(rts, tid);
  }
}
/* Perform model update */
void MdlUpdate(int_T tid)
{
  /* (no update code required) */
}
/* Terminate function */
void MdlTerminate(void)
{
  /* Level2 S-Function Block: <Root>/S-Function (foogain) */
  {
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnTerminate(rts);
  }
}
#include "model_reg.h"
/* [EOF] model.c */

インライン化された S-Function.  このコードは、foogain S-Function が完全にインライン化された model.c です。

/*
 * model.c
.
.
.
*/
/* Start the model */
void MdlStart(void)
{
  /* (no start code required) */
}

/* Compute block outputs */
void MdlOutputs(int_T tid)

  /* S-Function block: <Root>/S-Function */
  /* NOTE: There are no calls to the S-function API in the inlined 
     version of model.c. */
  rtB.S_Function = 0.0 * rtP.S_Function_Gain;
}

/* Perform model update */
void MdlUpdate(int_T tid)
{
  /* (no update code required) */
}

/* Terminate function */
void MdlTerminate(void)
{
  /* (no terminate code required) */
}

#include "model_reg.h"

/* [EOF] model.c */

この S-Function ブロック用にこのターゲット ファイルを含めた場合、結果として生成される model.c コードは以下のとおりです。

rtB.S_Function = 0.0 * rtP.S_Function_Gain;

TLC ファイルを含めることで、コード サイズが大幅に低下するとともに、生成コードの実行効率性が高まりました。以下のメモでは、TLC コードおよび生成される出力に関する情報を取り上げます。

  • TLC 命令 %implements がブロック ターゲット ファイルに必要であり、ブロック ターゲット ファイル内の最初の実行可能ステートメントでなければなりません。この命令は、Target Language Compiler が S-Function foogain 用に不適切なターゲット ファイルを実行しないようにします。

  • foo の入力は rtGROUND (0.0 に等しい Simulink Coder™ グローバル) です。これは、foo はモデル内の唯一のブロックであり、その入力が接続されていないためです。

  • foogain 用に TLC ファイルを含めると、foogain の S-Function 登録セグメントが不要になります。これにより、コード サイズが大幅に削減されます。

  • TLC コードは、ビルド プロセスがパラメーター値をインライン化するように構成されている場合、gain パラメーターをインライン化します。たとえば、S-Function ダイアログ ボックスで S-Function パラメーターが 2.5 に指定されている場合、TLC 関数 Outputs によって以下が生成されます。

    rtB.foo = input * 2.5;
  • ご使用のオペレーティング システムにファイル名サイズの制限があり、S-Function の名前が (制限を超えている) foosfunction である場合は、%generatefile 命令を使用します。この場合、システム ターゲット ファイル (該当する S-Function ブロック ターゲット ファイルが参照されるより前の任意の場所) に以下のステートメントを含めます。

    %generatefile foosfunction "foosfunc.tlc"

    このステートメントは、foosfunction.tlc ではなく foosfunc.tlc を開くように Target Language Compiler に指示します。

model_reg.h のインライン化されていないバージョンとインライン化されたバージョンの比較

レベル 2 の S-Function をインライン化すると、model_reg.h コードのサイズが大幅に削減されます。モデル登録関数は長いため、この例ではコードのほとんどを省略しています。以下のコードでは、model_reg.h のインライン化されていないバージョンとインライン化されたバージョンの差異を取り上げます。インライン化により、以下のコードが削除されます。

/*
 * model_reg.h
 *
*/
/* Normal model initialization code independent of 
      S-functions  */

/* child S-Function registration */
  ssSetNumSFunctions(rtS, 1);

  /* register each child */
  {
    static SimStruct childSFunctions[1];
    static SimStruct *childSFunctionPtrs[1];

    (void)memset((char_T *)&childSFunctions[0], 0, 
                  sizeof(childSFunctions));
    ssSetSFunctions(rtS, &childSFunctionPtrs[0]);
    {
      int_T i;

      for(i = 0; i < 1; i++) {
        ssSetSFunction(rtS, i, &childSFunctions[i]);
      }
    }

    /* Level2 S-Function Block: untitled/<Root>/S-Function 
       (foogain) */
    {
      extern void foogain(SimStruct *rts);
      SimStruct *rts = ssGetSFunction(rtS, 0);

      /* timing info */
      static time_T sfcnPeriod[1];
      static time_T sfcnOffset[1];
      static int_T sfcnTsMap[1];

      {
        int_T i;

        for(i = 0; i < 1; i++) {
          sfcnPeriod[i] = sfcnOffset[i] = 0.0;
        }
      }
      ssSetSampleTimePtr(rts, &sfcnPeriod[0]);
      ssSetOffsetTimePtr(rts, &sfcnOffset[0]);
      ssSetSampleTimeTaskIDPtr(rts, sfcnTsMap);
      ssSetMdlInfoPtr(rts, ssGetMdlInfoPtr(rtS));

      /* inputs */
      {
        static struct _ssPortInputs inputPortInfo[1];

        _ssSetNumInputPorts(rts, 1);
        ssSetPortInfoForInputs(rts, &inputPortInfo[0]);

        /* port 0 */
        {
          static real_T const *sfcnUPtrs[1];

          sfcnUPtrs[0] = &untitled_RGND;
          ssSetInputPortWidth(rts, 0, 1);
          ssSetInputPortSignalPtrs(rts, 0, 
              (InputPtrsType)&sfcnUPtrs[0]);
        }
      }

      /* outputs */
      {
        static struct _ssPortOutputs outputPortInfo[1];
        _ssSetNumOutputPorts(rts, 1);
        ssSetPortInfoForOutputs(rts, &outputPortInfo[0]);
        ssSetOutputPortWidth(rts, 0, 1);
        ssSetOutputPortSignal(rts, 0, &rtB.S_Function);
      }

      /* path info */
      ssSetModelName(rts, "S-Function");
      ssSetPath(rts, "untitled/S-Function");
      ssSetParentSS(rts, rtS);
      ssSetRootSS(rts, ssGetRootSS(rtS));
      ssSetVersion(rts, SIMSTRUCT_VERSION_LEVEL2);

      /* parameters */
      {
        static mxArray const *sfcnParams[1];

        ssSetSFcnParamsCount(rts, 1);
        ssSetSFcnParamsPtr(rts, &sfcnParams[0]);

        ssSetSFcnParam(rts, 0, &rtP.S_Function_P1Size[0]);
      }

      /* registration */
      foogain(rts);

      sfcnInitializeSizes(rts);
      sfcnInitializeSampleTimes(rts);

      /* adjust sample time */
      ssSetSampleTime(rts, 0, 0.2);
      ssSetOffsetTime(rts, 0, 0.0);
      sfcnTsMap[0] = 0;

      /* Update the InputPortReusable and BufferDstPort flags for 
        each input port */
      ssSetInputPortReusable(rts, 0, 0);
      ssSetInputPortBufferDstPort(rts, 0, -1);

      /* Update the OutputPortReusable flag of each output port */
    }
  }

S-Function foogain をインライン化する TLC ファイル

S-Function の不要な呼び出しを回避し、S-Function に必要な最小限のコードを生成するために、以下の TLC ファイル foogain.tlc が例として用意されています。

%implements "foogain" "C"

%function Outputs (block, system) Output
  /* %<Type> block: %<Name> */
  %%
  %assign y = LibBlockOutputSignal (0, "", "", 0)
  %assign u = LibBlockInputSignal (0, "", "", 0)
  %assign p = LibBlockParameter (Gain, "", "", 0)
  %<y> = %<u> * %<p>;
%endfunction

コード生成を見据えたブロック インスタンス データの管理

インスタンス データは、Simulink モデル内のブロックの各インスタンスに固有の追加のデータまたは作業メモリです。これは、パラメーターや状態データ (それぞれモデル パラメーターと状態ベクトルに格納) を含みませんが、パラメーターおよびモードの中間結果や派生表現をキャッシュするために使用されます。インスタンス データの一例として、Transport Delay ブロックで使用されるバッファーが挙げられます。

レベル 2 の S-Function でインスタンスごとにメモリを割り当てて使用するには、ssSetUserData、作業ベクトル (たとえば、ssSetRWorkValuessSetIWorkValue)、DWork ベクトルと呼ばれるデータ型付き作業ベクトルを使用するなど、複数の方法を利用できます。S-Function およびブロック ターゲット ファイルの記述にかかる労力を最小限にし、grt などのターゲットで静的と malloc の両方のインスタンス データに自動的に準拠するには、インスタンス データを使用して S-Function を記述する際にデータ型付き作業ベクトルを使用します。

利点は 2 つあります。まず、メモリの割り当てと解放が Simulink によって自動的に処理されるため、S-Function を記述するとより効率的になります。次に、DWork 名、データ型、サイズなど、DWork ベクトルが model.rtw ファイルに自動的に書き込まれます。これにより、DWork メモリの割り当てと解放を行う TLC コードを記述する必要がなくなるため、ブロック ターゲット ファイルの記述が容易になります。

さらに、DWork ベクトルのグループを、関数に渡すために構造体にバンドルする場合に、S-Function の関数 mdlStart とブロック ターゲット ファイルの Start メソッドの両方で DWork 配列へのポインターを構造体に設定し、S-Function と生成コードのデータ処理間の整合性を確保できます。

最後に、DWork を使用すれば、S-Function の実装に一致するブロック インスタンスごとにコードの特定のバージョン (データ型、スカラーかベクトル化かなど) を作成するのが単純になります。両方の実装で DWork を同じように使用することで、C MEX S-Function やブロック ターゲット ファイルを変更することなく、インライン化されたコードを Simulink Accelerator™ ソフトウェアで使用できるようにします。

Simulink Accelerator ソフトウェアでのインライン化されたコードの使用

既定では、Simulink Accelerator ソフトウェアは高速化されたモデルのシミュレーションの一部として C MEX S-Function を呼び出します。高速化されたモデルを実行する前にアクセラレータで S-Function をインライン化する場合は、該当する S-Function の関数 mdlInitializeSizesssSetOptions() の呼び出しに SS_OPTION_USE_TLC_WITH_ACCELERATOR フラグを設定して S-Function をインライン化するためにブロック ターゲット ファイルを使用するようにアクセラレータに指示します。

TLC 生成コードと C MEX S-Function でメモリおよび作業ベクトルのサイズと使用法が同じでなければならない点に注意してください。同じになっていないと、Simulink Accelerator ソフトウェアではインライン化されたコードを適切に実行できません。これは、ブロックおよびその作業ベクトルを初期化するために C MEX S-Function が呼び出され、関数 mdlInitializeSizesmdlInitializeConditionsmdlCheckParametersmdlProcessParameters、および mdlStart が呼び出されるためです。定数信号伝播の場合、モデル実行の初期化フェーズ中に mdlOutputs が C MEX S-Function から呼び出されます。

高速化されたモデルの実行のタイム ステップ フェーズ中に、Output および Update ブロックの TLC メソッドで生成されたコードが実行されます。さらに Derivatives およびゼロクロッシング メソッドが存在すればそれも加わります。ブロック ターゲット ファイルの Start メソッドは、高速化されたモデルのコードの生成時に使用されません。

関連するトピック