2.4.9. Other considerations¶
2.4.9.1. Atomic access¶
16-bit reads/writes are atomic.
32-bit float reads/writes are atomic except: writing a 32-bit float constant is only atomic if performed with a single opcode.
32-bit integer reads/writes:
32-bit reads/writes that use a single opcode are atomic.
Atomic accesses within an ISR: By default, accesses within an ISR are atomic. The INTM bit is automatically set (disable interrupts) by the hardware during the context switch. The exception would be if the application re-enables interrupts within the ISR in order to nest interrupts.
If possible, group atomic accesses together or create a function to perform the sequence disable-interrupts/atomic-accesses/enable-interrupts.
For writes to global variables larger than 32 bits (64 bit long double, structures) disable/re-enable interrupts around the write. This ensures the writer updates the entire variable before the reader accesses it and avoids leaving the variable in an inconsistent or incomplete state.
For other atomic operations, there are two recommended approaches:
Use an atomic compiler intrinsic if one is available. These are documented in the compiler user’s guide (www.ti.com/lit/SPRU514). The description will say “in an atomic way”.
- Disable / enable interrupts around atomic operations using below intrinsics:
__disable_interrupts( ); __enable_interrupts( );
Listing 2.3 is a code snippet from the Digital Control Library in C2000Ware illustrating disabling interrupts around updates to a struct to ensure atomic updates to the entire structure.
Refer to the TMS320C28x Optimizing C/C++ Compiler User’s Guide, Table 7-6, TMS320C28x C/C++ Compiler Intrinsics for details on the __enable_interrupt()
and __disable_interrupt()
intrinsics.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | uint16_t val = __disable_interrupts();
p->Kp = p->sps->Kp;
p->Ki = p->sps->Ki;
p->Kd = p->sps->Kd;
p->Kr = p->sps->Kr;
p->c1 = p->sps->c1;
p->c2 = p->sps->c2;
p->Umax = p->sps->Umax;
p->Umin = p->sps->Umin;
DCL_restoreInts(v);
// If interrupts were originally enabled, re-enable them
if (0U == (val & 0x1))
__enable_interrupts();
|
2.4.9.2. Calling asm functions from C code¶
Any ASM functions called from C code must follow the C calling and register conventions. Refer to the TMS320C28x Optimizing C/C++ Compiler User’s Guide, Sections 7.2 Register Conventions, 7.3 Function Structure and Calling Conventions and 7.5 Interfacing C and C++ With Assembly Language.
Any violation of these conventions can result in application passing with -Ooff, but failing at higher optimization levels.
2.4.9.3. Uninitialized variables¶
Using variables without initialization can lead to undefined behavior
The behavior of an application with uninitialized variables can change with optimization levels, making debug difficult
Local variables
Must be explicitly initialized in the application before any use
Global variables
C standard specifies that global (extern) and static variables without explicit initializations must be initialized to 0 before the program begins running
C runtime initialization behavior differs across COFF and EABI
Refer to the TMS320C28x Optimizing C/C++ Compiler User’s Guide for details - Sections 7.10.3 Automatic Initialization of Variables for COFF and 7.10.4 Automatic Initialization of Variables for EABI.
2.4.9.4. Interrupts¶
RPT instructions are not interruptible, and can potentially delay or block interrupts from executing * For example, if there is a memcpy() instruction in a background function, and the compiler generates RPT instructions for this function, that section of code will be un-interruptible * If the compiler generates RPT instructions within an ISR, interrupts will be blocked, even if interrupt nesting is enabled * To avoid this issue, there are two compiler options available - –no_rpt which will tell the compiler not to generate RPT instructions, or –rpt_threshold which will limit the number of consecutive RPT instructions generated