简体   繁体   中英

STM32H7 + external SDRAM - memcpy with length 3 crashes - word boundaries, cache settings?

I have a project running on an STM32H753ieval board with heap in external memory, with freeRTOS, modelled on the STM32 cube demos.

At the moment the MPU and cache are not enabled. (as far as I can tell, their functions are commented out)

this works in the main() function, where a and b are in internal ram.

int* aptr;
int* bptr;

int main()
{
    //  MPU_Config();
    //  CPU_CACHE_Enable();

    int a[100]; int b[100];
    memcpy(a, b, 3);
    aptr = a;
    bptr = b;
    ...

however, when a freeRTOS thread creates variables on the heap, memcpy doesnt work with some length values.

static void mymemcpy(char* dst, char* src, int len)
{
    for (int i = 0; i < len; i++)
    {
        dst[i] = src[i];
    }
}

void StartThread(void* arg)
{
    int a[100]; int b[100];
    for (int i = 0; i < 10; i++)
    {
        memcpy(aptr, bptr, i);   //works, using same mem as main
    }
    for (int i = 0; i < 10; i++)
    {
        mymemcpy(a, b, i);       //works, using external ram mem, but with mymemcpy
    }
    memcpy(a, b, 4);             //works, seems not a overrun issue
    for (int i = 0; i < 10; i++)
    { 
        memcpy(a, b, i);         //jumps to random memory when i == 3, probably an undefined handler
    }
    while(1);
}

This is the first time I've dealt with a caching micro, and external ram.

Is this a cache issue, ram issue, library issue? How do i fix it?

Note: I don't care that the arrays are uninitialised. I'm happy copying garbage.

The device likely crashes because reads to initialized memory may not have correct ECC bits set and when the processor discovers this during the read operation it faults on a double bit error.

Write to the memory first and then read it or configure your linker to zero initialize your heap area... this may require assembly code to get the correct sequencing going to enable the ram first othewise the zero init may fail

This issue has given me plenty of grief too. It has nothing to do with uninitialised buffers or ECC bits. It's caused by data alignment errors when accessing external SDRAM. The micro read instruction can access any group of byte(s) within a 4 byte boundary. It can't perform a read across a 4 byte boundary. Examples:

Load Register R0 (4-bytes) @ 0xc0000000; // good juju
Load Register R0 (2-bytes) @ 0xc0000002; // good juju
Load Register R0 (1-byte)  @ 0xc0000003; // good juju
Load Register R0 (4-bytes) @ 0xc0000004; // good juju
Load Register R0 (4-bytes) @ 0xc0000002; // bad juju
Load Register R0 (2-bytes) @ 0xc0000003; // bad juju

Performing a read across a 4-byte boundary causes a bus exception (mine gets caught by the hardfault handler).

Your a & b buffers are declared on the stack, so you won't know their starting address. Your mymemcpy() is safe, because it copies 1 byte at a time. However, the newlib memcpy actually copies 4 bytes at a time. That code will potentially try to read across a 4-byte boundary and throw an exception. Even if the start address is on a 4-byte boundary, the end address might not be.

The same issue applies to memset, memcmp, etc. But it also happens under the hood. Example:

std::array<uint8_t, 10> a; 
std::array<uint8_t, 10> b; 
a = b;  // potentially bad juju because = operator calls memcpy

To get around this problem I've enabled the data cache and setup the MPU region. The micro doesn't access the external SDRAM directly. Instead the data gets loaded into the cache which doesn't have the 4-byte boundary restriction. It seems to work ok, but it doesn't fill me with confidence.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM