exponenta event banner

Изучение имен переменных и свертывания циклов

timesN Обзор учебного пособия по закольцовыванию

Цель: В этом примере показано, как можно влиять на циклическое поведение сгенерированного кода.

Папка: matlabroot/toolbox/rtw/rtwdemos/tlctutorial/timesN (открыто)

Работа с моделью sfun_xN в tlctutorial/timesN. Он имеет один источник (блок генератора синусоидальной волны), блок усиления с умножением N, блок Out и блок Scope.

В учебном пособии описаны следующие шаги.

  1. Начало работы - настройка упражнения и запуск модели

  2. Изменить модель (Modify the Model) - изменение ширины ввода и просмотр результатов

  3. Изменение порога свертывания цикла - изменение порога и просмотр результатов

  4. Дополнительные сведения о качении цикла TLC - параметризация поведения цикла

Начало

  1. Сделать tlctutorial/timesN текущую папку, чтобы можно было использовать предоставленные файлы.

    Примечание

    Необходимо использовать или создавать рабочую папку вне matlabroot для моделей Simulink ®. Нельзя строить модели в исходных папках.

  2. В окне команд MATLAB ® создайте файл MEX для функции S:

    mex timesN.c

    Это позволяет избежать выбора версии, поставляемой с Simulink.

    Примечание

    Ошибка может возникнуть, если вы ранее не запускали mex -setup.

  3. Открытие файла модели sfun_xN.

  4. Просмотр ранее созданного кода в sfun_xN_grt_rtw/sfun_xN.c. Обратите внимание, что в коде не существует циклов. Это происходит потому, что входной и выходной сигналы являются скалярными.

Изменение модели

  1. Замените блок синусоидальной волны блоком константы.

  2. Установите для параметра блока «Константа» значение 1:4 и измените верхнюю метку. model: sfun_xNКому model: sfun_vec.

  3. Сохранить отредактированную модель как sfun_vectlctutorial/timesN). Модель теперь выглядит так.

  4. Поскольку блок константы генерирует вектор значений, это векторизированная модель. Создание кода для модели и просмотр /*Model output function */ раздел sfun_vec.c в редакторе, чтобы наблюдать, как переменные и for обрабатываются петли. Эта функция выглядит следующим образом:

    /* Model output function */
    static void sfun_vec_output(int_T tid)
    {
      /* S-Function Block: <Root>/S-Function */
      /* Multiply input by 3.0 */
      sfun_vec_B.timesN_output[0] = sfun_vec_P.Constant_Value[0] * 3.0;
      sfun_vec_B.timesN_output[1] = sfun_vec_P.Constant_Value[1] * 3.0;
      sfun_vec_B.timesN_output[2] = sfun_vec_P.Constant_Value[2] * 3.0;
      sfun_vec_B.timesN_output[3] = sfun_vec_P.Constant_Value[3] * 3.0;
    
      /* Outport: '<Root>/Out' */
      sfun_vec_Y.Out[0] = sfun_vec_B.timesN_output[0];
      sfun_vec_Y.Out[1] = sfun_vec_B.timesN_output[1];
      sfun_vec_Y.Out[2] = sfun_vec_B.timesN_output[2];
      sfun_vec_Y.Out[3] = sfun_vec_B.timesN_output[3];
      UNUSED_PARAMETER(tid);
    }

    Обратите внимание, что существует четыре экземпляра кода, который генерирует выходные данные модели, соответствующие четырем итерациям.

  5. Задайте для параметра блока «Константа» значение 1:10 и сохраните модель.

  6. Создание кода для модели и просмотр /*Model output function */ раздел sfun_vec.c в редакторе, чтобы наблюдать, как переменные и for обрабатываются петли. Эта функция выглядит следующим образом:

    /* Model output function */
    static void sfun_vec_output(int_T tid)
    {
      /* S-Function Block: <Root>/S-Function */
      /* Multiply input by 3.0 */
      {
        int_T i1;
        const real_T *u0 = &sfun_vec_P.Constant_Value[0];
        real_T *y0 = sfun_vec_B.timesN_output;
        for (i1=0; i1 < 10; i1++) {
          y0[i1] = u0[i1] * 3.0;
        }
      }
    
      {
        int32_T i;
        for (i = 0; i < 10; i++) {
          /* Outport: '<Root>/Out' */
          sfun_vec_Y.Out[i] = sfun_vec_B.timesN_output[i];
        }
      }
    
      UNUSED_PARAMETER(tid);
    }

