Represent Variant Source and Sink Blocks in Generated Code

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.

Represent Variant Source and Variant Sink blocks in Simulink

This example shows how Variant Source blocks make model elements conditional.

  1. 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.

  2. 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.

  3. Make four copies of the Sine Wave Function block.

  4. Connect and name the blocks as shown.

  5. Insert values of 2, 3, and 4 in the Gain2, Gain3, and Gain4 blocks, respectively.

  6. Give the model the name inline_variants_example.

  7. Open the Block Parameters dialog box for Variant Source.

  8. In the Variant control column, for Port 1, replace Variant_1 with V==1. For Port 2, replace Variant_2 with V==2.

  9. Open the Block Parameters dialog box for Variant Source1.

  10. In the Variant control column, replace Variant_1 with W==1. For Port 2, replace Variant_2 with W==2.

  11. 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).

  12. 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.

Specify Conditions That Control Variant Choice Selection

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.

  1. In the Modeling tab of the Simulink toolstrip, click Model Settings.

  2. Select the Code Generation pane, and set System target file to ert.tlc.

  3. In your model, open the block parameters dialog box for Variant Source.

  4. 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.

  5. Clear the Allow zero active variant controls parameter.

  6. Open the Block Parameters dialog box for Variant Source 1. Repeat steps 5 through 7.

  7. Build the model. When code generation is complete, the generated code is displayed in the Code view.

Review the Generated Code

  1. In the Code view, select the inline_variants_example.c file.

  2. 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.

Generate Code with Zero Active Variant Controls

You can generate code in which blocks connected to the input and the output of a Variant Source block are conditional.

  1. For Variant Source, open the Block Parameters dialog box. Select the parameter Allow zero active variant controls.

  2. 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 */
 
 ...

Related Topics