Используйте сворачивание выражений для повышения эффективности кода, генерируемого собственными встроенными блоками S-Function, путем вызова макросов, предоставляемых в API S-Function.
S-Function API позволяет указать, должен ли данный S-Function блок номинально принимать выражения на данном входном порте. Блок не всегда должен принимать выражения. Например, если используется адрес сигнала на входе, выражения не должны приниматься на этом входе, поскольку невозможно взять адрес выражения.
S-Function API также позволяет указать, может ли выражение представлять вычисления, связанные с данным портом вывода. При запросе выражения во входном или выходном порту блока модуль Simulink ® определяет, может ли он выполнить этот запрос, учитывая контекст блока. Например, обработчик может отклонить запрос блока на вывод выражения, если блок назначения не принимает выражения на входе, если блок назначения имеет функцию обновления или если существует несколько мест назначения вывода .
Решение выполнить или отклонить запрос на вывод выражения также может зависеть от категории выходного выражения, используемого блоком.
Чтобы использовать преимущества сворачивания выражений в S-функциях, следует понимать, когда запрашивать принятие и создание выражений для определенных блоков. Вам не нужно понимать алгоритм, с помощью которого движок Simulink принимает или отклоняет эти запросы. Однако, если требуется выполнить трассировку между моделью и сгенерированным кодом, полезно понять некоторые из наиболее распространенных ситуаций, которые приводят к отклонению запроса.
При реализации C MEX S-функции можно указать, должен ли код, соответствующий выходному сигналу блока, генерироваться как выражение. Если блок генерирует выражение, необходимо указать, что выражение является постоянным, тривиальным или базовым.
Константное выходное выражение - это прямой доступ к одному из параметров блока. Например, вывод блока Constant определяется как константное выражение, поскольку выходное выражение является просто прямым доступом к блоку Value параметр.
Тривиальное выходное выражение - это выражение, которое может быть повторено без снижения производительности, если выходной порт имеет несколько выходных адресов. Например, выходной сигнал блока единичной задержки определяется как тривиальное выражение, поскольку выходное выражение является просто прямым доступом к состоянию блока. Поскольку выходное выражение не имеет вычислений, оно может повторяться более одного раза без ухудшения производительности генерируемого кода.
Универсальное выходное выражение - это выражение, которое при повторении должно иметь ограничение производительности. По существу, универсальное выходное выражение не подходит для повторения, когда выходной порт имеет несколько выходных адресов. Например, вывод блока Sum является обобщенным, а не тривиальным выражением, потому что дорого пересчитать выходное выражение блока Sum в качестве ввода в несколько блоков.
Рассмотрим эту блок-схему. Блок задержки имеет несколько пунктов назначения, но его выход обозначается как тривиальное выходное выражение, так что он может использоваться более одного раза без ухудшения эффективности кода.

Этот фрагмент кода показывает код, сгенерированный из блока «Unit Delay» в данной блок-схеме. Три корневых выхода назначаются непосредственно из состояния блока единичной задержки, который хранится в поле глобальной структуры данных. rtDWork. Поскольку назначение является прямым, без выражений не существует никакого штрафа производительности, связанного с использованием тривиального выражения для нескольких назначений.
void MdlOutputs(int_T tid)
{
...
/* Outport: <Root>/Out1 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out1 = rtDWork.Unit_Delay_DSTATE;
/* Outport: <Root>/Out2 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out2 = rtDWork.Unit_Delay_DSTATE;
/* Outport: <Root>/Out3 incorporates:
* UnitDelay: <Root>/Unit Delay */
rtY.Out3 = rtDWork.Unit_Delay_DSTATE;
...
}Созданный код показывает, как создается код для блоков Sum с одним и несколькими адресатами.
С другой стороны, рассмотрим блоки суммы в этой модели:

