Main Content

Programmatically Customize C Functions Generated from Scoped Simulink Functions Located in Top Model

In the generated model code, each Simulink function corresponds to one of the generated functions. The return value and input arguments of the generated function represent the Argument Inport and Argument Outport blocks of the Simulink function, and the function body corresponds to the Simulink function logic.

A scoped Simulink® function is a Simulink Function block with the Function visibility parameter of its Trigger block specified as scoped. When a scoped Simulink function is located in the top model, it is callable from the top model and from any subsystem in its hierarchy. To read more about scoped Simulink functions, see Scoped Simulink Function Blocks in Models.

This example shows how to programmatically customize the name, return value, input arguments, and qualifier of a function generated from a scoped Simulink function locate in the top model.

Load Model and Explore Scoped Simulink Function Block

Load the model.

sfModel = "SimFuncs";
load_system(sfModel)

Verify that scopedSimFunc is a scoped Simulink Function block by getting the value of the Function visibility parameter of its Trigger block:

scopedSfTrig = "SimFuncs/scopedSimFunc/s_func";
get_param(scopedSfTrig,"FunctionVisibility")
ans = 
'scoped'

Prepare to Customize Generated Function

To customize the function generated from the scoped Simulink function programmatically, you use the code mapping object associated with the model and the Embedded Coder Dictionary of the model (or a shared dictionary linked to the model). Get handles to the code mappings object of the model and to the model dictionary. Store the handles in variables.

codeMapObj = coder.mapping.api.get(sfModel);
dataDictionary = coder.dictionary.open(sfModel);

Customize Generated Function Name

A function customization template is a set of specifications for generating functions. These templates are defined in the Embedded Coder Dictionary of the model, or in a shared dictionary linked to the model. To learn more about templates, see Embedded Coder Dictionary > Function Customization Templates.

The name of the function generated from the scoped Simulink function depends on the function customization template of the execution function category of the model. If the template for this category is specified as Default, then the function is generated with the default name modelName_functionName, where modelName is the model name and functionName is the function name specified in the Simulink Function block. If the specified template for the execution category is not Default, then the function naming rule of the template is used for generating the function name. The naming rule is a combination of valid C language characters and the macros $R, $N, $U, $C, and $M. For the definition of each macro, see Embedded Coder Dictionary > Function Naming Rule.

Note:

  • The names of all functions generated from scoped Simulink Function blocks located in the top model adhere to the same naming rule. Customizing names of individual such Simulink functions is not supported.

  • The individual function customization template you specify for the Simulink function itself has no effect on the generated function name.

  • This example uses the internal Embedded Coder Dictionary of the model, but you can alternatively use a shared dictionary linked to the model.

Get a handle to the Function Customization Template section of the dictionary and store it in a variable.

dictTempSect = getSection(dataDictionary,"FunctionCustomizationTemplates");

Add a new template to the Function Customization Template section (or get the handle of the existing template). Store the handle to the template object in the variable funcTemplate.

funcTemplateName = "SimFuncTemplate";
if(exist(dictTempSect,funcTemplateName))
  funcTemplate = getEntry(dictTempSect,funcTemplateName);
else
  funcTemplate = addEntry(dictTempSect,funcTemplateName);
end

Use the function set with the template handle to specify the naming rule of the template as custName_$N. The macro $N is replaced by the Simulink Function block name.

set(funcTemplate,FunctionName="custName_$N");

Use the setFunctionDefault function to specify the function customization template of the execution function category as the template SimFuncTemplate.

setFunctionDefault(codeMapObj,"Execution",FunctionCustomizationTemplate=funcTemplateName)

Customize Function Argument

The generated function uses its return value and its input arguments to represent the Argument Inport and Argument Outport blocks of the Simulink function.

Customize Argument Identifiers

With scoped Simulink functions located in the top model, customizing individual argument identifiers or arguments of individual Simulink functions is not supported. You can, however, specify the naming rule used for creating argument identifiers of all such Simulink functions in the model. Argument identifiers are defined by the naming rule specified in the configuration parameter Subsystem method arguments. You specify the naming rule as a combination of valid C language characters and the macros $I, $M, $N, and $U.

  • The macro $I is replaced with the letter u for each input argument that corresponds to an Argument Inport block, the letter y for each input argument that corresponds to an Argument Outport block, and the letter pair uy for each combined input argument, that corresponds to one Argument Inport block and one Argument Outport block. A demonstration of combined arguments is provided in the example, in the section Use Combined Inports and Outports.

  • The macro $N is replaced by the name of the Argument Inport or Argument Outport block.

  • The macro $M is replaced by an auto generated name-mangling text, required to avoid naming collisions. If there is no name collision, this macro is ignored.

