Main Content

Simulink Function ブロックとコードの生成

Simulink Function ブロックと関数呼び出し元からコードを生成する理由

Simulink Function ブロックは、共有リソースを表すモデリング コンポーネントの C コードまたは C++ コードを生成するためのメカニズムを提供します。Simulink Function ブロックでロジックをリソースとして定義すると、関数インターフェイス (名前と引数) がロジックの実装と分離されます。関数の呼び出し元 (Function Caller ブロック、MATLAB Function ブロック、および Stateflow® チャート) はモデルの階層構造のさまざまなレベルで関数ロジックを再利用できます。

Simulink Function ブロックは、再利用可能なサブシステムに代わる方法を提供します。たとえば、Subsystem ブロックではなく Simulink Function ブロックを使用する場合、Simulink Function ブロックでは関数の呼び出し元間で状態が共有される点に注意します。コード ジェネレーターは 1 つの関数を生成します。Simulink Function ブロックに、遅延やメモリなどの状態をもつブロックが含まれている場合、状態は関数の呼び出し元間で維持されます。関数の呼び出し順序は、重要な考慮事項です。

コード ジェネレーターがサブシステムから生成する再利用可能な関数では、状態は共有されません。コード ジェネレーターはサブシステムの複数のインスタンスに対して 1 つの関数を最適化として生成します。該当のサブシステムに状態をもつブロックが含まれる場合、コード ジェネレーターでは 1 つの関数が生成されますが、異なる状態変数が各インスタンスに渡されます。インスタンスは状態を共有しません。

Simulink Function ブロックと呼び出し元のその他の用途は次のとおりです。

  • 関数の呼び出しを入れ子にする。

  • 1 つのモデリング コンポーネントで定義された関数を別のモデリング コンポーネントから呼び出す。

  • グローバルにアクセス可能な関数またはスコープが設定された関数を生成する。

  • クライアント/サーバー アプリケーション用のコードを生成する。

実装オプション

コード生成要件に基づいて、Simulink® 関数および関数の呼び出し元を実装する方法を選択します。考慮事項は以下のとおりです。

  • モデルで関数および関数の呼び出し元を表す方法

  • 関数のスコープ

  • 関数をモデルからエクスポートするかどうか (Embedded Coder® が必要)

  • 関数コード インターフェイスのカスタマイズ (Embedded Coder が必要)

  • コード生成に関する要件

  • コード生成の制限

モデリング パターンの選択

この表には、入力値を 2 倍にする関数と、Simulink モデルでこの関数を表すことができる Simulink Function ブロックを表す C コードを示します。

関数定義モデル要素

codegen-folder/subsystem.c

#include "timestwo_sf.h"

#include "ex_slfunc_comp_sf.h"
#include "ex_slfunc_comp_sf_private.h"

void timestwo_sf(real_T rtu_x, real_T *rty_y)
{
  *rty_y = 2.0 * rtu_x;
}

Simulink Function ブロック

Simulink 関数の呼び出し元は、Simulink Function ブロックで定義された関数を呼び出します。モデルまたはチャート階層のいずれかの場所から、次のいずれかのモデル要素を使用して、Simulink Function ブロックで定義された関数を呼び出すことができます。

関数呼び出しモデル要素

codegen-folder/model.c

