Main Content

mdlRTW ルーチンでの完全インライン化 S-Function の記述

S-Function mdlRTW ルーチンを使用することで、より複雑な S-Function をインライン化できます。mdlRTW ルーチンにより、TLC ファイルで使用する調整できないパラメーターのパラメーター レコードを作成することによって、S-Function のインライン方法に関する詳細情報をコード生成プロセスに対して提供します。mdlRTW ルーチンは情報を model.rtw ファイルに配置します。関数 mdlRTW は、テキスト ファイル matlabroot/simulink/src/sfuntmpl_doc.c に記述されます。

関数 mdlRTW を使用するには、直接インデックス ルックアップ S-Function を作成する手順に従います。ルックアップ テーブルとは、順序付けられた関数データ点の集まりです。通常、これらのテーブルでは、いくつかの内挿法を用いて既知のデータ点間にある関連した関数の近似値が求められます。ルックアップ テーブル アルゴリズムの例を Simulink® モデルに組み込むために、まず、関数 mdlOutputs でアルゴリズムを実行する S-Function を記述します。最も効率的なコードを生成するために、次に、対応する TLC ファイルを作成して計算オーバーヘッドを取り除き、ルックアップの計算速度を高めます。

Simulink 製品は、1 次元、2 次元、および n 次元の汎用のルックアップ アルゴリズムに対するサポートを提供します。これらのアルゴリズムをそのまま使用することも、必要に応じてカスタムのルックアップ テーブル S-Function を作成することもできます。1 次元のルックアップ S-Function sfun_directlook.c と、これに対応するインライン化 sfun_directlook.tlc ファイルを作成できます (詳細については、Target Language Compilerを参照)。以下が可能です。

  • S-Function パラメーターのエラーをチェックする。

  • モデルの実行中に変更されない S-Function の情報をキャッシュする。

  • 関数 mdlRTW を使用してコード ジェネレーターをカスタマイズし、指定された一連のブロック パラメーターに最適なコードを生成する。

  • ルックアップ テーブルのコードの完全インライン化、または、ルックアップ テーブル アルゴリズムのラッパー関数の呼び出しのいずれかの S-Function 用に TLC ファイルを作成する。

S-Function RTWdata

RTWdata はブロックのプロパティで、S-Function のインライン化時に Target Language Compiler で使用できます。RTWdata はブロックに接続できる文字ベクトルの構造体です。RTWdata はモデルと共に保存され、コードの生成時に model.rtw ファイルに配置されます。たとえば、この一連の MATLAB® コマンドは次のとおりです。

mydata.field1 = 'information for field1';
mydata.field2 = 'information for field2';
set_param(gcb,'RTWdata',mydata)
get_param(gcb,'RTWdata')

次の結果が生成されます。

ans = 
 
    field1: 'information for field1'
    field2: 'information for field2'

model.rtw ファイル内で関連付けられている S-Function ブロックの情報は次のとおりです。

