Placing a function in RAM

Hi there,

been following the blog series and trying to get my head around a few things. I have a requirement to execute some system control functions from RAM rather than flash and after some reading found a suggestion to just add the .data section attribute to the function. So having added the following:

__attribute__((long_call, section (".data"))) void Test_gpio_Toggle(uint32_t gpio_msk) { blah }

I can see the relevant code in the disassembly being placed at the start of my RAM region but when I run the test (in a simulation) I can see that the code is still executed from flash. Itā€™s strange as I can see the RAM location being initialised with the correct instructions but thatā€™s the only time they are accessed.

Having done some more reading I saw a note mentioning that the attribute should also be applied to any function that this function calls. But all Iā€™m doing is accessing a memory mapped peripheral to toggle a GPIO as a first pass test.

Am I not using the correct attribute or making another mistake?

Cheers.

OK, should have tried this before but reducing the optimisation level seems to fix the issue. Passing -O0 instead of -O3 to GCC seems to work.

Not having much experience here, woud this be classed as a GCC bug or just expected pain?

So having got the basic case working by adding the above attribute I went a step further and tried to place these functions in my ā€œinstructionā€ ram. I created a new section in my linker script but running objcopy now creates a huge binary.

This seems to be a common issue with a broken linker script but I canā€™t a solution. Iā€™ve copied my linker scipt below. My section name is .iram and that should go into the IRAM memory.

/******************************************************************************
 * @file     gcc_arm.ld
 * @brief    GNU Linker Script for Cortex-M based device
 * @version  V2.0.0
 * @date     21. May 2019
 ******************************************************************************/
/*
 * Copyright (c) 2009-2019 Arm Limited. All rights reserved.
 *
 * SPDX-License-Identifier: Apache-2.0
 *
 * Licensed under the Apache License, Version 2.0 (the License); you may
 * not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an AS IS BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
;-------- <<< Use Configuration Wizard in Context Menu >>> -------------------
*/

/*--------------------- Flash Configuration ----------------------------------
; <h> Flash Configuration
;   <o0> Flash Base Address <0x0-0xFFFFFFFF:8>
;   <o1> Flash Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
 -----------------------------------------------------------------------------*/
__ROM_BASE = 0x00000000;
__ROM_SIZE = 0x00008000;

/*--------------------- Embedded RAM Configuration ---------------------------
; <h> RAM Configuration
;   <o0> RAM Base Address    <0x0-0xFFFFFFFF:8>
;   <o1> RAM Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
 -----------------------------------------------------------------------------*/
__IRAM_BASE = 0x10000000;
__IRAM_SIZE = 0x00004000;

__DRAM_BASE = 0x20000000;
__DRAM_SIZE = 0x00008000;

/*--------------------- Stack / Heap Configuration ---------------------------
; <h> Stack / Heap Configuration
;   <o0> Stack Size (in Bytes) <0x0-0xFFFFFFFF:8>
;   <o1> Heap Size (in Bytes) <0x0-0xFFFFFFFF:8>
; </h>
 -----------------------------------------------------------------------------*/
__STACK_SIZE = 0x00000400;
__HEAP_SIZE  = 0x00000000;

/*
;-------------------- <<< end of configuration section >>> --------------------
*/

INCLUDE "lib-nosys.ld"

MEMORY
{
  FLASH (rx)  : ORIGIN = __ROM_BASE,  LENGTH = __ROM_SIZE
  IRAM  (rwx) : ORIGIN = __IRAM_BASE, LENGTH = __IRAM_SIZE
  DRAM  (rwx) : ORIGIN = __DRAM_BASE, LENGTH = __DRAM_SIZE
}

/* Linker script to place sections and symbol values. Should be used together
 * with other linker script that defines memory regions FLASH and RAM.
 * It references following symbols, which must be defined in code:
 *   Reset_Handler : Entry of reset handler
 *
 * It defines following symbols, which code can use without definition:
 *   __exidx_start
 *   __exidx_end
 *   __copy_table_start__
 *   __copy_table_end__
 *   __zero_table_start__
 *   __zero_table_end__
 *   __etext
 *   __data_start__
 *   __preinit_array_start
 *   __preinit_array_end
 *   __init_array_start
 *   __init_array_end
 *   __fini_array_start
 *   __fini_array_end
 *   __data_end__
 *   __bss_start__
 *   __bss_end__
 *   __end__
 *   end
 *   __HeapLimit
 *   __StackLimit
 *   __StackTop
 *   __stack
 */
ENTRY(Reset_Handler)

