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