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

Прежде, чем исследовать определенные части генерации кода Компилятора выходного языка (TLC), рассмотрите, как Компилятор Выходного языка генерирует код для простой модели. От следующей фигуры вы видите, что блоки помещают код в стандартные программы 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;
}

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

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

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

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

  • Системные конечные файлы

  • Блокируйте конечные файлы

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

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

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

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

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

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

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

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

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

Похожие темы