exponenta event banner

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

Обзор встроенной функции S

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

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

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

ФункцияЦель

mdlInitializeSizes

Инициализация массива размеров

mdlInitializeSampleTimes

Инициализация массива времени выборки

mdlInitializeConditions

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

mdlOutputs

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

mdlUpdate

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

mdlDerivatives

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

mdlTerminate

Очистка после завершения моделирования

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Предположим, что у вас есть простая S-функция, которая имитирует блок усиления, с одним входом, одним выходом и скалярным усилением. То есть 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-функций. Дополнительные сведения о создании кода см. в разделах Настройка модели и Создание кода, а также Выбор подхода построения и настройка процесса построения.

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

Без файла TLC для определения специфики S-функции генератор кода должен вызвать S-функцию MEX-файла через API S-функции. Следующий код: 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-функционального блока, результат model.c код -

rtB.S_Function = 0.0 * rtP.S_Function_Gain;

Включение файла TLC резко уменьшило размер кода и увеличило эффективность выполнения сгенерированного кода. В этих примечаниях представлена некоторая информация о коде TLC и сгенерированных выходных данных:

  • Директива TLC %implements требуется для целевых файлов блоков и должна быть первой исполняемой инструкцией в целевом файле блоков. Эта директива запрещает компилятору целевого языка выполнять неподходящий целевой файл для S-функции foogain.

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

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

  • Код TLC встроен в gain когда процесс построения сконфигурирован для встроенных значений параметров. Например, если параметр S-функции указан как 2.5 в диалоговом окне S-функции, TLC Outputs функция генерирует

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

    %generatefile foosfunction "foosfunc.tlc"

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

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

Включение S-функции уровня 2 значительно уменьшает размер 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 */
    }
  }

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

Чтобы избежать ненужных вызовов 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 или целевого файла блока.

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

По умолчанию программа Simulink Accelerator вызывает функцию C MEX S как часть ускоренного моделирования модели. Если перед запуском ускоренной модели акселератор должен быть встроен в S-функцию, попросите акселератор использовать целевой файл блока для встраивания 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 будут выполняться блочные методы TLC, плюс Derivatives и методы пересечения нуля, если они существуют. Start способ целевого файла блока не используется при формировании кода для ускоренной модели.

Связанные темы