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

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

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

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

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

ФункцияЦель

mdlInitializeSizes

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

mdlInitializeSampleTimes

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

mdlInitializeConditions

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

mdlOutputs

Вычислите выходные параметры

mdlUpdate

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

mdlDerivatives

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

mdlTerminate

Вымойтесь, когда симуляция остановится

Уровень 2 C S-функции 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-function под названием 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. Файл реализации S-function TLC может затем непосредственно получить доступ к значениям этих установок параметров от записи SFcnParamSettings в блоке.

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

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

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

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

#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.

Для получения дополнительной информации о них S-функция связанные библиотечные функции C, смотрите, Конфигурируют Функции S-функции C/C++ (Simulink). Для получения информации о том, как сгенерировать код, смотрите, Конфигурируют Модель и Генерируют Код (Simulink Coder) и Выбирают Build Approach и Configure Build Process (Simulink Coder).

Сравнение Невстроенных и Встроенных Версий model.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 с S-функцией foogain, полностью встроенной:

/*
 * 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 и сгенерированном выводе:

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

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

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

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

    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; встраивание устраняет этот код:

/*
 * 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™ без изменений в S-функции MEX C или конечном файле блока.

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

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

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

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

Похожие темы