Run Executables for Variant Blocks Without Recompiling Code for Changing Active Choices Using Startup Activation Time - MATLAB & Simulink (original) (raw)

This example explains how to generate code for multiple implementations of a component represented using a Variant Subsystem block with [startup](../../simulink/var/activate-variant-during-different-stages-of-simulation-and-code-generation-workflow.html#mw%5F9f3ca965-7d50-4cb1-9c47-99f759a579cf) activation time. With startup variants, the different variations, referred to as variant choices, are guarded by if and else if conditional statements in the generated code. These conditional statements enable conditional execution, selectively including or excluding code based on the variant condition that evaluates to true during code execution. By including multiple variant choices, you are not required to recompile code each time the value of the variant control variable changes. This approach also allows for analyzing variant choices for potential incompatibilities, such as data type and dimension mismatches, prior to simulation and code generation. For more information on variant activation time, see Activate Variant During Different Stages of Simulation and Code Generation Workflow.

Prerequisites

To learn more about how to use Variant Source and Variant Sink blocks in SimulinkĀ®, see Propagate Variant Conditions to Define Variant Regions Using Variant Source and Variant Sink Blocks.

To get started with variant code generation, see Generate Code for Variant Source and Variant Sink Blocks.

Represent Variant Choices in Variant Source and Variant Sink Blocks

The slexVariantSourceAndSink model contains two Variant Source blocks, Variant Source1 and Variant Source2, and a Variant Sink block. The variant conditions at the input ports and the output ports of the Variant Source and the Variant Sink blocks determine the activation and deactivation of the blocks connected to them.

model = "slexVariantSourceAndSink"; open_system(model);

Specify Variant Controls for Variant Choice Selection

Each variant choice in the model is associated with a variant control. Variant controls determine which variant choice is active. By changing the value of a variant control, you can switch the active variant choice. While each variant choice is associated with a variant control, only one variant control can evaluate to true. When a variant control evaluates to true, Simulink activates the variant choice that corresponds to that variant control. For more information, see Introduction to Variant Controls.

1. Right-click the variant badge on the variant blocks and select Block Parameters. In this example, each variant choice in the variant blocks is associated with variant control variables V and W. The variables V and W are [Simulink.Parameter](../../simulink/slref/simulink.parameter.html) objects. The storage classes of variables V and W are set to ExportedGlobal to generate global variable definitions and declarations for both V and W in the generated code.

V = Simulink.Parameter; V.Value = 1; V.DataType = "int32"; V.CoderInfo.StorageClass = "ExportedGlobal";

W = Simulink.Parameter; W.Value = 1; W.DataType = "int32"; W.CoderInfo.StorageClass = "ExportedGlobal";

You can access the values of V and W using #include statements in your external code. You must specify the source file of your external code in Model Settings > Code Generation > Custom Code > Code information > Source files.

configSet = getActiveConfigSet(gcs); set_param(configSet,CustomSource="ReadVarControl.c") set_param(configSet,CustomHeaderCode="ReadVarControl.h")

In this example, the ReadVarControl.c file sets the values of V and W to 2, as shown. You can modify the code to read the values of V and W from a sensor or a hardware, if required.

/* Copyright 2021-2024 The MathWorks, Inc. */ #include "rtwtypes.h" extern int32_T V; extern int32_T W; void SetValueOfVarControls(void) { // The values of V and W can be read from a sensor or hardware // for simplicity it is hardcoded. V = 2; W = 2; }

For the list of storage classes supported for the Simulink.Parameter type of variant control variables, see Storage Classes for Different Variant Activation Times.

2. In the MATLABā„¢ Command Window, set the value of V to 1 and W to 1. Simulate the model. During simulation, the variant conditions V == 1 and W == 1 evaluate to true, activating the first input ports of the Variant Source2 and Variant Sink blocks.

V.Value = 1; W.Value = 1; sim(model);

3. Set the value of V to 2 and W to 1. Simulate the model again. During simulation, the variant conditions V == 2 and W == 1 evaluate to true, activating the first input port of the Variant Source1 block and the second input port of the Variant Source2 block.

V.Value = 2; W.Value = 1; sim(model);

For information on the variant conditions that activate the Variant Source1 and the Variant Sink blocks, see Propagate Variant Conditions to Define Variant Regions Using Variant Source and Variant Sink Blocks.

This mechanism allows you to swap the active and inactive choices to the variant blocks without modifying the model structure, making the model flexible and adaptable to different scenarios.

If the Simulink.Parameter objects are not suitable for your requirements, you can use different types of variant controls as described in Compare Different Types of Variant Control Modes in Variant Blocks.

Configure Model to Generate Code with if Conditional Statements

By default, Simulink supports generating code only for a specific choice of a variant block. You can customize your model to generate code for multiple variant choices of the block using the Variant activation time parameter. Using the Variant activation time parameter enables you to set the active variant choice at different stages of simulation and code generation. This can improve the speed of simulation and allow you to reuse the artifacts from previous runs in code generation. It also enables you to analyze variant choices for incompatibilities, such as data type and dimension mismatches, prior to simulation and code generation. For more information, see Activate Variant During Different Stages of Simulation and Code Generation Workflow.

To generate code for all the variant choices of the variant blocks guarded by if and else if conditional statements, ensure that the Variant activation time parameter of the blocks are set to startup.

set_param(model+"/Variant Source1","VariantActivationTime","startup"); set_param(model+"/Variant Source2","VariantActivationTime","startup"); set_param(model+"/Variant Sink","VariantActivationTime","startup");

1. In the Apps tab of the toolstrip, navigate to Simulink Coder or Embedded Coder.

2. In the C code tab, select Build > Generate code. Observe that the generated code includes the logic for all the variant choice blocks.

For detailed information on settings to generate code, see Generate Code Using Simulink Coder.

Searching for referenced models in model 'slexVariantSourceAndSink'.

Total of 1 models to build.

Starting build procedure for: slexVariantSourceAndSink

Successful completion of build procedure for: slexVariantSourceAndSink

Build Summary

Top model targets:

Model Build Reason Status Build Duration

slexVariantSourceAndSink Information cache folder or artifacts were missing. Code generated and compiled. 0h 0m 21.645s

1 of 1 models built (0 models already up to date) Build duration: 0h 0m 23.43s

Review Generated Code

In the C Code tab of the toolstrip, select Open Report. Locate and select the slexVariantSourceAndSink.c file from the left pane. In the generated code:

The latching is applicable to externally accessible variant control variables of these types:

- [Simulink.Parameter](../../simulink/slref/simulink.parameter.html) objects with built-in or custom storage classes that are unstructured

- [Simulink.VariantControl](../../simulink/slref/simulink.variantcontrol-class.html) objects used in variant blocks, but not in variant parameters

The latched variables are declared as extern or static based on these conditions:

- If a single file in the generated code uses the variant control variable, the coder declares the variable as static, limiting its scope and visibility to that file. For example, static int32_T rtpVLatch_V.

- If multiple files in the generated code use a variant control variable, the coder declares the variable as extern, indicating that its actual definition is located elsewhere, allowing multiple files to reference the same variable. For example, extern int32_T rtpVLatch_V.

In this example, the file defines variant controls as static to read the values of V and W from an external source ReadVarControl.c.

cfile=fullfile(pwd, "slexVariantSourceAndSink_ert_rtw", "slexVariantSourceAndSink.c"); coder.example.extractLines(cfile, "/* Latched Startup Variant Control Variables */", " * '/W'", 1, 1);

/* Latched Startup Variant Control Variables */ static int32_T rtpVLatch_W; static int32_T rtpVLatch_V;

/* Exported block parameters / int32_T V = 2; / Variable: V * Referenced by: * '/Scope1' * '/Out1' * '/Gain3' * '/Sine1' * '/Sine2' * '/Sine3' * '/Add1' * '/V' / int32_T W = 1; / Variable: W * Referenced by: * '/Gain2' * '/Subtract' * '/Terminator' * '/Out3' * '/Gain5' * '/Sine3' * '/Sum' * '/W'

Observe the use of #include in your external code ReadVarControl.c to set the values of V and W to 2.

/* Copyright 2021-2024 The MathWorks, Inc. */ #include "rtwtypes.h" extern int32_T V; extern int32_T W; void SetValueOfVarControls(void) { // The values of V and W can be read from a sensor or hardware // for simplicity it is hardcoded. V = 2; W = 2; }

cfile=fullfile(pwd, "slexVariantSourceAndSink_ert_rtw", "slexVariantSourceAndSink.c"); coder.example.extractLines(cfile, '/* Model step', '/* Model initialize', 1, 0);

/* Model step function */ void step(void) { real_T rtb_Gain5; real_T rtb_Sine6; real_T rtb_VM_Conditional_Signal_Sum_1;

/* Sin: '/Sine1' incorporates:

/* End of Sin: '/Sine1' */

/* Sin: '/Sine3' / if ((rtpVLatch_V == 2) && (rtpVLatch_W == 1)) { / VariantMerge generated from: '/Variant Source1' incorporates: * Sin: '/Sine3' */ slexVariantSourceAndSink_B.VariantMerge_For_Variant_Source = 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; }

/* End of Sin: '/Sine3' */

/* Sum: '/Add1' */ if (rtpVLatch_V == 2) { rtb_Sine6 = rtb_Gain5 + slexVariantSourceAndSink_B.VariantMerge_For_Variant_Source; }

/* End of Sum: '/Add1' */

/* Gain: '/Gain3' incorporates:

/* End of Gain: '/Gain3' */

/* Sin: '/Sine5' */ rtb_Gain5 = 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: '/Gain5' incorporates:

/* End of Gain: '/Gain5' */

/* Outport: '/Out2' incorporates:

/* Outport: '/Out3' */ if (rtpVLatch_W == 1) { slexVariantSourceAndSink_Y.Out3 = rtb_Gain5; }

/* End of Outport: '/Out3' */

/* Update for Sin: '/Sine1' incorporates:

/* End of Update for Sin: '/Sine1' */

/* Update for Sin: '/Sine3' */ if ((rtpVLatch_V == 2) && (rtpVLatch_W == 1)) { slexVariantSourceAndSink_DW.counter_f++; if (slexVariantSourceAndSink_DW.counter_f == slexVariantSourceAndSink_P.Sine3_NumSamp) { slexVariantSourceAndSink_DW.counter_f = 0; } }

/* End of Update for Sin: '/Sine3' */

/* Update for Sin: '/Sine6' */ slexVariantSourceAndSink_DW.counter_g++; if (slexVariantSourceAndSink_DW.counter_g == slexVariantSourceAndSink_P.Sine6_NumSamp) { slexVariantSourceAndSink_DW.counter_g = 0; }

/* End of Update for Sin: '/Sine6' */

/* Update for Sin: '/Sine5' */ slexVariantSourceAndSink_DW.counter_d++; if (slexVariantSourceAndSink_DW.counter_d == slexVariantSourceAndSink_P.Sine5_NumSamp) { slexVariantSourceAndSink_DW.counter_d = 0; }

/* End of Update for Sin: '/Sine5' */ }

cfile=fullfile(pwd, "slexVariantSourceAndSink_ert_rtw", "slexVariantSourceAndSink.c"); coder.example.extractLines(cfile, "/* Model initialize", "/* Model terminate", 1, 0);

/* Model initialize function / void initialize(void) { / Registration code */

/* initialize error status */ rtmSetErrorStatus(slexVariantSourceAndSink_M, (NULL));

/* block I/O */ (void) memset(((void *) &slexVariantSourceAndSink_B), 0, sizeof(B_slexVariantSourceAndSink_T));

/* states (dwork) */ (void) memset((void *)&slexVariantSourceAndSink_DW, 0, sizeof(DW_slexVariantSourceAndSink_T));

/* external outputs */ (void)memset(&slexVariantSourceAndSink_Y, 0, sizeof (ExtY_slexVariantSourceAndSink_T));

/* initialization code for latched startup variant control variables */ rtpVLatch_W = W; rtpVLatch_V = V;

/* InitializeConditions for Sin: '/Sine1' */ slexVariantSourceAndSink_DW.counter = 0;

/* InitializeConditions for Sin: '/Sine2' */ slexVariantSourceAndSink_DW.counter_i = 0;

/* InitializeConditions for Sin: '/Sine3' */ slexVariantSourceAndSink_DW.counter_f = 0;

