How to place RAM func in GCC environment???


It is so easy to place a function in RAM when you use IAR EWARM, all you have to do is  put one magical word. Then, EWARM do automatically handle the function and place it in RAM.

Now, how about MCUXpresso IDE? that is a GCC environment, how can you do that?

This time, I will give you a hint how you can place a function in RAM in case of GCC environment.

As a matter of fact, it is also so easy to do it even in GCC environment like it is in IAR EWARM environment.

Basically, all you need is just one key word to be placed before the function or variable you want. The function or variable with the keyword will be placed in RAM.

Just as your reference, here is the previous post about IAR EWARM case.

Ref. Sooo easy! Just 1 magical word ! To execute a function from RAM using IAR EWARM.

Here we go!

Points

  1. Required “__attribute__” keyword
  2. Need to place it in .data section
  3. It is automatically copied from FlashROM to RAM in order to place it in RAM.

__attribute__ key word

__attribute__ keyword is described as below in the GCC online documentation.

In GNU C, you can use function attributes to declare certain things about functions called in your program which help the compiler optimize calls and check your code more carefully. Using the GNU Compiler Collection (GCC)
You can also use attributes to control memory placement, code generation options or call/return conventions within the function being annotated. Many of these attributes are target-specific.Using the GNU Compiler Collection (GCC)

Particularly, it is said that it is used to declare the attribute of the function or variable, then it helps the compiler to optimize calls and check the code.

For example, if you use the keyword for an interrupt handler function, then the entry/exit of the function optimized for an interrupt handler is generated.

And, you can control the code placement. Yes, this is what we need it.

Attributes

longcall:This is an attribute for function. It declares that the function with this attribute is to be called from different place. The compiler always uses a pointer to call the function.

section :This attribute places variables and/or functions in section you specify.

With these attributes specified, you can place your functions or variables in RAM. If you want a variable to be placed in RAM, you may not use longcall attribute. That attribute requires for function, but not for variables.

Code example :  __attribute__ ((longcall, section(“memory_section”)))
“memory_section” is the section in which you want your function to be placed.

You need a double parenthesis to have those attributes inside. if you have multiple attributes, you can separate them with comma.

Linker script

If you want to place a function in specific section, you might as well create section in liker script.

Then, let’s see the inside.


.data_RAM2 : ALIGN(4)
    {
        FILL(0xff)
        PROVIDE(__start_data_RAM2 = .) ;
        *(.ramfunc.$RAM2)
        *(.ramfunc.$SRAM_LOWER)
        *(.data.$RAM2*)
        *(.data.$SRAM_LOWER*)
        . = ALIGN(4) ;
        PROVIDE(__end_data_RAM2 = .) ;
     } > SRAM_LOWER AT > PROGRAM_FLASH

/* DATA section for FLEX_RAM */
.data_RAM3 : ALIGN(4)
    {
        FILL(0xff)
        PROVIDE(__start_data_RAM3 = .) ;
        *(.ramfunc.$RAM3)
        *(.ramfunc.$FLEX_RAM)
        *(.data.$RAM3*)
        *(.data.$FLEX_RAM*)
        . = ALIGN(4) ;
        PROVIDE(__end_data_RAM3 = .) ;
     } > FLEX_RAM AT > PROGRAM_FLASH

.data : ALIGN(4)
    {
       FILL(0xff)
       _data = . ;
       *(vtable)
       *(.ramfunc*)
       *(.data*)
       . = ALIGN(4) ;
       _edata = . ;
    } > SRAM_UPPER AT>PROGRAM_FLASH

MCUXpresso SDK gives you several RAM section in default where you can place your function, data, data_RAM2 and data_RAM3.

.data/.data_RAM2/.data_RAM3 is a section name. Besides these sections, there are a code section (.text) or readonly data section (.rodata) for instance.

The section name is followed by {} brackets.

In the .data section of bracket, there is *(.ramfunc*).  [*] is is a wild card of object files.

‘.ramfunc*’ is a section name which is defined in a source code.

Placement in section

For .data section, the above linker script places all object file in .data section if the object files are with attribute of section .ramfunc*.

If it is matched with .ramfunc.$RAM2 or .ramfunc.$RAM3, then it will be placed in .data_RAM2 or RAM3 instead of .data section. If the later part of the section name like .$RAM2 doesn’t match, t is placed in .data section.

Actual place to have code stored, and a place to be executed

>RAM_Area AT>PROGRAM_FLASH

This line means that a code or variables and functions are actually stored in PROGRAM_FLASH. But, the code is executed from RAM.

MCUXpresso SDK defines several RAM areas. For example, Kinetis MCU supports three kinds of RAM, upper side of RAM (SRAM_UPPER), lower side of RAM (SRAM_LOWER) and FLEX_RAM if it supports Flex Memory feature.

If you want to place a function in the lower side of RAM, you need to define the attribute with .ramfunc.$SRAM_LOWER (.ramfunc.$RAM2 is also ok).

Then, you will see the function is now placed in SRAM_LOWER section.

If you just declare a function with attribute section .ramfunc, the function will be placed in SRAM_UPPER in default.

Automatically copy will be done from FlashROM to RAM

Even If you define your function in RAM, the function is actually still stored in FlashROM (PROGRAM_FLASH).  The code is however executed from RAM you specified.

RAM is a volatile memory, meaning that data in RAM stays there while the device is powered but RAM loses its data when it is shutting down.

This means that Data needs to be copied from FlashROM to RAM whenever the device is powered and initialized.

if using MCUXpresso SDK, you don’t need to do it. It is automatically done that data or code are copied from FlashROM to the specified attribute section (.data, ,data_RAM2, .data_RAM3) if you define __attribute__ keyword. It is so easy.

So, all you have to do is to declare __attribute__ keyword with section you want like I already mentioned above.

Let’s see the actual code in below test code.

Experiment Test code


volatile uint32_t cnt;

/* I want to place below test() in RAM(SRAM_LOWER). */
__attribute__ ((longcall, section(".data.$SRAM_LOWER"))) void test(){
	cnt++;
}

int main(void)
{
    char ch;

    /* Init board hardware. */
    BOARD_InitPins();
    BOARD_BootClockRUN();
    BOARD_InitDebugConsole();

    PRINTF("hello world.\r\n");

    while (1)
    {
    	test();
        ch = GETCHAR();
        PUTCHAR(ch);
    }
}

Map file



.data_RAM2      0x000000001fff0000       0x1c load address 0x0000000000003600
 FILL mask 0xff
                [!provide]                        PROVIDE (__start_data_RAM2, .)
 *(.ramfunc.$RAM2)
 *(.ramfunc.$SRAM_LOWER)
 *(.data.$RAM2*)
 *(.data.$SRAM_LOWER*)
 .data.$SRAM_LOWER
                0x000000001fff0000       0x1c ./source/hello_world.o
                0x000000001fff0000                test
                0x000000001fff001c                . = ALIGN (0x4)
                [!provide]                        PROVIDE (__end_data_RAM2, .)

.rel.dyn        0x000000001fff001c        0x0 load address 0x000000000000361c
.rel.iplt      0x000000001fff001c        0x0 ./utilities/fsl_assert.o

You can see the test() in RAM, the address is 0x1FFF0000 (SRAM_LOWER).

Summary

For IAR EWARM, there is a magical keyword “__ramfunc” to place code in RAM and it is so easy, whereas in GCC environment there is a similar keyword of it, that is “__attribute__”.

In the attribute keyword, if you specified with RAM section where you want to place, the code with the attribute section is automatically copied from FlashROM to RAM, and it will be executed from RAM.

If you want to place your function in RAM, please try this article.