S-Functions Incorporate Legacy C Code - MATLAB & Simulink (original) (raw)

Setup Working Environment for Incorporating Legacy C Code

This example opens up a directory containing files required for this topic.

Open this example to access the following files.

Overview

C MEX S-functions allow you to call existing C code within your Simulink® models. For example, consider the simple C function [doubleIt.c](https://mdsite.deno.dev/matlab:open%28'doubleIt.c'%29;) that outputs a value two times the value of the function input.

double doubleIt(double u) { return(u * 2.0); }

You can create an S-function that calls doubleIt.c by either:

You can also call external C code from a Simulink model using a MATLAB Function block. For more information seeIntegrate C Code by Using the MATLAB Function Block.

The following sections describe how to create S-functions for use in a Simulink simulation and with Simulink Coder™ code generation, using the previous three methods. The example model contains blocks that use these S-functions. If you plan to create the model, copy the files [doubleIt.c](https://mdsite.deno.dev/matlab:open%28'doubleIt.c'%29;) and[doubleIt.h](https://mdsite.deno.dev/matlab:open%28'doubleIt.h'%29;) into your working folder.

Using a Hand-Written S-Function to Incorporate Legacy Code

The S-function [wrapsfcn.c](https://mdsite.deno.dev/matlab:open%28'wrapsfcn.c'%29;) calls the legacy function doubleIt.c in its mdlOutputs method. Save the wrapsfcn.c file into your working folder, if you are planning to create the example model.

To incorporate the legacy code into the S-function, wrapsfcn.c begins by declaring doubleIt.c with the following line:

extern real_T doubleIt(real_T u);

Once declared, the S-function can use doubleIt.c in itsmdlOutputs method. For example:

/* Function: mdlOutputs =======================================

*/ static void mdlOutputs(SimStruct *S, int tid){
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0); real_T *y = ssGetOutputPortRealSignal(S,0);

 *y = doubleIt(*uPtrs[0]);

}

To compile the wrapsfcn.c S-function, run the followingmex command. Make sure that the doubleIt.c file is in your working folder.

mex wrapsfcn.c doubleIt.c

To generate code for the S-function using the Simulink Coder code generator, you need to write a Target Language Compiler (TLC) file. The following TLC file [wrapsfcn.tlc](https://mdsite.deno.dev/matlab:open%28'wrapsfcn.tlc'%29;) uses the BlockTypeSetup function to declare a function prototype for doubleIt.c. The TLC file'sOutputs function then tells the Simulink Coder code generator how to inline the call to doubleIt.c. For example:

%implements "wrapsfcn" "C" %% File : wrapsfcn.tlc %% Abstract: %% Example tlc file for S-function wrapsfcn.c %%

%% Function: BlockTypeSetup ================================ %% Abstract: %% Create function prototype in model.h as: %% "extern double doubleIt(double u);" %%

%function BlockTypeSetup(block, system) void %openfile buffer

%% PROVIDE ONE LINE OF CODE AS A FUNCTION PROTOTYPE extern double doubleIt(double u);

%closefile buffer %<LibCacheFunctionPrototype(buffer)> %%endfunction %% BlockTypeSetup

%% Function: Outputs ======================================= %% Abstract: %% CALL LEGACY FUNCTION: y = doubleIt( u ); %%

%function Outputs(block, system) Output

/* % Block: % */

%assign u = LibBlockInputSignal(0, "", "", 0) %assign y = LibBlockOutputSignal(0, "", "", 0)

%% PROVIDE THE CALLING STATEMENT FOR "doubleIt" % = doubleIt( % );

%endfunction %% Outputs

For more information on the TLC, see Target Language Compiler Basics (Simulink Coder).

Using the S-Function Builder to Incorporate Legacy Code

The S-Function Builder automates the creation of S-functions and TLC files that incorporate legacy code. For this example, in addition to doubleIt.c, you need the header file [doubleIt.h](https://mdsite.deno.dev/matlab:open%28'doubleIt.h'%29;) that declares the doubleIt.c function format, as follows:

extern real_T doubleIt(real_T in1);

Use the S-Function Builder block to configure the block dialog box to call the legacy function doubleIt.c. In the S-Function Builder block dialog box:

When you click Build, the S-Function Builder generates three files.

File Name Description
builder_wrapsfcn.c The main S-function.
builder_wrapsfcn_wrapper.c A wrapper file containing separate functions for the code entered in theOutputs, Continuous Derivatives, andDiscrete Updates panes of the S-Function Builder.
builder_wrapsfcn.tlc The S-function's TLC file.

The [builder_wrapsfcn.c](https://mdsite.deno.dev/matlab:open%28'builder%5Fwrapsfcn.c'%29;) file follows a standard format:

#define NUM_INPUTS 1
/* Input Port 0 */
#define IN_PORT_0_NAME in1
#define INPUT_0_WIDTH 1
#define INPUT_DIMS_0_COL 1
#define INPUT_0_DTYPE real_T
#define INPUT_0_COMPLEX COMPLEX_NO
#define IN_0_FRAME_BASED FRAME_NO
#define IN_0_DIMS 1-D
#define INPUT_0_FEEDTHROUGH 1

The wrapper function [builder_wrapsfcn_wrapper.c](https://mdsite.deno.dev/matlab:open%28'builder%5Fwrapsfcn%5Fwrapper.c'%29;) has three parts:

#if defined(MATLAB_MEX_FILE)
#include "tmwtypes.h"
#include "simstruc_types.h"
#else
#include "rtwtypes.h"
#endif
/* %%%-SFUNWIZ_wrapper_includes_Changes_BEGIN --- EDIT HERE TO _END /
#include <math.h>
#include <doubleIt.h>
/
%%%-SFUNWIZ_wrapper_includes_Changes_END --- EDIT HERE TO _BEGIN */

{
/* %%%-SFUNWIZ_wrapper_Outputs_Changes_BEGIN --- EDIT HERE TO _END /
/
Call function that multiplies the input by 2 */
*out1 = doubleIt(in1);
/
%%%-SFUNWIZ_wrapper_Outputs_Changes_END --- EDIT HERE TO _BEGIN */
}

Note

Compared to a handwritten S-function, the S-Function Builder places the call to the legacy C function down an additional level through the wrapper filebuilder_wrapsfcn_wrapper.c.

The TLC file [builder_wrapsfcn.tlc](https://mdsite.deno.dev/matlab:open%28'builder%5Fwrapsfcn.tlc'%29;) generated by the S-Function Builder is similar to the previous handwritten version. The file declares the legacy function inBlockTypeSetup and calls it in the Outputs method.

%% File : builder_wrapsfun.tlc %% %% Description: %% Simulink Coder wrapper functions interface generated for %% S-function "builder_wrapsfun.c". %% %implements builder_wrapsfun "C" %% Function: BlockTypeSetup =================================================== %% %% Purpose: %% Set up external references for wrapper functions in the %% generated code. %% %function BlockTypeSetup(block, system) Output %assign realType = LibGetDataTypeNameFromId(::CompiledModel.tSS_DOUBLE)

%if IsModelReferenceSimTarget() || CodeFormat == "S-Function" || ::isRAccel %assign hFileName = "builder_wrapsfun_accel_wrapper" %assign hFileNameMacro = FEVAL("upper", hFileName) %openfile hFile = "%.h" %selectfile hFile #ifndef _%H #define _%H

#ifdef MATLAB_MEX_FILE
#include "tmwtypes.h"
#else
#include "rtwtypes.h"
#endif
#ifdef __cplusplus
#define SFB_EXTERN_C extern "C"
#else
#define SFB_EXTERN_C extern
#endif
SFB_EXTERN_C void builder_wrapsfun_Outputs_wrapper_accel(const %<realType> *in1,
        %<realType> *out1);
#undef SFB_EXTERN_C
#endif
%closefile hFile

%assign cFileName = "builder_wrapsfun_accel_wrapper"
%openfile cFile = "%<cFileName>.c"
%selectfile cFile
#include <string.h>
#ifdef MATLAB_MEX_FILE
#include "tmwtypes.h"
#else
#include "rtwtypes.h"
#endif
#include "%<hFileName>.h"


extern void builder_wrapsfun_Start_wrapper(void);
extern void builder_wrapsfun_Outputs_wrapper(const %<realType> *in1,
        %<realType> *out1);
extern void builder_wrapsfun_Terminate_wrapper(void);
void builder_wrapsfun_Outputs_wrapper_accel(const %<realType> *in1,
        %<realType> *out1){
builder_wrapsfun_Outputs_wrapper(in1,
        out1);
}

%closefile cFile

%<LibAddToCommonIncludes("%<hFileName>.h")>

%else %openfile externs

#ifdef __cplusplus
#define SFB_EXTERN_C extern "C"
#else
#define SFB_EXTERN_C extern
#endif

SFB_EXTERN_C void builder_wrapsfun_Start_wrapper(void);

SFB_EXTERN_C void builder_wrapsfun_Outputs_wrapper(const %<realType> *in1,
        %<realType> *out1);

SFB_EXTERN_C void builder_wrapsfun_Terminate_wrapper(void);

#undef SFB_EXTERN_C

%closefile externs %<LibCacheExtern(externs)>

%endif %% %endfunction

%% Function: Outputs ========================================================== %% %% Purpose: %% Code generation rules for mdlOutputs function. %% %function Outputs(block, system) Output %% %assign pu0 = LibBlockInputSignalAddr(0, "", "", 0) %assign py0 = LibBlockOutputSignalAddr(0, "", "", 0)

%if IsModelReferenceSimTarget() || CodeFormat == "S-Function" || ::isRAccel builder_wrapsfun_Outputs_wrapper_accel(%, %); %else builder_wrapsfun_Outputs_wrapper(%, %); %endif

%% %endfunction

%% [EOF] builder_wrapsfun.tlc

Using the Legacy Code Tool to Incorporate Legacy Code

The section Integrate C Functions into Simulink Models with Legacy Code Tool in “Writing S-Functions in C” shows how to use the Legacy Code Tool to create an S-function that incorporates doubleIt.c. For a script that performs the steps in that example, copy the file [lct_wrapsfcn.m](https://mdsite.deno.dev/matlab:open%28'lct%5Fwrapsfcn.m'%29;) to your working folder. Make sure that the [doubleIt.c](https://mdsite.deno.dev/matlab:open%28'doubleIt.c'%29;) and [doubleIt.h](https://mdsite.deno.dev/matlab:open%28'doubleIt.h'%29;) files are in your working folder then run the script by typing lct_wrapsfcn at the MATLAB command prompt. The script creates and compiles the S-functionlegacy_wrapsfcn.c and creates the TLC filelegacy_wrapsfcn.tlc via the following commands.

% Create the data structure def = legacy_code('initialize');

% Populate the data struture def.SourceFiles = {'doubleIt.c'}; def.HeaderFiles = {'doubleIt.h'}; def.SFunctionName = 'legacy_wrapsfcn'; def.OutputFcnSpec = 'double y1 = doubleIt(double u1)'; def.SampleTime = [-1,0];

% Generate the S-function legacy_code('sfcn_cmex_generate', def);

% Compile the MEX-file legacy_code('compile', def);

% Generate a TLC-file legacy_code('sfcn_tlc_generate', def);

The S-function [legacy_wrapsfcn.c](https://mdsite.deno.dev/matlab:open%28'legacy%5Fwrapsfcn.c'%29;) generated by the Legacy Code Tool begins by including the doubleIt.h header file. The mdlOutputs method then directly calls the doubleIt.c function, as follows:

static void mdlOutputs(SimStruct S, int_T tid) { /

/*

The S-function generated by the Legacy Code Tool differs from the S-function generated by the S-Function Builder as follows:

The TLC file [legacy_wrapsfcn.tlc](https://mdsite.deno.dev/matlab:open%28'legacy%5Fwrapsfcn.tlc'%29;) supports expression folding by definingBlockInstanceSetup and BlockOutputSignal functions. The TLC file also contains a BlockTypeSetup function to declare a function prototype for doubleIt.c and an Outputs function to tell the Simulink Coder code generator how to inline the call todoubleIt.c.:

%% Function: BlockTypeSetup =============================================== %% %function BlockTypeSetup(block, system) void %% %% The Target Language must be C %if ::GenCPP==1 %<LibReportFatalError("This S-Function generated by the Legacy Code Tool must be only used with the C Target Language")> %endif %<LibAddToCommonIncludes("doubleIt.h")> %<LibAddToModelSources("doubleIt")> %% %endfunction

%% Function: BlockInstanceSetup =========================================== %% %function BlockInstanceSetup(block, system) void %% %<LibBlockSetIsExpressionCompliant(block)> %% %endfunction

%% Function: Outputs ====================================================== %% %function Outputs(block, system) Output %% %if !LibBlockOutputSignalIsExpr(0) %assign u1_val = LibBlockInputSignal(0, "", "", 0) %assign y1_val = LibBlockOutputSignal(0, "", "", 0) %% %<y1_val = doubleIt( %); %endif %% %endfunction

%% Function: BlockOutputSignal ============================================ %% %function BlockOutputSignal(block,system,portIdx,ucv,lcv,idx,retType) void %% %assign u1_val = LibBlockInputSignal(0, "", "", 0) %assign y1_val = LibBlockOutputSignal(0, "", "", 0) %% %switch retType %case "Signal" %if portIdx == 0 %return "doubleIt( %)" %else %assign errTxt = "Block output port index not supported: %" %endif %default %assign errTxt = "Unsupported return type: %" %<LibBlockReportError(block,errTxt)> %endswitch %% %endfunction