Block {
  Type                    "S-Function"
  RTWdata {
    field1                  "information for field1"
    field2                  "information for field2"
  }

メモ

RTWdata は、ライブラリにリンクされていない S-Function のモデル ファイルに保存されます。ただし、RTWdata は、ライブラリにリンクされている S-Function ブロックでは保持されません。

直接インデックス ルックアップ テーブル アルゴリズム

Simulink ライブラリで提供される 1 次元のルックアップ テーブル ブロックでは、出力の計算時に内挿や外挿が使用されます。次の例では、現在の入力 (x データ) 点に基づいて出力ベクトル (y データ ベクトル) に直接インデックスを付けるルックアップ テーブルを作成します。

この直接 1 次元ルックアップの例では、x データ ベクトルと y データ ベクトルの形式でデータ点のペア (x,y) が指定され、x=x0 における部分的に既知の関数 f(x) の近似解 p(x) を計算します。指定されるデータ ペア (i'th のペアなど) では、y_i = f(x_i) です。x データの値は単調増加することとします。x0x データ ベクトルの範囲外にあると、最初または最後の点が返されます。

S-Function のパラメーターは次のようになります。

XData, YData, XEvenlySpaced

XDataYData は未知の関数の値を表す同じ長さの倍精度ベクトルです。XDataEvenlySpaced はスカラーであり、false の場合は 0.0、true の場合は 1.0 です。XData ベクトルが等間隔である場合、XDataEvenlySpaced1.0 となり、より効率的なコードが生成されます。

このグラフでは、パラメーター XData=[1:6]YData=[1,2,7,4,5,9] の処理方法を説明します。たとえば、S-Function ブロックの入力 (x 値) が 3 の場合、出力 (y 値) は 7 になります。

直接インデックス ルックアップ テーブルの例

TLC ファイルで直接インデックス S-Function をインライン化することによって、ルックアップ テーブルを向上させます。この直接インデックス ルックアップ テーブル S-Function で TLC ファイルは必要ありません。次の例では、直接インデックス ルックアップ テーブル S-Function に TLC ファイルを使用して、コード サイズを縮小して生成コードの効率を上げます。

インライン化された TLC ファイルで直接インデックス アルゴリズムを実装するには、S-Function メイン モジュール、sfun_directlook.c と対応する lookup_index.c モジュールが必要です。lookup_index.c モジュールには関数 GetDirectLookupIndex が含まれています。この関数は、XData が等間隔ではないときに現在の x 入力値に対する XData のインデックスを求めるために使用されます。GetDirectLookupIndex ルーチンは、S-Function と生成コードから呼び出されます。この例では、Simulink MEX ファイルと生成コードで C/C++ コードを共有するためにラッパーの概念を使用します。

XData が等間隔である場合、S-Function メイン モジュールと生成コードの両方には、指定された x 値の y 値を計算するためのルックアップ アルゴリズムが含まれます。これは、アルゴリズムが短いからです。

インライン化された TLC ファイルは sfun_directlook.tlc で、ラッパーの呼び出しを実行するか、S-Function の最適な C/C++ コードを組み込むために使用されます。(mdlRTW の使用の例を参照してください)。

エラーの処理

sfun_directlook.tlc では、mdlCheckParameters ルーチンは次の内容を検証します。

  • 新しいパラメーター設定は有効である。

  • XDataYData は有限実数を含んでいる同じ長さのベクトルである。

  • XDataEvenlySpaced はスカラー値である。

  • XData ベクトルは単調増加のベクトルであり、等間隔に分布している。

関数 mdlInitializeSizes は、S-Function に渡されるパラメーターの数を確認すると、明示的に mdlCheckParameters を呼び出します。Simulink エンジンは mdlInitializeSizes を呼び出した後、パラメーターの変更やその再評価のたびに mdlCheckParameters を呼び出します。

ユーザー データのキャッシュ

sfun_directlook.tlc では、mdlStart ルーチンは、シミュレーション中 (または生成コードの実行中) にも変わらない情報をキャッシュする方法を示します。例では、SimStruct のフィールドである UserDataXDataEvenlySpaced パラメーターの値をキャッシュします。以下の mdlInitializeSizes の行によって、XDataEvenlySpaced への変更は認められないということが Simulink エンジンに指示されます。

ssSetSFcnParamTunable(S, iParam, SS_PRM_NOT_TUNABLE);

実行中、mdlOutputsmxGetPr MATLAB API 関数を呼び出さずに UserData から XDataEvenlySpaced の値にアクセスします。

mdlRTW の使用

コード ジェネレーターは、model.rtw ファイルの生成中にルーチン mdlRTW を呼び出します。Simulink に最適なコードを生成するために、S-Function ブロックが動作するモードに関する情報を model.rtw ファイルに追加することができます。

この例では、パラメーターの設定を model.rtw ファイルに追加します。パラメーターの設定内容が実行中に変化することはありません。今回、XDataEvenlySpaced S-Function パラメーターは実行中に変化しません (ssSetSFcnParamTunable は、mdlInitializeSizes のパラメーターで false (0) として指定されました)。パラメーター設定 (XSpacing) は関数 ssWriteRTWParamSettings を使用します。

xDatayDatamdlSetWorkWidths でランタイム パラメーターとして登録されているため、コード ジェネレーターは自動的に model.rtw ファイルに書き込みます。

S-Function およびインライン化 TLC ファイルを調査する前に、このモデルの生成コードを検討してください。

モデルは、上部の S-Function ブロックでは等間隔の XData を使用し、下部の S-Function ブロックでは等間隔ではない XData を使用します。このモデルを作成するときは、各 S-Function ブロックに次のコマンドを指定します。

set_param('sfun_directlook_ex/S-Function','SFunctionModules','lookup_index')
set_param('sfun_directlook_ex/S-Function1','SFunctionModules','lookup_index')

このビルド プロセスでは実行可能ファイルの作成時にモジュール lookup_index.c を使用します。

このモデルのコードを生成する際、コード ジェネレーターは S-Function mdlRTW メソッドを使用して model.rtw ファイルを生成します。このとき、上部 S-Function ブロックの XSpacing パラメーターには値 EvenlySpaced が、下部 S-Function ブロックの XSpacing パラメーターには値 UnEvenlySpaced が使用されます。TLC ファイルは、XSpacing の値を使用して生成コードに含めるアルゴリズムを決定します。生成コードは XData が等間隔であればルックアップ アルゴリズムを含みますが、XData が等間隔でないときには GetDirectLookupIndex ルーチンを呼び出します。ルックアップ テーブルのモデル例について生成される model.c または model.cpp コードは次のコードと同様です。

/*
 * sfun_directlook_ex.c
 * 
 * Code generation for Simulink model 
 * "sfun_directlook_ex.slx".
 *
...
 */

#include "sfun_directlook_ex.h"
#include "sfun_directlook_ex_private.h"

/* External outputs (root outports fed by signals with auto storage) */
ExtY_sfun_directlook_ex_T sfun_directlook_ex_Y;

/* Real-time model */
RT_MODEL_sfun_directlook_ex_T sfun_directlook_ex_M_;
RT_MODEL_sfun_directlook_ex_T *const sfun_directlook_ex_M =
  &sfun_directlook_ex_M_;

/* Model output function */
void sfun_directlook_ex_output(void)
{
  /* local block i/o variables */
  real_T rtb_SFunction;
  real_T rtb_SFunction1;

  /* Sin: '<Root>/Sine Wave' */
  rtb_SFunction1 = sin(sfun_directlook_ex_M->Timing.t[0]);
  
/* Code that is inlined for the top S-function block in the 
   * sfun_directlook_ex model
   */
  /* S-Function (sfun_directlook): '<Root>/S-Function' */
  {
    const real_T *xData = sfun_directlook_ex_ConstP.SFunction_XData;
    const real_T *yData = sfun_directlook_ex_ConstP.SFunction_YData;
    real_T spacing = xData[1] - xData[0];
    if (rtb_SFunction1 <= xData[0] ) {
      rtb_SFunction = yData[0];
    } else if (rtb_SFunction1 >= yData[20] ) {
      rtb_SFunction = yData[20];
    } else {
      int_T idx = (int_T)( ( rtb_SFunction1 - xData[0] ) / spacing );
      rtb_SFunction = yData[idx];
    }
  }

  /* Outport: '<Root>/Out1' */
  sfun_directlook_ex_Y.Out1 = rtb_SFunction;

/* Code that is inlined for the bottom S-function block in the 
   * sfun_directlook_ex model
   */
  /* S-Function (sfun_directlook): '<Root>/S-Function1' */
  {
    const real_T *xData = sfun_directlook_ex_ConstP.SFunction1_XData;
    const real_T *yData = sfun_directlook_ex_ConstP.SFunction1_YData;
    int_T idx;
    idx = GetDirectLookupIndex(xData, 5, rtb_SFunction1);
    rtb_SFunction1 = yData[idx];
  }

  /* Outport: '<Root>/Out2' */
  sfun_directlook_ex_Y.Out2 = rtb_SFunction1;
}

/* Model update function */
void sfun_directlook_ex_update(void)
{
  /* signal main to stop simulation */
  {                                    /* Sample time: [0.0s, 0.0s] */
    if ((rtmGetTFinal(sfun_directlook_ex_M)!=-1) &&
        !((rtmGetTFinal(sfun_directlook_ex_M)-sfun_directlook_ex_M->Timing.t[0])
          > sfun_directlook_ex_M->Timing.t[0] * (DBL_EPSILON))) {
      rtmSetErrorStatus(sfun_directlook_ex_M, "Simulation finished");
    }
  }

  /* Update absolute time for base rate */
  /* The "clockTick0" counts the number of times the code of this task has
   * been executed. The absolute time is the multiplication of "clockTick0"
   * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not
   * overflow during the application lifespan selected.
   * Timer of this task consists of two 32 bit unsigned integers.
   * The two integers represent the low bits Timing.clockTick0 and the high bits
   * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment.
   */
  if (!(++sfun_directlook_ex_M->Timing.clockTick0)) {
    ++sfun_directlook_ex_M->Timing.clockTickH0;
  }

  sfun_directlook_ex_M->Timing.t[0] = sfun_directlook_ex_M->Timing.clockTick0 *
    sfun_directlook_ex_M->Timing.stepSize0 +
    sfun_directlook_ex_M->Timing.clockTickH0 *
    sfun_directlook_ex_M->Timing.stepSize0 * 4294967296.0;
}
...

関連するトピック