Wrap User Code with TLC
wrapper
Tutorial Overview
Objective: Learn the architecture of wrapper S-functions and how to create an inlined wrapper S-function using TLC.
Open the Example:
openExample('simulinkcoder/AdviceAboutTLCTutorialsExample') cd('tlctutorial/wrapper')
Wrapper S-functions enable you to use existing C functions without fully rewriting them in the context of Simulink® S-functions. Each wrapper you provide is an S-function “shell” that merely calls one or more existing, external functions. This tutorial explains and illustrates wrappers as follows:
Why Wrap User Code? — Reason for building TLC wrapper functions
Getting Started — Set up the wrapper exercise
Generate Code Without a Wrapper — How the code generator handles external functions by default
Generate Code Using a Wrapper — Bypass the API overhead
Why Wrap User Code?
Many Simulink users want to build models incorporating algorithms that they have already coded, implemented, and tested in a high-level language. Typically, such code is brought into Simulink as S-functions. To generate an external application that integrates user code, you can take several approaches:
You can construct an S-function from user code that hooks it to the Simulink generic API. This is the simplest approach, but sacrifices efficiency for standalone applications.
You can inline the S-function, reimplementing it as a TLC file. This improves efficiency, but takes time and effort, can introduce errors into working code, and leads to two sets of code to maintain for each algorithm, unless you use the Legacy Code Tool (see Import Calls to External Code into Generated Code with Legacy Code Tool).
You can inline the S-function via a TLC wrapper function. By doing so, you need to create only a small amount of TLC code, and the algorithm can remain coded in its existing form.
The next figure illustrates how S-function wrappers operate.
Wrapping a function eliminates the need to recode it, requiring only a bit of extra TLC code to integrate it. Wrappers also enable object modules or libraries to be used in S-functions. This may be the only way to deploy functions for which source code is unavailable, and also allows users to distribute models to others without divulging implementation details that may be proprietary.
For example, you might have an existing object file compiled for a processor on which Simulink does not run. You can write a dummy C S-function and use a TLC wrapper that calls the external function, despite not having its source code. You could similarly access functions in a library of algorithms optimized for the target processor. Accomplishing this requires making changes to a template makefile, or otherwise providing a means to link against the library.
Note
Object files that lack source code and are created with Microsoft® Visual C and Microsoft Visual C++® Compiler (MSVC) work only with MSVC.
The only restriction on S-function wrappers is for the number of block inputs and outputs match number of inputs and outputs of the wrapped external function. Wrapper code may include computations, but usually these are limited to transforming values (for example, scaling or reformatting) passed to and from the wrapped external functions.
Getting Started
In the example folder, the “external function” is found in the file
my_alg.c
. You are also provided with a C S-function called
wrapsfcn.c
that integrates my_alg.c
into Simulink. Set up the exercise as follows:
Make
tlctutorial/wrapper
your current folder.In MATLAB®, open the model
externalcode
from your working folder. The block diagram looks like this:Activate the Scope block by double-clicking it.
Run the model (from the Simulation tab, or type Ctrl+T). You will get an error telling you that
wrapsfcn
does not exist. Can you figure out why?The error occurs because a
mex
file does not exist forwrapsfcn
. To rectify this, in the MATLAB Command Window typemex wrapsfcn.c
Note
An error might occur if you have not previously run
mex -setup
.Run the simulation again with the S-function present.
The S-Function block multiplies its input by two. Looking at the
Scope block, you see a sine wave that oscillates between -2.0 and 2.0. The
variable yout
that is created in your MATLAB workspace steps through these values.
In the remainder of the exercise, you build and run a standalone version of the model,
then write some TLC code that allows the code generator to build a standalone executable
that calls the S-function my_alg.c
directly.
Generate Code Without a Wrapper
Before creating a wrapper, generate code that uses the Simulink generic API. The first step is to build a standalone model.
On the C Code tab, click Build.
The code generator creates the standalone program in your working folder and places the source and object files in your build folder. The file will be called
externalcode.exe
on Microsoft Windows® platforms orexternalcode
on UNIX® platforms.As it generates the program, the code generator reports its progress in the MATLAB Command Window. The final lines are:
### Created executable: externalcode.exe ### Successful completion of build procedure for model: externalcode
Run the standalone program to see that it behaves the same as the Simulink version. There should not be differences.
!externalcode ** starting the model ** ** created externalcode.mat **
Notice this line in
wrapsfcn.c
:#include "my_alg.c"
This pulls in the external function. That function consists entirely of
/* * Copyright 1994-2002 The MathWorks, Inc. */ double my_alg(double u) { return(u * 2.0); }
Inspect the
mdlOutputs()
function of the code inwrapsfcn.c
to see how the external function is called.static void mdlOutputs(SimStruct *S, int tid) { int_T i; InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0); int_T width = ssGetOutputPortWidth(S,0); *y = my_alg(*uPtrs[0]); }
Tip
For UNIX platforms, run the executable program in the Command Window with the syntax
!./
executable_name
. If
preferred, run the executable program from an OS shell with the syntax
./
executable_name
. For more
information, see Run External Commands, Scripts, and Programs.
Generally, functions to be wrapped are either included in the wrapper, as above, or, when object modules are being wrapped, resolved at link time.
Generate Code Using a Wrapper
To create a wrapper for the external function my_alg.c
, you need to
construct a TLC file that embodies its calling function, wrapsfcn.c
. The
TLC file must generate C code that provides:
A function prototype for the external function that returns a double, and passes the input
double u
.A function call to
my_alg()
in the outputs section of the code.
To create a wrapper for my_alg()
, do the following:
Open the file
change_wrapsfcn.tlc
in your editor, and add lines of code where comments indicate to create a workable wrapper.Save the edited file as
wrapsfcn.tlc
. It must have the same name as the S-function block that uses it or TLC is not called to inline code.In MATLAB, open the model
externalcode
from your working folder. Activate the Scope block by double-clicking it, and run the model (from the Simulation tab, or type Ctrl+T). This gives you a baseline result.Inform Simulink that your code has an external reference to be resolved. To update the model’s parameters, in the MATLAB Command Window, do one of the following:
Type
set_param('externalcode/S-Function','SFunctionModules','my_alg')
In the S-Function block parameters dialog box, in the
S-function modules
field, specify'my_alg'
.
Create the standalone application, by entering one of the following commands in the Command Window:
slbuild('my_alg','ForceTopModelBuild',true)
slbuild('my_alg', 'StandaloneCoderTarget','ForceTopModelBuild',true)
These commands force the code generator to rebuild the top model, which is required when you make changes associated with external or custom code.
Alternatively, you can force regeneration of top model code by deleting folders in the Code generation folder, such as
slprj
or the generated model code folder.For more information, see Control Regeneration of Top Model Code.
Run the new standalone application and verify that it yields identical results as in the scope window.
!externalcode
If you had problems building the application:
Find the error messages and try to determine what files are at fault, paying attention to which step (code generation, compiling, linking) failed.
Be sure you issued the
set_param()
command as specified above.Chances are that problems can be traced to your TLC file. It may be helpful to use TLC debugger to step through
wrapsfcn.tlc
.As a last resort, look at
wrapsfcn.tlc
in thesolutions/tlc_solution
folder, also listed below:%% File : wrapsfcn.tlc %% Abstract: %% Example tlc file for S-function wrapsfcn.c %% %% Copyright 1994-2002 The MathWorks, Inc. %% %% %implements "wrapsfcn" "C"
%% Function: BlockTypeSetup ================================ %% Abstract: %% Create function prototype in model.h as: %% "extern double my_alg(double u);" %% %function BlockTypeSetup(block, system) void %openfile buffer %% ASSIGNMENT: PROVIDE A LINE OF CODE AS A FUNCTION PROTOTYPE %% FOR "my_alg" AS DESCRIBED IN THE WRAPPER TLC ASSIGNMENT extern double my_alg(double u); %closefile buffer %<LibCacheFunctionPrototype(buffer)> %endfunction %% BlockTypeSetup
%% Function: Outputs ======================================= %% Abstract: %% y = my_alg( u ); %% %function Outputs(block, system) Output /* %<Type> Block: %<Name> */ %assign u = LibBlockInputSignal(0, "", "", 0) %assign y = LibBlockOutputSignal(0, "", "", 0) %% PROVIDE THE CALLING STATEMENT FOR "wrapfcn" %<y> = my_alg( %<u> ); %endfunction %% Outputs
Look at the highlighted lines. Did you declare
my_alg()
asextern double
? Did you callmy_alg()
with the expected input and output? Fix mistakes and rebuild the model.