Архитектура кода

Прежде чем исследовать конкретные части генерации кода Target Language Compiler (TLC), рассмотрите, как Target Language Compiler генерирует код для простой модели. На следующем рисунке вы видите, что блоки помещают код в Mdl стандартные программы. Это показывает MdlOutputs.

static void simple_output(int_T tid)
{

  /* Sin Block: '<Root>/Sine Wave' */

  simple_B.SineWave_d = simple_P.SineWave_Amp *
    sin(simple_P.SineWave_Freq * simple_M->Timing.t[0] +
    simple_P.SineWave_Phase) + simple_P.SineWave_Bias;

  /* Gain: '<Root>/Gain' */
  simple_B.Gain_d = simple_B.SineWave_d * simple_P.Gain_Gain;

  /* Outport: '<Root>/Out1' */
  simple_Y.Out1 = simple_B.Gain_d;
}

Блоки имеют входы, выходы, параметры, состояния и другие общие свойства. Например, входы и выходы блоков обычно записываются в структуру ввода-вывода блоков (сгенерированную идентификаторами типа model_B, где model - имя модели). Блочные входы могут также исходить от внешней входной структуры (model_U) или структуру состояния при соединении с портом состояния интегратора (model_X), или заземление (rtGround), если он не соединен или заземлен. Выходы блоков могут также переходить к внешней структуре output, (model_Y). Следующая схема показывает общие отображения блочных данных.

Это обсуждение должно дать вам общее представление о том, как выглядит объект блока. Теперь можно просмотреть конкретные части процесса генерации кода, характерные для Target Language Compiler.

Процесс генерации кода

Генератор кода вызывает Target Language Compiler после компиляции модели в частичное представление модели (model.rtw) подходит для генерации кода. Чтобы сгенерировать код, Target Language Compiler использует свою библиотеку функций, чтобы преобразовать два класса целевых файлов:

  • Системные целевые файлы

  • Блокируйте целевые файлы

Системные целевые файлы используются для определения общей структуры сгенерированного кода, настройки для определенных целевых окружений. Блочные целевые файлы используются для реализации функциональности Simulink® блоки, включая пользовательские блоки s-function.

Можно создать целевые файлы блоков для C MEX, Фортран и MATLAB® язык S-функционирует, чтобы полностью встроить функциональность блока в тело сгенерированного кода. Функции MEX S на C могут быть неинлинфицированными, встроенными в оболочку или полностью встроенными. S-функции ФОРТРАН должны быть встроены в оболочку или полностью встроены.

Как TLC определяет статус инкрустации S-функции

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

Потому что они не могут использовать SimStructs, S-функции языка ФОРТРАН и MATLAB должны быть встроены. Это встраивание может быть либо в форме целевого файла с полным блоком, либо в виде целевого файла с одним строчным блоком, который ссылается на замещающий исходный файл S-функции C MEX.

Целевой компилятор языка выбирает S-функцию C MEX для вставки, если явная mdlRTW() функция существует в коде S-функции или если целевой файл для текущего целевого языка для текущего блока находится в пути поиска файлов TLC-файла. Если функция MEX S на C имеет явную mdlRTW() функция, должен быть соответствующий целевой файл или результаты условия ошибки.

Целевой файл для S-функции должен иметь то же имя корня, что и S-функция, и должен иметь расширение .tlc. Например, целевой файл для S-функции C MEX с именем sfix_bitop имеет имя файла sfix_bitop.tlc.

Inlined и Nonlined S-Function Code

Этот пример фокусируется на S-функции MEX С с именем sfix_bitop. Опции генерации кода установлены, чтобы позволить повторно использовать память сигнала для сигнальных линий, которые не были установлены как настраиваемые сигналы.

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

/* Bitwise Logic Block: <Root>/Bitwise Logical Operator */
/* [input] OR 'F00F' */
rtb_temp2 |= 0xF00F;

Код инициализации или настройки не требуется для этого встроенного блока.

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

/* Level2 S-Function Block: <Root>/Bitwise Logical Operator (sfix_bitop) */
  {
    SimStruct *rts = ssGetSFunction(rtS, 0);
    sfcnOutputs(rts, tid);
  }

Все mdlOutputs функция вызывается и запускается так же, как и во время симуляции. Но это не все. Существует также код регистрации, инициализации и завершения для неинстрируемой S-функции. Инициализация и оконечные вызовы аналогичны указанному выше фрагменту. Затем регистрационный код для S-функции всего с одним входным и одним выходным портами составляет 72 линии кода С, сгенерированного как часть файла model_reg.h.

/*Level2 S-Function Block: <Root>/Bitwise Logical Operator (sfix_bitop) */
    {
      extern void untitled_sf(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] = &rtU.In1;
          ssSetInputPortSignalPtrs(rts, 0, (InputPtrsType)&sfcnUPtrs[0]);
          _ssSetInputPortNumDimensions(rts, 0, 1);
          ssSetInputPortWidth(rts, 0, 1);
        }
      }
.
.
.

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

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

Похожие темы