Встроенные функции MEX S на C

Встроенный обзор S-функций

Когда Simulink® модель содержит S-функцию, и соответствующий целевой файл блока TLC существует для этой S-функции, генератор кода встраивает S-функцию. Введение S-функции может создать более эффективный код путем исключения слоя S-функции API из сгенерированного кода.

Для S-функций, которые могут выполнять самые разные задачи, вставка их дает вам возможность сгенерировать код только для текущего режима работы, установленного для каждого образца блока. В качестве примера этого, если S-функция принимает произвольную ширину сигнала и закольцовывает через каждый элемент сигнала, вы хотели бы сгенерировать inlined код, который имеет циклы, когда сигнал имеет два или более элемента, но генерирует простое некольцованное вычисление, когда сигнал имеет только один элемент.

MEX S-функции уровня 1 C (записанные в более раннюю форму S-функционального API), которые не встроены, заставят сгенерированный код выполнять вызовы всех этих функций, даже если стандартная программа пуста для конкретной S-функции.

ФункцияЦель

mdlInitializeSizes

Инициализируйте массив размеров

mdlInitializeSampleTimes

Инициализируйте массив шагов расчета

mdlInitializeConditions

Инициализируйте состояния

mdlOutputs

Вычислите выходы

mdlUpdate

Обновление дискретных состояний

mdlDerivatives

Вычислите производные непрерывных состояний

mdlTerminate

Очистка, когда симуляция заканчивается

MEX S-функции уровня 2 C (т.е. те, которые записаны в текущий API S-функции), которые не встроены, осуществляют вызовы вышеуказанных функций за следующими исключениями:

  • mdlInitializeConditions вызывается только в том случае, если MDL_INITIALIZE_CONDITIONS объявлен как #define.

  • mdlStart вызывается только в том случае, если MDL_START объявлен как #define.

  • mdlUpdate вызывается только в том случае, если MDL_UPDATE объявлен как #define.

  • mdlDerivatives вызывается только в том случае, если MDL_DERIVATIVES объявлен как #define.

Путем вставки S-функции можно исключить вызовы этих, возможно, пустых функций в цикле симуляции. Это может значительно улучшить эффективность сгенерированного кода.

Для ввода S-функции, вызываемой sfuncИмя, вы создаете пользовательский целевой файл блока s-function sfunc_name.tlc и поместите его в ту же папку, что и Файл MEX S-функции. Затем во время сборки выполняется целевой файл вместо настройки вызовов функции в S-функцию .c файл. Целевой файл S-функции «inlines» S-функции путем направления Target Language Compiler вставлять только операторы, определенные в целевом файле.

В целом, встраивание S-функции особенно полезно, когда

  • Время, необходимое для выполнения содержимого S-функции, невелико по сравнению с накладными расходами, необходимыми для вызова S-функции.

  • Некоторые стандартные программы S-функций являются пустыми (например, mdlUpdate).

  • Поведение S-функции изменяется между симуляцией и генерацией кода. Для примера драйвер устройства ввода-вывода S-функции могут считываться из MATLAB® рабочая область во время симуляции, но считанная с фактического аппаратного адреса в сгенерированном коде.

Параметры S-функции

S-функция может записать два разных типа параметров в model.rtw файл для доступа к файлам компилятора целевого языка:

  • Настройки параметра: Они соответствуют нетронутым параметрам (обычно устанавливаемым из флажков и меню на маскированной S-функции), которые записываются через mdlRTW метод S-функции с использованием ssWriteRTWParamSettings. Файл реализации S-функции TLC может затем получить прямой доступ к значениям этих настроек параметра из SFcnParamSettings запись в блок.

  • Настраиваемые параметры: К этому классу параметров можно получить доступ, когда они зарегистрированы как параметры во время выполнения в S-функции. Обратите внимание, что такие настраиваемые параметры автоматически записываются в model.rtw файл. В рамках файла TLC для S-функции можно получить доступ к параметрам во время выполнения и их атрибутам с помощью LibBlockParameter библиотечная функция и ее варианты.

Для получения дополнительной информации о том, как создать и использовать параметры во время выполнения, смотрите Создать и обновить параметры во время выполнения S-функции. Также см. пример sfcndemo_runtime в примерах S-функций для создания и использования двух классов параметров. Примерами исходных файлов, которые можно просматривать и адаптировать, являются

Пример кода для S-функции

Предположим, что у вас есть простая S-функция, которая имитирует блок Gain с одним входом, одним выходом и скалярным усилением. То есть y = u * p. Если имя блока Simulink foo и имя S-функции уровня 2 foogain, S-функция C MEX должна содержать этот код:

#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

В следующих двух разделах показано различие в сгенерированном коде для model.c содержащие нелинейные и встроенные версии S-функции foogain. Модель не содержит других блоков Simulink.

Для получения дополнительной информации об этих функциях библиотеки C, связанных с S-функциями, смотрите Конфигурирование функций C/C + + S-Function. Для получения информации о том, как сгенерировать код, смотрите Configure Model and Generate Code и Choose Build Approach и Configure Build Process.

Сравнение неинлинфицированных и встроенных версий модели c

Без файла для определения спецификы S-функции генератор кода должен вызвать Файл MEX через S-function API. Следующий код является model.c файл для нелинейной S-функции (т.е. не существует соответствующего файла TLC).