For definitions of each macro, see Subsystem method arguments Settings.

Use the set_param function to specify the argument identifier naming rule as custArg_$I_$N$M:

set_param(sfModel,CustomSymbolStrFcnArg="custArg_$I_$N$M")

Use the slbuild function in the Command Window to generate code from the model. Use the evalc function to suppress the output of the slbuild function.

evalc("slbuild(sfModel)");

Store the path to the generated header file in headerFile, and use the coder.example.extractLines function to see the customized function name and identifiers in the generated file.

headerFile = "SimFuncs_ert_rtw/SimFuncs.h";
coder.example.extractLines(headerFile,"s_func(",";",true,true);
void custName_s_func(const real_T custArg_u_s_in1, const real_T custArg_u_s_in2,
                     real_T *custArg_y_s_out1, real_T *custArg_y_s_out2);

The function name and argument identifiers adhere to the naming rules you specified.

Customize Argument Specifications

With each scoped Simulink function located in the top model, you can:

  • Optionally designate one of the Argument Outport blocks to become the return value of the generated function. Each of the remaining Argument Outport blocks corresponds to a pointer input argument.

  • Specify the type qualifier of each argument that corresponds to Simulink function Argument Inport blocks — passing the argument by value, or by reference as a pointer input argument.

  • Customize the order of arguments.

To customize the arguments of the generated function, use the function setFunction this way:

setFunction(codeMapObj,scopedSfCodeMappingID,Arguments=arg_spec)

Here, codeMapObj is the code mappings object of the model, scopedSfCodeMappingID is the identifier of the Simulink function as defined in the code mappings of the model, and arg_spec is a variable with the expression that specifies how the Argument Inport and Argument Outport blocks are represented and arranged in the prototype of the generated function.

The generic structure of the argument specifications expression is "return_value_outport = (arg1, arg2, ... , argN)".

The part before the parenthesis is optional. Include it to designate the Simulink function Argument Outport block return_value_outport as the return value of the generated function. The rest of the Argument Outport blocks become pointer input arguments in the generated function. If you do not designate an Argument Outport block as the return value, all Argument Outport blocks become pointer input arguments.

Except for the Argument Outport block designated as the return value of the generated function (if there is one), each Argument Inport and Argument Outport block corresponds to one element in the comma separated list arg1, arg2, ... , argN. The element includes the name of the block, and in some cases, additional specifications:

  • An element that corresponds to an Argument Outport block must be preceded by an asterisk (*) to indicate that the generated argument is a pointer.

  • An element that corresponds to an Argument Inport block can optionally be preceded by const*. Use this prefix to specify that the generated argument is a pointer to a constant. If you do not include the const* prefix, the code generator attempts to generate the argument without a pointer declarator (passed by constant value). But, in certain conditions the code generator still generates the argument as a pointer to constant.

  • The order of the arguments in the generated function is the order of the elements in the comma separated list arg1, arg2, ... , argN.

Use the find function with the code mappings object to find the identifier of the Simulink function and store it in the variable scopedSfCodeMappingID.

funcNames = find(codeMapObj,"Functions");
scopedSfCodeMappingID = funcNames(contains(funcNames,"s_func"))
scopedSfCodeMappingID = 
"SimulinkFunction:s_func"

In this example, store these argument specifications in the variable arg_spec:

arg_spec = "s_out2 = (const* s_in2 , *s_out1 , s_in1)";

Here is a breakdown of the specifications used to customize the arguments of the generated function:

Use the function setFunction with arg_spec:

setFunction(codeMapObj,scopedSfCodeMappingID,Arguments=arg_spec)

Use the set_param function to update the model.

set_param(sfModel,SimulationCommand="Update")

Generate model code again.

evalc("slbuild(sfModel)");

Store the path to the generated source code file in srcFile, and use the coder.example.extractLines function to see the definition of the generated function.

srcFile = "SimFuncs_ert_rtw/SimFuncs.c";
coder.example.extractLines(srcFile,"s_func(","}",true,true);
real_T custName_s_func(const real_T *custArg_u_s_in2, real_T *custArg_y_s_out1,
  const real_T custArg_u_s_in1)
{
  real_T custArg_y_s_out2_0;

  /* Gain: '<S2>/sGain5' incorporates:
   *  Gain: '<S2>/sGain1'
   *  Gain: '<S2>/sGain2'
   *  Gain: '<S2>/sGain3'
   *  Gain: '<S2>/sGain4'
   *  SignalConversion generated from: '<S2>/s_in1'
   *  Sum: '<S2>/sAdd'
   */
  custArg_y_s_out2_0 = (23.0 * custArg_u_s_in1 * 12.0 + 21.0 * *custArg_u_s_in2 *
                        4.0) * 2005.0;

  /* SignalConversion generated from: '<S2>/s_out1' incorporates:
   *  Gain: '<S2>/Gain'
   */
  *custArg_y_s_out1 = -custArg_y_s_out2_0;
  return custArg_y_s_out2_0;
}

