Electronic – Pointer to array gets highest bit toggled by … compiler? (dsPIC33EP & XC16)

picpointersxc16

Brief:

  • Pointer to a const-int-array, nested in a struct
  • Using ptr to this struct (type) in various places
  • ptr-deref causes address-error, because value was changed from i.e. 0x00AE to 0x80AE -> highest bit was toggled.

Detailed:

Declarations etc:

typedef struct {
    const uint8_t *rawDataPtr;  
    uint16_t width;      ///<in Bits!    
    uint16_t height;     ///<in Bytes!       
    } image_t;

const uint8_t image_data_timer_53x56[53] = {
    0x00, 0x00, 0x02, 0xff, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0x1f, 0xff, 0xf0, 0x00, 0x00, 
    0x00, 0x00, 0xff, 0xff, 0xfc, 0x00, 0x00, 
    0x00, 0x03, 0xff, 0xff, 0xff, 0x00, 0x00, 
    0x00, 0x0f, 0xff, 0xff, 0xff, 0xc0, 0x00, 
    0x00, 0x1f, 0xff, 0xff, 0xff, 0xe0, 0x00, 
    0x00, 0x7f, 0xff, 0xff, 0xff, 0xf0, 0x00, 
    0x00, 0x7f, 0xff, 0xff}

image_t image_timer3 = {image_data_timer_53x56, 53, 1};  

Edit: These definitions (above) are outside of any function-body.

void LCD_draw_image(uint16_t x, uint16_t y, image_t *img, uint16_t fg_color, uint16_t bg_color);

"image_timer_3" is global and extern.

Call of function "LCD_draw_image":

LCD_draw_image(X_WIDTH_FRAME, Y_POS_ICON, &image_timer3, COL_BLACK, COL_WHITE);

…using "img" in the subroutine in various ways. Doesn't actually matter how, because the pointer is already corrupted.
See debugger output:
(Edit: Breakpoint BEFORE calling the subroutine mentioned => deref inside of it is not crucial to this question)
Debugger output - pointer-value gets highest bit toggled

Now, I tried size-changing of the initial int-array and it does have an effect since it did work for a while. But now the error reoccurred and I assume it is a setting of the compiler but I cannot figure out what!
Each time I use the pointer I get ADDRESS ERROR (traps code 2), of course, since it does not exist.

WHAT HAPPENS TO THIS POINTER?

dsPIC33EP64GS804, Mplab 5.4, XC16, ICD3

Best Answer

Ok, I'll answer myself, but credits to @BRHANS, who gave the correct hint!

  • Default setting of XC16 memory model: "CONST IN CODE" = variables declared as CONST will be placed in flash rom. In this case it is this variable:
  const uint8_t image_data_timer_53x56[53]
  • The PSV-feature of the dsPIC allows using those const-rom-variables like any other variable in RAM. For more info on PSV see section "Memory Organization" in the DS.
  • BUT...common "RAM"-variables will be found in the lower half of data memory space while the upper half is reserved for PSV-variables (mapped).
  • So, when using a POINTER to such a variable it makes a big difference. The compiler needs to know, that the variable is PSV-managed to handle it correctly.
  • This is done by the "psv" type qualifier:
  const uint8_t *rawDataPtr
 needs to be:
  __psv__ const uint8_t *rawDataPtr
  • Here we go.. it works!

2 more comments:

  1. If you know it (and now you and I do) you can see it in the debugger watch already: There is a "P" on the symbol and a "(PSV)" in the type-label.
  2. The distinction between upper and lower half of the memory space is made by bit 15 of the EA-word, which is exactly why I saw that paricular bit as "toggled" (see headline). EA<15> = 1 means PSV. So the shown address was not necessarily wrong, but the compiler didn't handle it correctly.