Обратите внимание, что:

  • Код, генерирующий выходные данные модели, «сворачивается» в цикл. Это происходит по умолчанию, когда число итераций превышает 5.

  • Индекс цикла i1 выполняется от 0 до 9.

  • Указатель *y0 используется и инициализируется в матрице выходных сигналов.

Изменение порога качения цикла

Генератор кода создает итерации или циклы в зависимости от текущего значения параметра Порог разгрузки цикла.

Значение по умолчанию для порога разгрузки цикла: 5. Чтобы изменить поведение цикла для блоков в модели, выполните следующие действия.

  1. На панели Оптимизация (Optimization) диалогового окна Параметры конфигурации (Configuration Parameters) установите значение Порог разгрузки цикла (Loop unrolling threshold) 12 и нажмите Apply.

    Параметр RollThreshold сейчас 12. Петли будут генерироваться только тогда, когда ширина сигналов, проходящих через блок, превысит 12.

    Примечание

    Изменение невозможно RollThreshold для определенных блоков в диалоговом окне «Параметры конфигурации».

  2. Нажмите Ctrl + B, чтобы регенерировать выходные данные.

  3. Осмотреть sfun_vec.c. Это будет выглядеть следующим образом:

    /* Model output function */
    static void sfun_vec_output(int_T tid)
    {
      /* S-Function Block: <Root>/S-Function */
      /* Multiply input by 3.0 */
      sfun_vec_B.timesN_output[0] = sfun_vec_P.Constant_Value[0] * 3.0;
      sfun_vec_B.timesN_output[1] = sfun_vec_P.Constant_Value[1] * 3.0;
      sfun_vec_B.timesN_output[2] = sfun_vec_P.Constant_Value[2] * 3.0;
      sfun_vec_B.timesN_output[3] = sfun_vec_P.Constant_Value[3] * 3.0;
      sfun_vec_B.timesN_output[4] = sfun_vec_P.Constant_Value[4] * 3.0;
      sfun_vec_B.timesN_output[5] = sfun_vec_P.Constant_Value[5] * 3.0;
      sfun_vec_B.timesN_output[6] = sfun_vec_P.Constant_Value[6] * 3.0;
      sfun_vec_B.timesN_output[7] = sfun_vec_P.Constant_Value[7] * 3.0;
      sfun_vec_B.timesN_output[8] = sfun_vec_P.Constant_Value[8] * 3.0;
      sfun_vec_B.timesN_output[9] = sfun_vec_P.Constant_Value[9] * 3.0;
    
      /* Outport: '<Root>/Out' */
      sfun_vec_Y.Out[0] = sfun_vec_B.timesN_output[0];
      sfun_vec_Y.Out[1] = sfun_vec_B.timesN_output[1];
      sfun_vec_Y.Out[2] = sfun_vec_B.timesN_output[2];
      sfun_vec_Y.Out[3] = sfun_vec_B.timesN_output[3];
      sfun_vec_Y.Out[4] = sfun_vec_B.timesN_output[4];
      sfun_vec_Y.Out[5] = sfun_vec_B.timesN_output[5];
      sfun_vec_Y.Out[6] = sfun_vec_B.timesN_output[6];
      sfun_vec_Y.Out[7] = sfun_vec_B.timesN_output[7];
      sfun_vec_Y.Out[8] = sfun_vec_B.timesN_output[8];
      sfun_vec_Y.Out[9] = sfun_vec_B.timesN_output[9];
      UNUSED_PARAMETER(tid);
    }
  4. Чтобы снова активировать прокрутку цикла, измените пороговое значение для разворачивания цикла на 10 (или менее) на панели Оптимизация (Optimization).