Нелинейная S-функция

/*
 * 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-функция.  Этот код model.c с foogain S-функция полностью встроена:

/*
 * 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 требуется для блока целевых файлов и должен быть первым исполняемым оператором в блок целевом файле. Эта директива препятствует выполнению компилятором целевого языка неприемлемого целевого файла для S-функции foogain.

  • Вход в foo является rtGROUND (a Simulink Coder™ глобальное значение равно 0,0), поскольку foo является единственным блоком в модели, и его вход не связан.

  • Включение файла для foogain устраняет необходимость в сегменте S-функции регистрации для foogain. Это значительно уменьшает размер кода.

  • Код TLC вводит gain параметр, когда процесс сборки сконфигурирован на встроенные значения параметров. Для примера, если параметр S-функции задан как 2,5 в диалоговом окне S-функции, TLC Outputs функция генерирует

    rtB.foo = input * 2.5;
  • Используйте %generatefile директива, если ваша операционная система имеет ограничение по размеру имени файла, и имя S-функции: foosfunction (что превышает предел). В этом случае вы включите следующий оператор в системный целевой файл (где угодно до ссылки на этот целевой файл блока s-function).

    %generatefile foosfunction "foosfunc.tlc"

    Этот оператор сообщает целевому языковому компилятору открыть foosfunc.tlc вместо foosfunction.tlc.

Сравнение неинлинфицированных и встроенных версий model_reg.h

Включение S-функции уровня 2 значительно уменьшает размер model_reg.h код. Функции регистрации модели являются длительными; большая часть кода была исключена в этом примере. Приведенный ниже код подчеркивает различие между неинлинговыми и встроенными версиями model_reg.h; inlining устраняет этот код:

/*
 * 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 */
    }
  }

Файл TLC для встроенного foogain S-функции

Чтобы избежать ненужных вызовов S-функции и сгенерировать минимальный код, требуемый для S-функции, следующий файл 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. Это не включает в себя данные о параметре или состоянии (которые хранятся в параметре модели и векторах состояния, соответственно), а скорее используется для кэширования промежуточных результатов или производных представлений параметров и режимов. Одним из примеров данных образца является буфер, используемый блоком задержки транспорта.

Выделение и использование памяти на основе экземпляров по экземплярам может быть сделано несколькими способами в S-функции уровня 2: через ssSetUserData, рабочие векторы (например ssSetRWorkValue, ssSetIWorkValue), или рабочие векторы с типом данных, известные как DWork векторы. Для наименьших усилий в записи S-функции и блочного целевого файла и для автоматического соответствия как статическим, так и malloc образцы для целевых объектов, таких как grt, используйте рабочие векторы с типом данных при записи S-функций с данными образца.

Преимущества двоякие. Во-первых, запись S-функции более проста, в том, что выделение памяти и освобождение обрабатываются для вас Simulink. Во-вторых, DWork векторы записываются в model.rtw файл для вас автоматически, включая DWork имя, тип и размер данных. Это облегчает запись целевого файла блока, потому что вам не нужно записывать код TLC для выделения и освобождения DWork память.

Кроме того, если необходимо объединить группы DWork векторы в структуры для перехода к функциям, можно заполнить структуру указателями на DWork массивы в обеих ваших S-функциях mdlStart функция и целевой файл блока Start способ, достигающий согласованности между S-функцией и обработкой данных сгенерированного кода.

Наконец, использование DWork упрощает создание определенной версии кода (типы данных, скаляр и векторизованные и т.д.) для каждого образца блока, которая соответствует реализации в S-функции. В обеих реализациях используются DWork точно так же, чтобы встроенный код мог использоваться с программным обеспечением Simulink Accelerator™ без изменений функции C MEX S-function или блочного целевого файла.

Использование Inlined Code с программным обеспечением Simulink Accelerator

По умолчанию программное обеспечение Simulink Accelerator вызывает вашу S-функцию C MEX как часть ускоренной симуляции модели. Если вы предпочитаете, чтобы Accelerator включил вашу S-функцию перед запуском ускоренной модели, скажите Accelerator использовать ваш блочный целевой файл, чтобы встроить S-функцию с SS_OPTION_USE_TLC_WITH_ACCELERATOR флаг в вызове на ssSetOptions() в mdlInitializeSizes функция этой S-функции.

Обратите внимание, что память и рабочие размеры вектора и использование должны быть одинаковыми для сгенерированного кода TLC и S-функции C MEX, или программное обеспечение Simulink Accelerator не может правильно выполнить встроенный код. Это связано с тем, что S-функция C MEX вызывается, чтобы инициализировать блок и его рабочие векторы, вызывая mdlInitializeSizes, mdlInitializeConditions, mdlCheckParameters, mdlProcessParameters, и mdlStart функций. В случае постоянного распространения сигнала, mdlOutputs вызывается из S-функции C MEX во время фазы инициализации выполнения модели.

Во время фазы шага ускоренного выполнения модели код, сгенерированный Output и Update block TLC методы будут выполняться, плюс Derivatives и методы пересечения нулем, если они существуют. Start способ блочного целевого файла не используется при генерации кода для ускоренной модели.

Похожие темы