S-функции могут использоваться в моделях с несколькими скоростями дискретизации и развертываться в многозадачных целевых окружениях. Аналогичным образом, сами S-функции могут иметь несколько скоростей, с которыми они работают. Генератор кода производит код для многозадачных моделей многозадачности с помощью подхода, называемого группировкой скорости. В коде, сгенерированном для целевых объектов на основе ERT, группировка скоростей генерирует отдельные model
_step
функции для задачи базовой скорости и каждой задачи подрейса в модели. Хотя группировка скоростей является функцией генерации кода, найденной только в целевых устройствах ERT, ваши S-функции могут использовать ее в других контекстах, когда вы кодируете их, как объяснено ниже.
Чтобы воспользоваться преимуществами группировки скоростей, необходимо встроить мультирейсовые S-функции, если вы этого не сделали. Вам необходимо следовать определенным протоколам Target Language Compiler, чтобы использовать группировку скоростей. Кодирование TLC для использования группировки скоростей не препятствует правильному функционированию встроенных S-функций в GRT. Точно так же ваши встроенные S-функции все еще будут генерировать действительные ERT кода даже если вы не сделаете их совместимыми с группировкой скоростей. Однако, если вы это сделаете, они сгенерируют более эффективный код для многоскоростных моделей.
Инструкции и примеры кода Target Language Compiler, иллюстрирующие, как создать и обновить S-функции для генерации кода, совместимого с группировкой скоростей, см. в разделе Вопросы податливости группировке скоростей и совместимости.
Для каждой многократной S-функции, которая не совместима с группировкой скоростей, генератор кода выдает следующее предупреждение при сборке:
Warning: Simulink Coder: Code of output function for multirate block '<Root>/S-Function' is guarded by sample hit checks rather than being rate grouped. This will generate the same code for all rates used by the block, possibly generating dead code. To avoid dead code, you must update the TLC file for the block.
Вы также найдете комментарий, такой как следующий в коде, сгенерированном для каждой несовместимой S-функции:
/* Because the output function of multirate block <Root>/S-Function is not rate grouped, the following code might contain unreachable blocks of code. To avoid this, you must update your block TLC file. */
Слова «функция обновления» заменяются словами « выходными функциями» в этих предупреждениях.
Следующие инструкции показывают, как поддержать как детерминизм данных, так и целостность данных в многократных S-функциях. Они не охватывают случаи, когда нет ни детерминизма, ни целостности. Поддержка обработки на основе фрейма не влияет на требования.
Примечание
Медленные скорости должны быть кратны самой быстрой скорости. Инструкции не применяются, когда две скорости взаимодействия не умножаются или когда скорости не являются периодическими.
Правила, которые мультирейт S-функций должен наблюдать для входов,
Вход должен считываться только со скоростью, связанной со шагом расчета входного порта.
Обычно входные данные записываются в DWork
, и DWork
можно затем получить доступ с более медленной (нисходящей) скоростью.
Вход может быть считан при каждом выборочном попадании входной скорости и записан в DWork
память, но это DWork
в таком случае невозможно получить прямой доступ к памяти с помощью более медленной скорости. DWork
память, которая будет считываться медленной частотой, должна быть написана только быстрой частотой, когда есть специальная выборка хит. Специальный выборочный хит происходит, когда и эта скорость входного порта, и скорость, с которой он взаимодействует, имеют хит. В зависимости от их требований и проекта, алгоритмы могут обрабатывать данные в нескольких местах.
Правила, которые мультирейт S-функций должен наблюдать для выходов,
Этот выход не должен записываться с помощью скорости, отличной от скорости, назначенной порту выхода, за исключением оптимизированного случая, описанного ниже.
Этот выход должна всегда записываться, когда частота дискретизации выхода порта имеет хит.
Если эти условия выполняются, блок S-Function может указать, что входной порт и выходной порт могут быть локальными и переиспользуемыми.
Можно включать оптимизацию, когда обработка данных практически не требуется. В таких случаях код скорости входа может непосредственно записать в вывод (вместо использования DWork), когда есть специальная выборка хит. Однако если вы делаете это, необходимо объявить порт вывода глобальным и не переиспользуемым. Это результаты оптимизации на единицу меньше memcpy
но вводит неоднородные требования к обработке на более высокой скорости.
Используете ли вы эту оптимизацию или нет, самые последние входные данные, как видно из более медленной скорости, являются значением, когда и более быстрая, и более медленная скорость имели свои удары (и возможные более ранние входные данные также, в зависимости от алгоритма). Последующие шаги по более высокой скорости и связанным обновлениям входных данных не видны по более низкой скорости до тех пор, пока не произойдет следующее попадание по медленной скорости.
Псевдокод ниже абстрагирует, как вы должны записать свой код C MEX для обработки быстрых к медленным переходам, иллюстрируя со скоростью входом 0,1 секунды, управляя частотой выхода в одну секунду. Аналогичный подход может быть использован при встраивании кода. Блок имеет следующие характеристики:
Файл: sfun_multirate_zoh.c
, Уравнение: y = u(tslow)
Вход: локальный и переиспользуемый
Выход: локальный и переиспользуемый
DirectFeedthrough: да
OutputFcn if (ssIsSampleHit(".1")) { if (ssIsSepcialSampleHit("1")) { DWork = u; } } if (ssIsSampleHit("1")) { y = DWork; }
Альтернативный, слегка оптимизированный подход для простых алгоритмов:
Вход: локальный и переиспользуемый
Результат: глобальный и не переиспользуемый, потому что он должен сохраняться между специальными выборками хитами
DirectFeedthrough: да
OutputFcn if (ssIsSampleHit(".1")) { if (ssIsSpecialSampleHit("1")) { y = u; } }
Пример добавления простого алгоритма:
Файл: sfun_multirate_avg.c
; Уравнение: y = average(u)
Вход: локальный и переиспользуемый
Выход: локальный и переиспользуемый
DirectFeedthrough: да
(Предположим DWork[0:10]
и DWork[mycounter]
инициализируются в нуль)
OutputFcn if (ssIsSampleHit(".1")) { /* In general, processing on 'u' could be done here, it runs on every hit of the fast rate. */ DWork[DWork[mycounter]++] = u; if (ssIsSpecialSampleHit("1")) { /* In general, processing on DWork[0:10] can be done here, but it does cause the faster rate to have nonuniform processing requirements (every 10th hit, more code needs to be run).*/ DWork[10] = sum(DWork[0:9])/10; DWork[mycounter] = 0; } } if (ssIsSampleHit("1")) { /* Processing on DWork[10] can be done here before outputing. This code runs on every hit of the slower task. */ y = DWork[10]; }
Когда скорости вывода быстрее, чем входные скорости, вход должен считываться только со скоростью, которая связана со шагом расчета входного порта, соблюдая следующие правила:
Всегда считывайте входы от функции обновления.
Не используйте специальные проверки выборки при чтении входа.
Запишите вход в DWork.
Когда между скоростями появляется специальная выборка, скопируйте DWork во второй DWork в выходной функции.
Запись второго DWork в выход при каждом попадании выходной частоты выборки.
Блок может запросить, чтобы входной порт был сделан локальным, но он не может быть установлен на переиспользуемый. Порт выхода может быть установлен на локальный и переиспользуемый.
Как и в случае быстрого перехода к медленному, вход не должен считываться скоростью, отличной от той, которая назначена входному порту. Точно так же выход не должна записываться с другой скоростью, кроме скорости, назначенной порту выхода.
Оптимизация может быть произведена, когда реализуемый алгоритм требуется только для запуска с медленной скоростью. В таких случаях вы используете только один DWork. Вход все еще записывается в DWork в функции обновления. Когда существует специальная выборка хит между скоростями, выходная функция копирует тот же DWork непосредственно в выход. Вы должны задать выход порт как глобальный и не переиспользуемый в этом случае. Это результаты оптимизации на единицу меньше memcpy
операция по специальному удару выборки.
В любом случае данные, с которыми работают расчеты быстрой скорости, всегда задерживаются, то есть данные относятся к предыдущему этапу кода медленной скорости.
Псевдокод ниже абстрагирует то, что ваша S-функция должна сделать, чтобы обработать медленные к быстрым переходам, иллюстрируя со скоростью входа в одну секунду, приводящей скорость выхода 0,1 секунды. Блок имеет следующие характеристики:
Файл: sfun_multirate_delay.c
, Уравнение: y = u(tslow-1)
Вход: Установите локальный, будет локальным, если вывод/обновление объединены (ERT) в противном случае будут глобальными. Установите значение не переиспользуемым, поскольку вход должен быть сохранен до запусков функции обновления.
Выход: локальный и переиспользуемый
DirectFeedthrough: нет
OutputFcn if (ssIsSampleHit(".1") { if (ssIsSpecialSampleHit("1") { DWork[1] = DWork[0]; } y = DWork[1]; } UpdateFcn if (ssIsSampleHit("1")) { DWork[0] = u; }
Альтернативный, оптимизированный подход может использоваться некоторыми алгоритмами:
Вход: Установите локальный, будет локальным, если вывод/обновление объединены (ERT) в противном случае будут глобальными. Установите значение не переиспользуемым, поскольку вход должен быть сохранен до запусков функции обновления.
Выход: глобальный и не переиспользуемый, потому что он должен сохраняться между специальными выборками хитами.
DirectFeedthrough: нет
OutputFcn if (ssIsSampleHit(".1") { if (ssIsSpecialSampleHit("1") { y = DWork; } } UpdateFcn if (ssIsSampleHit("1")) { DWork = u; }
Пример добавления простого алгоритма:
Файл: sfun_multirate_modulate.c
, Уравнение: y = sin(tfast) + u(tslow-1)
Вход: Установите локальный, будет локальным, если вывод/обновление объединены (функция ERT) в противном случае будет глобальным. Установите значение не переиспользуемым, поскольку вход должен быть сохранен до запусков функции обновления.
Выход: локальный и переиспользуемый
DirectFeedthrough: нет
OutputFcn if (ssIsSampleHit(".1") { if (ssIsSpecialSampleHit("1") { /* Processing not likely to be done here. It causes * the faster rate to have nonuniform processing * requirements (every 10th hit, more code needs to * be run).*/ DWork[1] = DWork[0]; } /* Processing done at fast rate */ y = sin(ssGetTaskTime(".1")) + DWork[1]; } UpdateFcn if (ssIsSampleHit("1")) { /* Processing on 'u' can be done here. There is a delay of one slow rate period before the fast rate sees it.*/ DWork[0] = u;}