You can use Variant Source and Variant Sink blocks to perceive multiple implementations of a model in a single, unified block diagram. Each implementation depends on conditions that you set for Variant Source and Variant Sink blocks. Simulink® propagates these conditions to upstream and downstream blocks including root input and root output ports.
You can generate:
Code from a Simulink model containing Variant Sink and Variant Source blocks.
Code that contains preprocessor conditionals that control the activation of each variant choice.
Preprocessor conditionals that allow for no active variant choice.
This example shows how Variant Source blocks make model elements conditional.
From the Simulink Block Library, add 1 Sine Wave Function block, two Add blocks, three Gain blocks, two Outports, and two Variant Source blocks into a new model.
Open the Sine Wave Function block.
For the Sine type parameter, select Sample
based
. For the Time (t) parameter,
select Use simulation time
. For the Sample
time parameter, insert a value of 0.2
.
Make four copies of the Sine Wave Function block.
Connect and name the blocks as shown.
Insert values of 2
, 3
,
and 4
in the Gain2
, Gain3
,
and Gain4
blocks, respectively.
Give the model the name inline_variants_example
.
Open the Block Parameters dialog box for Variant
Source
.
In the Variant control column,
for Port 1, replace Variant_1
with V==1
.
For Port 2, replace Variant_2
with V==2
.
Open the Block Parameters dialog box for Variant
Source1
.
In the Variant control column,
replace Variant_1
with W==1
.
For Port 2, replace Variant_2
with W==2
.
In the MATLAB Command Window, use these commands to define V
and
W
as Simulink.Parameter
objects.
V = Simulink.Parameter; V.Value = 1; V.DataType='int32'; V.CoderInfo.StorageClass = 'custom'; V.CoderInfo.CustomStorageClass = 'Define'; V.CoderInfo.CustomAttributes.HeaderFile='inline_importedmacro.h' W = Simulink.Parameter; W.Value = 2; W.DataType='int32'; W.CoderInfo.StorageClass = 'custom'; W.CoderInfo.CustomStorageClass = 'Define'; W.CoderInfo.CustomAttributes.HeaderFile='inline_importedmacro.h'
In this example, the variant control variables are Simulink.Parameter
objects. For code generation, if you use Simulink.Variant
objects to
specify variant controls, use Simulink.Parameter
objects or MATLAB
variables to specify their conditions. .
Variant control variables defined as Simulink.Parameter
objects can have one
of these storage classes:
Define
with header file specified
ImportedDefine
with header file
specified
CompilerFlag
SystemConstant (AUTOSAR)
User-defined custom storage class that defines data as a macro in a specified header file
If you use scalar variant control variables to simulate the model, you can convert
those variables into Simulink.Parameter
objects. See Convert Variant Control Variables into Simulink.Parameter Objects (Simulink).
Simulate the model.
Input port 1 is the active choice for Variant Source
because
the value of variant control variable V
is 1
.
Input port 2 is the active choice for Variant Source1
because
the value of variant control variable W
is 2. The
inactive choices are removed from execution, and their paths are grayed-out
in the diagram.
You can generate code in which each variant choice is enclosed
within C preprocessor conditionals #if
and #endif
.
The compiler chooses the active variant at compile time and the preprocessor
conditionals determine which sections of the code to execute.
In the Modeling tab of the Simulink toolstrip, click Model Settings.
Select the Code Generation pane,
and set System target file to ert.tlc
.
In your model, open the block parameters dialog box
for Variant Source
.
Select the Analyze all choices during update diagram and generate preprocessor conditionals parameter. During an update diagram or simulation, when you select this parameter, Simulink analyzes all variant choices. This analysis provides early validation of the code generation readiness of variant choices. During code generation, when you select this parameter, the code generator generates preprocessor conditionals that control the activation of each variant choice.
Clear the Allow zero active variant controls parameter.
Open the Block Parameters dialog box for Variant
Source 1
. Repeat steps 5 through 7.
Build the model. When code generation is complete, the generated code is displayed in the Code view.
In the Code view, select the inline_variants_example.c
file.
In the inline_variants_example.c
file,
calls to the inline_variants_example_step
function
and the inline_variants_example_initialize
functions
are conditionally compiled as shown:
/* Model step function */ void inline_variants_example_step(void) { real_T rtb_Sine6; real_T rtb_VM_Conditional_Signal_Sum_1; /* Sin: '<Root>/Sine1' incorporates: * Sin: '<Root>/Sine2' * Sin: '<Root>/Sine3' * Sum: '<Root>/Add1' */ #if V == 1 rtb_Sine6 = sin(((real_T)slexVariantSourceAndSink_DW.counter + slexVariantSourceAndSink_P.Sine1_Offset) * 2.0 * 3.1415926535897931 / slexVariantSourceAndSink_P.Sine1_NumSamp) * slexVariantSourceAndSink_P.Sine1_Amp + slexVariantSourceAndSink_P.Sine1_Bias; #elif V == 2 /* Sum: '<Root>/Add1' incorporates: * Sin: '<Root>/Sine2' * Sin: '<Root>/Sine3' */ rtb_Sine6 = (sin(((real_T)slexVariantSourceAndSink_DW.counter_i + slexVariantSourceAndSink_P.Sine2_Offset) * 2.0 * 3.1415926535897931 / slexVariantSourceAndSink_P.Sine2_NumSamp) * slexVariantSourceAndSink_P.Sine2_Amp + slexVariantSourceAndSink_P.Sine2_Bias) + (sin(((real_T) slexVariantSourceAndSink_DW.counter_f + slexVariantSourceAndSink_P.Sine3_Offset) * 2.0 * 3.1415926535897931 / slexVariantSourceAndSink_P.Sine3_NumSamp) * slexVariantSourceAndSink_P.Sine3_Amp + slexVariantSourceAndSink_P.Sine3_Bias); #endif /* End of Sin: '<Root>/Sine1' */ /* Gain: '<Root>/Gain3' incorporates: * Outport: '<Root>/Out1' */ #if V == 1 || V == 2 slexVariantSourceAndSink_Y.Out1 = slexVariantSourceAndSink_P.Gain3_Gain * rtb_Sine6; #endif /* End of Gain: '<Root>/Gain3' */ /* Sin: '<Root>/Sine5' */ rtb_Sine6 = sin(((real_T)slexVariantSourceAndSink_DW.counter_d + slexVariantSourceAndSink_P.Sine5_Offset) * 2.0 * 3.1415926535897931 / slexVariantSourceAndSink_P.Sine5_NumSamp) * slexVariantSourceAndSink_P.Sine5_Amp + slexVariantSourceAndSink_P.Sine5_Bias; /* Gain: '<Root>/Gain5' incorporates: * Gain: '<Root>/Gain4' * SignalConversion generated from: '<Root>/Sum' * */ #if W == 1 rtb_Sine6 = slexVariantSourceAndSink_P.Gain4_Gain * rtb_Sine6 * slexVariantSourceAndSink_P.Gain5_Gain; rtb_VM_Conditional_Signal_Sum_1 = rtb_Sine6; #else /* SignalConversion generated from: '<Root>/Sum' */ rtb_VM_Conditional_Signal_Sum_1 = 0.0; #endif /* End of Gain: '<Root>/Gain5' */ /* Outport: '<Root>/Out2' incorporates: * Sin: '<Root>/Sine6' * Sum: '<Root>/Sum' */ slexVariantSourceAndSink_Y.Out2 = (sin(((real_T) slexVariantSourceAndSink_DW.counter_g + slexVariantSourceAndSink_P.Sine6_Offset) * 2.0 * 3.1415926535897931 / slexVariantSourceAndSink_P.Sine6_NumSamp) * slexVariantSourceAndSink_P.Sine6_Amp + slexVariantSourceAndSink_P.Sine6_Bias) + rtb_VM_Conditional_Signal_Sum_1; /* Outport: '<Root>/Out3' */ #if W == 1 slexVariantSourceAndSink_Y.Out3 = rtb_Sine6; #endif /* End of Outport: '<Root>/Out3' */ /* Update for Sin: '<Root>/Sine1' incorporates: * Sin: '<Root>/Sine2' * Sin: '<Root>/Sine3' */ #if V == 1 slexVariantSourceAndSink_DW.counter++; if (slexVariantSourceAndSink_DW.counter == slexVariantSourceAndSink_P.Sine1_NumSamp) { slexVariantSourceAndSink_DW.counter = 0; } #elif V == 2 /* Update for Sin: '<Root>/Sine2' */ slexVariantSourceAndSink_DW.counter_i++; if (slexVariantSourceAndSink_DW.counter_i == slexVariantSourceAndSink_P.Sine2_NumSamp) { slexVariantSourceAndSink_DW.counter_i = 0; } /* Update for Sin: '<Root>/Sine3' */ slexVariantSourceAndSink_DW.counter_f++; if (slexVariantSourceAndSink_DW.counter_f == slexVariantSourceAndSink_P.Sine3_NumSamp) { slexVariantSourceAndSink_DW.counter_f = 0; } #endif /* End of Update for Sin: '<Root>/Sine1' */ /* Update for Sin: '<Root>/Sine6' */ slexVariantSourceAndSink_DW.counter_g++; if (slexVariantSourceAndSink_DW.counter_g == slexVariantSourceAndSink_P.Sine6_NumSamp) { slexVariantSourceAndSink_DW.counter_g = 0; } /* End of Update for Sin: '<Root>/Sine6' */ /* Update for Sin: '<Root>/Sine5' */ slexVariantSourceAndSink_DW.counter_d++; if (slexVariantSourceAndSink_DW.counter_d == slexVariantSourceAndSink_P.Sine5_NumSamp) { slexVariantSourceAndSink_DW.counter_d = 0; } /* End of Update for Sin: '<Root>/Sine5' */ } }
The variables rtb_Sine4
and rtb_VariantMerge_For_Variant_So
hold
the input values to the Variant Source blocks. Notice
that the code for these variables is conditional. The variables inline_variants_example_Y.Out1
and inline_variants_example_Y.Out2
hold
the output values of the Variant Source blocks. Notice
that the code for these variables is not conditional.
You can generate code in which blocks connected to the input and the output of a Variant Source block are conditional.
For Variant Source
, open the Block
Parameters dialog box. Select the parameter Allow zero active
variant controls.
For Variant Source 1
, open the
Block Parameters dialog box. Select the parameter Allow
zero active variant controls.
When you select Allow zero active variant controls parameter, you can generate code for a model containing Variant Source and Variant Sink blocks even when you specify a value for a variant control variable that does not allow for an active variant. Choosing a value for a variant control variable that does not allow for an active variant and not selecting the Allow zero active variant controls parameter, produces an error.
Generate code for inline_variants_example
.
Notice in the inline_variants_example.c
file, that
the code for the variables inline_variants_example_Y.Out1
and inline_variants_example_Y.Out2
is
conditional.
/* Model step function */ void inline_variants_example_step(void) { ... #if V == 1 || V == 2 inline_variants_example_Y.Out1 = 3.0 * rtb_Sine4; #endif /* V == 1 || V == 2 */ ... #if (V == 1 && W == 1) || (V == 2 && W == 1) || W == 2 inline_variants_example_Y.Out2 = 4.0 * rtb_VariantMerge_For_Variant_So; #endif /* (V == 1 && W == 1) || (V == 2 && W == 1) || W == 2 */ ...