Main Content

Use Simulink.Parameter Type of Variant Control Variables for Code Generation in Variant Blocks

If you intend to generate code for a model containing variant blocks, specify variant control variables as Simulink.Parameter objects. The Simulink.Parameter objects allow you to specify other attributes, such as data type and storage class, and control the appearance and placement of variant control variables in generated code.

You can define a variant control variable of type Simulink.Parameter only in the base workspace or in a data dictionary. Defining Simulink.Parameter type of variant control variables in a mask or model workspace is not supported. For more information on storage locations for variant control variables, see Storage Locations for Variant Control Variables (Operands) in Variant Blocks.

Simulink.Parameter objects within structures and that have data types other than Simulink.Bus objects are not supported.

Prerequisites

To learn more about how to use Variant Subsystems in Simulink®, Implement Variations in Separate Hierarchy Using Variant Subsystems example.

Explore the Model

1. Open the slexVariantSubsystems model.

model = "slexVariantSubsystems"
open_system(model);
model = 

    "slexVariantSubsystems"

2.In the MATLAB® Editor, define a Simulink.Parameter object, V and set its value to 1. The parameter object V uses the custom storage class ExportedGlobal to define V in an external file. You can further control the appearance and placement of V and prevent optimizations from eliminating storage for V using the storage class property. For more information, see Storage Classes for Different Variant Activation Times.

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

Note: Variant control variables defined as Simulink.Parameter objects can have any of the storage classes listed in Storage Classes for Different Variant Activation Times. You can also convert a scalar variant control variable into a Simulink.Parameter object. For more information, see Convert Variant Control Variables into Simulink.Parameter Objects.

3. Specify the Simulink.Parameter object as the variant control variable in the Block Parameters dialog box of the Controller block. Also, change the Variant activation time to code compile.

linearBlkPath = "slexVariantSubsystems/Controller/Linear Controller";
nonLinearBlkPath = "slexVariantSubsystems/Controller/Nonlinear Controller";
set_param(linearBlkPath,"VariantControl","V == 1");
set_param(nonLinearBlkPath,"VariantControl","V == 2");
set_param(model+"/Controller","VariantActivationTime","startup");

Ensure that the Linear Controller and NonLinear Controller blocks are atomic.

set_param(linearBlkPath,"TreatAsAtomicUnit","on");
set_param(nonLinearBlkPath,"TreatAsAtomicUnit","on");

4. During simulation, the Linear Controller block becomes active. Double-click the Controller block to see the active choice.

sim(model);

Similarly, if you change the value of V to 2, the Nonlinear Controller block becomes active during simulation.

V.Value = 2;
sim(model);

Read Value of Variant Control Variable from Custom Header File

1. Specify the system target file as grt.tlc.

set_param(model,"SystemTargetFile","grt.tlc");

2. In the Apps tab of the toolstrip, navigate to Simulink Coder.

3. In the C code tab, select Build > Generate code. Alternatively, enter this command in the Command Window.

slbuild(model);
### Starting build procedure for: slexVariantSubsystems
### Successful completion of build procedure for: slexVariantSubsystems

Build Summary

Top model targets:

Model                  Build Reason                                         Status                        Build Duration
========================================================================================================================
slexVariantSubsystems  Information cache folder or artifacts were missing.  Code generated and compiled.  0h 0m 10.864s 

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

As the storage class for variable V is configured as ExportedGlobal, the code generator creates global definitions and declarations for V in the generated code. You can then access the value of V by using #include in your external code. This value of V determines which of the specified variants must be activated in the embedded executable. However, the value of V specified in the Simulink.Parameter object is still used during simulation, while the value specified in the external code is used for compiling the generated code and activating the desired variant in the embedded executable.

The generated code contains both Linear and Nonlinear choices in if and else if conditions because of the startup activation time.

cfile=fullfile(pwd, 'slexVariantSubsystems_grt_rtw', 'slexVariantSubsystems.c');
coder.example.extractLines(cfile, '/* Model step', '/* Model initialize', 1, 0);
/* Model step function */
void slexVariantSubsystems_step(void)
{
  real_T denAccum;
  real_T rtb_VariantMergeForOutportOut1;
  real_T rtb_sine1;
  real_T rtb_sine3;
  real_T sine2;

  /* Sin: '<Root>/sine1' */
  if (slexVariantSubsystems_DW.systemEnable != 0) {
    rtb_sine1 = slexVariantSubsystems_M->Timing.taskTime0;
    slexVariantSubsystems_DW.lastSin = sin(1.0471975511965976 * rtb_sine1);
    slexVariantSubsystems_DW.lastCos = cos(1.0471975511965976 * rtb_sine1);
    slexVariantSubsystems_DW.systemEnable = 0;
  }

  rtb_sine1 = ((slexVariantSubsystems_DW.lastSin * 0.99994516936551214 +
                slexVariantSubsystems_DW.lastCos * -0.010471784116245792) *
               0.99994516936551214 + (slexVariantSubsystems_DW.lastCos *
    0.99994516936551214 - slexVariantSubsystems_DW.lastSin *
    -0.010471784116245792) * 0.010471784116245792) * 0.1;

  /* End of Sin: '<Root>/sine1' */

  /* Sin: '<Root>/sine2' */
  if (slexVariantSubsystems_DW.systemEnable_e != 0) {
    sine2 = slexVariantSubsystems_M->Timing.taskTime0;
    slexVariantSubsystems_DW.lastSin_g = sin(sine2);
    slexVariantSubsystems_DW.lastCos_n = cos(sine2);
    slexVariantSubsystems_DW.systemEnable_e = 0;
  }

  /* Sin: '<Root>/sine2' */
  sine2 = ((slexVariantSubsystems_DW.lastSin_g * 0.99995000041666526 +
            slexVariantSubsystems_DW.lastCos_n * -0.0099998333341666645) *
           0.99995000041666526 + (slexVariantSubsystems_DW.lastCos_n *
            0.99995000041666526 - slexVariantSubsystems_DW.lastSin_g *
            -0.0099998333341666645) * 0.0099998333341666645) * 2.0;

  /* Sin: '<Root>/sine3' */
  if (slexVariantSubsystems_DW.systemEnable_b != 0) {
    rtb_sine3 = slexVariantSubsystems_M->Timing.taskTime0;
    slexVariantSubsystems_DW.lastSin_i = sin(0.52359877559829882 * rtb_sine3);
    slexVariantSubsystems_DW.lastCos_i = cos(0.52359877559829882 * rtb_sine3);
    slexVariantSubsystems_DW.systemEnable_b = 0;
  }

  rtb_sine3 = ((slexVariantSubsystems_DW.lastSin_i * 0.99998629224742674 +
                slexVariantSubsystems_DW.lastCos_i * -0.00523596383141958) *
               0.99998629224742674 + (slexVariantSubsystems_DW.lastCos_i *
    0.99998629224742674 - slexVariantSubsystems_DW.lastSin_i *
    -0.00523596383141958) * 0.00523596383141958) * 0.3;

  /* End of Sin: '<Root>/sine3' */

  /* Outputs for Atomic SubSystem: '<Root>/Controller' */
  if (V == 1) {
    /* Outputs for Atomic SubSystem: '<S1>/Linear Controller' */
    /* Update for DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn' */
    denAccum = (sine2 - 0.09 *
                slexVariantSubsystems_DW.DiscreteTransferFcn_states[0]) - 0.5 *
      slexVariantSubsystems_DW.DiscreteTransferFcn_states[1];

    /* Sum: '<S2>/Add' incorporates:
     *  DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn'
     */
    rtb_VariantMergeForOutportOut1 = ((0.7 *
      slexVariantSubsystems_DW.DiscreteTransferFcn_states[1] +
      slexVariantSubsystems_DW.DiscreteTransferFcn_states[0]) + rtb_sine1) +
      rtb_sine3;

    /* Update for DiscreteTransferFcn: '<S2>/Discrete Transfer Fcn' */
    slexVariantSubsystems_DW.DiscreteTransferFcn_states[1] =
      slexVariantSubsystems_DW.DiscreteTransferFcn_states[0];
    slexVariantSubsystems_DW.DiscreteTransferFcn_states[0] = denAccum;

    /* End of Outputs for SubSystem: '<S1>/Linear Controller' */
    /* End of Outputs for SubSystem: '<Root>/Controller' */
  } else if (V == 2) {
    /* Outputs for Atomic SubSystem: '<S1>/Nonlinear Controller' */
    /* Sum: '<S3>/Add' incorporates:
     *  Lookup_n-D: '<S3>/1-D Lookup Table'
     *  Sin: '<Root>/sine2'
     */
    rtb_VariantMergeForOutportOut1 = (rtb_sine1 + look1_binlxpw(sine2,
      slexVariantSubsystems_ConstP.uDLookupTable_bp01Data,
      slexVariantSubsystems_ConstP.uDLookupTable_tableData, 10U)) + rtb_sine3;

    /* End of Outputs for SubSystem: '<S1>/Nonlinear Controller' */
  }

  /* Outport: '<Root>/Out1' */
  slexVariantSubsystems_Y.Out1 = rtb_VariantMergeForOutportOut1;

  /* Update for Sin: '<Root>/sine1' */
  rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin;
  slexVariantSubsystems_DW.lastSin = slexVariantSubsystems_DW.lastSin *
    0.99994516936551214 + slexVariantSubsystems_DW.lastCos *
    0.010471784116245792;
  slexVariantSubsystems_DW.lastCos = slexVariantSubsystems_DW.lastCos *
    0.99994516936551214 - rtb_VariantMergeForOutportOut1 * 0.010471784116245792;

  /* Update for Sin: '<Root>/sine2' */
  rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin_g;
  slexVariantSubsystems_DW.lastSin_g = slexVariantSubsystems_DW.lastSin_g *
    0.99995000041666526 + slexVariantSubsystems_DW.lastCos_n *
    0.0099998333341666645;
  slexVariantSubsystems_DW.lastCos_n = slexVariantSubsystems_DW.lastCos_n *
    0.99995000041666526 - rtb_VariantMergeForOutportOut1 * 0.0099998333341666645;

  /* Update for Sin: '<Root>/sine3' */
  rtb_VariantMergeForOutportOut1 = slexVariantSubsystems_DW.lastSin_i;
  slexVariantSubsystems_DW.lastSin_i = slexVariantSubsystems_DW.lastSin_i *
    0.99998629224742674 + slexVariantSubsystems_DW.lastCos_i *
    0.00523596383141958;
  slexVariantSubsystems_DW.lastCos_i = slexVariantSubsystems_DW.lastCos_i *
    0.99998629224742674 - rtb_VariantMergeForOutportOut1 * 0.00523596383141958;

  /* Matfile logging */
  rt_UpdateTXYLogVars(slexVariantSubsystems_M->rtwLogInfo,
                      (&slexVariantSubsystems_M->Timing.taskTime0));

  /* signal main to stop simulation */
  {                                    /* Sample time: [0.01s, 0.0s] */
    if ((rtmGetTFinal(slexVariantSubsystems_M)!=-1) &&
        !((rtmGetTFinal(slexVariantSubsystems_M)-
           slexVariantSubsystems_M->Timing.taskTime0) >
          slexVariantSubsystems_M->Timing.taskTime0 * (DBL_EPSILON))) {
      rtmSetErrorStatus(slexVariantSubsystems_M, "Simulation finished");
    }
  }

  /* Update absolute time for base rate */
  /* The "clockTick0" counts the number of times the code of this task has
   * been executed. The absolute time is the multiplication of "clockTick0"
   * and "Timing.stepSize0". Size of "clockTick0" ensures timer will not
   * overflow during the application lifespan selected.
   * Timer of this task consists of two 32 bit unsigned integers.
   * The two integers represent the low bits Timing.clockTick0 and the high bits
   * Timing.clockTickH0. When the low bit overflows to 0, the high bits increment.
   */
  if (!(++slexVariantSubsystems_M->Timing.clockTick0)) {
    ++slexVariantSubsystems_M->Timing.clockTickH0;
  }

  slexVariantSubsystems_M->Timing.taskTime0 =
    slexVariantSubsystems_M->Timing.clockTick0 *
    slexVariantSubsystems_M->Timing.stepSize0 +
    slexVariantSubsystems_M->Timing.clockTickH0 *
    slexVariantSubsystems_M->Timing.stepSize0 * 4294967296.0;
}

If you are using Embedded Coder® with an ERT-based system target file, for example, ert.tlc, and you want to generate a code that guards the variant choices in preprocessor conditionals #if and #elif, specify the Variant activation time parameter of the variant blocks to code compile. These preprocessor conditionals enable you to conditionally compile variant choices as described in Compile Code Conditionally for Variations of Component Represented Using Variant Block.

Related Topics