Main Content

Use Variant Models to Generate Code That Uses C Preprocessor Conditionals

This example shows you how to use variant models to generate code that uses preprocessor conditionals to control which code is linked into the embedded executable.

Overview of Variant Models

You can use a Model block to reference one Simulink® model (the child model) from another Simulink® model (the parent model). A Variant Subsystem block can have different variants. The variants can include a set of Model blocks, from which the Variant Subsystem block selects one. The models referenced by the Model blocks provide variations upon a nominal, prescribed functionality.

A Variant Subsystem block has only one variant active at a time. You can use the Variant Subsystem block dialog box to select the active variant. Alternatively, you can parameterize the selection of the active variant, and make it dependent on the values of variables and objects in the base MATLAB® workspace. When you generate code, you can generate code for all variants and defer the choice of active variant until it is time to compile that code.

Specifying Variant Models

Opening the example model PreprocessorConditionalsUsingVariantModel runs the PostLoadFcn defined in the "ModelProperties > Callbacks" dialog box. This populates the base workspace with the control variables for the Variant Subsystem blocks.

open_system('PreprocessorConditionalsUsingVariantModel')

The Left Controller variant subsystem contains two child Model blocks: Linear and Nonlinear. The Left Controller/Linear child model executes when the Simulink.VariantExpression object LINEAR evaluates to true, and the Left Controller/Nonlinear child model executes when the Simulink.VariantExpression object NONLINEAR evaluates to true.

Simulink.VariantExpression objects are specified for the Left Controller subsystem by right-clicking the Left Controller subsystem and selecting Subsystem Parameters, which opens the Left Controller Variant Subsystem block dialog box.

The Left Controller subsystem block dialog box creates an association between the Linear and Nonlinear Model blocks with the two Simulink.VariantExpression objects from the base workspace, LINEAR and NONLINEAR. These objects have a property named Condition, which is an expression that evaluates to a Boolean value and determines the active variant model (Linear or Nonlinear). The condition is also shown in the subsystem block dialog box. In this example, the conditions of LINEAR and NONLINEAR are 'VSSMODE == 0' and 'VSSMODE == 1', respectively.

In this example, Simulink.VariantExpression objects are created in the base workspace.

LINEAR = Simulink.VariantExpression;
LINEAR.Condition = 'VSSMODE==0';
NONLINEAR = Simulink.VariantExpression;
NONLINEAR.Condition = 'VSSMODE==1';

Specifying a Variant Control Variable

Variant objects allow you to reuse arbitrarily complex conditions throughout a model. Multiple Variant Subsystem blocks can use the same Simulink.VariantExpression objects, allowing you to toggle the activation of variant models 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, Left Controller and Right Controller reference the same variant objects, so that you can toggle them simultaneously.

The nonlinear controller models implement hysteresis, while the linear controller models act as simple low-pass filters. Open the subsystem for the left channel. The models for the right channel are similar.

The generated code accesses the variant control variable VSSMODE as a user-defined macro. In this example, variants_importedmacros.h supplies 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:

  • Define or ImportedDefine with header file specified

  • CompilerFlag

  • SystemConstant (AUTOSAR)

  • 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 = 'variants_importedmacros.h';

Simulating the Model with Different Variants

Because you set the value of VSSMODE to 1, the model uses the nonlinear controllers during simulation.

sim('PreprocessorConditionalsUsingVariantModel')
youtnl = yout;

If you change the value of VSSMODE to 0, the model uses the linear controllers during simulation.

VSSMODE.Value = int32(0);
sim('PreprocessorConditionalsUsingVariantModel')
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]);

Using C Preprocessor Conditionals

This example model has been configured to generate C preprocessor conditionals. To generate code for the model, in C Code tab of the toolstrip select Build.

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, set the Variant activation time parameter to code compile.

In this example, the generated code includes references to the Simulink.VariantExpression objects LINEAR and 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 variants_importedmacros.h. The active variant is determined by using preprocessor conditionals (#if) on the macros (#define) LINEAR and NONLINEAR.

The macros LINEAR and NONLINEAR are defined in the generated PreprocessorConditionalsUsingVariantModel_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 PreprocessorConditionalsUsingVariantModel.c, the calls to the step and initialization functions of each variant are conditionally compiled.

Close the model, figure, and workspace variables from the example.

bdclose('PreprocessorConditionalsUsingVariantModel')
close(findobj(0,'Tag','CloseMe'));
clear LINEAR NONLINEAR VSSMODE
clear tout yout youtl youtnl