Confusion regarding named address spaces (e.g. __flash )

Go To Last Post
6 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hello fellow freaks,

 

I'm using AS6.2 which comes with GCC 4.8.1, which supports named address spaces such as __flash. However, I have some confusion about how and when I can use variables that are declared as being in that space. Is my understanding correct that once I've declared a variable "const __flash", I can use that variable anywhere (in C code, not ASM) that I could use a variable only declared "const", and the compiler will take care of the rest? For example, this means that I only have to write one version of a function that accepts a pointer to an array of const chars:

void function(const char *string);

and then I can pass it a string in RAM or in flash:

const char string1[] = "This string is in RAM";
const __flash char string2[] = "This string is in flash";

function(string1);
function(string2);

without any changes?

 

However, if the function is written in ASM, like much of avr-libc, then different functions are required for accessing RAM or flash (e.g. the "_P" suffixed functions) because the compiler doesn't generate the code for accessing the parameters? And so I can't pass a string declared with "__flash" into either the regular or "_P" version of an avr-libc function, so I still need to use the PSTR() macro if I want to pass a string located in flash?

 

Hopefully someone can help me understand named address spaces better.

 

Thanks in advance,

Aaron T.

This topic has a solution.
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

> I can use that variable anywhere (in C code, not ASM) that I could use a variable only declared "const", and the compiler will take care of the rest?

 

The problem is, that you are not going to use the "const __flash" qualified *variable* - that would work as expected right away - but you intend to use a *reference* (pointer) to that variable. That's where the devil rests.

 

The current state of affair (gcc 4.8.1) appears to be that typecasts between namespace-qualified pointers are performed quietly, even if the resulting pointer does not point to a valid object anymore. In other words, if you cast a const __flash sometype_t * pointer into a plain const sometype_t * pointer, the compiler simply does nothing at the moment of cast (as both are implemented with the same width, 16-bit), and it simply uses the resulting pointer as if it points to RAM. This applies to the implicit cast of function parameters during function call as well. The result is, that in your example, the content of string2 residing in _flash would not be used by function(string2), a nonsense residing in RAM at the same address would be used instead.

 

But there is a mechanism coming to rescue: there is a *generic* address space, qualified as __memx. Note, that __memx is *not* intended for variables to be placed into it  - you still ought to place (define) them into __flash or into RAM (as variable with no address-space qualifier); only to provide a mechanism to tag *pointers* as *generic*, i.e. that these pointers can contain *both* RAM and FLASH addresses (read https://gcc.gnu.org/onlinedocs/g... for the details of how is this implemented).

 

So, if you intend to write a function, which shall take a pointer to *both* FLASH and RAM, you qualify the parameter to point into __memx. The compiler then performs the needed conversion when the pointer is implicitly cast during the function invocation. So, your example should be:

void foo(const __memx char * s) {
  // volatile char c = s[0];
}

const char string1[] = "this string is in RAM"
const __flash char string2[] = "this string is in flash";

int main(void) {
  foo(string1);
  foo(string2);
  while(1);
}

Note, that usage of __memx qualified pointers is significantly inefficient - at every access to memory a runtime check has to be performed to find out into which memory does the pointer point and then one of LD/LPM/ELPM executed (the latter accompanied with appropriate RAMPZ load) - at the moment, AFAIK, a function is called to do this, for every memory access.

 

(And yes, avr-libc does not provide functions declared with __memx in mind at the moment; they could maybe provide string.h functions (and perhaps others) similar to the _P and _PF functions; but it would open a nasty can of worms and I personally don't think the hassle is worth it.)

 

---- rationale:

 

This may seem weird, but it has a historical and rational context. Most of the existing code simply expects the pointers to point into RAM (and there is a nonstandard mechanism (pgmspace.h) to use objects in FLASH although not through real pointers). If now the new version of avr-gcc would make the standard, default pointers "wide", "generic" (i.e. with the same behaviour as the __memx pointers are now), new code would work as expected with standard pointers to *all* objects (both in RAM and FLASH), plus all existing code would continue to work, BUT, there would be a significant increase in both code size, occupied RAM size, and execution time for that existing code. The chosen solution - i.e. that default pointers point to a specific address space (RAM) and the generic pointer has to be explicitly named, means that existing code will compile optimally as expected, while new code which wishes to utilize the named address spaces has to be written with increased care.

 

Note, that many if not all 8051 C compilers ares implemented the other way round, as they did not have this historical burden - they use the "generic pointers are default" model, when a "memory model" implying usage of multiple "named address spaces" (called there "memory class") is used (memory models are chosen by command-line switches), as 8051 has by its architecture several such address spaces, which have to be used inevitably for any but the simplest programs.

 

JW

 

 

Last Edited: Fri. Oct 24, 2014 - 07:58 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

One more thing, not directly related to the original question: at the moment, there is no efficient implementation of progmem_far, i.e. 24-bit-address flash-only address space; it is to be handled by __memx, which is inefficient for that purpose as it has to take into account the possibility of target being located in RAM. It may be asked for e.g. in gcc tracker; OTOH, the usage for this is marginal, I admit, and I personally won't pursue it. It still can be handled by a mixture of __memx for allocating variables and generating, storing and passing pointers, and some hand-tweaked code based on the _PF facilities of avr-libc, to use them.

 

JW

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks, that was very helpful. I have a related question you could probably answer, about the differences between the _P and _PF versions of the string.h functions, namely, when and how to use the _PF versions? I'm currently using a mega1284, so should I be using the _PF versions in case the function and the string end up on opposite ends of the flash? In the past I've always used the _P versions and fed them PSTR("strings"), but when I just tried that with a _PF function, I get warnings that "passing argument 2 of 'strstr_PF' makes integer from pointer without a cast" and "expected 'uint_farptr_t' but argument is of type 'const char *'". Is that how it's supposed to be, or should I typecast the (const char *) PSTR into a uint_farptr_t, or should I be using something other than PSTR to locate the string in flash?

 

Thanks again,

Aaron T.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

But pointers (apart from __memx) in avr-gcc are only 16bit so they can only address 64K bytes. Your best bet is to try and ensure that all const data is in the bottom 64K so its location can be represented in 16bits. This is the reason that the default linker scripts place .progmem.data immediately after .vectors in the flash layout.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You need _PF only if the data in flash blows the 64kbyte boundary. Then, use either a combination of statically defined string (i.e. not "inline" using PSTR() but "normal" string variable, char PROGMEM s[] = "blah") and take its address using pgm_get_far_address (i.e. somefunction_PF(pgm_get_far_address(s[0])) ), or use the PFSTR() macro, which back then did not make it into the official pgmspace.h http://www.avrfreaks.net/forum/p...

 

JW