'__flash' specified for auto variable?

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

I'm writing some code to use one of those SPI/I2C OLED screens that are all over eBay (using Atmel Studio 7/GCC).  The initialisation of the screen requires sending a set of commands to configure it before use.  Since the init values are never changing I packed everything into an array:

 

void oled_init(void)
{
    uint8_t initData[] =
    {
        SETCMDLOCK, 0x12,		// Command unlock
        SETDISPOFF,				// Set display off
        SETCONTRASTCRTL, 0x01,	// Set contrast
        SETDISPFROMRAM,			// Set display to display from RAM
        // ...
        // etc, etc
        // ...
        SSD1309_SETDISPON				// Set display on									0xAF
    };
    
    // ...
    // ...
    
    // Send above to display over SPI
    for (int i = 0; i < (sizeof(initData) / sizeof(initData[0])); i++)
	{
		SPDR = initData[i];
		while((SPSR & (1 << SPIF)) == 0);
	}

    return;
}

 

My understanding is that all this data gets copied from program memory to SRAM, then used in my program code, and the more sensible way (less overhead) of dealing with is is to use the "const __flash" qualifier, ie:

 

const __flash uint8_t initData[]

However this leads to the error: '__flash' specified for auto variable 'initData'.  A search shows that the solution is to make initData static or global, but I don't understand why.  Also, are there any consequences for doing so, and would one be preferred over the other (static vs global)?

 

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

>but I don't understand why

 

You cannot create a flash stored var at runtime, which is why it has to be done at compile time by making it global/static. If the function is the only user of the array, make it static to the function. It does not matter, but its generally a good habit to limit access to only those that need it.

 

 

>My understanding is that all this data gets copied from program memory to SRAM, then used in my program code,

 

Its probably a little worse than you think. In addition, that data also lives in ram for the duration- you now have it in flash and in ram, plus you copy it to the stack (ram)-

https://godbolt.org/z/2yf8ba

you can see room is created on the stack, then filled with the ram stored data before it uses it (you now have 3 copies of the data- flash, ram, stack, although stack you get back when function is done).

 

That online compiler is c++ only (backend always uses g++), so cannot create __flash examples, but you can view your own asm output to see the difference (one copy in flash, access via lpm).

 

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

Do you see some kind of problem in doing it as:

const __flash uint8_t initData[] =
{
    SETCMDLOCK, 0x12,		// Command unlock
    SETDISPOFF,				// Set display off
    SETCONTRASTCRTL, 0x01,	// Set contrast
    SETDISPFROMRAM,			// Set display to display from RAM
    // ...
    // etc, etc
    // ...
    SSD1309_SETDISPON				// Set display on									0xAF
};

void oled_init(void)
{

    // ...
    // ...
    
    // Send above to display over SPI
    for (int i = 0; i < (sizeof(initData) / sizeof(initData[0])); i++)
	{
		SPDR = initData[i];
		while((SPSR & (1 << SPIF)) == 0);
	}

    return;
}

Are you worried about name pollution? Two possible approaches:

 

1) I don't think it'll make any difference if you add "static" to the initData so the only issue would then be another initData in the same file

 

2) while C doesn't have C++'s namespaces you can just use prefixes so make it oledInitData rather than the more generic initData to make it more specific to the OLED support.

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

This is how I would do it. The flash data can live in the init function as no one else has need of it. Making it static in the function is equivalent to making it global as far as storage is concerned, you simply just limited the scope of its visibility. Its a local with global storage, or a global with local access. Tomato, potato.

 

Remove the static, and you are trying to create a __flash var at runtime- it would have to store the init data in flash (and ram), use that data to init the __flash data by writing to flash to init the initData, in addition to the flash write capabilities needed you would also be storing the data twice in flash (edit, and you still get a copy in ram). Be thankful for the error message.

 

void oled_init(){
    __flash static const uint8_t initData[] = {1,2,3,4,5,6,7,8.9,10,11,12,13,14};
    for( uint8_t i = 0; i < sizeof(initData); i++ ) SREG = initData[i];
}

/*
00000064 <oled_init>:
  64:    90 e0           ldi    r25, 0x00    ; 0
  66:    80 e0           ldi    r24, 0x00    ; 0
  68:    fc 01           movw    r30, r24
  6a:    ec 5c           subi    r30, 0xCC    ; 204
  6c:    ff 4f           sbci    r31, 0xFF    ; 255
  6e:    24 91           lpm    r18, Z
  70:    2f bf           out    0x3f, r18    ; 63
  72:    01 96           adiw    r24, 0x01    ; 1
  74:    8d 30           cpi    r24, 0x0D    ; 13
  76:    91 05           cpc    r25, r1
  78:    b9 f7           brne    .-18         ; 0x68 <oled_init+0x4>
  7a:    08 95           ret

*/

Last Edited: Wed. May 6, 2020 - 01:21 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

curtvm wrote:

You cannot create a flash stored var at runtime, which is why it has to be done at compile time by making it global/static. If the function is the only user of the array, make it static to the function. It does not matter, but its generally a good habit to limit access to only those that need it.

 