In the generated code:

  • The argument order and qualifiers adhere to your specifications.

  • During function execution, the return value is stored in the temporary variable custArg_y_s_out2_0, which corresponds to the Argument Outport block s_out2.

Use Combined Argument Inport and Argument Outport Blocks

You can customize the generated function to have combined, in-out arguments, corresponding each to one Argument Inport block and one Argument Outport block of the Simulink function. A combined argument is passed by pointer, and is used in the generated function for both input and output. To use combined arguments, specify the same identifier for both the Argument Inport block and the Argument Outport block. In the example, update the Simulink function prototype with the argument s_in_out as both the first Argument Inport block and the first Argument Outport block.

Store the new Simulink function prototype in the variable inOutPrototype, and store the paths to the Simulink function and to the Simulink function caller in the variables scopedSf and scopedSfCaller, respectively.

inOutPrototype = "[s_in_out,s_out2] = s_func(s_in_out,s_in2)";
scopedSf = "SimFuncs/scopedSimFunc";
scopedSfCaller = "SimFuncs/scopedSfCaller";

Use the set_param function to update the Simulink function and its caller with the new prototype.

set_param(scopedSf,FunctionPrototype=inOutPrototype)
set_param(scopedSfCaller,FunctionPrototype=inOutPrototype)

Use the set_param function to update the model.

set_param(sfModel,SimulationCommand="Update")

Update arg_spec to conform to the new Simulink function prototype:

arg_spec = "(* s_in_out , s_in2 , *s_out2)";

Use the function setFunction again, with the updated argument specifications:

setFunction(codeMapObj,scopedSfCodeMappingID,Arguments=arg_spec)

Generate code from the model again:

evalc("slbuild(sfModel)");

Use the function coder.example.extractLines again to see the new definition of the generated function.

coder.example.extractLines(srcFile,"s_func(","}",true,true);
void custName_s_func(real_T *custArg_uy_s_in_out, const real_T custArg_u_s_in2,
                     real_T *custArg_y_s_out2)
{
  real_T rtb_sGain5;

  /* Gain: '<S2>/sGain5' incorporates:
   *  Gain: '<S2>/sGain1'
   *  Gain: '<S2>/sGain2'
   *  Gain: '<S2>/sGain3'
   *  Gain: '<S2>/sGain4'
   *  SignalConversion generated from: '<S2>/s_in2'
   *  Sum: '<S2>/sAdd'
   */
  rtb_sGain5 = (23.0 * *custArg_uy_s_in_out * 12.0 + 21.0 * custArg_u_s_in2 *
                4.0) * 2005.0;

  /* SignalConversion generated from: '<S2>/s_in_out~' incorporates:
   *  Gain: '<S2>/Gain'
   */
  *custArg_uy_s_in_out = -rtb_sGain5;

  /* SignalConversion generated from: '<S2>/s_out2' */
  *custArg_y_s_out2 = rtb_sGain5;
}

The argument custArg_uy_s_in_out is used for both input and output in the function body. Its value is used in the calculation and is being updated.

Customize Function Qualifiers

You can customize the qualifier of the generated function to be static or extern by enabling the configuration parameter for either qualifier.

Enable the parameter for extern by using the set_param function:

set_param(sfModel,PreserveExternInFcnDecls=true)

You can enable the qualifier for static in the same way:

set_param(sfModel,PreserveStaticInFcnDecls=true)

Note:

  • If both parameters are enabled, the code generator ignores the parameter for extern.

  • Customizing individual scoped Simulink functions in the model to use the static or extern qualifier is not supported.

Use the slbuild function again to generate code from the model:

evalc("slbuild(sfModel)");

Use the coder.example.extractLines function with the generated header file to see the generated function declaration.

coder.example.extractLines(headerFile,"s_func(",";",true,true);
extern void custName_s_func(real_T *custArg_uy_s_in_out, const real_T
  custArg_u_s_in2, real_T *custArg_y_s_out2);

The function declaration has the extern qualifier.

See Also

| | | | |

Topics