SECTIONS
{
	.text :
	{
		KEEP(*(.vectors))
		*(.text*)

		KEEP(*(.init))
		KEEP(*(.fini))

		/* .ctors */
		*crtbegin.o(.ctors)
		*crtbegin?.o(.ctors)
		*(EXCLUDE_FILE(*crtend?.o *crtend.o) .ctors)
		*(SORT(.ctors.*))
		*(.ctors)

		/* .dtors */
 		*crtbegin.o(.dtors)
 		*crtbegin?.o(.dtors)
 		*(EXCLUDE_FILE(*crtend?.o *crtend.o) .dtors)
 		*(SORT(.dtors.*))
 		*(.dtors)

		*(.rodata*)

		KEEP(*(.eh_frame*))
	} > FLASH

/* SG veneers:
   All SG veneers are placed in the special output section .gnu.sgstubs. Its start address
   must be set, either with the command line option ā€˜--section-startā€™ or in a linker script,
   to indicate where to place these veneers in memory.
 */
/*
    .gnu.sgstubs :
    {
        . = ALIGN(32);
    } > FLASH
*/

	.ARM.extab :
	{
		*(.ARM.extab* .gnu.linkonce.armextab.*)
	} > FLASH

	__exidx_start = .;
	.ARM.exidx :
	{
		*(.ARM.exidx* .gnu.linkonce.armexidx.*)
	} > FLASH
	__exidx_end = .;


	.copy.table :
	{
		. = ALIGN(4);
		__copy_table_start__ = .;
		LONG (__etext)
		LONG (__data_start__)
		LONG (__data_end__ - __data_start__)
    /** Add each additional data section here */
		LONG (__etext2)
		LONG (__data2_start__)
		LONG (__data2_end__ - __data2_start__)
		__copy_table_end__ = .;
	} > FLASH


	.zero.table :
	{
		. = ALIGN(4);
		__zero_table_start__ = .;
    /** Add each additional bss section here */
    /*
    LONG (__bss2_start__)
		LONG (__bss2_end__ - __bss2_start__)
    */
		__zero_table_end__ = .;
	} > FLASH

	/* Location counter can end up 2byte aligned with narrow Thumb code but
	   __etext is assumed by startup code to be the LMA of a section in RAM
	   which must be 4byte aligned */
	__etext = ALIGN (4);

	.data : AT (__etext)
	{
		__data_start__ = .;
		*(vtable)
		*(.data)
   		*(.data.*)

		. = ALIGN(4);
		*(.dram)

		. = ALIGN(4);
		/* preinit data */
		PROVIDE_HIDDEN (__preinit_array_start = .);
		KEEP(*(.preinit_array))
		PROVIDE_HIDDEN (__preinit_array_end = .);

		. = ALIGN(4);
		/* init data */
		PROVIDE_HIDDEN (__init_array_start = .);
		KEEP(*(SORT(.init_array.*)))
		KEEP(*(.init_array))
		PROVIDE_HIDDEN (__init_array_end = .);


		. = ALIGN(4);
		/* finit data */
		PROVIDE_HIDDEN (__fini_array_start = .);
		KEEP(*(SORT(.fini_array.*)))
		KEEP(*(.fini_array))
		PROVIDE_HIDDEN (__fini_array_end = .);

		KEEP(*(.jcr*))
		. = ALIGN(4);
		/* All data end */
		__data_end__ = .;

	} > DRAM



  /**
   * Secondary data section, optional
   *
   * Remember to add each additional data section
   * to the .copy.table above to asure proper
   * initialization during startup.
   */
	__etext2 = ALIGN (4);

	.data2 : AT (__etext2)
	{
    . = ALIGN(4);
		__data2_start__ = .;
		*(.iram)
		. = ALIGN(4);
		__data2_end__ = .;

	} > IRAM


	.bss :
	{
		. = ALIGN(4);
		__bss_start__ = .;
		*(.bss)
		*(.bss.*)
		*(COMMON)
		. = ALIGN(4);
		__bss_end__ = .;
	} > DRAM

  /**
   * Secondary bss section, optional
   *
   * Remember to add each additional bss section
   * to the .zero.table above to asure proper
   * initialization during startup.
   */
  /*
	.bss2 :
	{
		. = ALIGN(4);
		__bss2_start__ = .;
    *(.bss2)
		*(.bss2.*)
		. = ALIGN(4);
		__bss2_end__ = .;
	} > RAM2
  */

	.heap :
	{
    . = ALIGN(4);
		__end__ = .;
		PROVIDE(end = .);
		. = . + __HEAP_SIZE;
    . = ALIGN(4);
		__HeapLimit = .;
	} > DRAM

	.stack :
	{
    . = ORIGIN(DRAM) + LENGTH(DRAM) - __STACK_SIZE;
    . = ALIGN(4);
    __StackLimit = .;
    . = . + __STACK_SIZE;
    . = ALIGN(4);
    __StackTop = .;
	} > DRAM

	PROVIDE(__stack = __StackTop);

	/* Check if data + heap + stack exceeds RAM limit */
	ASSERT(__StackLimit >= __HeapLimit, "region RAM overflowed with stack")
}
1 Like

I think your problem might be that you need both the declaration and the definition to have the attribute. If I read your code right, you only put it in the .h file (not the .c)?

Thanks, the original issue was solved by reducing the optimisation level. Iā€™m now stuck with understanding how to reduce the binary size after my linker script changes.

I think the problem is from this snippet:

    __etext2 = ALIGN (4);
    
    .data2 : AT (__etext2)

The first line is setting the cursor to the end of the previous placed section, so the binary ends up with a very high address for the LMA. I think you want the AT expression to point to the end of the LMA for the .data section.

Thanks. Yeah, I solved it in the end after some help on the Slack channel. I ended up using __etext to define the end of the text section and then the second .data2 section was started at __etext + SIZEOF(.data).

I had assumed that the syntax of AT() would take care of that for you. Iā€™m still not clear on how it works as the .bss section that comes after doesnā€™t have any special treatment and ends up in the right place.

1 Like