You can integrate new or existing C code into Simulink® using the C Caller block. To create custom blocks in your Simulink models, the C Caller block allows you to call external C functions specified in external source code and libraries. The advantages of the C Caller block are:
Automated integration of simple C functions
Integration with Simulink Coverage™, Simulink Test™, and Simulink Design Verifier™
Integration with Simulink Coder™
The C Caller block allows you to bring C algorithms into Simulink. To model dynamic systems, use the S-Function Builder instead. Next steps describe the workflow to integrate C code into Simulink using the C Caller block.
Specify your external source code file that contains your C functions.
From Simulink toolstrip, open the Configuration Parameters.
In the left pane, select Simulation Target.
To enable code parsing by the C Caller block, ensure that the Import custom code box is selected.
The directories and file paths can be absolute and relative file paths to model directories or to the current working directory. See Specify Relative Paths to Your Custom Code (Stateflow).
Select Header file and enter the name of your header file
with the #include
tag.
Under Additional build information, select Include directories, and enter the folders where additional build information, such as header files, are stored.
Select Source files and enter the path and the name of the source file. If the model and the source files are in different directories, enter the directory that contains the source file before the file name.
Note
If a function is declared in the header file but not implemented in the source code, an empty stub function is automatically generated to simulate and compile the model.
Note
To use a C Caller block in a For Each subsystem or with continuous sample time, or to optimize the use of the block in conditional input branch execution, the custom code function called by the block must be deterministic, that is, always producing the same outputs for the same inputs. Identify which custom code functions are deterministic by using the Deterministic functions and Specify by function parameters in the Simulation target pane. For a conditional input branch execution example, see Use C Caller Block with Conditional Execution.
You can specify the order of how your matrix data is stored in Simulink. Matrix data passed to and from your C functions is converted to the default function array layout you specify. If the function array layout is not specified, the matrix data is passed through the C Caller in the same order of your Simulink data, and computational errors may occur due to row-column major disarrangement. Ensure that you follow the same default function array layout for all Simulink data.
Column-Major — The C Caller block handles Simulink data in column-major order. Suppose that you have a 3-by-3 matrix. In the C Caller block, this matrix is stored in this sequence: first column, second column, and third column.
Row-Major — The C Caller block handles Simulink data in row-major order. Suppose that you have a 3-by-3 matrix. In the C Caller block, this matrix is stored in this sequence: first row, second row, and third row.
Any — Array data can be stored both in column-major and row-major order in the C Caller block. As a result, you can generate code both in column-major and row-major settings.
Not specified — Array data can be stored in both column-major and row-major order. Compared to Any setting, you can only generate code in column-major setting.
To learn more about the row-major and column-major array layouts in Simulink, see Default function array layout.
Select an array layout option under Default Array Function Layout.
If you need to apply a specific array layout to some of the functions in your code, click Specify by Function to select these functions.
Click Apply to accept your changes.
Click OK to close the Configuration Parameters.
You can start your custom C code integration into Simulink by typing C Caller in the Simulink canvas. Alternatively, drag a C Caller block from the Library Browser > User-Defined Functions. Double-click the block to open the Block Parameters dialog box to see the names of your functions and port specifications.
Click on the Refresh custom code to import your source code and its dependencies.
Your C functions are displayed under Function Name. If you can't see your full list of functions, click on the to reimport your source code.
To view function definitions in the source file, click the . The source code for the selected function is displayed in the MATLAB® Editor. If the source code is not available, the function declaration in the header file is displayed.
To change source files and their dependencies, or to define and select function array layouts, click the Custom code settings to open the Simulation Target tab in Configuration Parameters.
You can map C function arguments from your source code to Simulink ports using the Port specification table in the C
Caller block and by creating a FunctionPortSpecification
object through the command line . In your source code,
the header file includes the C function arguments to be connected to Simulink
ports.
extern void mean_filter(const unsigned char* src, unsigned char* dst, unsigned int width, unsigned int height, unsigned int filterSize);
Port specification shows the details of your arguments and how they connect to your C Caller block in Simulink.
Name — Specifies the name of input and output arguments. Name is the function argument or parameter name as defined in your C functions from source code. This column is for reference purposes only.
Scope — Specifies how C function arguments map to the Simulink scope. Your arguments have default scopes depending on the function definition and you can change the scopes depending your function definition in the source code.
Simulink Scope | Scope to Block Mapping |
---|---|
Input | Block input port |
Output | Block output port |
InputOutput | Block input and output port |
Global | Global variable used by the block |
Parameter | Block tunable parameter |
Constant | Constant value |
For an argument passed by pointer, when you have a constant qualifier definition such as
const double *u
, the argument can only be an input or a parameter. When
there is no constant qualifier, the argument is an InputOutput
by
default, and you can change it to an Input
, Output
, or
Parameter
scope. In the case of an Input
or
Parameter
scope, ensure that the C function does not modify the memory
pointed by the pointer. If the argument is of an Output
scope, every
element pointed to by this pointer should be reassigned in every call for the
function.
C Argument | Simulink Scope |
---|---|
Function return |
|
| Input , Parameter ,
Constant |
| InputOutput (default),
Output , Input ,
Parameter |
|
|
Use the InputOutput
port to map an input passed by a pointer
in your C functions. Ports created using an InputOutput
port have
the same name for input and output ports. InputOutput
ports
enables reuse of buffer for input and output ports. This may optimize the memory use
depending on the signal size and the block layout.
To map C function arguments to an InputOutput
port, define
the variable as a pointer in your function definitions.
extern void mean_filter(unsigned char* src, unsigned int width, unsigned int height, unsigned int filterSize);
Then, select the port specification to the InputOutput
scope
in the Port Specification table, and assign the resulting
function output to the input variable in the custom function.
You can use global variables in your custom code, mapping them to the appropriate
Simulink scope. To enable the use of global variables in your model, select Enable global variables as function interfaces from Model Settings > Configuration Parameters > Simulation Target. You can map the global variables to an Input
,
Output
, InputOutput
or
Global
scope on the C Caller block. The
availability of the these scopes depend on the use of the global variable in your custom
code.
A Global
scope enables you to transfer data between custom
code and the C Caller block and lets you use the global variable during
calculations on the block. Values transferred using Global
scope
are not visible on the block interface. This table shows example code snippets and their
default and available ports.
Example Code | Simulink Scope |
---|---|
double data; void foo(void) { int temp = data; } | Global variable data only reads the variable
|
double data; void bar(void) { data = 0; } | Data is written to a global variable. Available scopes are:
|
double data; void foo2(void) { data = data + 1; } | Data is both read and written on a global variable. Available scopes are:
|
Label — Indicates the label for the corresponding argument in a Simulink block. By default, your argument label is the same as the argument name, unless you change it.
Simulink Scope | Simulink Port Label |
---|---|
| Port name |
inputoutput | Port name in both input and output port |
Global | Port name and global variable name |
| Parameter name |
| Expression for the constant value. size expressions using
input argument names, for example |
Type — Demonstrates the match between the Simulink data type and the C function argument data type.
C Argument Data Type | Simulink Data Type |
---|---|
signed char | int8 |
unsigned char | uint8 |
char | int8 or uint8, depending on the compiler |
int* | int32 |
unsigned int* | uint32 |
short * | int16 |
long * | int32 or fixdt(1,64,0), depending on the operating system |
float | single |
double | double |
int8_t* | int8 |
uint8_t* | int8 |
int16_t* | int16 |
uint16_t* | uint16 |
int32_t* | int32 |
uint32_t* | uint32 |
typedef struct {…} AStruct** | Bus: AStruct |
typedef enum {..} AnEnum** | Enum: AnEnum |
* If the C Caller takes an integer type, for example, int16_t, you can modify it to a fixed-point type with matching base type, for example to fixdt(1, 16, 3). ** The C Caller sync button prompts you to import struct or enum types used by a C function as Simulink bus and enumeration types. |
Size — Specifies the data dimensions in the argument.
C Argument Dimensions | Simulink Port Dimensions |
---|---|
| scalar (1) |
| inherited (-1) (default) If the argument is for an output port, the size should be specified. The size of an output port cannot be inherited. |
| inherited (-1) (default) If the argument is for an
For global variables, size is scalar (1). |
| Size is [2, 3]. |
FunctionPortSpecification
Object and Edit C Caller Block PropertiesTo change Port Specification table properties programmatically,
you can create a FunctionPortSpecification
object and modify its properties. To create a
FunctionPortSpecification
object for a selected C
Caller block in a model, type in the command
line:
myCCallerConfigObj = get_param(gcb, 'FunctionPortSpecification')
myCCallerConfigObj = FunctionPortSpecification with properties: CPrototype: 'real_T add(real_T u1, real_T u2);' InputArguments: [1×2 Simulink.CustomCode.FunctionArgument] ReturnArgument: [1×1 Simulink.CustomCode.FunctionArgument] GlobalArguments: [1×0 Simulink.CustomCode.FunctionArgument]
CPrototype
property is read-only, and shows the declaration of C
function input variables. The InputArgument
and
ReturnArgument
properties create a
FunctionArgument
object that you can further edit its properties
according to the rules defined for Port Specification table above.
You can See FunctionPortSpecification
to learn more.To modify the global arguments in a C Caller block, create a handle to
the GlobalArguments
object using getGlobalArg
and modify its properties.
You can create a library model to group your C Caller blocks and keep your models organized.
Open a new library model. On the Simulation tab, select New > Library.
On the Modeling tab, under Design, click Simulation Custom Code.
Select C
or C++
in the
Language option, depending on your code, and ensure the
Import custom code box is selected.
Follow the instructions in Specify Source Code and Dependencies to add your source files and their dependencies.
Create C Caller blocks to call C functions.
To insert a block from your library model to a Simulink model, simply drag the block into your model.
To attach an external debugger to the MATLAB process, and debug external C code, generate the debug symbols using:
Simulink.CustomCode.debugSymbols('on')
Turn this setting off using:
Simulink.CustomCode.debugSymbols('off')
When simulating a model containing custom C or C++ code, you have the option to run the custom code in a separate process outside of MATLAB. This option may be useful when debugging your custom code. By running in a separate process, problems with the custom code do not cause MATLAB to crash, and you can more easily debug and resolve such problems. Problems could arise due to unexpected exceptions in the custom code or errors in the interface between Simulink and the custom code.
To enable this option, in the model configuration parameters, in the Simulation target pane, select Simulate custom code in a separate process. The option applies to custom C/C++ code integrated into your model using any of these blocks:
C Caller
C Function
MATLAB Function
MATLAB System
Stateflow® chart
For example, this model contains a C Caller block that calls the function
adder()
, which accesses an object called adderObj
.
Before calling the function, the object must be created, which can be done by calling
initAdder()
from the Initialize function of the
Simulation Target pane of the model configuration parameters.
static Adder* adderObj = nullptr; int adder(int increment) { adderObj->add_one(increment); return adderObj->get_val(); } void initAdder() { adderObj = new Adder(); }
If initAdder()
is not called before adder()
, then
adder()
attempts to access an uninitialized pointer, which causes a
run-time exception. If the Simulate custom code in a separate process
parameter is not selected, this exception could cause MATLAB to crash when you simulate the model. However, if the parameter is selected,
simulating the model produces an error message within Simulink.
You can then click Open to launch your external debugger and resolve the issue that caused the error.
After the debugger launches, it will restart the simulation and stop at the breakpoints of custom function entries automatically.
Once your custom code is fully debugged, you can unselect Simulate custom code in a separate process for faster simulation time.
Global Variables — Global variables as function input outputs do not support multi dimensional arrays.
Initialization/Termination of Custom Code Settings — If you need to allocate and deallocate memory for your custom code, insert allocate and deallocate in the Initialize function and Terminate function fields of custom code settings, or use a C Function block.
Complex Data Support — The C Caller block does not support complex data types in Simulink.
Variable Arguments — Variable arguments in C are
not supported, for example, int sprintf(char *str, const char *format,
...)
.
C++ Syntax — The C Caller block does not support native C++ syntax directly. You need to write a C function wrapper to interface with C++ code.
To test models that include C Caller blocks, see Test Integrated C Code (Simulink Test).
Note
If a model has custom code, after the model is updated or run, the
slprj
folder may be locked due to the loaded custom code simulation
executable file. The folder cannot be deleted when it is locked. To unload the executable
file and unlock the slprj
folder, use the clear mex
command. See clear
.
C Caller | Comparison of Custom Block Functionality | FunctionPortSpecification
| getGlobalArg
| legacy_code | MATLAB Function | MATLAB System | S-Function | S-Function Builder