Electronic – ATmega: Can one determine if an address is program memory or data memory

atmegaavrmemory

On an Atmel ATmega1284 (or any of its AVR cousins), is there any way to determine at compile time or at runtime whether an address that is passed to a function belongs to program memory space or data memory space?

Specifically, I want to provide a debug trace where the input string may be stored in program memory space or in data memory space, but I prefer to use just one function for it so the caller doesn't need to bother: if the input string is stored in program memory, I can copy it to a stack-allocated buffer and then pretend it was always in data memory. For example, in C-like pseudocode:

function my_trace(const char *string) {
    if (is_in_program_memory_space(string)) {
        strcpy_P(local_buffer, string);
        print_the_(local_buffer);
    }
    else {
        print_the_(string);
    }
}

const char data_memory_string[] = "data memory string";
const char program_memory_string[] PROGMEM = "program memory string";

my_trace(data_memory_string);
my_trace(program_memory_string);

I tried to put in a breakpoint in the compiled code and it seems the addresses of a data-memory–allocated string and a program-memory–allocated string happen to be very close (like 0x800040 versus 0x800360) so I can't rely on address ranges. (And, doing that would probably be unsafe anyway, but that's a different discussion.)

Edit: I'm using the toolchain that comes with Atmel Studio, which in this case is version AVR_8_bit_GNU_Toolchain_3.6.2_1778.

Edit 2: The solution provided by user @NStorm indeed solves the problem. However, on my system it was necessary to typecast the return value of __builtin_avr_flash_segment() to a signed char.

Best Answer

As noted in other answer, __memx might help. Check it's description:

This is a 24-bit address space that linearizes flash and RAM: If the high bit of the address is set, data is read from RAM using the lower two bytes as RAM address. If the high bit of the address is clear, data is read from flash with RAMPZ set according to the high byte of the address. See __builtin_avr_flash_segment.

So it's possible to tell where data are stored in FLASH or RAM by checking that high bit. But let's take a look at that __builtin_avr_flash_segment description:

char __builtin_avr_flash_segment (const __memx void*)

This built-in takes a byte address to the 24-bit address space __memx and returns the number of the flash segment (the 64 KiB chunk) where the address points to. Counting starts at 0. If the address does not point to flash memory, return -1.

Seems to be just what you need. So basically you can go with this example (taken from here):

void func (void * dest, const __memx void *source) 
{ 
    if (__builtin_avr_flash_segment (source) < 0)
        strcpy (dest, source);
    else
        strcpy_P (dest, source);
}

PS. I suggest to read whole Named Address Spaces manual page. It has some caveats and limatitions.