cancel
Showing results for 
Search instead for 
Did you mean: 

Forcing static initialized arrays into FLASH (not in SRAM)

Lynn Linse
Associate III
Posted on December 29, 2016 at 17:57

THIS ISN'T A QUESTION - IS A RESULT.

I was having issues getting some static arrays to stay in FLASH, not SRAM. Since they NEVER change, allowing them to load into SRAM just means the exist in both flash & SRAM (aka: init'd values copied to SRAM). Since I feel the full solution isn't trivially obvious, I'll record it here for others.

I am using AC6/STM System Workbench and STM32L152RE, so my solution might be specific to the CGG used and default linker scripts.

The simple array was easy - just adding the CONST caused &data_flags to be in the FLASH (0x080XXXXX range) instead of SRAM (0x200XXXXX range).

static const uint8_t data_flags[] = {

    (DS_FLAG_PROTECT),                                  // DB_COUNTERS_CTBOT

    (DS_FLAG_PROTECT),                                  // DB_COUNTERS_CTWDG

    (DS_FLAG_PROTECT | DS_FLAG_MIN),       // DB_COUNTERS_CTDLY

 ...

};

However, tables of strings proved more challenging. The working solution was this:

static const char const *data_tags[] __attribute__ ((section ('.rodata'))) = {

        'CT.BOT',                           // DB_COUNTERS_CTBOT

        'CT.WDG',                           // DB_COUNTERS_CTWDG

        'CT.DLY',                           // DB_COUNTERS_CTDLY

 ...

};

Despite the 2 x CONST, &data_tags continued to be in SRAM, as a table of FLASH pointers to constant strings. So &data_tag was in SRAM (0x200XXXXX range) while data_tag[0] was a pointer to flash (0x080XXXXX range). Adding the __attribute__ ((section ('.rodata'))) phrase moved data_tag itself into FLASH nicely.  '.rodata' is in my STM32L152RETx_FLASH.ld file as the name of a FLASH section. You might need to change this if your linker file uses different names.

#stm32l #flash-variable #static
1 ACCEPTED SOLUTION

Accepted Solutions
Posted on December 30, 2016 at 10:55

IMHO, this is regular C semantic, this shall not be compiler specific.

In your example both const type qualifier applies to char (it is a degree of freedom permitted by the langage). When you want the array of pointers to be constant, the keyword must be next to the identifier.

Well the change is actually moving the 'const' around '*', to make it qualify the 'data_tags' identifier.

This method is better than naming explicitly the section, because the compiler might prevent your code to alter the array (which would lead to hard fault).

View solution in original post

5 REPLIES 5
stm322399
Senior
Posted on December 29, 2016 at 20:54

What if you declare your array the following way:

static const char * const data_tags[] = { ... }

Posted on December 29, 2016 at 21:32

So move the '*' around the CONST?

I can try that. It's also possible this is a specific compiler (as-configured) issue.

Posted on December 30, 2016 at 10:55

IMHO, this is regular C semantic, this shall not be compiler specific.

In your example both const type qualifier applies to char (it is a degree of freedom permitted by the langage). When you want the array of pointers to be constant, the keyword must be next to the identifier.

Well the change is actually moving the 'const' around '*', to make it qualify the 'data_tags' identifier.

This method is better than naming explicitly the section, because the compiler might prevent your code to alter the array (which would lead to hard fault).

Posted on December 30, 2016 at 16:01

No, you're right - your form works without the __attribute__ ... this is just a area of programming I've never worked in before (I've been a Python programmer for 10 years ... now back to C! Quite a change in world-view!)

FYI, I'm also using these inlines in my unittest/regression tests to confirm that variables are where they should be. You need to define the BASE/END values to match your CPU:

inline int i_am_primary_fw(void)   { return (int)(SCB->VTOR == FLASH_BASE); }

inline int i_am_secondary_fw(void) { return (int)(SCB->VTOR == FLASH_BANK2_BASE); }

inline int ptr_is_flash(const uint32_t *ptr)

    { return (int)(FLASH_BASE <= (uint32_t)ptr && (uint32_t)ptr <= FLASH_BANK2_END); }

inline int ptr_is_flash_bank1(const uint32_t *ptr)

    { return (int)(FLASH_BASE <= (uint32_t)ptr && (uint32_t)ptr <= FLASH_BANK1_END); }

inline int ptr_is_flash_bank2(const uint32_t *ptr)

    { return (int)(FLASH_BANK2_BASE <= (uint32_t)ptr && (uint32_t)ptr <= FLASH_BANK2_END); }

inline int ptr_is_eeprom(const uint32_t *ptr)

    { return (int)(FLASH_EEPROM_BASE <= (uint32_t)ptr && (uint32_t)ptr <= FLASH_EEPROM_END); }

inline int ptr_is_eeprom_bank1(const uint32_t *ptr)

    { return (int)(EEPROM_BANK1_BASE <= (uint32_t)ptr && (uint32_t)ptr <= EEPROM_BANK1_END); }

inline int ptr_is_eeprom_bank2(const uint32_t *ptr)

    { return (int)(EEPROM_BANK2_BASE <= (uint32_t)ptr && (uint32_t)ptr <= EEPROM_BANK2_END); }

inline int ptr_is_sram(const uint32_t *ptr)

    { return (int)(SRAM_BASE <= (uint32_t)ptr && (uint32_t)ptr <= SRAM_BASE_END); }
Posted on December 30, 2016 at 16:11

Oh, regrading the i_am_primary_fw() / i_am_secondary_fw() tests, the L152 officially has 2 banks of flash & we're designing our HW to support 2 different co-existing FW. So while the L152 only has '1 bank' of EEPROM, we've split it into kind-of like 2 to give each FW image 1/2 the EEPROM.