Main Content

Generate Code for Variant Elements Within Buses

This example explains how to generate code for buses with elements having different variant conditions.

Prerequisites

To learn more about how to use Variant bus, see Variant Elements Within Buses.

Explore the Model

Open the slexVariantBus model.

model = "slexVariantBus";
open_system(model)

The model consists of two signals, a and b, which are merged into a bus. These signals have variant conditions, V == 1 and W == 1, respectively. During simulation, the bus selector receives two signals with variant conditions V == 1 and W == 1.

Simulate the model.

sim(model)
ans = 

  Simulink.SimulationOutput:

     SimulationMetadata: [1x1 Simulink.SimulationMetadata] 
           ErrorMessage: [0x0 char] 

When you select individual signals from the bus using the bus selector, the corresponding variant condition is also selected. There is no difference in the propagation behavior of variant conditions when passing through virtual or nonvirtual buses. For more information on buses, see Simulink Bus Capabilities.

Generate Code for Variant Buses with Regular Conditions if and else if

1. To generate code with the variant choices guarded by conditions if and else if, specify the Variant activation time of the variant blocks to startup.

set_param(model+"/VS2","VariantActivationTime","startup");
set_param(model+"/VS3","VariantActivationTime","startup");

3. On the Apps tab of toolstrip, click Simulink Coder. In the C code tab, select Build > Generate code. Alternatively, enter this command in the Command Window.

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

Build Summary

Top model targets:

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

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

Note

When generating code with conditions, the bus types and hierarchies of all bus inputs must be the same. For information on bus hierarchies, see View Bus Hierarchy.

Virtual and Nonvirtual Bus in Conditions if and else if

A virtual bus does not appear as a structure or any other coherent unit in the generated code. A separate copy of any algorithm that manipulates the bus exists for each bus element. In general, virtual buses do not affect the generated code. In this example, the buses a and b pass through a Unit Delay block. The generated slexVariantBus.h file defines one Unit Delay block for each element of the virtual bus. The Unit Delay blocks are defined unconditionally to avoid code execution with incorrect or unintended values when variant conditions are not met.

cfile=fullfile(pwd, "slexVariantBus_grt_rtw", "slexVariantBus.h");
coder.example.extractLines(cfile, "/* Block states (default storage) for system '<Root>' */", "/* Real-time Model Data Structure */", 1, 0);
coder.example.extractLines(cfile, "* Exported Global Parameters", "/* Model entry point functions */", 1, 0);
/* Block states (default storage) for system '<Root>' */
typedef struct {
  real_T UnitDelay_1_DSTATE;           /* '<Root>/Unit Delay' */
  real_T UnitDelay_2_DSTATE;           /* '<Root>/Unit Delay' */
} DW_slexVariantBus_T;

/* External inputs (root inport signals with default storage) */
typedef struct {
  real_T a;                            /* '<Root>/In1' */
  real_T b;                            /* '<Root>/In2' */
} ExtU_slexVariantBus_T;

/* External outputs (root outports fed by signals with default storage) */
typedef struct {
  real_T Out2;                         /* '<Root>/Out2' */
  real_T Out1;                         /* '<Root>/Out1' */
} ExtY_slexVariantBus_T;

 * Exported Global Parameters
 *
 * Note: Exported global parameters are tunable parameters with an exported
 * global storage class designation.  Code generation will declare the memory for
 * these parameters and exports their symbols.
 *
 */
extern int32_T V;                      /* Variable: V
                                        * Referenced by:
                                        *   '<Root>/In1'
                                        *   '<Root>/Bus Creator1'
                                        *   '<Root>/Bus Selector'
                                        *   '<Root>/Unit Delay'
                                        *   '<Root>/Out2'
                                        *   '<Root>/V'
                                        */
extern int32_T W;                      /* Variable: W
                                        * Referenced by:
                                        *   '<Root>/In2'
                                        *   '<Root>/Bus Creator1'
                                        *   '<Root>/Bus Selector'
                                        *   '<Root>/Unit Delay'
                                        *   '<Root>/Out1'
                                        *   '<Root>/W'
                                        */

The variant choices are enclosed in conditions if and else if.

