S-функции, которые Поддержка складывание выражений

Используйте складывание выражений, чтобы повысить эффективность кода, сгенерированного вашими собственными встроенными блоками S-Function, путем вызова макросов, предусмотренных в S-Function API.

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

S-Function API также позволяет вам задать, может ли выражение представлять расчеты, связанную с данным выходом портом. Когда вы запрашиваете выражение во входном или выходном порте блока, Simulink® engine определяет, может ли он удовлетворить этот запрос, учитывая контекст блока. Для примера механизм может отклонить запрос блока на вывод выражения, если целевой блок не принимает выражения на своем входе, если целевой блок имеет функцию обновления или если существует несколько выход пунктов назначения.

Решение выполнить или отклонить запрос на вывод выражения может также зависеть от категории выхода выражения, которое использует блок.

Чтобы воспользоваться преимуществами складывания выражений в S-функциях, вы должны понять, когда запрашивать принятие и генерацию выражений для определенных блоков. Вы не должны понимать алгоритм, с помощью которого механизм Simulink решает принять или отклонить эти запросы. Однако, если вы хотите проследить между моделью и сгенерированным кодом, полезно понять некоторые из более распространенных ситуаций, которые приводят к отклонению запроса.

Категории Выхода выражений

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

Постоянное выходное выражение является прямым доступом к одному из параметров блока. Например, выход блока Constant определяется как постоянное выражение, потому что выходное выражение является просто прямым доступом к Value блока параметр.

Тривиальный выход выражение является выражением, которое может быть повторено, без эффективности штрафа, когда порт выхода имеет несколько выход пунктов назначения. Например, выход блока Unit Delay определяется как тривиальное выражение, потому что выходное выражение является просто прямым доступом к состоянию блока. Поскольку выход выражение не имеет расчетов, оно может быть повторено более одного раза, не ухудшая эффективности сгенерированного кода.

Родовое выходное выражение является выражением, которое должно быть принято с штрафом эффективности, если оно повторяется. Как таковое, общий выход выражение не подходит для повторения, когда выход порт имеет несколько выход пунктов назначения. Например, выход блока Sum является обобщенным, а не тривиальным выражением, потому что пересчитать выходное выражение блока Sum как вход в несколько блоков дорого.

Примеры тривиальных и родовых выходных выражений

Рассмотрим этот блок. Блок Delay имеет несколько адресатов, но его выход обозначен как тривиальное выходное выражение, так что он может использоваться более одного раза, не ухудшая эффективность кода.

Этот фрагмент кода показывает код, сгенерированный из блока Unit Delay в этой блок-схеме. Три корневых выхода непосредственно назначаются из состояния блока 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 с одним и несколькими адресатами.

С другой стороны, рассмотрим блоки Sum в этой модели:

Верхний блок 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 предоставляет макросы, которые позволяют вам объявить, должен ли выход блока быть выражением, и если да, то задать категорию выражения. Эта таблица задает, когда объявить выход блока постоянным, тривиальным или общим выходным выражением.

Типы Выхода выражений

Категория выражения

Когда использовать

Постоянный

Используйте только, если выход блока является прямым доступом памяти к параметрам блоков.

Тривиальный

Используйте только, если block выхода является выражением, которое может появляться несколько раз в коде, не снижая эффективность (для примера, прямой доступ к памяти в поле DWork вектор, или литерал).

Универсальный

Используйте, если output является выражением, но не постоянным или тривиальным.

Вы должны объявить выходы как выражения в 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 для задания принятия входного выражения

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

Чтобы воспользоваться преимуществами складывания выражения, измените реализацию блока TLC inlined S-Function таким образом, чтобы она информировала механизм Simulink, генерирует ли он выражения или принимает их

В этой теме рассматриваются необходимые изменения в реализации 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 функция используется для генерации кода скалярного выходного выражения или одного элемента нескалярного выходного выражения. Если ваш блок выводит выражение, вы должны добавить a BlockOutputSignal функция. Прототип системы BlockOutputSignal является

%function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void

Аргументы в адрес BlockOutputSignal являются ли следующие:

  • block: запись для блока, для которого генерируется выход выражение

  • system: запись для системы, содержащей блок

  • portIdx: нулевой индекс выходного порта, для которого генерируется выражение

  • ucv: пользовательская переменная управления, определяющая выходной элемент, для которого генерируется код

  • lcv: циклическая переменная управления, определяющая выходной элемент, для которого генерируется код

  • idx: индекс сигнала, определяющий выходной элемент, для которого генерируется код

  • retType: вектор символов, определяющий тип требуемого доступа к сигналу:

    "Signal" задает содержимое или адрес выхода сигнала

    "SignalAddr" задает адрес выхода сигнала

BlockOutputSignal функция возвращает вектор символов для сигнала выхода или адреса. Вектор символов должен обеспечивать приоритет выражения с помощью открывающих и оконечных круглых скобок, если выражение не состоит из вызова функции. Адрес выражения может быть возвращен только для постоянного выражения; это адрес параметра, к памяти которого осуществляется доступ. Код, реализующий BlockOutputSignal функция для блока Constant является:

%% 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 функция для блока Relational Operator:

%% 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)>

Похожие темы