Code Generator Features
Internal functions in the generated code
The Engee code generator supports virtual and atomic subsystems for code generation. Virtual subsystems serve only for the visual hierarchy in the model, while atomic subsystems represent a single execution unit (or function in the generated code). Configuration in subsystem properties Treat as atomic unit allows you to make a subsystem virtual or atomic.
All calculations inside virtual subsystems are embedded (inline) directly into the code of the enclosing subsystem or model. This can improve the performance of the generated code, but degrade readability and traceability to the model.
All calculations inside atomic subsystems are generated in a separate function with the subsystem name. This function is then called automatically in the code of the underlying subsystem or model. This can improve the readability and traceability of the generated code to the model, but degrade performance.
For example, turn RollMode from the model autopilot_roll.engee (see Code generation example) to the atomic subsystem:

Save the model and generate the code again. The generated code now has a function named RollMode (corresponding to the subsystem name in the model). This function is called inside the function autopilot_roll_step, which represents the code of the enclosing model. Function RollMode declared as static and, thus, is an internal function of this model.
|
The code generator ignores disabled blocks, and the associated code is not created.
|
Parameters in the generated code
Parameters in the model blocks
In the model blocks, the parameters can be set:
-
In the form of numbers (for example,
0.2); -
In the form of expressions (for example,
0.2 + 0.7); -
In the form of variable names from the Engee workspace (for example,
Kp).
In the generated code, the parameters are embedded (inline) with the values specified in the model.:
-
Simple numerical values are added to the code "as is", and no additional actions are required.;
-
Expressions consisting only of numbers can be simplified (constant folding), and the calculated numeric value will get into the code.;
|
Configurable parameters
By default, the rule applies: any reference to a variable (for example, Kp) or an expression containing a variable (for example, Kp + 1) makes the parameter configurable and puts it in modelname_P — regardless of the setting Built-in/Customizable. In the "Configurable" mode in modelname_P all parameters are included, including numeric literals.
Some block parameters can be made configurable (tunable) so that they can be changed after code generation. These parameters are stored in the structure modelname_P which looks like this:
-
If the parameter is set via a variable name (for example,
Kp), then its name is saved in the structure:struct P_modelname_T_ { double Kp; }; -
If the parameter is set as an expression with a variable (for example,
Kp + 1), then the name of the parameter in the block is used (for example,Amplitude):struct P_modelname_T_ { double Amplitude; };
In all cases, the parameters in the structure are modelname_P they are filled with calculated values available from the model, for example:
P_modelname_T modelname_P = {
0.5 // Parameter value
};
|
Using the option Default parameter behaviour in settings window
|
example: Built-in mode, the amplitude is set by a variable `a`
When selecting "Embedded" and the block parameter Amplitude = a:
-
The field in will appear in the code.
modelname_P(for example,double a;or the name of the field by the parameter label, if the expression is used witha); -
File
model_data.cgets the original valueafrom the workspace; -
The * value itself will not be fixed, it can be changed without reassembling.
This is the expected behavior: a reference to a variable is treated as "configurable".
Interfaces of the generated code and integration into the external development environment
Interfaces of the generated code
The external interfaces of the generated code can be seen in the generated file. modelname.h. External interfaces include functions and structures for working with generated code.:
-
modelname_init— is a model initialization function — must be called once; -
modelname_step— is the entry point to the model and contains the algorithm of the model — must be called periodically in accordance with the step of calculating the model; -
modelname_U— the structure contains the external input ports of the model; -
modelname_Y— the structure contains the external output ports of the model; -
modelname_S— the structure contains the internal states of the model; -
modelname_P— the structure contains configurable model parameters.
Integration into an external development environment
To use the functions and structures of the generated file modelname.h in the external code (written manually), you need to do the following:
-
Attach the generated header file:
#include "modelname.h" -
Organize a single call to the generated function
initin yourmain:modelname_init(); -
Organize a periodic call to the generated function
stepin yourmainwith the right step:modelname_step(); -
Organize the transfer of input and output data to and from the function
step:/* Setting the values of the inputs */ modelname_U.input1 = 42; /* Calling the step function */ modelname_step(); /* You can use modelname_Y.output1 */
Generated files
After building the model in Engee and generating its code (using the command engee.generate_code() or buttons Generate code
) automatically generated files are created that contain the implementation of the model in the C language. Below is a description of these files and their purpose.