Циклическая обработка является важной функцией TLC для оптимизации генерируемого кода. Уделите некоторое время изучению и изучению его последствий перед созданием кода для производственных требований.

Подробнее о качении шлейфа TLC

Следующий TLC %roll код - Outputs функция timesN.tlc:

%function Outputs(block, system) Output
  /* %<Type> Block: %<Name> */
  %%
  /* Multiply input by %<gain> */
  %assign rollVars = ["U", "Y"]
  %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
     %<LibBlockOutputSignal(0, "", lcv, idx)> = \
    %<LibBlockInputSignal(0, "", lcv, idx)> * %<gain>;
  %endroll
%endfunction %% Outputs

Аргументы для %roll

Линии между %roll и%endroll может быть повторен или закольцован. Ключ к пониманию %roll директива в своих аргументах:

%roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
АргументОписание
sigIdx

Укажите индекс в вектор (сигнала), который используется в сгенерированном коде. Если сигнал является скалярным, при анализе этого блока model.rtw файл, TLC определяет, что требуется только одна строка кода. В этом случае он устанавливает sigIdx кому 0 так, чтобы получить доступ только к первому элементу вектора, и петля не строится.

lcv

Управляющая переменная, обычно указанная в %roll директива как lcv = RollThreshold. RollThreshold - глобальный (общемодельный) порог со значением по умолчанию 5. Поэтому всякий раз, когда блок содержит более пяти смежных и скользящих переменных, TLC сворачивает строки, вложенные между ними %roll и %endroll в петлю. Если существует менее пяти смежных скользящих переменных, %roll не создает цикл и вместо этого создает отдельные строки кода.

block

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

"Roller"

Это, указано в rtw/c/tlc/roller.tlc, форматирует цикл. Обычно вы проходите это как есть, но другие конструкции управления контуром возможны для расширенного использования (см. LibBlockInputSignal в функции входного сигнала).

rollVars

Сообщает TLC, какие типы элементов должны быть свернуты: входные сигналы, выходные сигналы и/или параметры. Вы не должны использовать все из них. В предыдущей строке rollVars определяется с помощью %assign.

%assign rollVars = ["U", "Y"]
Этот список сообщает TLC, что он проходит через входные сигналы (U) и выходных сигналов (Y). В случаях, когда блоки задают массив параметров вместо скалярного параметра, rollvars указывается как
%assign rollVars = ["U", "Y", "P"]

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

Посмотрите на линии, которые появляются между %roll и %endroll:

%<LibBlockOutputSignal(0, "", lcv, idx)> = \
%<LibBlockInputSignal (0, "", lcv, idx)> * 2.0;

Функции библиотеки TLC LibBlockInputSignal и LibBlockOutputSignal разверните, чтобы создать скалярные или векторные идентификаторы, которые именуются и индексируются. LibBlockInputSignal, LibBlockOutputSignalи ряду связанных функций TLC передаются четыре канонических аргумента:

АргументОписание

первый аргумент - 0

Соответствует индексу входного порта для данного блока. Первый входной порт имеет индекс 0. Второй входной порт имеет индекс 1 и т.д.

второй аргумент - " "

Переменная индекса, зарезервированная для расширенного использования. Теперь укажите второй аргумент как пустую строку. В расширенных приложениях можно определить собственное имя переменной для использования в качестве индекса с %roll. В этом случае TLC объявляет эту переменную целым числом в местоположении в сгенерированном коде.

третий аргумент - lcv

Как описано выше, lcv = RollThreshold установлен в %roll чтобы указать, что цикл должен быть создан всякий раз, когда RollThreshold (значение по умолчанию - 5).

четвертый аргумент - sigIdx

Позволяет TLC обрабатывать особые случаи. В случае, если RollThreshold не превышен (например, если блок подключен только к скалярному входному сигналу) TLC не сворачивает его в цикл. Вместо этого TLC предоставляет целочисленное значение для индексной переменной в соответствующей строке «встроенного» кода. Всякий раз, когда RollThreshold превышено, TLC создает for и использует индексную переменную для доступа к входам, выходам и параметрам внутри цикла.

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