Showing posts with label Simulink. Show all posts
Showing posts with label Simulink. Show all posts

Monday, February 10, 2025

Verifying Simulink generated C code

I generated embedded C code for the plant in the following MATLAB Simulink model:

To verify that the generated code behaves the same as the Simulink model, I could save the controller outputs to a file, create a C project with a main() function to read those outputs, and feed them to the plant C code at each time step—essentially converting a closed-loop simulation into an open-loop one. This approach would work if I use a single-step numerical integrator/solver like ode1 (Euler method). However, I usually prefer ode4 (Runge-Kutta 4) due to its better accuracy with less computation. Despite RK4 having 4 intermediate computations, it has a global error of O(h^4) and Euler O(h). If RK4 uses a step size of h, Euler would need h^4 to achieve similar accuracy. So, in total Euler would require more function evaluations than RK4—specifically, (1/h^3)/4 times more. For example, if hRK4 = 0.1, Euler would need 250x more computations.

Unfortunately, RK4 is a multi-step method with minor step sizes. Since the Simulink model saves controller outputs only at major steps, reading them from a file to drive the plant might result in divergent behavior—especially if the system has high-frequency dynamics—because controller outputs could differ at minor steps, while reading from the file only accounts for major step outputs.


You can only fully verify the system by integrating the C code into your embedded setup, which generates controller outputs based on real time plant-sensor data. Since testing on an embedded system takes time, to catch issues such as uninitialized variables in the plant code, you can create a C project, build and run your plant model with constant controller inputs. If it compiles and runs without NaN values, you can proceed to the embedded testing phase.

If you want to be more rigorous, you could save the plant outputs together with the controller outputs. In your C project, you read the controller output, feed it to the plant, run it for only one time step, read the Simulink plant output for that time instance from a file, and compare it with the C code plant output. Even this comparison is not perfect and only works for the first time step because Simulink integrator block outputs depend on the system state in the previous time step. To check the whole time span, you also have to save the system state and initialize the system with the correct state at each time step. But this in effect would be equal to running the system for one time step, because you initialize the system, apply a constant control input and check the output.

Friday, November 15, 2024

Strange error when generating C code from Simulink

When I tried to generate code from a recently updated Simulink model, I got "index exceeds the number of array elements. Index must not exceed 0". This error happened only during code generation, not when running the model. After spending a day, I found out that the reason was forgetting the folder separator "\" in an entry in Model Configuration Parameters > Code Generation > Custom Code > Source Code list, i.e. instead of "abc\file.cpp" the entry was "abcfile.cpp". A very unhelpful error message for such a simple error... A better error message would be "could not find file abcfile.cpp".

By the way, you can generate code from the MATLAB command line with slbuild('your_model_name').

Another error is "Invalid setting for input port dimensions of ...". If you are sure that there is no problem with port dimensions, close MATLAB, delete existing .mexw64 files, open MATLAB and regenerate them.

Sunday, September 22, 2024

Using code from Simulink model in HIL

Steps of converting a Simulink model to code usable in Hardware-in-the-loop (HIL) simulation:
  1. Checkout/pull Simulink model from repository to your local.
  2. Run Simulink model and confirm it finishes as expected. If not, inform the model maintainer and ask them to commit/push the model with correct settings to repo. 
  3. Confirm that C/C++ code can be generated from model. Sometimes an s-function build file (mexw64) exists but its source code is missing, which allows the model to run but prevents code generation.
  4. Copy code to Visual Studio and verify that you can build and run it. There are cases where Simulink is more forgiving of errors like uninitialized variables, or missing #include <cmath> but Visual Studio cannot build the code.
  5. Copy code to real time Linux PC and verify code can be build there too.
  6. Commit code to its own repo.
  7. Run HIL with new code and verify HIL works as expected.

Tuesday, July 30, 2024

LONG_MAX is different for Windows 64 and Linux 64

When you generate code with Simulink (MATLAB R2023b) using ert.tlc, the default OS is Windows 64, see Configuration Parameters - Hardware Implementation - Device type. When you generate C code, the <model name>_private.h file will contain checks for ULONG_MAX and LONG_MAX.

On 64-bit Windows, the long type is typically 32 bits, which causes the LONG_MAX to be 0x7FFFFFFF. On 64-bit Linux systems, the long type is typically 64 bits, i.e. LONG_MAX is 0x7FFFFFFFFFFFFFFF. When you use code generated with the Windows 64 setting and use that on a Linux 64 OS, the check in <model name>_private.h will fail. The solution is to use the Linux 64 setting in Simulink which removes the LONG_MAX check from header file.

