Write Fully Inlined S-Functions - MATLAB & Simulink (original) (raw)
Main Content
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 theOutputs
section of your wrapsfcn.tlc
file, instead of writing:
Use:
This code is the code produced in mdlOutputs
:
void mdlOutputs(int_T tid) { /* Sin Block: /Sin */ rtB.Sin = rtP.Sin.Amplitude * sin(rtP.Sin.Frequency * ssGetT(rtS) + rtP.Sin.Phase);
/* S-Function Block: /S-Function */ rtB.S_Function = 2.0 * rtB.Sin; /* Explicit embedding of algorithm */
/* Outport Block: /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](https://mdsite.deno.dev/matlab:sfunddg%5Fcb%5Fedit%28'sfun%5Fmultiport'%29;)
and [sfun_multiport.tlc](https://mdsite.deno.dev/matlab:edit%28fullfile%28matlabroot,'toolbox','simulink','sfuntemplates','tlc%5Fc','sfun%5Fmultiport.tlc'%29%29;)
. 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
_`matlabroot`_/rtw/c/tlc/public_api
directory. Avoid using undocumented functions or directly accessing fields or records from the_`model`_.rtw
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.
The 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 theNeedFloatTime field from a sample time record %if SampleTime[tid].NeedFloatTime == "yes" %if LibGetSampleTimeNeedsFloatTime(tid) Set the value for theNeedFloatTime 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
_`model`_.rtw
such asBlock
,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 updatingLookupBlockCount 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 recordLookupBlockCount 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
(seeS-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_`model`_.rtw
file and makes the.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.