Products & Services Solutions Academia Support User Community Company

Learn more about Simulink   

Example Using S-Functions to Incorporate Legacy C Code

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 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:

The following sections describe how to create S-functions for use in a Simulink simulation and with Real-Time Workshop code generation, using the previous three methods. The model sfcndemo_choosing_sfun.mdl contains blocks that use these S-functions. Copy this model and the files doubleIt.c and doubleIt.h from the directory docroot/toolbox/simulink/sfg/examples into your working directory if you plan to step through the examples.

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

The S-function wrapsfcn.c calls the legacy function doubleIt.c in its mdlOutputs method. Save the wrapsfcn.c file into your working directory, if you are planning to compile the S-function to run in the example model sfcndemo_choosing_sfun.mdl.

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 its mdlOutputs method. For example:

/* Function: mdlOutputs =======================================
 * Abstract:
 *    Calls the doubleIt.c function to multiple the input by 2.
 */
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 following mex command. Make sure that the doubleIt.c file is in your working directory.

mex wrapsfcn.c doubleIt.c

To generate code for the S-function using the Real-Time Workshop code generator, you need to write a Target Language Compiler (TLC) file. The following TLC file wrapsfcn.tlc uses the BlockTypeSetup function to declare a function prototype for doubleIt.c. The TLC file's Outputs function then tells the Real-Time Workshop 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

  /* %<Type> Block: %<Name> */

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

  %% PROVIDE THE CALLING STATEMENT FOR "doubleIt"
  %<y> = doubleIt( %<u> );

%endfunction %% Outputs

See Real-Time Workshop Target Language Compiler for more information on writing TLC files.

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 that declares the doubleIt.c function format, as follows:

extern real_T doubleIt(real_T in1);

The S-Function Builder block in sfcndemo_choosing_sfun.mdl shows how to configure the block dialog to call the legacy function doubleIt.c. In the S-Function Builder block dialog:

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

File NameDescription
builder_wrapsfcn.cThe main S-function.
builder_wrapsfcn_wrapper.cA wrapper file containing separate functions for the code entered in the Outputs, Continuous Derivatives, and Discrete Updates panes of the S-Function Builder.
builder_wrapsfcn.tlcThe S-function's TLC file.

The builder_wrapsfcn.c file follows a standard format:

The wrapper function builder_wrapsfcn_wrapper.c has three parts:

The TLC file builder_wrapsfcn.tlc generated by the S-Function Builder is similar to the previous handwritten version. The file declares the legacy function in BlockTypeSetup and calls it in the Outputs method.

%implements  builder_wrapsfcn "C"
%% Function: BlockTypeSetup ====================================
%%
%% Purpose:
%%      Set up external references for wrapper functions in the 
%%      generated code.
%%
%function BlockTypeSetup(block, system) Output
 %openfile externs
    
 extern void builder_wrapsfcn_Outputs_wrapper(const real_T *in1,
                          real_T *out1);
 %closefile externs
 %<LibCacheExtern(externs)>
 %%
%endfunction

%% Function: Outputs ===========================================
%%
%% Purpose:
%%      Code generation rules for mdlOutputs function.
%%
%function Outputs(block, system) Output
   /* S-Function "builder_wrapsfcn_wrapper" Block: %<Name> */

 %assign pu0 = LibBlockInputSignalAddr(0, "", "", 0)
 %assign py0 = LibBlockOutputSignalAddr(0, "", "", 0)
 %assign py_width = LibBlockOutputSignalWidth(0)
 %assign pu_width = LibBlockInputSignalWidth(0)
 builder_wrapsfcn_Outputs_wrapper(%<pu0>, %<py0> );

 %%
%endfunction

Using the Legacy Code Tool to Incorporate Legacy Code

The section Example of Integrating Existing C Functions into Simulink Models with the 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 to your working directory. Make sure that the doubleIt.c and doubleIt.h files are in your working directory then run the script by typing lct_wrapsfcn at the MATLAB command prompt. The script creates and compiles the S-function legacy_wrapsfcn.c and creates the TLC file legacy_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 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)
{
  /*
   * Get access to Parameter/Input/Output/DWork/size information
   */
  real_T *u1 = (real_T *) ssGetInputPortSignal(S, 0);
  real_T *y1 = (real_T *) ssGetOutputPortSignal(S, 0);

  /*
   * Call the legacy code function
   */
  *y1 = doubleIt( *u1);
}

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 supports expression folding by defining BlockInstanceSetup 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 Real-Time Workshop code generator how to inline the call to doubleIt.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( %<u1_val>);
    %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( %<u1_val>)"
      %else
        %assign errTxt = "Block output port index not supported: %<portIdx>"
%endif
    %default
      %assign errTxt = "Unsupported return type: %<retType>"
      %<LibBlockReportError(block,errTxt)>
  %endswitch
  


Related Products & Applications

Learn more about Simulink through this collection of videos, articles, technical literature and the Getting Started with Simulink Guide.

 © 1984-2009- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS