cancel
Showing results for 
Search instead for 
Did you mean: 

STM32H743 Linker configuration to place specific functions in ITCM RAM

AZdzi.1
Associate

Hi everyone,

I'd like to be able to locate some functions in ITCM RAM instead of FLASH using section attributes. I have a Nucleo-H743ZI. I already have it semi-working in that this code works as expected and runs from ITCM_RAM (checked with gdb):

static volatile uint32_t dummy_i, dummy_end = 1000000UL;
static __attribute__((section(".fastcode"))) int dummy_fastfunc(void)  {
     for(dummy_i=0; dummy_i<dummy_end; dummy_i++) {
        asm volatile("nop;");
     }
     return 0;
 }

But if I use any local variables (like say for(int i=0; .....) the whole thing stops working. I found the reason using gdb: Local variables inside that function get a read-only address in FLASH (>0x8000000). That seems weird because my linker script puts all other local variables faithfully in DTCMRAM.

Linker script:

/* Entry Point */
ENTRY(Reset_Handler)
 
/* Highest address of the user mode stack */
_estack = 0x20020000;    /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200;      /* required amount of heap  */
_Min_Stack_Size = 0x400; /* required amount of stack */
 
/* Specify the memory areas */
MEMORY
{
DTCMRAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
RAM_D1 (xrw)      : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw)      : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw)      : ORIGIN = 0x38000000, LENGTH = 64K
ITCMRAM (xrw)      : ORIGIN = 0x00000000, LENGTH = 64K
FLASH (rx)      : ORIGIN = 0x8000000, LENGTH = 2048K
}
 
/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH
 
  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(4);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)
 
    KEEP (*(.init))
    KEEP (*(.fini))
 
    . = ALIGN(4);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH
 
  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(4);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH
 
  .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
  .ARM : {
    __exidx_start = .;
    *(.ARM.exidx*)
    __exidx_end = .;
  } >FLASH
 
  .preinit_array     :
  {
    PROVIDE_HIDDEN (__preinit_array_start = .);
    KEEP (*(.preinit_array*))
    PROVIDE_HIDDEN (__preinit_array_end = .);
  } >FLASH
  .init_array :
  {
    PROVIDE_HIDDEN (__init_array_start = .);
    KEEP (*(SORT(.init_array.*)))
    KEEP (*(.init_array*))
    PROVIDE_HIDDEN (__init_array_end = .);
  } >FLASH
  .fini_array :
  {
    PROVIDE_HIDDEN (__fini_array_start = .);
    KEEP (*(SORT(.fini_array.*)))
    KEEP (*(.fini_array*))
    PROVIDE_HIDDEN (__fini_array_end = .);
  } >FLASH
 
  /* used by the startup to initialize data */
  _sidata = LOADADDR(.data);
 
  /* Initialized data sections goes into RAM, load LMA copy after code */
  .data : 
  {
    . = ALIGN(4);
    _sdata = .;        /* create a global symbol at data start */
    *(.data)           /* .data sections */
    *(.data*)          /* .data* sections */
 
    . = ALIGN(4);
    _edata = .;        /* define a global symbol at data end */
  } >DTCMRAM AT> FLASH
 
  
  /* Uninitialized data section */
  . = ALIGN(4);
  .bss :
  {
    /* This is used by the startup in order to initialize the .bss secion */
    _sbss = .;         /* define a global symbol at bss start */
    __bss_start__ = _sbss;
    *(.bss)
    *(.bss*)
    *(COMMON)
 
    . = ALIGN(4);
    _ebss = .;         /* define a global symbol at bss end */
    __bss_end__ = _ebss;
  } >DTCMRAM
 
  /* User_heap_stack section, used to check that there is enough RAM left */
  ._user_heap_stack :
  {
    . = ALIGN(4);
    PROVIDE ( end = . );
    PROVIDE ( _end = . );
    . = . + _Min_Heap_Size;
    . = . + _Min_Stack_Size;
    . = ALIGN(4);
  } >DTCMRAM
 
  .bigsram(NOLOAD) :
  {
      . = ALIGN(4);
      *(.bigsram)
      *(.bigsram*)
  } >RAM_D1
 
  _sifastcode = LOADADDR(.fastcode);
  .fastcode :
  {
	  . = ALIGN(4)+4;
	  _sfastcode = .;
	  *(.fastcode)
	  *(.fastcode*)
	  . = ALIGN(4);
	  _efastcode = .;
  } >ITCMRAM AT> FLASH
 
  /* Remove information from the standard libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }
 
  .ARM.attributes 0 : { *(.ARM.attributes) }
}

As you can see, I added the .bigsram section (for DMA arrays in AXI SRAM, works fine) and the .fastcode section to a standard linker script by STM which puts everything else in either FLASH or DTCMRAM. I am calling this at the start of main to copy the .fastcode section from FLASH to ITCMRAM:

extern uint32_t _sfastcode, _efastcode, _sifastcode;
static void init_fastcode_section(void) {
    uint8_t *fastcode_ram_start = (uint8_t *) &_sfastcode,
            *fastcode_ram_end = (uint8_t *) &_efastcode,
            *fastcode_flash_start = (uint8_t *) &_sifastcode;
    size_t len = fastcode_ram_end - fastcode_ram_start;
    for(size_t i=0; i<len; i++) {
        *(fastcode_ram_start+i) = *(fastcode_flash_start+i);
    }
}

As said this works nicely for functions without any local variables (or constants) but comes crashing down otherwise. I am sure I am making a stupid mistake but I can't find it :/.

I had a look at this where someone successfully mapped the ISRs to ITCM but it seems similar: https://www.openstm32.org/tiki-view_forum_thread.php?comments_parentId=2381

Hope someone has an idea... Thanks for your time!

-Andy

2 REPLIES 2

I don't particularly care for GNU/GCC and it's roughness of implementation, but put a bounty on this and I'll make it work.

I'd put the section after the other >*RAM AT> FLASH sections, before the BSS one, and I'd clean up the startup.s implementation so it initialized it's workspace properly.

Local variables should work, they are supposed to use the stack or registers. Perhaps stop using the static directive so liberally.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
AZdzi.1
Associate

Thanks for your answer.

Actually, I figured it out. Was indeed a stupid mistake: It seems Linker script line 139 with . = ALIGN(4)+4;

caused the linker to go haywire. I tried to be clever as the ITCMRAM starts at address 0x0 and I didn't want to have a usable function at what is essentially the definition of the NULL pointer. So I thought I could just advance the start of the section by 4 Bytes. Apparently I was wrong. :shrug: Everything works fine once I remove the +4 and get comfortable having the PC jump to 0x0. 🙂