This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

C2000: 32-bit pointers (not pointers to 32-bit data)

I've found some inconsistent behavior in the C2000 compiler. Here's some example code:

void long_pointer_bug_demo(void)
{

uint32_t address;
volatile uint16_t *ptr;

//First read
address = 0x01080000;
ptr = (volatile uint16_t *)address;
*ptr;

//Second read
ptr = (volatile uint16_t *)(uint32_t)0x01080000;
*ptr;

}

With optimizations off, the two reads give different results. The first one loads 0x01080000 into the ACC, then copies it into XAR4, then reads from that address. The second one loads 0x00080000 into XAR4 as an immediate value, truncating the upper 8 bits of the address. It then reads from the truncated address. Here's the assembly:

  FE04        ADDB         SP, #4
  FF2F0210    MOV          ACC, #0x210 << 15
  1E42        MOVL         *-SP[2], ACC
  0642        MOVL         ACC, *-SP[2]
  1E44        MOVL         *-SP[4], ACC
  8A44        MOVL         XAR4, *-SP[4]
  92C4        MOV          AL, *+XAR4[0]
  8F080000    MOVL         XAR4, #0x080000
  A844        MOVL         *-SP[4], XAR4
  8A44        MOVL         XAR4, *-SP[4]
  92C4        MOV          AL, *+XAR4[0]
  FE84        SUBB         SP, #4
  0006        LRETR        

At optimization level -O2, both reads are optimized to load the truncated address into XAR4 as an immediate value, then read from the truncated address twice in a row:

  8F080000    MOVL         XAR4, #0x080000
  92C4        MOV          AL, *+XAR4[0]
  92C4        MOV          AL, *+XAR4[0]
  0006        LRETR       

If I change address to be a parameter of the function instead of a local variable, I get results similar to no optimizations:

  8AA9        MOVL         XAR4, @ACC
  92C4        MOV          AL, *+XAR4[0]
  8F080000    MOVL         XAR4, #0x080000
  92C4        MOV          AL, *+XAR4[0]
  0006        LRETR       

The C28x Compiler Guide (spru514) states that a pointer is 22 bits, so I think the truncation is intentional. My question is whether the lack of truncation during conversion from a variable is intentional, or a compiler bug, or whether this is a gray area of the C standard.

I know there are compiler intrinsics for 32-bit address accesses (__read32_*() and __write32_*()), but these are not always convenient. Since the C28x CPU does have a 32-bit data address space (per spru430 section 1.3), I think not truncating is better behavior, but perhaps there's a reason why it must be done.

Thanks,

  • Up until the v.6.4 compiler, pointer size was 22 bits on C28x (as of large memory model).  In v.6.4, pointer size was changed to 32 bits.  However, data is assumed to not be at addresses above the 22 bit range unless it is annotated with a source level attribute "__attribute__((far))" to indicate this is the case.  This is in support of some specific Soprano devices which have SDRAM above the 22 bit range.  Because the intrinsics were cumbersome and did not permit using pointers, the attribute support was added.  An app note was being prepared to explain proper usage-- I will see if it is available and provide a link.

    The reason addresses are still assumed to be within 22 bits is because some addressing modes as well as single loads of immediate values only support 22 bits, and the SDRAM above the 22-bit address space was not intended to be general purpose.  Additionally, the program memory bus cannot access addresses above 22-bits so some instructions that use the program memory bus to access a second data memory operand will not work with 32-bit addresses in that operand.  Therefore, for performance reasons, it was best not to assume that addresses could fall between the 22 and 32-bit range in the general case.  

    However, address registers are 32 bits wide.  When you don't have optimization enabled and the 32-bit value is already in a register, casting it to a pointer is not truncating the value because pointers are also 32-bits now.  This behavior is inconsistent and should not be relied upon-- I don't believe there is a C standard violation here, just a discrepancy between pointer size and the assumed upper bound on addresses.  If you want to access data above the 22-bit range, the far attribute or the intrinsics should be used.  (Volatile should also be applied in addition to the far attribute-- this will be explained in the app note and is to prevent generating the instructions that use the program memory bus.) 

  • Thanks, Anna. Please let me know when the app note is available. I will pass it along to the various C2000 Applications teams.