Верхний блок суммы в модели генерирует сигнал с меткой non_triv. Вычисление этого выходного сигнала включает в себя два умножения и сложение. Если бы выводу блока Sum было разрешено генерировать выражение, даже если блок имел несколько мест назначения, то операции блока дублировались бы в сгенерированном коде. В этом случае генерируемые выражения будут размножаться до четырех умножений и двух дополнений. Это приведет к снижению эффективности программы. Соответственно, выходной сигнал блока Sum не может быть выражением, поскольку он имеет несколько адресатов.
Механизм Simulink не позволяет выходному сигналу верхнего блока Sum быть выражением, поскольку сигнал non_triv маршрутизируется в два места назначения вывода. Вместо этого результат операций умножения и сложения сохраняется во временной переменной. (rtb_non_triv) это дважды упоминается в следующих инструкциях, как показано в приведенном ниже фрагменте кода.
Напротив, нижний блок Sum, который имеет только одно выходное назначение (Out2), создает выражение.
void MdlOutputs(int_T tid)
{
/* local block i/o variables */
real_T rtb_non_triv;
real_T rtb_Sine_Wave;
/* Sum: <Root>/Sum incorporates:
* Gain: <Root>/Gain
* Inport: <Root>/u1
* Gain: <Root>/Gain1
* Inport: <Root>/u2
*
* Regarding <Root>/Gain:
* Gain value: rtP.Gain_Gain
*
* Regarding <Root>/Gain1:
* Gain value: rtP.Gain1_Gain
*/
rtb_non_triv = (rtP.Gain_Gain * rtU.u1) + (rtP.Gain1_Gain *
rtU.u2);
/* Outport: <Root>/Out1 */
rtY.Out1 = rtb_non_triv;
/* Sin Block: <Root>/Sine Wave */
rtb_Sine_Wave = rtP.Sine_Wave_Amp *
sin(rtP.Sine_Wave_Freq * rtmGetT(rtM_model) +
rtP.Sine_Wave_Phase) + rtP.Sine_Wave_Bias;
/* Outport: <Root>/Out2 incorporates:
* Sum: <Root>/Sum1
*/
rtY.Out2 = (rtb_non_triv + rtb_Sine_Wave);
}S-Function API предоставляет макросы, которые позволяют объявить, должен ли вывод блока быть выражением, и если да, то указать категорию выражения. В этой таблице указывается, когда объявить вывод блока константой, тривиальным или обобщенным выражением вывода.
Типы выходных выражений
Категория выражения | Когда использовать |
|---|---|
Постоянный | Используется только в том случае, если вывод блока является прямым доступом памяти к параметру блока. |
Тривиальный | Используется только в том случае, если вывод блока - это выражение, которое может появляться в коде несколько раз без снижения эффективности (например, прямой доступ к памяти в поле |
Универсальный | Используйте, если вывод является выражением, но не константой или тривиальным. |
Необходимо объявить выходные данные как выражения в mdlSetWorkWidths с использованием макросов, определенных в S-Function API. Макросы имеют следующие аргументы:
SimStruct *S: указатель на блок SimStruct.
int idx: отсчитываемый от нуля индекс выходного порта.
bool value: в TRUE, если порт генерирует выходные выражения.
Для задания вывода в качестве константы, тривиального или общего выражения доступны следующие макросы:
void ssSetOutputPortConstOutputExprInRTW(SimStruct *S, int idx, bool value)
void ssSetOutputPortTrivialOutputExprInRTW(SimStruct *S, int idx, bool value)
void ssSetOutputPortOutputExprInRTW(SimStruct *S, int idx, bool value)
Для запроса состояния, заданного предыдущими вызовами макросов выше, доступны следующие макросы:
bool ssGetOutputPortConstOutputExprInRTW(SimStruct *S, int idx)
bool ssGetOutputPortTrivialOutputExprInRTW(SimStruct *S, int idx)
bool ssGetOutputPortOutputExprInRTW(SimStruct *S, int idx)
Набор базовых выражений является надсетью набора тривиальных выражений, а набор тривиальных выражений является надсетью набора константных выражений.

Поэтому при запросе выходного документа, для которого задано постоянное выражение с ssGetOutputPortTrivialOutputExprInRTW, возвращается True. Константное выражение считается тривиальным выражением, поскольку это прямой доступ к памяти, который может повторяться без ухудшения эффективности генерируемого кода.
Аналогично, вывод, настроенный как константа или тривиальное выражение, возвращает True при запросе состояния в качестве общего выражения.
Блок может запросить, чтобы его выходные данные были представлены в коде как выражение. Такой запрос может быть отклонен, если блок назначения не может принять выражения на своем входном порту. Кроме того, условия, независимые от запрашивающего блока и его целевых блоков, могут препятствовать принятию выражений.
Блок не должен быть настроен на прием выражений на своем входном порте при следующих условиях:
Блок должен принимать адрес своих входных данных. Невозможно взять адрес большинства типов входных выражений.
Код, сгенерированный для блока, ссылается на вход более одного раза (например, блоки Abs или Max). Это приведет к дублированию потенциально сложного выражения и последующему ухудшению эффективности кода.
Если блок отказывается принимать выражения во входном порту, то блоку, подключенному к этому входному порту, не разрешается выводить общее или тривиальное выражение.
Запрос на вывод константного выражения не отклоняется, так как для константного выражения нет штрафа производительности, и программа может взять адрес параметра.
S-Function API предоставляет макросы, которые позволяют:
Укажите, должен ли вводимый блок принимать несогласованные выражения (то есть тривиальные или универсальные выражения).
Запрос о том, принимает ли входной блок неконкретные выражения.
По умолчанию входные данные блоков не принимают некондиционные выражения.
Необходимо вызвать макросы в mdlSetWorkWidths функция. Макросы имеют следующие аргументы:
SimStruct *S: указатель на SimStruct блока.
int idx: отсчитываемый от нуля индекс входного порта.
bool value: значение TRUE, если порт принимает входные выражения; в противном случае передайте значение FALSE.
Макрос, доступный для указания, должен ли вводимый блок принимать неконкретное выражение:
void ssSetInputPortAcceptExprInRTW(SimStruct *S, int portIdx, bool value)
Соответствующий макрос, доступный для запроса состояния, установленного любыми предыдущими вызовами ssSetInputPortAcceptExprInRTW является:
bool ssGetInputPortAcceptExprInRTW(SimStruct *S, int portIdx)
Даже после того, как конкретный блок запрашивает разрешение на создание выходного выражения, этот запрос может быть отклонен по общим причинам. Эти причины включают, но не ограничиваются ими:
Выходное выражение нетривиально, а выходные данные имеют несколько назначений.
Выходное выражение не является постоянным, и выход соединен по крайней мере с одним пунктом назначения, который не принимает выражения в своем входном порту.
Выходные данные являются контрольной точкой.
Выходному документу присвоен класс внешнего хранилища.
Выходные данные должны храниться с использованием глобальных данных (например, вход в блок слияния или блок с состояниями).
Выходной сигнал сложен.
Не нужно учитывать эти общие факторы при принятии решения об использовании сворачивания выражения для конкретного блока. Однако эти правила могут быть полезны при изучении сгенерированного кода и анализе случаев, когда подавляется оптимизация сворачивания выражения.
Чтобы воспользоваться преимуществом сворачивания выражений, измените реализацию блока TLC встроенной S-функции так, чтобы она сообщала подсистеме Simulink, генерирует ли она выражения или принимает их
Входные порты, как объясняется в S-Function API для указания приемки входного выражения.
Выходные порты, как описано в разделе Категории выходных выражений.
В этом разделе рассматриваются необходимые модификации реализации TLC.
В BlockInstanceSetup функция вашей S-функции, зарегистрируйте ваш блок, чтобы он соответствовал сворачиванию выражений. В противном случае затребованное или разрешенное сворачивание выражения на выходах или входах блока будет отключено, и будут использоваться временные переменные.
Для регистрации соответствия сворачивания выражений вызовите функцию библиотеки TLC LibBlockSetIsExpressionCompliant(block), которая определена в . Например:matlabroot/rtw/c/tlc/lib/utillib.tlc
%% Function: BlockInstanceSetup =========================================== %% %function BlockInstanceSetup(block, system) void %% %<LibBlockSetIsExpressionCompliant(block)> %% %endfunction
Можно условно отключить сворачивание выражения на входах и выходах блока, сделав вызов этой функции условно.
При переопределении одной из реализаций TLC-блоков, поставляемых генератором кода с собственной реализацией, не следует выполнять предыдущий вызов до тех пор, пока не будет обновлена реализация.
BlockOutputSignal функция используется для генерации кода для скалярного выходного выражения или одного элемента нескалярного выходного выражения. Если блок выводит выражение, необходимо добавить BlockOutputSignal функция. Прототип BlockOutputSignal является
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
Аргументы для BlockOutputSignal таковы:
blockЗапись для блока, для которого создается выходное выражение
systemЗапись для системы, содержащей блок
portIdx: отсчитываемый от нуля индекс выходного порта, для которого создается выражение
ucv: пользовательская управляющая переменная, определяющая элемент вывода, для которого создается код
lcv: переменная управления контуром, определяющая выходной элемент, для которого создается код
idx: индекс сигнала, определяющий выходной элемент, для которого формируется код
retType: символьный вектор, определяющий тип требуемого доступа к сигналу:
"Signal" задает содержимое или адрес выходного сигнала
"SignalAddr" задает адрес выходного сигнала
BlockOutputSignal функция возвращает символьный вектор для выходного сигнала или адреса. Символьный вектор должен обеспечивать приоритет выражения, используя открывающие и завершающие круглые скобки, если выражение не состоит из вызова функции. Адрес выражения может быть возвращен только для константного выражения; это адрес параметра, к памяти которого осуществляется доступ. Код, реализующий BlockOutputSignal функция для блока Константа:
%% Function: BlockOutputSignal =================================================
%% Abstract:
%% Return the reference to the parameter. This function *may*
%% be used by Simulink when optimizing the Block IO data structure.
%%
%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void
%switch retType
%case "Signal"
%return LibBlockParameter(Value,ucv,lcv,idx)
%case "SignalAddr"
%return LibBlockParameterAddr(Value,ucv,lcv,idx)
%default
%assign errTxt = "Unsupported return type: %<retType>"
%<LibBlockReportError(block,errTxt)>
%endswitch
%endfunctionКод, реализующий BlockOutputSignal функция для блока реляционного оператора:
%% Function: BlockOutputSignal ================================================= %% Abstract: %% Return an output expression. This function *may* %% be used by Simulink when optimizing the Block IO data structure. %% %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void %switch retType %case "Signal" %assign logicOperator = ParamSettings.Operator %if ISEQUAL(logicOperator, "~=") %assign op = "!=" elseif ISEQUAL(logicOperator, "==") %assign op = "==" %else %assign op = logicOperator %endif %assign u0 = LibBlockInputSignal(0, ucv, lcv, idx) %assign u1 = LibBlockInputSignal(1, ucv, lcv, idx) %return "(%<u0> %<op> %<u1>)" %default %assign errTxt = "Unsupported return type: %<retType>" %<LibBlockReportError(block,errTxt)> %endswitch %endfunction
Когда блок имеет один выход, Outputs функция в TLC-файле блока вызывается только в том случае, если выходной порт не является выражением. В противном случае BlockOutputSignal вызывается функция.
Если блок имеет несколько выходов, Outputs вызывается, если любой выходной порт не является выражением. Outputs функция должна защищать от генерации кода для выходных портов, которые являются выражениями. Это достигается путем защиты разделов кода, соответствующих отдельным портам вывода с вызовами LibBlockOutputSignalIsExpr().
Например, рассмотрим S-функцию с двумя входами и двумя выходами, где
Первый выход, y0, равен двухкратному первому входу.
Второй выход, y1, равен четырехкратному второму входу.
Outputs и BlockOutputSignal функции для S-функции показаны в этом фрагменте кода:
%% Function: BlockOutputSignal ================================================= %% Abstract: %% Return an output expression. This function *may* %% be used by Simulink when optimizing the Block IO data structure. %% %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void %switch retType %case "Signal" %assign u = LibBlockInputSignal(portIdx, ucv, lcv, idx) %case "Signal" %if portIdx == 0 %return "(2 * %<u>)" %elseif portIdx == 1 %return "(4 * %<u>)" %endif %default %assign errTxt = "Unsupported return type: %<retType>" %<LibBlockReportError(block,errTxt)> %endswitch %endfunction %% %% Function: Outputs ================================================= %% Abstract: %% Compute output signals of block %% %function Outputs(block,system) Output %assign rollVars = ["U", "Y"] %roll sigIdx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars %assign u0 = LibBlockInputSignal(0, "", lcv, sigIdx) %assign u1 = LibBlockInputSignal(1, "", lcv, sigIdx) %assign y0 = LibBlockOutputSignal(0, "", lcv, sigIdx) %assign y1 = LibBlockOutputSignal(1, "", lcv, sigIdx) %if !LibBlockOutputSignalIsExpr(0) %<y0> = 2 * %<u0>; %endif %if !LibBlockOutputSignalIsExpr(1) %<y1> = 4 * %<u1>; %endif %endroll %endfunction
В прошлом блоки предваряли свои выходные коды комментариями формы
/* %<Type> Block: %<Name> */
Если блок совместим со складыванием выражений, начальная строка, показанная выше, генерируется автоматически. Не включайте комментарий в реализацию TLC блока. Зарегистрируйте дополнительную информацию с помощью LibCacheBlockComment функция.
LibCacheBlockComment функция принимает вектор символов в качестве ввода, определяя тело комментария, за исключением открывающего заголовка, последней новой строки однострочного или многострочного комментария и закрывающего трейлера.
Следующий код TLC иллюстрирует регистрацию блочного комментария. Обратите внимание на использование функции LibBlockParameterForComment, который возвращает вектор символов, подходящий для комментария блока, указывающий значение параметра блока.
%openfile commentBuf $c(*) Gain value: %<LibBlockParameterForComment(Gain)> %closefile commentBuf %<LibCacheBlockComment(block, commentBuf)>