I guess that makes sense... Anything in flash will exist for the entire duration of the program, effectively matching a static or global variable.

 

I haven't fully figured out the assembly yet, but my version with "const __flash static" does have the lpm.  And shorter oled_init code.

 

clawson wrote:

Do you see some kind of problem in doing it as:

 

Nope, but like curtvm I'll make it static in the init function.  The array doesn't get used anywhere else, so I find it cleaner to keep it where it gets used.

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

As I understand it, they changed the way the "Flash" is used.  It's now an "Attribute".  I was editing a program, trying to weed out the errors and ran across this in the AVR-LIBC users manual, page 22.

 

5.1 Introduction
So you have some constant data and you're running out of room to store it? Many AVRs have limited amount of R -
AM in which to store data, but may have more Flash space available. The AVR is a Harvard architecture processor,
where Flash is used for the program, RAM is used for data, and they each have separate address spaces. It is a
challenge to get constant data to be stored in the Program Space, and to retrieve that data to use it in the AVR
application.
The problem is exacerbated by the fact that the C Language was not designed for Harvard architectures, it was
designed for Von Neumann architectures where code and data exist in the same address space. This means that
any compiler for a Harvard architecture processor, like the AVR, has to use other means to operate with separate
address spaces.
Some compilers use non-standard C language keywords, or they extend the standard syntax in ways that are
non-standard. The AVR toolset takes a different approach.
GCC has a special keyword, __attribute__ that is used to attach different attributes to things such as function
declarations, variables, and types. This keyword is followed by an attribute specification in double parentheses. In
AVR GCC, there is a special attribute called progmem. This attribute is use on data declarations, and tells the
compiler to place the data in the Program Memory (Flash).
AVR-Libc provides a simple macro PROGMEM that is defined as the attribute syntax of GCC with the progmem
attribute. This macro was created as a convenience to the end user, as we will see below. The PROGMEM macro is
defined in the <avr/pgmspace.h> system header file.
It is difficult to modify GCC to create new extensions to the C language syntax, so instead, avr-libc has created
macros to retrieve the data from the Program Space. These macros are also found in the <avr/pgmspace.h>
system header file.

 

I had a code fragment that I got working (or not producing an error, I'll see if I can locate it).

 

Hope this helps, or is at least relevant.

 

Whoops I didn't get it working, but the TWI LCD (AVR App Note 155) LCD_Control.C used the "Flash" function and I had to remove it, didn't quite get the Attribute part working, just found out how it was supposed to be used, but didn't implement it yet, next on the list.....

Just gettin' started, again....

Last Edited: Fri. May 15, 2020 - 11:15 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

OldMicroGuy wrote:
As I understand it, they changed the way the "Flash" is used.  It's now an "Attribute".
I think you are conflating __flash with PROGMEM.  The the latter is part of AVR Libc and is implemented via GCC's support for attributes.  The former is part of AVR GCC itself and is, in fact, a qualifier, denoting a named address space.

 

PROGMEM in AVR Libc predates __flash in AVR GCC.  The later is preferred, unless working with C++ (no support for __flash).

 

https://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html

6.17 Named Address Spaces

As an extension, GNU C supports named address spaces as defined in the N1275 draft of ISO/IEC DTR 18037. Support for named address spaces in GCC will evolve as the draft technical report changes. Calling conventions for any target might also change. At present, only the AVR, M32C, RL78, and x86 targets support address spaces other than the generic address space.

Address space identifiers may be used exactly like any other C type qualifier (e.g., const or volatile). See the N1275 document for more details.

6.17.1 AVR Named Address Spaces

On the AVR target, there are several address spaces that can be used in order to put read-only data into the flash memory and access that data by means of the special instructions LPM or ELPM needed to read from flash.

Devices belonging to avrtiny and avrxmega3 can access flash memory by means of LD* instructions because the flash memory is mapped into the RAM address space. There is no need for language extensions like __flash or attribute progmem. The default linker description files for these devices cater for that feature and .rodata stays in flash: The compiler just generates LD* instructions, and the linker script adds core specific offsets to all .rodata symbols: 0x4000 in the case of avrtiny and 0x8000 in the case of avrxmega3. See AVR Options for a list of respective devices.

For devices not in avrtiny or avrxmega3, any data including read-only data is located in RAM (the generic address space) because flash memory is not visible in the RAM address space. In order to locate read-only data in flash memory and to generate the right instructions to access this data without using (inline) assembler code, special address spaces are needed.

__flash

The __flash qualifier locates data in the .progmem.data section. Data is read using the LPM instruction. Pointers to this address space are 16 bits wide.

__flash1

__flash2

__flash3

__flash4

__flash5

These are 16-bit address spaces locating data in section .progmemN.data where N refers to address space __flashN. The compiler sets the RAMPZ segment register appropriately before reading data by means of the ELPM instruction.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]

 

Last Edited: Sat. May 16, 2020 - 04:39 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Probably, I'm still trying to figure this out.  Just troubleshooting that LCD file, it complained about the Flash being used, and it looked like to me it was changed so it's not used.

 

Thanks for the tip, maybe this will help in that LCD code.

Just gettin' started, again....