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_api
file, 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 IdAliasedThruTo
field for a data type record%assign aIdx = dt.IdAliasedThruTo
%assign aIdx = LibGetDataTypeIdAliasedThruToFromId(id)
Access the InputPortContiguous
field for the input signal record of a block%assign isContig = block.Connections.InputPortContiguous[0]
%assign isContig = LibBlockIsInputPortContiguous(0)
Check the value of the NeedFloatTime
field from a sample time record%if SampleTime[tid].NeedFloatTime == "yes"
%if LibGetSampleTimeNeedsFloatTime(tid)
Set the value for the NeedFloatTime
field 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
rtw
records.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
LookupBlockCount
to 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 %endfunction
This code creates a global record
LookupBlockCount
to 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 %endfunction
Do not write block TLC code that relies on a specific execution order for block interface functions. For example, requiring an
Outputs
function of one block to execute before theStart
function 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, theOutputs
function andStart
function 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 the
file and makes themodel
.rtw.rtw
file 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
.rtw
file that the code generator produces for the block includes the comments specified in the structuremydata
.Consider using the
mdlRTW
function 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.