-
modelname.h— a header file that describes the external interfaces of the model. It includes the functions and structures necessary to work with the model from external code (for more information, see above). The process of connecting these functions and structures to an external application is also described in the section above. -
modelname.c— a file with the implementation of the logic of the model. It contains definitions of all variables and the implementation of key functions of the model.:-
init()— initialization function that sets the initial values of variables and model states; -
step()— the main computational function. Performs the calculation of one simulation step and updates the output values; -
term()— the shutdown function of the model.
-
-
model_data.c— a file with the initial initialization of the model parameters. It sets the values of all the configurable parameters that are used when executing the model. For example:-
Signal amplitude and frequency;
-
Phase, offset;
-
The number of samples and other parameters.
These global parameters are configurable and can be changed while the executable code is running. This allows you to flexibly control the behavior of the model without having to reassemble the logic or edit the main functions.
In the "Embedded" mode in model_data.conly parameters specified through variables or expressions with variables are included; numeric literals and purely numeric expressions are not written there (they are embedded in the code).
-
-
main.c— a file with an example of using the model. This is the main executable file that shows how to initialize and invoke a model from a C program. Contains:-
init()— the initialization function of the model, called once before starting work; -
step()— the main computational function that performs one step of calculating the model. It is usually called periodically, according to the specified integration step.; -
term()— the shutdown function of the model.File
main.cIt does not contain an algorithm for the operation of the model, but only demonstrates how to correctly launch and manage its execution in an application with C code support.main.cIt should be used as a template for integrating the model into user projects.
-
Supported data types
The data types in the generated code correspond to the data types in the Engee model.
If the user has not specified a data type in the model, then a double-precision floating-point data type corresponding to the type is used. double in C. For logical operations or calculations that generate Boolean results (true (1) or false (0)), the data type is used boolean, corresponding to an eight-bit unsigned data type in Si.
|
To display data types in the model, in settings window |
Vectorization
The code generator supports vector data types in models, including vectors and matrices. Standard loops are generated in the code for vector operations. for without calling specific BLAS or LAPACK functions.
Complex numbers
The code generator supports complex data types in models. For complex types, the types from the standard header file are used. <complex.h>.
Multi-frequency models
Engee models can be multi-frequency, that is, they contain several different sampling rates. Applying blocks Rate Transition, as well as by setting the parameter Sample Time blocks and subsystems, the user can control which calculation step blocks or groups of blocks work with.
The Engee code generator supports code generation for multi-frequency models. To do this, the blocks are grouped by frequency, and the user can control how these different frequencies are represented in the generated code.
Customization Multirate code generation allows you to control the mode of operation of the generated code for a multi-frequency model. The "single-tasking" code allows you to generate a single function step for the model. The "multitasking" code allows you to generate multiple functions. step for the model, each of which corresponds to a specific sampling rate in the model.

Single-task code contains conditions (in the form of if) inside the generated function step, which wrap calls of slower frequencies in the model. Function step It contains blocks operating at the base frequency of the model (the fastest frequency). From the custom binding, it is enough to provide a function call step at the same time, the scheduling of calls of slower frequencies and the exchange of information between frequencies is carried out automatically in the generated code itself.
Multitasking code contains several functions step_N in the generated code, each of which corresponds to its frequency in the model. Function step_0 corresponds to the fastest frequency in the model (base frequency), the function step_1 the second slower frequency, and so on. It is required to provide a function call from the custom binding step_N with the necessary calculation steps. Data interactions between different frequencies are provided automatically in the generated code itself.
Comments in the generated code
By default, the generated code contains comments that allow you to trace the code to the blocks in the model.
Customization Include comments in the panel Code generation they allow you to manage comments in the generated code and, if necessary, disable them completely.
Code verification
Verification refers to the generation of a verification model with a block C Function, whose simulation results must match the simulation results of the original model with the same input data.
Block C Function it can be created automatically to verify the generated code. To do this, check the box Generate C Function block in the settings window
:

The next time the code is generated (via the interface, context menu, or command generate_code) in the directory ouput_dir the file will appear modelname_verification.jl (if the directory does not exist, it will be created automatically). This file will contain a script that can be executed in the script editor (by double-clicking)
or the command line
:
include("path/to/verification.jl")
The script will create an Engee model with the name modelname_verification.engee and a block C Function, which uses the generated code:

Code generation in the Verilog language is also available in Engee. For more information, see the article Verilog (HDL) code generation.
Code generation based on custom templates
Engee supports code generation based on custom templates (for more information, see the article Code generation based on custom templates).
Managing signal names
The code generator uses the names of the signals specified by the user in the model to make the generated code clearer and easier to match with the model. For example:

If the signal (block output) has a name, then the generated code will use the same variable name for this signal. This is possible only if the signal has not been optimized and does not contain characters that are not supported in the C code.

