7
\$\begingroup\$

I'm working with an ARM Cortex M4 microcontroller that has a DMA, or peripheral DMA controller. One requirement of the DMA is that it cannot access pointers to addresses that exist in flash, but only pointers to addresses that are in RAM. Is there a way to determine if a particular pointer points to flash or RAM?

If not, I think I need to actually initialize a buffer in RAM then strcpy or something like that to transfer what is in flash into RAM so the DMA can read it. Does anyone know of a better way to do this so as not to waste CPU time if there is no way to tell if the pointer points to flash or RAM?

This is on an Atmel SAM4S.

\$\endgroup\$
4
  • 1
    \$\begingroup\$ Does it have to be determined in run-time? Are we talking C here? \$\endgroup\$
    – Eugene Sh.
    Aug 20, 2015 at 17:25
  • \$\begingroup\$ Yes, so macro is out of the question I guess. Editing now. \$\endgroup\$
    – Pugz
    Aug 20, 2015 at 17:27
  • \$\begingroup\$ Could you share which MCU has this requirement? This may be a useful question + answer for others as well. \$\endgroup\$
    – corecode
    Aug 20, 2015 at 17:43
  • \$\begingroup\$ This is an Atmel SAM4S. I'll edit the question. \$\endgroup\$
    – Pugz
    Aug 20, 2015 at 17:44

4 Answers 4

5
\$\begingroup\$

You know the memory map of your microcontroller, so you can simply test your pointer against the start and end RAM addresses. In most cases your linkerscript will provide these addresses, so you don't need to type them in yourself (details will depends on yourlinkerscript). Note that wehen you use the linkerscript, the values won't be known at compilation time, so the compiler cannot optimize comparisions against know pointers.

\$\endgroup\$
5
\$\begingroup\$

Main problem with checking whether a pointer is in specific range is unfortunately the C standard itself. Pointer arithmetic has a defined behaviour only if performed on pointers of the same type within the memory range of the same object (i.e. within the same allocated array range). A the comparison operators are defined as arithmetic operations, this restriction apply to them as well. A workaround for this problem would be casting the pointers to the uintptr_t type defined in stdint.h, which is an integer type guaranteed to hold a pointer value. And then perform your comparisons.

Now to the part of obtaining the right boundaries. Typically a C project would contain some kind of linker script defining memory regions of the specific architecture. Commonly it will include lines similar to the following:

MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 0x40000
    SRAM (rwx) : ORIGIN = 0x20000000, LENGTH = 0x8000
}

then, in the same script the following definitions can be added:

  _flash_start = ORIGIN(FLASH);
  _flash_end = ORIGIN(FLASH) + LENGTH(FLASH);

And then in C code these symbols can be accessed using:

extern int _flash_start ;  
extern int _flash_end;

And then a tricky part: The address of _flash_start will correspond to the flash starting address, and the address of _flash_end will correspond to the flash end address. I.e. the &_flash_start and &_flash_end are giving you the desired range.

\$\endgroup\$
5
\$\begingroup\$

If your MCU follows the ARM Cortex conventions, you can look at the topmost nibble of the address in question:

int
inflash_p(void *addr)
{
    uintptr_t addr_int = addr;
    unsigned int addrtype = addr_int >> 28;

    // 0 = flash, 1,2 = ram, 4 = periph, e = system
    return (addrtype == 0);
}
\$\endgroup\$
4
  • \$\begingroup\$ Thanks, I wasn't aware of that! What's the source of this, TRM? \$\endgroup\$
    – domen
    Aug 21, 2015 at 8:49
  • \$\begingroup\$ Sorry, I don't remember at the moment. When I come across it again, I will edit the answer. \$\endgroup\$
    – corecode
    Aug 21, 2015 at 15:56
  • \$\begingroup\$ 1+2 - what do you mean by that? Do you mean a return of 1 or 2 means it's in RAM, or 3 means it is in RAM? \$\endgroup\$
    – Pugz
    Aug 24, 2015 at 4:38
  • \$\begingroup\$ I think it means 0x10000000 - 0x2fffffff, or said otherwise, every address that starts with nibble 1 or 2. \$\endgroup\$
    – domen
    Aug 24, 2015 at 9:10
2
\$\begingroup\$

You can cast the pointer to an integer, then compare its value to the flash address space as defined in your MCU's memory map. For example, if your flash memory is at addresses 0x10000000 - 0x1001ffff, you could do:

void DMA_Function(uint8_t *ptr)
{
    uint32_t ptrAddress = (uint32_t)ptr;

    if (ptrAddress >= 0x10000000 && ptrAddress < 0x10020000)
        //The address is in flash
    else
        //The address is not in flash

    ...
}

Of course, in actual code it's better to use constants instead of hardcoded addresses -- FLASH_START_ADDR and FLASH_END_ADDR, for instance. If you want to be extra-flexible you can define linker variables for the flash addresses, which keeps the addresses out of your source code altogether.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.