cfile=fullfile(pwd, "slexVariantBus_grt_rtw", "slexVariantBus.c");
coder.example.extractLines(cfile, "/* Model step", "/* Model initialize", 1, 0);
/* Model step function */
void slexVariantBus_step(void)
{
  real_T rtb_a;
  real_T rtb_b;
  real_T rtb_b_b;

  /* UnitDelay generated from: '<Root>/Unit Delay' incorporates:
   *  SignalConversion generated from: '<Root>/Unit Delay'
   */
  if (V == 1) {
    rtb_b_b = slexVariantBus_DW.UnitDelay_1_DSTATE;
  } else if ((W == 1) && (V != 1)) {
    /* SignalConversion generated from: '<Root>/Unit Delay' */
    rtb_b_b = 0.0;
  }

  /* Outport: '<Root>/Out2' */
  if (V == 1) {
    slexVariantBus_Y.Out2 = rtb_b_b;
  }

  /* End of Outport: '<Root>/Out2' */

  /* UnitDelay generated from: '<Root>/Unit Delay' incorporates:
   *  SignalConversion generated from: '<Root>/Unit Delay'
   */
  if (W == 1) {
    rtb_b_b = slexVariantBus_DW.UnitDelay_2_DSTATE;
  } else if ((V == 1) && (W != 1)) {
    /* SignalConversion generated from: '<Root>/Unit Delay' */
    rtb_b_b = 0.0;
  }

  /* Outport: '<Root>/Out1' incorporates:
   *  Inport: '<Root>/In2'
   *  SignalConversion generated from: '<Root>/Bus Creator1'
   */
  if (W == 1) {
    slexVariantBus_Y.Out1 = rtb_b_b;
    rtb_b = slexVariantBus_U.b;
  }

  /* End of Outport: '<Root>/Out1' */

  /* SignalConversion generated from: '<Root>/Bus Creator1' incorporates:
   *  Inport: '<Root>/In1'
   */
  if (V == 1) {
    rtb_a = slexVariantBus_U.a;
  } else if ((W == 1) && (V != 1)) {
    /* SignalConversion generated from: '<Root>/Bus Creator1' */
    rtb_a = 0.0;
  }

  /* End of SignalConversion generated from: '<Root>/Bus Creator1' */

  /* SignalConversion generated from: '<Root>/Bus Creator1' */
  if ((V == 1) && (W != 1)) {
    rtb_b = 0.0;
  }

  /* End of SignalConversion generated from: '<Root>/Bus Creator1' */

  /* Update for UnitDelay generated from: '<Root>/Unit Delay' */
  if (V == 1) {
    slexVariantBus_DW.UnitDelay_1_DSTATE = rtb_a;
  }

  /* Update for UnitDelay generated from: '<Root>/Unit Delay' */
  if (W == 1) {
    slexVariantBus_DW.UnitDelay_2_DSTATE = rtb_b;
  }

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

  /* signal main to stop simulation */
  {                                    /* Sample time: [0.2s, 0.0s] */
    if ((rtmGetTFinal(slexVariantBus_M)!=-1) &&
        !((rtmGetTFinal(slexVariantBus_M)-slexVariantBus_M->Timing.taskTime0) >
          slexVariantBus_M->Timing.taskTime0 * (DBL_EPSILON))) {
      rtmSetErrorStatus(slexVariantBus_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 (!(++slexVariantBus_M->Timing.clockTick0)) {
    ++slexVariantBus_M->Timing.clockTickH0;
  }

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

A nonvirtual bus appears as a structure in the generated code, and only one copy exists of any algorithm that uses the bus. The use of a structure in the generated code can be helpful when tracing the correspondence between the model and the code. In this example, the buses a and b pass through a Unit Delay block. The Simulink.Bus object myBus appears in the generated slexVariantBus_types.h file as a structure for the nonvirtual buses. The bus object used in the bus is unconditional and it generates unguarded code, as shown below. For more information on bus objects, see Specify Bus Properties with Bus Objects.

typedef struct {
real_T a;
real_T b;
} myBus;

The generated slexVariantBus.h file defines one Unit Delay block for the nonvirtual bus, using the myBus structure.

/* Block states (default storage) for system '<Root>' */
typedef struct {
  myBus UnitDelay_DSTATE;              /* '<Root>/Unit Delay' */
} DW_slexVariantBus_T;

Virtual and Nonvirtual Bus in Preprocessor Conditionsals #if and #elif

When you generate code with the code compile variant activation time for a model using a ERT-based system target file, such as ert.tlc, the number of copies and the data type of the Unit Delay block in the generated code are the same as those for the startup activation time. Unlike with startup activation time, each definition of the Unit Delay block is enclosed in the preprocessor conditional #if of the corresponding element.

set_param(model,"SystemTargetFile","ert.tlc");
set_param(model+"/VS2","VariantActivationTime","code compile");
set_param(model+"/VS3","VariantActivationTime","code compile");

Make sure you change the value of the variant control variables V and W to a type supported for the code compile activation time when using Embedded Coder. For more details, see Storage Classes for Different Variant Activation Times.

V.CoderInfo.StorageClass = "Custom";
V.CoderInfo.CustomStorageClass = "Define";
V.CoderInfo.CustomAttributes.HeaderFile = "macros.h";
W.CoderInfo.StorageClass = "Custom";
W.CoderInfo.CustomStorageClass = "Define";
W.CoderInfo.CustomAttributes.HeaderFile = "macros.h";

This is a sample of the code generated for a virtual variant bus:

slbuild(model)
cfile=fullfile(pwd, "slexVariantBus_grt_rtw", "slexVariantBus.c");
coder.example.extractLines(cfile, "/* Model step", "/* Model initialize", 1, 0);
### Starting build procedure for: slexVariantBus
### Successful completion of build procedure for: slexVariantBus

Build Summary

Top model targets:

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

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

/* Model step function */
void slexVariantBus_step(void)
{
  real_T rtb_a;
  real_T rtb_b;
  real_T rtb_b_b;

  /* UnitDelay generated from: '<Root>/Unit Delay' incorporates:
   *  SignalConversion generated from: '<Root>/Unit Delay'
   */
  if (V == 1) {
    rtb_b_b = slexVariantBus_DW.UnitDelay_1_DSTATE;
  } else if ((W == 1) && (V != 1)) {
    /* SignalConversion generated from: '<Root>/Unit Delay' */
    rtb_b_b = 0.0;
  }

  /* Outport: '<Root>/Out2' */
  if (V == 1) {
    slexVariantBus_Y.Out2 = rtb_b_b;
  }

  /* End of Outport: '<Root>/Out2' */

  /* UnitDelay generated from: '<Root>/Unit Delay' incorporates:
   *  SignalConversion generated from: '<Root>/Unit Delay'
   */
  if (W == 1) {
    rtb_b_b = slexVariantBus_DW.UnitDelay_2_DSTATE;
  } else if ((V == 1) && (W != 1)) {
    /* SignalConversion generated from: '<Root>/Unit Delay' */
    rtb_b_b = 0.0;
  }

  /* Outport: '<Root>/Out1' incorporates:
   *  Inport: '<Root>/In2'
   *  SignalConversion generated from: '<Root>/Bus Creator1'
   */
  if (W == 1) {
    slexVariantBus_Y.Out1 = rtb_b_b;
    rtb_b = slexVariantBus_U.b;
  }

  /* End of Outport: '<Root>/Out1' */

  /* SignalConversion generated from: '<Root>/Bus Creator1' incorporates:
   *  Inport: '<Root>/In1'
   */
  if (V == 1) {
    rtb_a = slexVariantBus_U.a;
  } else if ((W == 1) && (V != 1)) {
    /* SignalConversion generated from: '<Root>/Bus Creator1' */
    rtb_a = 0.0;
  }

  /* End of SignalConversion generated from: '<Root>/Bus Creator1' */

  /* SignalConversion generated from: '<Root>/Bus Creator1' */
  if ((V == 1) && (W != 1)) {
    rtb_b = 0.0;
  }

  /* End of SignalConversion generated from: '<Root>/Bus Creator1' */

  /* Update for UnitDelay generated from: '<Root>/Unit Delay' */
  if (V == 1) {
    slexVariantBus_DW.UnitDelay_1_DSTATE = rtb_a;
  }

  /* Update for UnitDelay generated from: '<Root>/Unit Delay' */
  if (W == 1) {
    slexVariantBus_DW.UnitDelay_2_DSTATE = rtb_b;
  }

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

  /* signal main to stop simulation */
  {                                    /* Sample time: [0.2s, 0.0s] */
    if ((rtmGetTFinal(slexVariantBus_M)!=-1) &&
        !((rtmGetTFinal(slexVariantBus_M)-slexVariantBus_M->Timing.taskTime0) >
          slexVariantBus_M->Timing.taskTime0 * (DBL_EPSILON))) {
      rtmSetErrorStatus(slexVariantBus_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 (!(++slexVariantBus_M->Timing.clockTick0)) {
    ++slexVariantBus_M->Timing.clockTickH0;
  }

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

The generated slexVariantBus.h file defines one Unit Delay block for the nonvirtual bus, using the myBus structure. The Unit delay block is enclosed in a logical OR of the variant conditions, V == 1 and W == 1, from all the elements of the nonvirtual bus.

/* Block states (default storage) for system '<Root>' */
typedef struct {
#if V == 1 || W == 1
  myBus UnitDelay_DSTATE;              /* '<Root>/Unit Delay' */
#define DW_SLEXVARIANTBUS_T_VARIANT_EXISTS
#endif
#ifndef DW_SLEXVARIANTBUS_T_VARIANT_EXISTS
 uchar_T rt_unused;
#endif
} DW_slexVariantBus_T;

Variant Bus with Model Block

Consider this model containing a Model block.

This model has two signals a and b that have variant conditions, V == 1 and V == 2 respectively. The Model block provides the bus selector with two signals having two different variant conditions (V == 1 and V == 2). During simulation, the Model block propagates the variant conditions received from the referenced model mVarBus_bot at its output ports. The variant condition at each output port is the logical OR of the variant conditions, V == 1 || V == 2 from the variant choices of the mVarBus_bot model. For more information on variant condition propagation with Model blocks, see Propagate Variant Conditions to Enable or Disable Model or Subsystem References in Hierarchy.

Limitations

States logging for blocks (for example, Unit Delay) that accept buses with inactive elements is not supported. For detailed information on generating code for buses, see Generate Code for Nonvirtual Buses.