This example shows how to use Simulink® variant subsystems to generate C preprocessor conditionals that control which child subsystem of the variant subsystem is active in the generated code produced by the Simulink® Coder™.
A Variant Subsystem block contains two or more child subsystems where one child is active during model execution. The active child subsystem is referred to as the active variant. You can programmatically switch the active variant of the Variant Subsystem block by changing values of variables in the base workspace, or by manually overriding variant selection using the Variant Subsystem block dialog. The active variant is programmatically wired to the Inport and Outport blocks of the Variant Subsystem by Simulink® during model compilation.
To programmatically control variant selection, a
Simulink.Variant object is associated with each child subsystem in the Variant Subsystem block dialog.
Simulink.Variant objects are created in the MATLAB® base workspace. These objects have a property named
Condition, an expression, which evaluates to a Boolean value and is used to determine the active variant child subsystem.
When you generate code, you can only generate code for the active variant. You can also generate code for all variants of a Variant Subsystem block and defer the choice of active variant until it is time to compile the generated code.
Opening the example model
rtwdemo_preprocessor_subsys will run the PostLoadFcn defined in the "File: ModelProperties: Callbacks" dialog. This will populate the base workspace with the variables for the Variant Subsystem blocks.
The LeftController variant subsystem contains two child subsystems: Linear and Nonlinear. The LeftController/Linear child subsystem executes when the Simulink.Variant object LINEAR evaluates to
true, and the LeftController/Nonlinear child subsystem executes when the Simulink.Variant object NONLINEAR evaluates to
Simulink.Variant objects are specified for the LeftController subsystem by right-clicking the LeftController subsystem and selecting
Subsystem Parameters, which will open the LeftController subsystem block dialog.
The LeftController subsystem block dialog creates an association between the Linear and Nonlinear subsystems with the two Simulink.Variant objects,
NONLINEAR, that exist in the base workspace. These objects have a property named
Condition, an expression, which evaluates to a Boolean value and determines the active variant child subsystem (Linear or Nonlinear). The condition is also shown in the subsystem block dialog. In this example, the conditions of
NONLINEAR are 'VSSMODE == 0' and 'VSSMODE == 1', respectively.
In this example, the Simulink.Variant objects are created in the base workspace.
LINEAR = Simulink.Variant; LINEAR.Condition = 'VSSMODE==0'; NONLINEAR = Simulink.Variant; NONLINEAR.Condition = 'VSSMODE==1';
Variant objects allow you to reuse arbitrarily complex conditions throughout a model. Multiple Variant Subsystem blocks can use the same Simulink.Variant objects, allowing you to toggle the activation of subsystem variants as a set. You can toggle the set prior to simulation by changing the value of
VSSMODE in the MATLAB environment or when compiling the generated code, as explained in the next section. In this example, LeftController and RightController reference the same variant objects, so that you can toggle them simultaneously.
The nonlinear controller subsystems implement hysteresis, while the linear controller subsystems act as simple low-pass filters. Open the subsystem for the left channel. The subsystems for the right channel are similar.
The generated code accesses the variant control variable
VSSMODE as a user-defined macro. In this example,
VSSMODE. Within the MATLAB environment, you specify
VSSMODE using a
Simulink.Parameter object. Its value will be ignored when generating code including preprocessor conditionals. However, the value is used for simulation. The legacy header file specifies the value of the macro to be used when compiling the generated code, which ultimately activates one of the two specified variants in the embedded executable.
Variant control variables can be defined as
Simulink.Parameter objects with one of these storage classes:
ImportedDefine with header file specified
User-defined custom storage class that defines data as a macro in a specified header file
VSSMODE = Simulink.Parameter; VSSMODE.Value = 1; VSSMODE.DataType = 'int32'; VSSMODE.CoderInfo.StorageClass = 'Custom'; VSSMODE.CoderInfo.CustomStorageClass = 'ImportedDefine'; VSSMODE.CoderInfo.CustomAttributes.HeaderFile = 'rtwdemo_importedmacros.h';
Because you set the value of
VSSMODE to 1, the model uses the nonlinear controllers during simulation.
sim('rtwdemo_preprocessor_subsys') youtnl = yout;
If you change the value of
VSSMODE to 0, the model uses the linear controllers during simulation.
VSSMODE.Value = int32(0); sim('rtwdemo_preprocessor_subsys') youtl = yout;
You can plot and compare the response of the linear and nonlinear controllers:
figure('Tag','CloseMe'); plot(tout, youtnl.signals(1).values, 'r-', tout, youtl.signals(1).values, 'b-') title('Response of Left Channel Linear and Nonlinear Controllers'); ylabel('Response'); xlabel('Time (seconds)'); legend('nonlinear','linear') axis([0 100 -0.8 0.8]);
This example model has been configured to generate C preprocessor conditionals. You can generate code for the model by selecting Code > C/C++ Code > Build Model.
To activate code generation of preprocessor conditionals, check whether the following conditions are true:
Select an Embedded Coder® target in Code Generation > System target file in the Configuration Parameters dialog box
In the Variant Subsystem block parameter dialog box, clear the option to Override variant conditions and use following variant
In the Variant Subsystem block parameter dialog box, Select the option to Analyze all choices during update diagram and generate preprocessor conditionals.
The Simulink® Coder™ code generation report contains sections in the Code Variants report dedicated to the subsystems that have variants controlled by preprocessor conditionals.
In this example, the generated code includes references to the Simulink.Variant objects
NONLINEAR, as well as the definitions of macros corresponding to those variants. Those definitions depend on the value of
VSSMODE, which is supplied in an external header file
rtwdemo_importedmacros.h. The active variant is determined by using preprocessor conditionals (#if) on the macros (#define)
NONLINEAR are defined in the generated
rtwdemo_preprocessor_subsys_types.h, header file:
#ifndef LINEAR #define LINEAR (VSSMODE == 0) #endif
#ifndef NONLINEAR #define NONLINEAR (VSSMODE == 1) #endif
In the generated code, the code related to the variants is guarded by C preprocessor conditionals. For example, in
rtwdemo_preprocessor_subsys.c, the calls to the step and initialization functions of each variant are conditionally compiled:
/* Outputs for atomic SubSystem: '<Root>/LeftController' */ #if LINEAR /* Output and update for atomic system: '<S1>/Linear' */ #elif NONLINEAR /* Output and update for atomic system: '<S1>/Nonlinear' */ #endif
Close the model, figure, and workspace variables from the example.
bdclose('rtwdemo_preprocessor_subsys') close(findobj(0,'Tag','CloseMe')); clear LINEAR NONLINEAR VSSMODE clear tout yout youtl youtnl