Crash when putting FlexCAN objects in RAM2

CollinK

Well-known member
Ok, so this might be a slightly weird one...

I have a very large program that uses a whole lot of RAM. If I don't move some things to RAM2 then RAM1 overflows and the program dies. But, it seems when I use the below lines:

Code:
DMAMEM FlexCAN_T4<CAN1, RX_SIZE_256, TX_SIZE_16> Can0; //Reg CAN or SWCAN depending on mode
DMAMEM FlexCAN_T4<CAN2, RX_SIZE_256, TX_SIZE_16> Can1; //Isolated CAN
DMAMEM FlexCAN_T4FD<CAN3, RX_SIZE_256, TX_SIZE_16> Can2; //Only CAN-FD capable output

Then any CAN reception causes a hard crash that reboots the Teensy. Removing the DMAMEM portion of these lines fixes the problem.

Here is the output of the crash dump:

Code:
E(0.676038) SYSTEM CRASHED! Analyzing the crash data.
CrashReport:
  A problem occurred at (system time) 20:3:38
  Code was executing from address 0x13C22000
  CFSR: 1
        (IACCVIOL) Instruction Access Violation
  Temperature inside the chip was 46.36 °C
  Startup CPU clock speed is 600MHz
  Reboot was caused by auto reboot after fault or bad interrupt detected

What is difficult about this output is that the code execution address is 0x13C22000 which is within FLASH space. Yes, I did in fact also move some code to execute from FLASH if I didn't think it was particularly speed critical. That brings up another question of "how in the heck do I use addr2line when code is executing from flash?!" I tried to remove the leading 1 (0x10000000) so that the data was not within the FLASH address but that of course doesn't exactly work. Are there any tricks to figuring out what code was executing when it was doing so from FLASH memory and not RAM1?

Second part of the question: is it a bad idea to simultaneously use both RAM2 and code execution from FLASH?

And, can I somehow dump the processor registers in the crash dump? IACCVIOL doesn't entirely tell me what happened, it would require that I could query the state of the processor registers at the point of the crash.

I guess the bottom line is that you can run into some really interesting crashes when your program gets large enough that you have to spread things around and somewhat counter to "the way it usually is done" (tm) I'm in uncharted territory (for me) with the Teensy and obviously I can't just attach a J-Link to this and get better info. So, any tips about how to better handle this in the future would be appreciated. For now I can just remove DMAMEM from those lines and be OK but I want to better understand how I messed up and do better.
 
I didn't expect that putting objects in RAM2 was a problem. I guess that was my mistake. When reading here https://www.pjrc.com/store/teensy40.html it plainly says that you must write the initial values as they can't be auto populated but I guess I didn't put two and two together to think that it could mean that initializing objects in RAM2 upon startup might not be a good idea. Seems like it might be fine if the objects were explicitly instantiated at runtime with new but I'm not doing that.
 
I guess in theory it should work if the objects don't contain any static data, or possibly also no const data depending on how the compiler decides to optimize things. The main point is that only the space will be reserved for it, any pre-initialized data won't be handled correctly (possibly including any const variables that the compiler decides to lift out of the constructor).
 
Here a quick test which shows that the compiler relys on initialization during startup.

C++:
class A
{
 public:
    int i = 42;
    const int c = 17;
    void print()
    {
        Serial.printf("i=%d &i=%p\n", i, &i);
        Serial.printf("c=%d &c=%p\n", c, &c);
    }
};

DMAMEM A a;

void setup()
{
    while (!Serial) {}
    a.print();
}

void loop(){}
Here it prints:
Code:
i=-552857307 &i=0x20200000
c=-1792851954 &c=0x20200004
I.e., the variables are placed in DMAMEM as requested but are not initialized. In this case, adding a constructor (even if it does nothing) makes the compiler initializing the variables at "runtime". But, I wouldn't rely on this. A lot of things can go wrong if the implementation doesn't initalize memory as required by the standard.
Seems like it might be fine if the objects were explicitly instantiated at runtime with new but I'm not doing that.
There is nothing wrong with using new. Memory fragmentation can only be a problem if you dynamically allocate stuff. I.e. newing/deleting memory repeatetly. However, if for some other reason you really don't want or can't using new, you can always use placement new to separate the memory allocation from constructing the object. Here an example:
C++:
#include <new>
class A
{
 public:
    int i = 42;
    const int c = 17;
    void print()
    {
        Serial.printf("i=%d &i=%p\n", i, &i);
        Serial.printf("c=%d &c=%p\n", c, &c);
    }
};

DMAMEM uint8_t buf[sizeof (A)]; // statically allocate memory for an object of type A in RAM2

A* a;

void setup()
{
    while (!Serial) {}

    a = new(buf) A; //construct an object of type A in the  given buffer. No allocation of memory happens here.
    a->print();     //works as expected
}

void loop(){}
which prints:
Code:
i=42 &i=0x20200000
c=17 &c=0x20200004
 
Last edited:
You could potentially have problems with memory alignment if you used a uint8_t buffer like that though.
 
You could potentially have problems with memory alignment if you used a uint8_t buffer like that though.
You are right of course. To be on the save side one should use
C++:
DMAMEM alignas(A) uint8_t buf[sizeof (A)]; // statically allocate memory for an object of type A
to allocate the buffer.
 
Last edited:
Back
Top