Write Fully Inlined S-Functions
A fully inlined S-function builds your algorithm (block) into generated code that you cannot distinguish from a built-in block. Typically, a fully inlined S-function requires you to implement your algorithm twice: once for the Simulink model (C/C++ MEX S-function) and once for code generation (TLC file).
Using the example in Write Wrapper S-Function and TLC Files, you can eliminate the
call to my_alg entirely by specifying the explicit code (that is,
2.0 * u) in wrapsfcn.tlc. While this can
improve performance, if you are working with a large
amount of C/C++ code, the task can be lengthy. You also have to maintain your algorithm
in two places, the C/C++ S-function itself and the corresponding TLC file. Consider
whether the performance gains might outweigh the
disadvantages. To inline the algorithm used in this example, in the
Outputs section of your wrapsfcn.tlc file,
instead of writing:
%<y> = my_alg(%<u>);
Use:
%<y> = 2.0 * %<u>;
This code is the code produced in mdlOutputs:
void mdlOutputs(int_T tid)
{
/* Sin Block: <Root>/Sin */
rtB.Sin = rtP.Sin.Amplitude *
sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase);
/* S-Function Block: <Root>/S-Function */
rtB.S_Function = 2.0 * rtB.Sin; /* Explicit embedding of algorithm */
/* Outport Block: <Root>/Out */
rtY.Out = rtB.S_Function;
}The Target Language Compiler replaces the call to my_alg with the
algorithm itself.
Multiport S-Function
A more advanced multiport inlined S-function example is sfun_multiport.c and sfun_multiport.tlc. This S-function illustrates how to
create a fully inlined TLC file for an S-function that contains multiple
ports.
Guidelines for Writing Inlined S-Functions
To generate efficient code for S-Function blocks and to prevent unexpected behavior, adhere to these guidelines when writing inlined S-Functions.
Implementing the Block TLC Interface
Enable the enhanced TLC block interface for all blocks and target TLC files to integrate the S-Function block code more effectively into the generated code for the model. To understand how the enhanced TLC interface optimizes S-Function block code integration, see Enhanced TLC Block Interface.
When accessing model-related data such as configset records, block input records, block output records, block parameter records and data type records, always use the documented public functions available in the
directory. Avoid using undocumented functions or directly accessing fields or records from thematlabroot/rtw/c/tlc/public_apifile, as this can lead to unexpected results. For a comprehensive list of documented functions and their effective usage, see Target Language Compiler Library Functions Overview.model.rtwThe following examples illustrate how to interact with model-related data using documented public functions, including accessing fields within datatype and block input signal records, checking and setting sample time fields, and determining if an input signal of a block is complex.
Use Case Direct Access (Not Recommended) Recommended Access Access the IdAliasedThruTofield for a data type record%assign aIdx = dt.IdAliasedThruTo
%assign aIdx = LibGetDataTypeIdAliasedThruToFromId(id)
Access the InputPortContiguousfield for the input signal record of a block%assign isContig = block.Connections.InputPortContiguous[0]
%assign isContig = LibBlockIsInputPortContiguous(0)
Check the value of the NeedFloatTimefield from a sample time record%if SampleTime[tid].NeedFloatTime == "yes"
%if LibGetSampleTimeNeedsFloatTime(tid)
Set the value for the NeedFloatTimefield of a sample time%assign ::CompiledModel.SampleTime[tid].NeedFloatTime = "yes"
%<LibSetSampleTimeNeedsFloatTime(tid, TLC_TRUE)>
Check if the input signal of a block is complex %assign ipRecord = FcnGetInputPortRecord(0) %% FcnGetInputPortRecord is not documented %assign dataRecord = SLibGetSourceRecord(ipRecord, 0) %% SLibGetSourceRecord is not documented %assign isComplex = LibCGTypeIsComplex(dataRecord.CGTypeIdx)
%assign isComplex = LibBlockInputSignalIsComplex(0) %% LibBlockInputSignalIsComplex is a documented function
Using TLC library functions not only provides a stable interface for effectively managing model behavior and structure but also helps prevent issues that may arise from future updates to
rtwrecords.Avoid modifying existing records in
such asmodel.rtwBlock,System, orCompiledModel, as they are read-only, and changes to them can result in data loss during the code generation process. However, if data needs to be created during the block TLC interface execution for later use in the target TLC phase, create global records. These records persist throughout the entire Simulink Coder TLC execution, remaining accessible and reliable for the target TLC phase unless explicitly modified by the user.This example shows how to use a global record to effectively manage block instance counts in TLC for logging without modifying the compiled model file.
Modifying Existing Records (Not Recommended) Creating Global Records (Recommended) This code directly modifies the compiled model by updating
LookupBlockCountto track instances of the custom S-Function blocksfcn_custom_lookup.%implements "sfcn_custom_lookup" "C" %function BlockTypeSetup(block, system) void %addtorecord ::CompiledModel LookupBlockCount 0 %endfunction %function BlockInstanceSetup(block, system) void %<LibEnableBlockFcnOptimizations(block)> %assign ::CompiledModel.LookupBlockCount = ::CompiledModel.LookupBlockCount + 1 %endfunctionThis code creates a global record
LookupBlockCountto track block instances without directly modifying the compiled model.%implements "sfcn_custom_lookup" "C" %function BlockTypeSetup(block, system) void %assign ::LookupBlockCount = 0 %endfunction %function BlockInstanceSetup(block, system) void %<LibEnableBlockFcnOptimizations(block)> %assign ::LookupBlockCount = ::LookupBlockCount + 1 %endfunctionDo not write block TLC code that relies on a specific execution order for block interface functions. For example, requiring an
Outputsfunction of one block to execute before theStartfunction of another block creates a dependency. Similarly, within the same block, all functions must operate independently and not depend on the execution order of each other. For example, theOutputsfunction andStartfunction of a block should not rely on the sequence in which they are executed. Avoid such dependencies, as they can lead to undefined behavior during code generation. The underlying infrastructure is subject to change with updates based on Simulink® requirements.
Using RTWdata and mdlRTW
Consider using the block property
RTWdata(see S-Function RTWdata). This property is a structure of character vectors that you can associate with a block. The code generator saves the structure with the model in thefile and makes themodel.rtw.rtwfile more readable. For example in the MATLAB Command Window, suppose you enter these commands:mydata.field1 = 'information for field1'; mydata.field2 = 'information for field2'; set_param(sfun_block, 'RTWdata', mydata);
The
.rtwfile that the code generator produces for the block includes the comments specified in the structuremydata.Consider using the
mdlRTWfunction to inline your C MEX S-function in the generated code for:Renaming tunable parameters in the generated code.
Introducing non-tunable parameters into a TLC file.