Simulink is a Matlab-based, graphical development environment for model-based design. Electrical and mechanical engineers can use Simulink to model the behavior of components of the bike, like the TMS or VCU. They can use these models to run simulations on the bike and gain an understanding of how it will perform during races. In the past, we have spent a fair amount of time modifying our code and our models to ensure they match up, and we've never had a good way of guaranteeing that they match exactly. Instead of spending that time and risking a mistake, this procedure describes a method of generating C++ code from the Simulink model that is guaranteed to behave exactly like the model. Effectively, this allows us to transfer the responsibility of the application-level business logic of some PCBs to the engineers. This allows them to do more rigorous testing at the MIL, SIL, and HIL levels, while freeing us up to focus on fleshing out our low-level drivers.
Install all necessary software.
First, you'll need to install Matlab with all necessary toolboxes. Matlab requires a paid license, but we have access to them through the school. You will have to spend some time to find the website that gives you access, but it is available. For toolboxes, you'll want to install all the toolboxes related to Simulink and all the toolboxes that mention "Coder," i.e. Matlab Coder, Simulink Coder, Embedded Coder. If you don't get the right toolboxes, you'll have some errors later, but can install them then.
If you want to build the generated code to validate it locally, you will need to install a C++ compiler. Firmware members will likely already have this on their computer, but electrical or mechanical members will need to get one. The GNU C Compiler should serve this purpose well enough.
Set up the model configuration.
To generate code that is usable by the firmware team, you'll want to use the template configuration for code generation. You should be able to get it from the top level directory of this GitHub repository. It should be named `codegen_cofig_template.mat`.
Once the configuration .mat file has been downloaded, open the Simulink model you'd like to import into. Go to the "MODELING" tab, and in the "DESIGN" section, select "Model Explorer". Right-click on the model you want to import the configuration into, hover over "Configuration..." and select "Import..." This will pull up a dialog box from which you should select the downloaded .mat file.
At this point, the configuration should be imported but not yet activated. To activate the configuration, go into the dropdown for the model on the left side and select "Configurations". Now, in the main window, you should see a configuration called "CodeGenConfiguration". Right-click this configuration and select "Activate". The gear next to this configuration should turn green.
Open the Embedded Coder app.
Go to the "APPS" tab, and select Embedded Coder.
If everything is set up correctly, a new tab titled "C++ CODE" should appear.
If you can't find Embedded Coder in the list of apps, check that you have the right toolboxes installed. If the tab that appears is titled "C CODE", the model configuration may not have been activated properly.
Set up inputs and outputs with proper data types, including subcomponents.
For normal components, double-click inports and outports, navigate to the "Signal Attributes" tab, and select the proper data type from the "Data type" dropdown. When you change this, you may also need to update the data types in any test scenarios you are using.
For stateflow components, first open the component. Go to the "MODELING" tab, and in the "DESIGN DATA" section, select "Model Explorer". In the main window, there will be a table of inputs and outputs for the component. You can set the data type of each value by clicking on the current data type in the table and selecting a new data type from the dropdown.
Before proceeding, make sure you discuss with the assigned firmware engineer what data types you've chosen. If there are problems with the selected data types, the firmware engineer may not be able to integrate your model into their code.
Set the generated class's namespace.
Ask the assigned firmware engineer what namespace they want the class to be in. This is important for keeping the code organized and varies from model to model. The namespace is case-sensitive.
In the "C++ CODE" tab, open the "Code Interface" dropdown and select "Class Name & Namespace".
Leave the class name as is, and put the provided namespace name into the namespace entry.
In settings, under the "Code Generation" tab, go to "Data Type Replacement" and make sure "Use C data types with fixed-width integers" is selected.
Run code generation.
In the "C++ CODE" tab, press the blue "Build" button to run code generation and attempt to build the resulting files.
If you don't have a C++ compiler installed, you can use the dropdown to select "Generate Code", so you don't get errors trying to build.
Because this process is very new for our team, we don't have any common debugging methods yet. If you get errors while trying to generate code, work with the assigned firmware engineer and senior members to find a solution.
Copy .h and .cpp files into the repository.
When code generation is run, several new files are generated, but the two we need are the .h and .cpp files associated with the model. They should be found in the generated ert_rtw directory.
You should copy these files into `include/models/` and `src/models`, respectively. If a models subdirectory doesn't exist, you will need to create one.
Fix the import in .cpp file.
The .cpp file will attempt to import the header file, but it will fail because it doesn't understand the file structure of our repository. You will need to update the include to be something like `#include <models/(something).h>`.
Integrate the model into the codebase.
If the interface of the model hasn't changed since the last time it was used in the codebase, this step is unnecessary. You should be able to simply update the two files above and recompile.
Add the source files to the list of source files in the `target_source` statement in the top-level CMakeLists.txt file.
The exact manner in which the model is integrated into the codebase varies widely from board to board. Be sure you are familiar with the model's purpose and intended use before you start integrating.