How to create framework functions from Embedded Coder#
The functions of the UltraZohm Software follow the principles outlined in Software Development Guidelines. Encapsulation and data hiding are important characteristics for these software modules. C code that is generated by using the Embedded Coder (C code) is not directly compatible with the software guidelines and requires a wrapper module to be included in the software framework. The guiding principle is that the interface has to be consistent across modules, since it is not relevant whether the code is handwritten or auto-generated.
The following guide builds on the example model of Embedded Coder (C code) and highlights how to create an interface that is consistent with the Software Development Guidelines (multiple-instance module).
Functional description of the module#
Fig. 42 Screenshot of the subsystem that is used for code generation in the example of Embedded Coder (C code)#
Fig. 42 shows the subsystem that is code generated in Embedded Coder (C code). The following guide only implements an interface for the sum functionality of the module. Thus, the code ought to calculate:
The Simulink model uses forward Euler integration (see MATLAB documentation discrete integrator). Note that the integration time \(T_s=\frac{1}{10000}=0.0001\) is hard-coded in the module, due to the model settings in Embedded Coder (C code). The chirp function is not used in this guide, but can be implemented with the same principles.
Create framework function#
Note
This guide uses the already generated files of the example in Embedded Coder (C code). For custom modules, this step has to be adjusted to match the path of the specific embedded coder settings!
Open
ultrazohm_swwith VS Code and the remote container (see VS Code Remote Container)Create a new module
uz_sumusing Ceedlingcd vitis/software/Baremetal ceedling module:create[uz/uz_sum/uz_sum]
Ceedling creates the header, source, and test files
Copy the generated files to
uz_sum, e.g., by using the following command:cp -R src/Codegen/uz_codegen0_ert_rtw/ src/uz/uz_sum/
Warning
Re-using this module with the same name breaks the software. In the finished code of this how to guide, everything in
uz_sumfolder is protected by#ifdef TESTsuch that it can only be used in the ceedling unit tests.Create the allocation scheme using the allocation snippet (see Static memory allocation)
#include "../uz_global_configuration.h" #if UZ_SUM_MAX_INSTANCES > 0U #include <stdbool.h> #include "../../uz_HAL.h" #include "uz_sum.h" struct uz_sum_t { bool is_ready; }; static uint32_t instance_counter = 0U; static uz_sum_t instances[UZ_SUM_MAX_INSTANCES] = { 0 }; static uz_sum_t* uz_sum_allocation(void); static uz_sum_t* uz_sum_allocation(void){ uz_assert(instance_counter < UZ_SUM_MAX_INSTANCES); uz_sum_t* self = &instances[instance_counter]; uz_assert_false(self->is_ready); instance_counter++; self->is_ready = true; return (self); } uz_sum_t* uz_sum_init(void) { uz_sum_t* self = uz_sum_allocation(); return (self); } #endif
Call the module
uz_sumAdd
UZ_SUM_MAX_INSTANCEStouz_global_configuration.hand set it to 5Add the typedef for the
uz_sum_ttouz_sum.has well as the function declaration for the init function:typedef struct uz_sum_t uz_sum_t; uz_sum_t* uz_sum_init(void);
In
test_uz_sum.c, change the existing test to:void test_uz_sum_NeedToImplement(void) { uz_sum_init(); }
Run the tests, they compile but
test_uz_sum.cdoes not perform any real testsCreate the interface for stepping the model once (one integration / time step) with the given summand in
uz_sum.h:void uz_sum_step(uz_sum_t* self, float a, float b, float c);
Add an interface for reading the results from the module in
uz_sum.hfloat uz_sum_get_sum(uz_sum_t* self); float uz_sum_get_integral_over_sum(uz_sum_t* self);
Write empty functions for the defined interface in
uz_sum.cvoid uz_sum_step(uz_sum_t* self, float a, float b, float c){ } float uz_sum_get_sum(uz_sum_t* self){ } float uz_sum_get_integral_over_sum(uz_sum_t* self){ }
Write a test that checks for the summation of three values:
void test_uz_sum_add_numbers(void) { uz_sum_t* test_instance=uz_sum_init(); float a=1.1f; float b=2.2f; float c=3.3f; float expected_result=6.6f; uz_sum_step(test_instance,a,b,c); float result=uz_sum_get_sum(test_instance); TEST_ASSERT_EQUAL_FLOAT(expected_result, result); }
Run the tests. They will compile, but fail.
Add the include for the generated code as well as private data to
uz_sum.c(note: this has to be in the.cfile!)#include "uz_codegen0_ert_rtw/uz_codegen0.h" struct uz_sum_t { bool is_ready; ExtY output; ExtU input; DW rtDW; /* Observable states */ RT_MODEL modelData; RT_MODEL *PtrToModelData; };
Implement the initialization of the code-generated software in
uz_sum_init.cuz_sum_t* uz_sum_init(void) { uz_sum_t* self = uz_sum_allocation(); self->PtrToModelData=&self->modelData; self->PtrToModelData->dwork=&self->rtDW; self->PtrToModelData->inputs=&self->input; self->PtrToModelData->outputs=&self->output; return (self); }
Note that
uz_sum_initis just wiring of private variables of the module to meet the interface of the generated code and to be able to pass all data of the model to the step function by a single pointer.Add
#include "uz_codegen0_ert_rtw/uz_codegen0.h"totest_uz_sum.cto enable calling the generated code in the testsImplement the function
uz_sum_stepinuz_sum.c:void uz_sum_step(uz_sum_t* self, float a, float b, float c){ self->input.summand1=a; self->input.summand2=b; self->input.summand3=c; uz_codegen0_step(self->PtrToModelData); }
Run the tests, they still fail.
Implement
uz_sum_get_suminuz_sum.cfloat uz_sum_get_sum(uz_sum_t* self){ return self->output.sum; }
Run tests, they pass.
Write a test for the integration
void test_uz_sum_integrate(void) { uz_sum_t* test_instance=uz_sum_init(); float a=1.1f; float b=2.2f; float c=3.3f; float expected_result=0.00198f; // Call step four times with sum=6.6, integration time Ts is 1/10000 // First call: y(0)=0 // Second call: y(1)= 1/10000*6.6=0.00066 // Third call: y(2)=0.00066+0.00066=0.00132 // Last call: y(3)=0.00132+0.00066=0.00198 // Step four times - no loop to make it explicit uz_sum_step(test_instance,a,b,c); uz_sum_step(test_instance,a,b,c); uz_sum_step(test_instance,a,b,c); uz_sum_step(test_instance,a,b,c); float result=uz_sum_get_integral_over_sum(test_instance); TEST_ASSERT_EQUAL_FLOAT(expected_result,result); }
Run tests, they fail.
Implement
uz_sum_get_integral_over_sumfloat uz_sum_get_integral_over_sum(uz_sum_t* self){ return self->output.IntegrationOfSum; }
Run tests, they pass.
Implement tests and interface for the chirp functionality
Write documentation for the software module
Note
The source Simulink model that is used for generating the code should be supplied in the same folder as the generated code. Do not commit anything expect .slx, .c, and .h files after the code generation, i.e., no Simulink cache or other build artifacts!