3
\$\begingroup\$

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.

\$\endgroup\$
8
  • \$\begingroup\$ Anything on this hardware that looks like a unified address space is an artificial creation of the toolchain requiring runtime interpretation by software before it can be used by the hardware; as such, this is a question about the behavior not of the chip but of the compiler which could invent any coding scheme it likes; thus this can only be answered in the context of a particular compiler with particular settings. None has been specified. \$\endgroup\$ – Chris Stratton Oct 24 '20 at 14:42
  • 1
    \$\begingroup\$ Please refer to my reply to your almost identical comment to one of the answers. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 15:06
  • 1
    \$\begingroup\$ In the answer where someone offered help, I replied that I'm using AVR_8_bit_GNU_Toolchain_3.6.2_1778. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 15:59
  • 1
    \$\begingroup\$ @TonyM: Done, and thank you. Hopefully that will prompt some people to be helpful instead of complaining. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 16:15
  • 2
    \$\begingroup\$ @ChrisStratton unfortunately avr-gcc toolchain doesn't handles pointers to a flash memory via it's libc functions like printf(). They have a copy endling with _P for these, like printf_P(), etc. So this still need to be handled manually. \$\endgroup\$ – NStorm Oct 26 '20 at 6:15
3
\$\begingroup\$

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.

\$\endgroup\$
10
  • \$\begingroup\$ Looks like I'm out of luck then, because Atmel's current AVR-GCC doesn't seem to support __builtin_avr_flash_segment. I guess I'll have to build the toolchain myself. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 14:11
  • \$\begingroup\$ @OleWolf which one do you call "current"? Atmel no longer exists, it was purchased by Microchip. I have avr-gcc (AVR_8_bit_GNU_Toolchain_3.6.2_1759) 5.4.0 from the official Microchip bundle and it's compiled this example with __builtin_avr_flash_segment() just perfectly fine. \$\endgroup\$ – NStorm Oct 24 '20 at 14:38
  • \$\begingroup\$ I was of course referring to the Atmel Studio (provided by Microchip, yes) that I downloaded only a few weeks ago. My toolchain is version 3.6.2_1778. I seem to have jumped to a conclusion though, but the function doesn't seem to work as documented: the compiler states that __builtin_avr_flash_segment(address) < 0 is always false. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 15:10
  • \$\begingroup\$ @OleWolf you have to provide a correct pointer with a __memx attribue for it to work. Probably you are calling it with __flash type pointer argument instead? \$\endgroup\$ – NStorm Oct 24 '20 at 15:38
  • 1
    \$\begingroup\$ @OleWolf I've just compiled and disassembled example (with avr-objdump). str1 are const __flash type, str2 are casual string in RAM. Disassembly shows it works like intended. See this listing screenshot: i.imgur.com/qzjZgev.png. When it's calling a function from main(), it loads 3 bytes (as __memx are 24-bit pointer) into registers where r22 stores MSB. As you can see it loads 0 for flash string there and 0x80 (1 in MSB) for RAM string. Later, __builtin...() checks that bit and jumps to appropriate function as expected. \$\endgroup\$ – NStorm Oct 24 '20 at 18:42
1
\$\begingroup\$

If your AVR GCC is recent enough you could use __memx address space, see Named Address Spaces in the GCC docu.

\$\endgroup\$
12
  • 1
    \$\begingroup\$ @ChrisStratton I'm fully aware of the difference between von Neumann and Harvard memory architectures. That's why I asked whether there was some compiler-provided support for determining which of the address spaces some address belongs to. (My addemdum that I might be able to derive a hint by looking at the address nonetheless came from the fact that although the address spaces are independent I might have been lucky to learn that the address spaces don't overlap. Regrettably, they seem to do.) \$\endgroup\$ – Ole Wolf Oct 24 '20 at 14:29
  • 2
    \$\begingroup\$ @ChrisStratton Yes, I know that there's no unified address space on a Harvard architecture, and that you can't compare an address in one address space with the address of another, thank you very much. Please acknowledge that I was asking if the compiler provides any help, because at least the toolchain knows. But, one may find that on some Harvard architecture, the address spaces nonetheless have different addresses and thus could be identifiable--even if this is an unsafe approach, like I said originally. \$\endgroup\$ – Ole Wolf Oct 24 '20 at 15:03
  • 2
    \$\begingroup\$ No, Chris, I'm not confusing anything. Please understand that I'm asking whether the TOOLCHAIN can help discern whether an address belongs to one or the other in a function call, either at compile time or (much less likely) at runtime. (I'm beginning to regret even mentioning that on some processors, one might derive a clue by looking at the address ranges, although it's trivial to understand that Harvard architectures, too, can place data and memory in different address ranges, even if they exist in different and indepenedent address spaces.) \$\endgroup\$ – Ole Wolf Oct 24 '20 at 16:27
  • 2
    \$\begingroup\$ @ChrisStratton it's not about the architecture. It's about how C Compiler handles this. As avr-gcc has helper macros to store data in flash space and access it, such as __flash attribute but OP was asking if there is a way later to check if the passed pointer to a data are supposed (by the compiler, as it knows how the variable was initialized) to point to a flash or ram space. \$\endgroup\$ – NStorm Oct 24 '20 at 16:30
  • 1
    \$\begingroup\$ @ChrisStratton you are right. But how does that comment relates to OP question where he was clearly asking about the "feature of the toolchain" and not the hardware architecture? \$\endgroup\$ – NStorm Oct 25 '20 at 8:19

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.