Selecting Data Types for Basic Operations
Restrict Data Type Word Lengths
If possible, restrict the fixed-point data type word lengths in your model so that they are equal to or less than the integer size of your target microcontroller. This results in fewer mathematical instructions in the microcontroller, and reduces ROM and execution time.
This recommendation strongly applies to global variables that consume global RAM. For example, Unit Delay blocks have discrete states that have the same word lengths as their input and output signals. These discrete states are global variables that consume global RAM, which is a scarce resource on many embedded systems.
For temporary variables that only occupy a CPU register or stack location briefly, the
space consumed by a long
is less critical. However, depending on the
operation, the use of long
variables in math operations can be expensive.
Addition and subtraction of long integers generally requires the same effort as adding and
subtracting regular integers, so that operation is not a concern. In contrast,
multiplication and division with long integers can require significantly larger and slower
code.
Avoid Fixed-Point Scalings with Bias
Whenever possible, avoid using fixed-point numbers with bias. In certain cases, if you choose biases carefully, you can avoid significant increases in ROM and execution time. Refer to Recommendations for Arithmetic and Scaling for more information on how to choose appropriate biases in cases where it is necessary; for example if you are interfacing with a hardware device that has a built-in bias. In general, however, it is safer to avoid using fixed-point numbers with bias altogether.
Inputs to lookup tables are an important exception to this recommendation. If a lookup table input and the associated input data use the same bias, then there is no penalty associated with nonzero bias for that operation.
Wrap and Round to Floor or Simplest
For most fixed-point and integer operations, the Simulink® software provides you with options on how overflows are handled and how calculations are rounded. Traditional user-written code, especially for control applications, almost always uses the “no effort” rounding mode. For example, to reduce the precision of a variable, that variable is shifted right. For unsigned integers and two's complement signed integers, shifting right is equivalent to rounding to floor. To get results comparable to or better than what you expect from traditional user-written code, you should round to floor in most cases.
The primary exception to this rule is the rounding behavior of signed integer division. The C language leaves this rounding behavior unspecified, but for most targets the “no effort” mode is round to zero. For unsigned division, everything is non-negative, so rounding to floor and rounding to zero are identical.
You can improve code efficiency by setting the value of the Model
Configuration Parameters > Hardware Implementation > Device details > Signed
integer division rounds to parameter to describe how your production target
handles rounding for signed division. For Product blocks that are doing only
division, setting the Integer rounding mode parameter to the rounding
mode of your production target gives the best results. You can also use the
Simplest
rounding mode on blocks where it is available. For
more information, refer to Rounding Mode: Simplest.
The options for overflow handling also have a big impact on the efficiency of your generated code. Using software to detect overflow situations and saturate the results requires the code to be much bigger and slower compared to simply ignoring the overflows. When overflows are ignored for unsigned integers and two's complement signed integers, the results usually wrap around modulo 2N, where N is the number of bits. Unhandled overflows that wrap around are highly undesirable for many situations.
However, because of code size and speed needs, traditional user-written code contains very little software saturation. Typically, the fixed-point scaling is very carefully set so that overflow does not occur in most calculations. The code for these calculations safely ignores overflow. To get results comparable to or better than what you would expect from traditional user-written code, the Saturate on integer overflow parameter should not be selected for Simulink blocks doing those calculations.
In a design, there might be a few places where overflow can occur and saturation protection is needed. Traditional user-written code includes software saturation for these few places where it is needed. To get comparable generated code, the Saturate on integer overflow parameter should only be selected for the few Simulink blocks that correspond to these at-risk calculations.
A secondary benefit of using the most efficient options for overflow handling and rounding is that calculations often reduce from multiple statements requiring several lines of C code to small expressions that can be folded into downstream calculations. Expression folding is a code optimization technique that produces benefits such as minimizing the need to store intermediate computations in temporary buffers or variables. This can reduce stack size and make it more likely that calculations can be efficiently handled using only CPU registers. An automatic code generator can carefully apply expression folding across parts of a model and often see optimizations that might not be obvious. Automatic optimizations of this type often allow generated code to exceed the efficiency of typical examples of user-written code.
Limit the Use of Custom Storage Classes
In addition to the tip mentioned in Wrap and Round to Floor or Simplest, to obtain the maximum benefits of expression
folding you also need to make sure that the Storage class is set to
Auto
for signals in your model. When you choose a setting other than
Auto
, you need to name the signal, and a separate statement is created
in the generated code. Therefore, only use a setting other than Auto
when
it is necessary for global variables.
For more information about setting the Storage class of signals, see Configure Signal Data for C Code Generation.