Other Parts Discussed in Thread: C2000-CGT, C2000WARE
Tool/software:
Hello,
according to the manual, the C compiler from C2000-CGT is not supposed to change sizes of volatile accesses (explained in spru514y.pdf, J.3.10 Qualifiers), but there seems to be a corner case where this is not actually the case, where the compiler splits a 32-bit volatile read into two 16-bit reads.
Example of the issue, compiled with "cl2000.exe --c99 -Ooff -v28 -k":
volatile unsigned long value; unsigned long foo_0(unsigned long arg) { return value & arg; } unsigned long foo_1(unsigned long arg) { return value | arg; } unsigned long foo_2(unsigned long arg) { return value ^ arg; }
The example above compiles to the following assembly instructions when compiled with 22.6.0.LTS, 22.6.1.LTS, and 22.6.2.LTS under Windows:
_foo_0: ADDB SP,#2 ; [CPU_ARAU] MOVL *-SP[2],ACC ; [CPU_ALU] |3| MOVW DP,#_value ; [CPU_ARAU] AND AL,@_value ; [CPU_ALU] |3| AND AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |3| SUBB SP,#2 ; [CPU_ARAU] LRETR ; [CPU_ALU] _foo_1: ADDB SP,#2 ; [CPU_ARAU] MOVL *-SP[2],ACC ; [CPU_ALU] |4| MOVW DP,#_value ; [CPU_ARAU] OR AL,@_value ; [CPU_ALU] |4| OR AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |4| SUBB SP,#2 ; [CPU_ARAU] LRETR ; [CPU_ALU] _foo_2: ADDB SP,#2 ; [CPU_ARAU] MOVL *-SP[2],ACC ; [CPU_ALU] |5| MOVW DP,#_value ; [CPU_ARAU] XOR AL,@_value ; [CPU_ALU] |5| XOR AH,@$BLOCKED(_value)+1 ; [CPU_ALU] |5| SUBB SP,#2 ; [CPU_ARAU] LRETR ; [CPU_ALU]
The issue is present regardless of the selected optimization level, even if the optimizations are completely turned off with "-Ooff", and this can then cause issues when a program is reading data from a peripheral device that only supports 32-bit accesses, or if you rely on a single volatile 32-bit read/write to be atomic with regards to thread-safety in an environment with interrupts (which both are intended approaches with C2000 as I understand, as they are also used in C2000Ware examples and drivers).
Possible workaround for this issue seems to be to use the __byte_peripheral_32 intrinsic to read the 32-bit data, which seems to stop the compiler from splitting the access, but it requires the user to find all places in source code relevant to this issue.
unsigned long foo_fixed(unsigned long arg) { return __byte_peripheral_32(&value) & arg; }
which then compiles to:
_foo_fixed: ADDB SP,#2 ; [CPU_ARAU] MOVL *-SP[2],ACC ; [CPU_ALU] |6| MOVL XAR4,#_value ; [CPU_ARAU] |6| MOVL ACC,*+XAR4[0] ; [CPU_ALU] |6| AND AL,*-SP[2] ; [CPU_ALU] |6| AND AH,*-SP[1] ; [CPU_ALU] |6| SUBB SP,#2 ; [CPU_ARAU] LRETR ; [CPU_ALU]
The above examples are the only ones with this issue that I am aware of. If I try other accesses to bitfields, or different operators, then the compiler correctly loads whole 32-bit words at once without splitting them.