void ex_slfunc_comp_sf_step(void)
{
  real_T rtb_FunctionCaller1;

timestwo_sf(ex_slfunc_comp_sf_U.In1, &rtb_FunctionCaller1);

ex_slfunc_comp_sf_Y.Out1 = rtb_FunctionCaller1;
.
.
.

Function Caller ブロック

codegen-folder/model.c

void ex_slfunc_comp_gf_step(void)
{
  real_T rtb_y1_l;

  timestwo_gf(ex_slfunc_comp_gf_U.In4, &rtb_y1_l);

  ex_slfunc_comp_gf_Y.Out4 = rtb_y1_l;  
  .
  .
  .

Stateflow チャート遷移

codegen-folder/model.c

void ex_slfunc_comp_mf_step(void)
{ 
  real_T rtb_y;
  
  timestwo_mf(ex_slfunc_comp_mf_U.In3, &rtb_y);

  ex_slfunc_comp_mf_Y.Out3 = rtb_y;
  .
  .
  .

MATLAB Function ブロック

モデリングの選択の詳細については、Simulink 関数の概要を参照してください。

関数スコープの指定

Simulink Function ブロックで定義した関数はグローバルまたはスコープ内にすることができます。

  • グローバル---コード ジェネレーターは、モデル コード ファイルとは別のソース ファイルとヘッダー ファイル (function.cfunction.h など) にグローバル関数のコードを配置します。これらの個別のファイルにより、関数コードを関数の呼び出し元間で共有できるようになります。

  • スコープ内---コード ジェネレーターは、モデル コード ファイル (model.cmodel.h) にスコープが設定された関数のコードを配置します。スコープが設定された関数をモデルのコンテキストで呼び出すには、関数の呼び出し元がモデルの階層構造内の関数と同じレベル、または 1 つ以上レベルが下でなければなりません。

    生成されたモデル コードの任意の場所からアクセスできる関数のライブラリを作成するには、各関数をスコープが設定された Simulink Function ブロックとして設定します。スコープが設定された各関数をモデルのルート レベルにあるバーチャル サブシステム内に配置します。

詳細については、スコープ内およびグローバルの Simulink Function ブロックの概要を参照してください。

エクスポート関数コードを生成するかどうかの決定

単一の最上位モデル設計で Simulink Function ブロックを使用できますが、関数コードは、スタンドアロンのアトミック コンポーネントとして生成した方が再利用しやすくなります。このようにするには、エクスポート関数モデルのコンテキストで関数を設計します。

詳細については、外部コード ベースにエクスポートするコンポーネント ソース コードの生成エクスポート関数モデルの概要を参照してください。

関数コード インターフェイスのカスタマイズの指定

Embedded Coder では、Simulink Function ブロックと Function Caller ブロックに対して生成された関数コード インターフェイスをカスタマイズして、生成コードと外部コードの統合を簡素化します。次の関数インターフェイスをカスタマイズできます。

  • グローバルな Simulink Function ブロック

  • モデルのルート レベルにある、スコープが設定された Simulink Function ブロック

詳細については、Configure Entry-Point Function Interfaces for Simulink Function and Function Caller Blocksを参照してください。

呼び出されない Simulink Function ブロック

Simulink Function ブロックがレートベース モデルで使用され、その関数が呼び出されない場合、コード ジェネレーターは Simulink Function ブロックを定数として扱い、関数のコードを生成しません。たとえば、モデル開発中に、関数を定義する準備ができていても、呼び出し元を特定する準備はできていない場合に発生する可能性があります。

レートベース モデル内でこのようなブロックを特定するには、シミュレーション中にサンプル時間の色を表示します。既定では、Constant ブロックはマゼンタで表示されます。コード ジェネレーターはシミュレーション時に呼び出されない Simulink Function ブロックを定数と認識するため、これらはマゼンダで表示されます。

要件

  • モデルの階層構造内で、関数名は一意です。コード ジェネレーターで同じ名前の複数の関数が検出されると、エラーが発行されます。いずれかの関数の名前を変更し、slprj フォルダーを削除します。

  • 関数および関数の呼び出し元のシグネチャ (引数と引数のデータ型など) が一致しなければなりません。

    • コード ジェネレーターで最初に関数が検出され、関数呼び出し元のシグネチャが一致しない場合、エラーが発行されます。関数の呼び出し元のシグネチャを変更して Simulink Function ブロックのシグネチャと一致させるか、slprj フォルダーを削除します。

    • コード ジェネレーターで最初に関数の呼び出し元が検出され、関数のシグネチャが一致しない場合、警告メッセージが発行されます。関数または関数の呼び出し元のシグネチャを変更してシグネチャを一致させます。

  • Simulink Function ブロックの定義では、Argument Inport ブロックと Argument Outport ブロックの入力信号と出力信号をストレージ クラスで定義しないでください。

  • Argument Inport および Argument Outport ブロックをテスト ポイントとして指定しないでください。

  • Argument Inport ブロックと Argument Outport ブロックの入力信号と出力信号のデータ型を Simulink.IntEnumTypeSimulink.AliasType、または Simulink.Bus として指定する場合は、DataScope プロパティを [インポート] または [エクスポート] として設定します。

  • 関数のインターフェイスと関数の呼び出し元は、データ型、実数/複素数、次元、引数の数が一致しなければなりません。

制限

  • スコープが設定された Simulink 関数を含むモデルからコードを生成するには、モデルがエクスポート関数モデルでなければなりません。コード ジェネレーターは、スコープが設定された Simulink 関数を含むレートベースのモデルをサポートしません。

  • Simulink Function ブロックを使って参照モデルでスコープが設定された関数を定義できます。ただし、アトミック サブシステムの Function Caller ブロックを使用して該当の関数を呼び出すエクスポート関数モデルのコードは生成できません。

  • コード ジェネレーターが Stateflow チャートから生成されたコードをもつ Simulink Function ブロックから生成する C++ 関数を呼び出すことができます。生成された C++ 関数の現在のスコープ制限により、それらの関数は Function Caller ブロックから生成されたコードで呼び出さなければなりません。

  • Simulink 関数および関数の呼び出し元は MaxStackSize パラメーターに従いません。

  • C++ クラス インターフェイスのコード生成では、スコープが設定された Simulink 関数のみサポートします。

再利用可能な関数コードの生成と呼び出し

この例では、Simulink Function ブロックと Function Caller ブロックを使用して再利用可能な関数コードを生成する方法を示します。コード ジェネレーターはグローバル関数を生成します。グローバル関数はコード要件に準拠し、既存の外部コードから関数とローカル関数を呼び出すことができます。コード ジェネレーターは、グローバル関数とローカル関数の呼び出しも生成します。グローバル関数の呼び出しでは、グローバル関数が再利用 (共有) されることが示されます。

グローバル関数のコード要件は次のとおりです。

  • 関数名は接頭辞 func_ から始まる。

  • 入力引数の名前は xn (n は一意の整数値) の形式で表される。

  • 出力引数の名前は yn (n は一意の整数値) の形式で表される。

  • 入力引数と出力引数は整数 (int) で、参照渡しされる。ターゲット ハードウェアのネイティブの整数サイズは 32 ビットとする。

生成された再利用可能な関数コードを呼び出す関数を作成します。次に、コード要件に合わせてモデルを作成および設定します。

この例で使用される一部の機能 (データ型のエイリアシングや置換など) には、Embedded Coder ソフトウェアが必要です。

再利用可能な関数を呼び出す外部コードの検証

コード生成ルート フォルダーで、ファイル call_times2.h および call_times2.c を作成します。必要に応じて、ファイルを matlabroot\help\toolbox\ecoder\examples からコピーできます。

call_times2.h

typedef int my_int;

call_times2.c

#include "call_times2.h"

void call_times2(void)
{
  int times2result;

  func_times2(x1, &y1);

  printf('Times 2 Value:', y1);
}

この C コードは、再利用可能な関数 func_times2 を呼び出します。関数は、整数の入力値 x1 に 2 を乗算し、結果を y1 として返します。

モデルの作成

モデル例 ex_slfunc_comp を開きます。このモデル例はフォルダー matlabroot\help\toolbox\ecoder\examples にあります。モデルには、Simulink Function ブロック func_times2 および func_times3 としてモデル化された 2 つの Simulink 関数と各関数の呼び出しが含まれます。モデルに示されているように、func_times2 の Simulink Function ブロックの可視性は [グローバル] に設定されています。この可視性設定により、関数コードに、生成コードを統合する外部コードを含む他のコードからアクセスできるようになります。func_times3 の可視性は [スコープ内] に設定されています。

[ファイル パッケージ化形式][モジュラー] に設定し、GRT システム ターゲット ファイルまたは ERT システム ターゲット ファイルでモデルを設定すると、コード ジェネレーターでモデルの関数コードが作成されます。

詳細については、Simulink 関数または関数の呼び出し元のコードの生成を参照してください。

カスタムのデータ型を再利用するための生成コードの設定

この例では、ネイティブの整数サイズが 32 ビットのターゲット ハードウェアで生成コードが実行されると仮定します。外部コードは、データ型が my_int (int のエイリアス) の整数を表します。既定で使用するデータ型 int32_T ではなく、my_int を使用するようにコード ジェネレーターを設定します。

  1. カスタムのデータ型 my_int を表す Simulink.AliasType オブジェクトを作成します。

    my_int = Simulink.AliasType
    
    my_int =
    
      AliasType with properties:
    
        Description: ''
          DataScope: 'Auto'
         HeaderFile: ''
           BaseType: 'double'
  2. エイリアス タイプのプロパティを設定します。説明を入力し、スコープを Imported に設定して、型定義を含むヘッダー ファイルを指定し、エイリアス タイプを Simulink 基本データ型 int32 に関連付けます。

    my_int.Description='Custom 32-bit int representation';
    my_int.DataScope='Imported';
    my_int.HeaderFile='call_times2.h';
    my_int.BaseType='int32';
    
    my_int
      AliasType with properties:
    
        Description: 'Custom 32-bit int representation'
          DataScope: 'Imported'
         HeaderFile: 'call_times2.h'
           BaseType: 'int32'
    
  3. int32_T 型のインスタンスを my_int と置き換えるようコード ジェネレーターを設定します。[コンフィギュレーション パラメーター] ダイアログ ボックスで、[コード生成][データ型置換] ペインを開きます。

    • [生成コード内のデータ型名を置換する] を選択します。

    • [データ型名] テーブルで、int32 の置換名の my_int を入力します。

再利用可能なグローバル関数の設定

グローバル関数を呼び出す外部コードでは、対応する Simulink Function ブロックにグローバル可視性と、外部の呼び出し元の期待値と一致するインターフェイスを設定します。

  1. 関数 times2 を表すブロックを開きます。

  2. Trigger Port ブロック パラメーターを設定して、関数の名前と可視性を設定します。たとえば、コード要件では、関数名が接頭辞 func_ から始まることを指定します。

    • [関数名]func_times2 に設定します。

    • [関数の可視性][グローバル] に設定します。

  3. Argument Inport ブロックと Argument Outport ブロックのパラメーターを設定して関数の入力引数と出力引数を設定します。たとえば、コード要件で、引数名を xn および yn の形式にすることを指定します。要件で、引数を my_int 型にすることも指定します。

    • [メイン] タブで、[引数名]x1 (入力) および y1 (出力) に設定します。

    • [信号属性] タブで、[データ型][int32] に設定します。以前指定したデータ型の置換により、[int32]myint として生成コードに表示されます。

  4. Simulink Function ブロック コードのインターフェイスを設定します。モデルの最上位レベルで、グローバル関数 func_times2 を表すブロックを右クリックします。コンテキスト メニューから [C/C++ コード][C/C++ 関数インターフェイスの設定] を選択します。

    • [C/C++ 関数名]func_times2 に設定します。

    • [C/C++ 戻り引数][void] に設定します。

    • 引数 x1[C/C++ 識別子名]x1 に設定します。

    • 引数 y1[C/C++ 識別子名]y1 に設定します。

    ダイアログ ボックスで、入力引数 x1 の前に出力引数 y1 がリストされている場合は、x1 の行を y1 の行の上にドラッグして引数を並べ替えます。

ローカル関数の設定

スコープが設定された可視性をもつローカル関数を表す Simulink Function ブロックを設定します。コード要件によっては、関数名、入力引数と出力引数の名前と型、関数インターフェイスも設定しなければならない場合があります。

  1. 関数 times3 を表すブロックを開きます。

  2. Trigger Port ブロック パラメーターを設定して、関数の名前と可視性を設定します。たとえば、コード要件では、関数名が接頭辞 func_ から始まることを指定します。

    • [関数名]func_times3 に設定します。

    • [関数の可視性][スコープ内] に設定します。

  3. Argument Inport ブロックと Argument Outport ブロックのパラメーターを設定して関数の入力引数と出力引数を設定します。たとえば、コード要件で、引数名を xn および yn の形式にすることを指定します。要件で、引数を my_int 型にすることも指定します。

    • [メイン] タブで、[引数名]x1 (入力) および y1 (出力) に設定します。

    • [信号属性] タブで、[データ型][int32] に設定します。以前指定したデータ型の置換により、[int32]my_int として生成コードに表示されます。

  4. Simulink Function ブロック コードのインターフェイスを設定します。モデルの最上位レベルで、スコープが設定された関数 func_times3 を右クリックします。メニューから [C/C++ コード][C/C++ 関数インターフェイスの設定] を選択します。この場合、コード ジェネレーターは関数と引数の命名を制御します。関数名には、Simulink Function ブロックのトリガー端子に指定したルート モデルの名前と関数名が組み合わされます。この例では、名前は ex_slfunc_comp_func_times3 です。

    [C/C++ 戻り引数] を、Argument Outport ブロックに指定した引数名または void に設定できます。この例では、void に設定します。

    引数に対して、コード ジェネレーターは、Argument Inport ブロックに指定した引数名の先頭に rtu_ (入力) または rty_ (出力) を追加します。

    ダイアログ ボックスで、入力引数 x1 の前に出力引数 y1 がリストされている場合は、x1 の行を y1 の行の上にドラッグして引数を並べ替えます。

関数の呼び出し元の設定

関数の呼び出し元ごとに、Function Caller ブロック パラメーターを次のように設定します。

  • [関数プロトタイプ]y1 = func_times2(x1) に設定する。

  • [入力引数の指定]int32(1) に設定する。

  • [出力引数の指定]int32(1) に設定する。

コードの生成と検査

モデルのコードを生成します。

  • グローバル関数のコード

    グローバルな再利用可能な関数 func_times2 のソース コードは、ビルド フォルダーのサブシステム ファイル func_times2.c にあります。

    #include "func_times2.h"
    
    /* Include model header file for global data */
    #include "ex_slfunc_comp.h"
    #include "ex_slfunc_comp_private.h"
    
    void func_times2(my_int x1, my_int *y1)
    {
      *y1 = x1 << 1;
    }
  • ローカル関数コード

    コード ジェネレーターは、ローカル (スコープ内) 関数 func_times3 の定義をファイル ビルド フォルダーのファイル ex_slfunc_comp.c に置きます。

    void ex_slfunc_comp_func_times3(my_int rtu_x1, my_int *rty_y1)
    {
      *rty_y1 = 3 * rtu_x1;
    }
  • 生成された関数の呼び出し

    モデルの実行 (ステップ) 関数は、モデル ファイル ex_slfunc_comp.c で、グローバル関数 func_times2 とローカル関数 ex_slfunc_comp_func_times3 の 2 つの Simulink 関数を呼び出します。名前 ex_slfunc_comp_func_times3 には、モデル名と関数名を組み合わせたローカル関数のスコープが表されます。

    void ex_slfunc_comp_step(void)
    {
      my_int rtb_FunctionCaller2;
    
      func_times2(ex_slfunc_comp_U.In1, &rtb_FunctionCaller2);
      
      ex_slfunc_comp_Y.Out1 = rtb_FunctionCaller2;
    
      ex_slfunc_comp_func_times3(ex_slfunc_comp_U.In2, &rtb_FunctionCaller2);
    
      ex_slfunc_comp_Y.Out2 = rtb_functionCaller2;
      .
      .
      .
  • ローカル関数のエントリポイント宣言

    モデル ヘッダー ファイル ex_slfunc_comp.h には、関数 ex_slfunc_comp_func_times3extern 宣言が含まれます。このステートメントでは、関数エントリ ポイントを宣言します。

    extern void ex_slfunc_comp_func_times3(my_int rtu_x1, my_int *rty_y1);
  • グローバル関数のステートメントを含める

    モデル ヘッダー ファイル ex_slfunc_comp.h リストには、グローバル関数 func_times2 のステートメントが含まれます。

    #include "func_times2_private.h"
    #include "func_times2.h"
    
  • グローバル関数のローカル マクロとデータ

    サブシステム ヘッダー ファイル func_times2_private.h はマクロを定義し、グローバル関数 func_times2 のデータと関数を宣言するヘッダー ファイルを含みます。

    #ifndef RTW_HEADER_func_times2_private_h_
    #define RTW_HEADER_func_times2_private_h_
    #ifndef ex_slfunc_comp_COMMON_INCLUDES_
    #define ex_slfunc_comp_COMMON_INCLUDES_
    #include "rtwtypes.h"
    #endif
    #endif
    
  • グローバル関数のエントリポイント宣言

    共有ヘッダー ファイル func_times2.h は共有ユーティリティ フォルダー slprj/stf/_sharedutils 内にあり、rtwtypes.h の共有型のインクルードを示します。このファイルには、グローバル関数 func_times2extern 宣言も含まれます。このステートメントでは、関数エントリ ポイントを宣言します。

    #ifndef RTW_HEADER_func_times2_
    #define RTW_HEADER_func_times2_
    
    #include "rtwtypes.h"
    
    extern void func_times2(my_int rtu_x1, my_int *rty_y1);
    
    #endif
    

Simulink 関数または関数の呼び出し元のコードの生成

この例では、Simulink Function ブロックと Function Caller ブロックの C コードを生成する方法と、関連の生成コードを表示する方法を示します。

モデル例 rtwdemo_export_functions を開きます。このモデルは Stateflow ソフトウェアを使用しますが、この例では参照モデルから生成されたコードのみを確認します。

関数定義のコードの生成

  1. サブシステムの内容を表示するには、rtwdemo_functions をダブルクリックします。Simulink Function ブロックは y = f3(u) として定義される f3 サブシステムです。

  2. コードを生成します。

コード ジェネレーターは rtwdemo_functions.c を作成します。このファイルには関数定義と関数の初期化コードが含まれます。

  • 関数 f3 の初期化コード:

    void f3_Init(void)
    {
      rtDWork.Delay_DSTATE = 1;
    }
  • 関数 f3 のコード:

    void f3(real_T rtu_u, real_T *rty_y)
    {
      rtY.TicToc10 = rtDWork.Delay_DSTATE;
    
      rtDWork.Delay_DSTATE = (int8_T)(int32_T)-(int32_T)rtY.TicToc10;
    
      adder_h(rtB.Subtract, rtU.U2, rtu_u, rtB.FunctionCaller);
    
      *rty_y = rtB.FunctionCaller;
    }
    
    void adder_h(real_T rtu_u1, 
                 real_T rtu_u2, 
                 real_T rtu_u3, 
                 real_T *rty_y)
    {
      *rty_y = (rtu_u1 + rtu_u2) + rtu_u3;
    }

  • 共有ヘッダー ファイル f3.h には、関数 f3 のエントリ ポイント宣言が含まれます。

    #include "rtwtypes.h"
    
    extern void f3(real_T rtu_u, real_T *rty_y);
    

関数の呼び出し元のコードの生成

  1. rtwdemo_export_functions モデルで、rtwdemo_caller をダブルクリックして、呼び出し元のサブシステムの内容を表示します。

  2. コードを生成します。

コード ジェネレーターはフォルダー rtwdemo_caller_ert_rtw にファイル rtwdemo_caller.hrtwdemo_caller.c を作成します。

rtwdemo_caller.h には関数のエントリポイント宣言を含む共有ヘッダー ファイル f3.h が含まれます。

rtwdemo_caller.c は関数 f3 を呼び出します。

void rtwdemo_caller_t_10tic(const real_T *rtu_u, 
                            real_T *rty_y)
{
  f3(*rtu_u, rty_y);
}

関連するトピック