/* InitializeConditions for Sin: '/Sine6' */ slexVariantSourceAndSink_DW.counter_g = 0;

/* InitializeConditions for Sin: '/Sine5' */ slexVariantSourceAndSink_DW.counter_d = 0; slexVaria_startupVariantChecker(); }

To generate code that includes logic for active and inactive choices, allowing for conditional compilation based on specific choices, see Compile Code Conditionally for Variations of Component Represented Using Variant Block.

Startup Variants with Continuous State Blocks

This example shows you how to simulate and generate code for variant choices of startup variant blocks comprising of continuous state blocks. In the generated code, the derivatives of the all the continuous blocks are set to zero and then assigned appropriate values in regular if statements.

Consider this model containing a Variant Source block with the startup activation time. The model contains the continuous blocks Integ1 with its Initial condition set to 0 and Integ2 with its Initial condition set to 1. The Variant Source block enables you to activate or deactivate certain parts of your model including the blocks with continuous states. During simulation, the input port of the Variant Source block becomes active when V == 1 evaluates to true, and the input port of the Variant Source block becomes inactive when V == 1 evaluates to false. The states of the inactive continuous blocks are initialized to zero.

To generate the code for the variant choices of startup variant blocks comprising of continuous state blocks, set the solver type to Fixed-step and the code generation target to a non-ERT target such as grt.tlc. The derivatives of the continuous blocks are set to zero in the generated code.

void test_cont2_abs_derivatives(void) { XDot_test_cont2_abs_T *_rtXdot; _rtXdot = ((XDot_test_cont2_abs_T *) test_cont2_abs_M->derivs);

/* Derivatives for Integrator: '/Integrator1' */ _rtXdot->Integrator1_CSTATE = 0.0;

/* Derivatives for Integrator: '/Integrator' */ _rtXdot->Integrator_CSTATE = 0.0;

/* Derivatives for Integrator: '/Integrator1' incorporates:

See Also

Considerations and Limitations for startup Variant Activation Time

Topics