Implement Symbolic Dimensions for Array Sizes in Generated Code
Symbolic dimensions allow you to use symbols instead of fixed numbers for dimensions when you are developing a model. Wherever you need to specify a signal dimension in your model, you can use symbolic dimensions instead of actual numbers, which provides flexibility for changing the dimensions. For more information, see Use Symbolic Dimensions for Signal Dimensions (Embedded Coder).
You can specify symbolic dimensions for the blocks and data objects that includes:
Inport
Outport
Signal Specification
Data Store Memory
Simulink.Signal
Simulink.Parameter
Simulink.BusElement
Simulink.LookupTable
Simulink.Breakpoint
AUTOSAR.Parameter
Tip
To identify blocks that support symbolic dimension specification, enter
showblockdatatypetable
in the MATLAB® command prompt and check the Block Support Table.
Define Symbolic Dimension Variable
This example uses the model DimensionVariants
to show how to implement symbolic dimensions. This model has four modeling patterns involving vectors and matrices.
To show block names, on the Debug tab, on the Information Overlays menu, clear the Hide Automatic Block Names parameter.
Open the Embedded Coder or Simulink Coder app. In the C Code tab, select Code Interface > Individual Element Code Mappings.
In the Code Mappings editor, under the Parameters tab, click the refresh button. Seven
Simulink.Parameter
objects appear. Four of these objects are designated for specifying symbolic dimensions. TheseSimulink.Parameter
objects are namedA
,B
,C
, andD
. Note that these parameters have a storage class ofCompilerFlag
.
To use a Simulink.Parameter
object for dimension specification, it must be defined in the base workspace and have one of these storage classes:
Define
orImportedDefine
with header file specifiedCompilerFlag
User-defined custom storage class that defines data as a macro in a specified header file
For Simulink.Parameter
objects with an ImportedDefine
custom storage class, provide a header file on the MATLAB path. Enter the name of the header file in the HeaderFile field in the Simulink.Parameter
dialog box.
To use a AUTOSAR.Parameter
object for dimension specification, the storage class can be mapped to something other than AUTO
in the AUTOSAR mappings.
Specify Symbolic Dimensions for Blocks and Data Objects
Click Inport Block
In1
. On the Property Inspector, on the Parameters tab, the Port Dimensions field contains theSimulink.Parameter
objectA
. For Inport blocks, you specify symbolic dimensions in the Port Dimensions field.Click Inport block
In2
. The Port Dimensions field contains theSimulink.Parameter
objectB
.Click the Constant block. The Constant value parameter has a value of
Data
. In the Code Mappings editor, on the Parameters tab, click theSimulink.Parameter
objectData
. On the Property Inspector, the Dimension field has the character vector'[1,C]'
, which is equivalent to'[1,5]'
becauseC
has a value of5
. The Value field contains an array with5
values, which is consistent with its dimensions. The dimensions of the data object must be consistent with the value of theSimulink.Parameter
object that is in the Dimensions field. Notice thatData
has a Storage class ofImportedExtern
.Open the
1-D Lookup Table1
block parameters dialog box. The Table data field contains theSimulink.Parameter
,PT
. The Breakpoints 1 field contains theSimulink.Parameter
,PB
.In the Code Mappings editor, click
PB
andPT
and view their properties. These parameters contain the character vector'[1,D]'
in their Dimensions field and are arrays consisting of 15 values. The value ofD
is consistent with the dimension of thePB
andPT
parameters becauseD
has a value of15
.Simulate the model. Simulink® propagates the dimensions symbolically in the diagram. During propagation, Simulink establishes modeling constraints among symbols. One modeling constraint for
DimensionVariants
is thatC=A+B
. The Diagnostic Viewer produces a warning for any violations of constraints.Change the dimension specification to a different configuration and simulate the model again.
Though not shown in this example, you can specify an n-D dimension expression with one or more of the dimensions being a symbol (for example, '[A,B,C]'
or '[1,A,3]'
). To preserve multi-dimensional information, you must specify an n-D dimension expression with each dimension that you want to preserve being a symbol.
Generate Code for a Model with Symbolic Dimension Variables
Once you have verified dimension specifications through model simulation, generate code for DimensionVariants
.
Build the model.
model = 'DimensionVariants';
slbuild(model);
### Starting build procedure for: DimensionVariants ### Successful completion of build procedure for: DimensionVariants Build Summary Top model targets: Model Build Reason Status Build Duration ==================================================================================================================== DimensionVariants Information cache folder or artifacts were missing. Code generated and compiled. 0h 0m 45.856s 1 of 1 models built (0 models already up to date) Build duration: 0h 0m 53.662s
View the generated code. In the DimensionVariants.h
file, symbolic dimensions are in data declarations.
hfile = fullfile('DimensionVariants_ert_rtw',... 'DimensionVariants.h'); coder.example.extractLines(hfile,'/* External inputs', '/* Real-time', 1, 0);
/* External inputs (root inport signals with default storage) */ typedef struct { real_T In1[A]; /* '<Root>/In1' */ real_T In2[B]; /* '<Root>/In2' */ } ExtU; /* External outputs (root outports fed by signals with default storage) */ typedef struct { real_T Out1[(A + B)]; /* '<Root>/Out1' */ real_T Out2[(A + B)]; /* '<Root>/Out2' */ } ExtY;
The DimensionVariants.h
file contains data definitions and preprocessor conditionals that define constraints established among the symbols during simulation. One of these constraints is that the value of a symbolic dimension must be greater than 1
. This file also includes the user-provided header file for any Simulink.Parameter
objects with an ImportedDefine
custom storage class.
hfile = fullfile('DimensionVariants_ert_rtw',... 'DimensionVariants.h'); coder.example.extractLines(hfile,'#ifndef A', '/* Macros for accessing', 1, 0);
#ifndef A #error The value of parameter "A" is not defined #endif #ifndef B #error The value of parameter "B" is not defined #endif #ifndef C #error The value of parameter "C" is not defined #endif #ifndef D #error The value of parameter "D" is not defined #endif /* * Constraints for division operations in dimension variants */ #if (1 == 0) || (((A+B) % 1) != 0) # error "The preprocessor definition '1' must not be equal to zero and the division of '(A+B)' by '1' must not have a remainder." #endif /* * Registered constraints for dimension variants */ /* Constraint 'C == (A+B)' registered by: * '<Root>/1-D Lookup Table1' */ #if C != (A+B) # error "The preprocessor definition 'C' must be equal to '(A+B)'" #endif #if A <= 1 # error "The preprocessor definition 'A' must be greater than '1'" #endif #if B <= 1 # error "The preprocessor definition 'B' must be greater than '1'" #endif /* Constraint 'D > 1' registered by: * '<Root>/1-D Lookup Table1' */ #if D <= 1 # error "The preprocessor definition 'D' must be greater than '1'" #endif /* Constraint 'C > 3' registered by: * '<S2>/Assignment' */ #if C <= 3 # error "The preprocessor definition 'C' must be greater than '3'" #endif #if A >= 11 # error "The preprocessor definition 'A' must be less than '11'" #endif #if B >= 11 # error "The preprocessor definition 'B' must be less than '11'" #endif /* Constraint 'D < 21' registered by: * '<Root>/1-D Lookup Table1' */ #if D >= 21 # error "The preprocessor definition 'D' must be less than '21'" #endif /* Constraint 'C < 11' registered by: * '<S2>/Assignment' */ #if C >= 11 # error "The preprocessor definition 'C' must be less than '11'" #endif
In the DimensionVariants.c
file, symbolic dimensions participate in loop bound calculations, array size and index offset calculations, and a parameterized utility function (for example, Lookup Table block) calculation.
cfile = fullfile('DimensionVariants_ert_rtw',... 'DimensionVariants.c'); coder.example.extractLines(cfile,'/* Model step', '/* Model initialize', 1, 0);
/* Model step function */ void DimensionVariants_step(void) { real_T rtb_VectorConcatenate[A + B]; real_T rtb_VectorConcatenate_0; int32_T ForEach_itr; int32_T i; int32_T s2_iter; /* Gain: '<Root>/Gain' incorporates: * Inport: '<Root>/In1' */ for (ForEach_itr = 0; ForEach_itr < A; ForEach_itr++) { rtb_VectorConcatenate[ForEach_itr] = 2.0 * rtU.In1[ForEach_itr]; } /* End of Gain: '<Root>/Gain' */ /* Gain: '<Root>/Gain1' incorporates: * Inport: '<Root>/In2' */ for (ForEach_itr = 0; ForEach_itr < B; ForEach_itr++) { rtb_VectorConcatenate[A + ForEach_itr] = 3.0 * rtU.In2[ForEach_itr]; } /* End of Gain: '<Root>/Gain1' */ /* Outputs for Iterator SubSystem: '<Root>/For Each Subsystem' incorporates: * ForEach: '<S1>/For Each' */ for (ForEach_itr = 0; ForEach_itr < A + B; ForEach_itr++) { /* Sum: '<Root>/Add' incorporates: * Constant: '<Root>/Constant' * Lookup_n-D: '<Root>/1-D Lookup Table1' */ rtb_VectorConcatenate_0 = look1_binlx(Data[ForEach_itr], PB, PT, (uint32_T) (D - 1)) + rtb_VectorConcatenate[ForEach_itr]; rtb_VectorConcatenate[ForEach_itr] = rtb_VectorConcatenate_0; /* ForEachSliceAssignment generated from: '<S1>/Out1' incorporates: * ForEachSliceSelector generated from: '<S1>/In1' * MATLAB Function: '<S1>/MATLAB Function' */ /* MATLAB Function 'For Each Subsystem/MATLAB Function': '<S3>:1' */ /* '<S3>:1:4' y = 2*u; */ rtY.Out1[ForEach_itr] = 2.0 * rtb_VectorConcatenate_0; } /* End of Outputs for SubSystem: '<Root>/For Each Subsystem' */ /* Outputs for Iterator SubSystem: '<Root>/For Iterator Subsystem' incorporates: * ForIterator: '<S2>/For Iterator' */ /* Constant: '<Root>/Constant1' */ ForEach_itr = ((int32_T)A); if (((int32_T)A) < 0) { ForEach_itr = 0; } /* End of Constant: '<Root>/Constant1' */ for (s2_iter = 0; s2_iter < ForEach_itr; s2_iter++) { /* Assignment: '<S2>/Assignment' incorporates: * Constant: '<S2>/Constant' * Outport: '<Root>/Out2' * Product: '<S2>/Product' * Selector: '<S2>/Selector' */ if (s2_iter == 0) { for (i = 0; i < A + B; i++) { rtY.Out2[i] = rtb_VectorConcatenate[i]; } } rtY.Out2[s2_iter] = rtb_VectorConcatenate[s2_iter] * 2.0; /* End of Assignment: '<S2>/Assignment' */ } /* End of Outputs for SubSystem: '<Root>/For Iterator Subsystem' */ }
Close the model and code generation report.
bdclose(model)
Set Parameter Value Based on Variant Choice
When you specify the dimensions of a parameter in a model by using a symbol, you must ensure that the parameter value aligns with the dimension value. To simulate different choices for the dimension value, you must manually correct the parameter value.
For example, in the DimensionVariants
model, the
Simulink.Parameter
object Data
stores a vector value,
[1 2 3 4 5]
, and uses a symbolic dimension C
with
initial value 5
. If you change the value of C
, to
simulate the model, ensure the vector's length matches the new value of
C
.
To reduce the effort of maintenance when you change the value of C
,
you can set the value of Data
to an expression involving
C
.
Open the model.
DimensionVariants
At the command prompt, inspect the initial values of
Data
andC
. The value ofData
is a vector of integers from1
toC
.Data.Value
ans = 1 2 3 4 5
C.Value
ans = 5
In MATLAB code syntax, the value of
Data
is1:C
. To preserve this relationship between the parameter objects, set the value ofData
by using theslexpr
function.Data.Value = slexpr('1:C');
To prevent data type propagation errors, set the data type of
Data
explicitly todouble
, which is the data type that the parameter acquired before you set the parameter value to an expression.Data.DataType = 'double';
Set the value of
C
to a different number, such as6
. Due to dimension constraints in the model, you must set the value of another dimension symbol,A
, to3
.C.Value = 6; A.Value = 3;
Simulate the model. The block diagram shows that the value of
Data
now has six elements.
For more complicated applications, you can write your own MATLAB function that returns parameter values based on dimension symbols. Set the
value of the parameter data to an expression that calls your function. For general
information about using an expression to set the value of a
Simulink.Parameter
object, see Set Variable Value by Using a Mathematical Expression.
Tip
You must provide the initialization code for Simulink.Parameter
objects that contain symbolic dimensions. To prevent the generated code from initializing
these parameters, you must either:
Configure the parameters to use a storage class with the Data scope property set to
Imported
, such as theImportedExtern
orImportedExternPointer
built-in storage classes.Configure the parameters to use a storage class with the Data initialization property set to
None
.