This checks seem to have been added after MATLAB R2022b because code generated with R2022b does not have them.

Friday, November 3, 2023

Simulink signal

After generating code from a Simulink model, if you want to read the value of a parameter inside the model which is not an output port, you can use signals. In Matlab R2022b, signal definition is different from R2019a. The following R2022b method (assuming you have Embedded Coder) is only valid if you generate C code, I have not been able to find a way to generate signal code for C++:
  1. Open Simulink model
  2. Open Configuration Parameter window, go to Code Generation
  3. System target file: ert.tlc (Embedded Coder)
  4. Language: C
  5. In Simulink APPS menu, click on Embedded Coder, A C CODE tabs appears to the right of APPS menu
  6. Find the signal line you want the generate code for and double click on it, give it a name, e.g. "mySignal"
  7. Single click on signal line, a pop up will appear, click on the left-most item, "Add selected signals to code mappings"
  8. Under C CODE tab, click on Code Interface and then click Default Code Mappings
  9. A Code Mappings panel will appear at the bottom of model
  10. Click on right most tab, Signals/States
  11. Under Signals, find mySignal
  12. Click to the right of mySignal (Store Class column), change Auto to Model Default.
  13. Generate code by pressing ctrl + b
  14. If your model name was MyModel, the generated code will have a file called MyModel_ert_rtw/MyModel.h containing the data structure MyModel_B which has mySignal as a field that you can access from your own C/C++ code. 
  15. If your code is C++, dont forget to use include in C wrapper: extern "C" {#include "MyModel_ert_rtw/MyModel.h"}

Wednesday, August 16, 2023

Compile s-function with C++17

To compile a Simulink s-function file with C++17 (assuming you have Visual Studio 2019 or higher) , use:
        mex COMPFLAGS='$COMPFLAGS /std:c++17' file.cpp

Note that this does NOT work: mex CXXFLAGS='$CXXFLAGS /std:c++17' file.cpp

Friday, May 13, 2022

Simulink: Generating code without model version and date

When you generate code with Simulink, the generated header and source files will have model version and date in code comments. This results in version changes in your version control system even when you have not changed any logic. In order to avoid unnecessary version updates, you should comment out the "Model version" and "C/C++ source code generated on" sections in the ert_code_template.cgt file (Configuration Parameters > Code Generation > Templates > Code Templates).

Monday, November 1, 2021

Simulink versions

Matlab release - Simulink version (helpful when you know the Simulink version and wonder which Matlab release it corresponds to):


Monday, November 30, 2020

Difference between Simulink model and executable results

When you generate code from a Simulink model, build an executable and compare the results of Simulink model and excutable, you will usually notice that there are differences. To make sure that these differences are normal, do the following:

  1. Verify that Simulink and executable inputs are exactly (up to 16 digits) the same. Use format long before printing Simulink inputs and outputs. Do the same for executable input and output printing. Most of the time, differences in results are due to differences in significant digits. Pro Tip: If your Simulink model uses constant blocks for all inputs, when you generate C code, those inputs will be embedded into the code. If you build this code and don't touch any of the inputs, you can be sure that Simulink and executable inputs are the same without doing anything extra.
  2. In the executable main(), use double (64bit floating point) data type for inputs and outputs.
  3. Run the executable two times and verify that results of these two runs are exactly the same. If they are not, this is possibly due to a random number generator in model. Add random seed to input so that same random numbers will be generated which then guarantees consecutive run results being the same.
  4. Build executable as 32bit, run and save results. Build as 64bit, run and save results. Compare 32bit and 64bit results. The difference in these results will give you an idea of difference order of magnitude to expect.
  5. Verify that the difference between Simulink run and executable run is of similar order of magnitude to the difference between 32bit and 64bit runs.

Tuesday, June 16, 2020

From Workspace uses first element as time

If you create a Simulink model with a From Workspace block and use it to get a vector, you will be surprised to see that the first element is missing:


The reason is that From Workspace treats the first element of a vector as time and the rest as data.

Thursday, May 7, 2020

Mex only on file change

For large Simulink projects with lots of s-function, performing a mex of all files can be time consuming. Leaving it up to the user might result in simulation runs with older mex files because the user might forget to re-mex the updated file. You can automate the process as follows:
  • Create a hash list file that consists of file names used by s-functions and corresponding hashes.
  • Before simulation start, in InitFcn, 
    • Calculate hashes of files used by s-functions and compare them with the values saved in hash list file. Mark files whose hashes are different as "changed".
    • If an existing binary mex file date is earlier than any of the dates of the source files used in its build, mark that binary as "changed".
  • Mex files marked as "changed".
For hash calculation you can use [status, cmdout] = system(['certutil -hashfile', fileName, ' SHA256']) and parse cmdout via str=splitlines(cmdout); hash=str[2]; to get hash of file.

From Workspace Sample Time

Let's say you have a single Simulink model with plant and controller, and another that just has the plant.

If you save outputs of plant&controller with to workspace blocks, add from workspace blocks to plant only model and run it with outputs of plant&controller, you might see differences between results. In order to have a close match of results, you shouldn't leave the sample time of from workspace blocks as zero, you have to enter them equal to sample time of plant&controller model.

Sunday, March 8, 2020

Simulink: Passing strings to s-functions

As of R2019b, you can't pass a string (e.g. a file path) to an s-function via string constant block. You can convert a string to an ASCII value array, pass it to s-function, and inside the s-function convert the array back to string. Note that this means you are limited to the ASCII table of characters. You can use the following function to construct string from s-function input:

Thursday, January 16, 2020

Using workspace variables in C++ Mex functions

When you have C or C++ files that you would like to use in your Simulink model, you have to interface them with a mex file. Variables in Matlab workspace can be used inside C++ mex functions. Example m file and mex function demonstrating scalar, vector, matrix, string and structure usage:

When you run mexDriver.m, you will get an output similar to this:


Tuesday, January 7, 2020

Comparing two models

When you have an mdl/slx model, to compare it with another version of the same model:
  1. Open model in Simulink.
  2. Go to Analysis menu, click Compare Simulink XML Files...
  3. Select the model file you want to compare with the opened one.
  4. Simulink will open a window displaying the differences. When you click on a line, it will display both models, highlighting the differences.

Monday, January 6, 2020

File closing bug

Recently, I saw that when I ran a simulink model multiple times in a row from a script with sim() command, the model would throw an error at the 509th time with a message indicating that it could not find a file, i.e. an fopen() operation returned null. The curious thing is that the file exists, otherwise it would not be able to run the previous 508 times.

After one day of debugging, I saw that I forgot to close a file in another unrelated s-function. I guess after a certain number of unclosed file pointers, fopen() commands start to fail. This reminded me of an old heap corruption bug that took me months to find an fix.

Friday, December 27, 2019

Generating code with reusable function

Recently, I needed to re-use Aerospace blockset's ISA atmosphere as a function in C code. When you just generate code, the atmosphere functionality will be embedded inside the simulation step() function. To generate a function that does not depend on simulation, you do the following (Matlab R2019a):

First, put the ISA atmosphere block inside a subsystem (so that it can be made atomic). Then right click on the subsytem:

Generated C code contains the names I set in Simulink model (with "rtu_" added for inputs and "rty_" added for outputs) and the code interface is simple. Contents of SamilsISAAtmosphere.h:

extern void SamilsISAAtmosphere(real_T rtu_hMSL_m, real_T *rty_Temperature_K, real_T *rty_speedOfSound_mps, real_T *rty_pressure_Pa, real_T *rty_airDensity_kgpm3);

Thursday, November 7, 2019

Input / output code generated from Simulink model

When you want to generate code from a Simulink model and drive that code from another C++ file, Simulink has a mechanism that can help you setting model inputs and getting model outputs.

For outputs, in Simulink you right click on the signal, give it a name and select storage class. For inputs that are normally read from Matlab workspace and have to be set from external C++ function, you configure them as inlined parameters:



When you define constants loaded from workspace in Simulink as Model Default/Simulink Global (Auto) or for signals Auto, and if you forget to define init structure in C++, you will get "LNK2001 unresolved external symbol _aa_P", which is a good thing because the compiler won't let you forget initialization.

If you define parameters as extern Global, Simulink sets initial values to the constants in Simulink and if you forget to provide your own initial conditions, C++ will happily run with embedded inits. Additionally, auto puts parameters into a structure while extern global makes them separate global variables which increases probability of naming conflicts.

Wednesday, July 31, 2019

Skills / Competencies

A software engineer tasked with generating code from an existing Simulink model must have these skills:
  1. A good understanding of C-Mex s-functions and calling frequencies of s-function methods like mdlUpdate().
  2. Profiling the Simulink model and improving speed by e.g. moving one time calculations to initialization instead of running it at every time step.
  3. Automation of code generation process with scripts (e.g. bat file).