Search code examples
arduinoavravr-gccarduino-mkr1000

Flash read at certain addresses crashes ATSAMW25


I have an Arduino MKR1000 with an ATSAMW25 chip, and I'm trying to debug a crash bug in my code.

This is the offending function:

void GuiDisplay::drawBitmap(rect_t frame, const uint16_t *data, rgb565_filter filter) {

    point_t origin = adjustPoint(frame.origin, _origin);
    uint16_t x = origin.x;
    uint16_t y = origin.y;

    tft->setAddrWindow(x, y, x + frame.width() - 1, y + frame.height() - 1);

    Serial.print("Drawing "); Serial.print((unsigned int)data, HEX); Serial.print(" ("); Serial.print(x); Serial.print(", "); Serial.print(y); Serial.print("; "); Serial.print(x + frame.width() - 1); Serial.print(", "); Serial.print(y + frame.height() - 1); Serial.println(")");

    for (int ii = 0; ii < frame.width() * frame.height(); ii++) {

        Serial.print("  "); Serial.print((unsigned int)data, HEX); Serial.print("["); Serial.print(ii); Serial.print("] = ");

        uint16_t word = *(data + ii);

        Serial.println(word);

        tft->pushColor(word);
    }

    Serial.println("Done");
}

Reading from data causes the crash, as you can see from the output

Drawing 8AB1 (267, 14; 300, 41)
8AB1[0] =

This is a flash memory location. The ATSAMW25 doesn't need PROGMEM declarations, just const.

What has me stumped is that it only appears to fail for some memory ranges. I have a number of definitions like this:

// Header

typedef struct bitmap_data_def {
    size_tt size;
    uint8_t count;
    const byte *data;
} bitmap_data_t;

extern const bitmap_data_t projector_bitmap;
extern const bitmap_data_t power_bitmap;
extern const bitmap_data_t slides_bitmap;
extern const bitmap_data_t camera_bitmap;
... and so on.


// CPP file

const byte power_bitmap_data[] = { [very long string!] }

const bitmap_data_t power_bitmap = {
    size_tt(26, 26),
    1,
    power_bitmap_data
};

They are all declared in the same file and most of them work fine:

Drawing 86A0 (199, 193; 224, 212)
  86A0[0] = 0
  86A0[1] = 65535
   ... and so on ...
  86A0[519] = 0
Done

Drawing AB44 (199, 133; 224, 151)
  AB44[0] = 0
  AB44[1] = 38034
   ... and so on ...
  AB44[493] = 0
Done

and so on.

So far I've seen the crash happen for memory locations between 0x8A00 and 0x8B00 and for locations around 0x95BD. As you can see from the above, memory locations above and below these have been fine.

If I add or remove some of the declarations, such that the compiler rearranges them in memory, then different declaration fail. It could be whichever falls in the memory ranges above that cause the crash.

I can't see how the program memory could be overwritten, and even if it was it shouldn't crash on read. Is there some subtle memory copy going on that I have missed? If that were the case then I might be exceeding the limit of 32KB of SRAM, but I can't see where/how/why that would be happening.

I've tried another MKR1000 and get exactly the same result, so the problem does not seem to be a hardware fault.

Any help would be greatly appreciated! Thank you.


Solution

  • OK, so I solved my own problem. I'm not sure how useful this is going to be for anyone else, but the crash was caused because I was reading memory on 1- or 3-byte boundaries. That's not allowed for the ARM Cortex M0+ family of processors.

    This article was extremely helpful in understanding the memory addressing requirements:

    http://www.sumidacrossing.org/Musings/files/160606_Memory_and_the_Arduino.php

    I used the suggestion from AdaFruit to solve the actual problem:

    https://learn.adafruit.com/adafruit-feather-m0-wifi-atwinc1500/adapting-sketches-to-m0#aligned-memory-access

    Basically I replaced the offending line:

    uint16_t word = *(data + ii);
    

    With this:

    uint16_t word;
    memcpy(&word, &(data[ii]), 2);