Linker script question

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Linker script question

5,203 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by charchar on Tue Mar 31 14:32:27 MST 2015
I have an LPC43xx platform that boots from flash, but executes from RAM, by copying the code upon startup.  It all works fine, but when I'm writing to/erasing the flash and get an interrupt (and the interrupt vector is fetched from flash), it crashes.

I'd like to copy the vector table into RAM and use the RAM vector table, but realize that I need two copies --- one in flash so the stack pointer and reset vector can be read upon reset and one in RAM for normal operation. 

My linker script is below, and I've tried adding these lines (see "*** added" lines).  And I've tried various permutations of this, but it doesn't relocate/copy the .isr_vector table to RAM, mostly because I'm just making stabs and educated guesses.  If this is simple, can someone help?

thanks :)

SECTIONS
{

    /* MAIN TEXT SECTION */   
    .text : ALIGN(4)
    {
        FILL(0xff)
        KEEP(*(.isr_vector))
       
        /* Global Section Table */
        . = ALIGN(4) ;
        __section_table_start = .;
        __data_section_table = .;

.....

       
    } >SPIflash

/* RUN_FROM_RAM */   
    .text_ram : ALIGN(4)
/* END RUN_FROM_RAM */    
    {
         __vectors_start__ = ABSOLUTE(.) ;  *** added
         *(.isr_vector)  *** added
         *(.text*)
        *(.rodata .rodata.* .constdata .constdata.*)
        . = ALIGN(4);

......

    } > RamLoc128 AT>SPIflash


------------------------------------

complete linker script:

INCLUDE "main_m4_SPIFI_lib.ld"

GROUP(libcr_nohost.a libcr_eabihelpers.a)

MEMORY
{
  /* Define each memory region */
  SPIflash (rx) : ORIGIN = 0x14000000, LENGTH = 0x20000 /* 128K bytes */
  RamLoc128 (rwx) : ORIGIN = 0x10000000, LENGTH = 0x20000 /* 128K bytes */
  RamAHB32 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x8000 /* 32K bytes */
  RamLoc72 (rwx) : ORIGIN = 0x10080000, LENGTH = 0x12000 /* 72K bytes */
  RamAHB16 (rwx) : ORIGIN = 0x20008000, LENGTH = 0x4000 /* 16K bytes */
  RamAHB_ETB16 (rwx) : ORIGIN = 0x2000c000, LENGTH = 0x4000 /* 16K bytes */


}
  /* Define a symbol for the top of each memory region */
  __top_SPIflash = 0x14000000 + 0x20000;
  __top_RamLoc128 = 0x10000000 + 0x20000;
  __top_RamAHB32 = 0x20000000 + 0x8000;
  __top_RamLoc72 = 0x10080000 + 0x12000;
  __top_RamAHB16 = 0x20008000 + 0x4000;
  __top_RamAHB_ETB16 = 0x2000c000 + 0x4000;

ENTRY(ResetISR)

SECTIONS
{

    /* MAIN TEXT SECTION */   
    .text : ALIGN(4)
    {
        FILL(0xff)
       
       
        /* Global Section Table */
        . = ALIGN(4) ;
        __section_table_start = .;
        __data_section_table = .;
        LONG(LOADADDR(.data));
        LONG(    ADDR(.data));
        LONG(  SIZEOF(.data));
        LONG(LOADADDR(.data_RAM2));
        LONG(    ADDR(.data_RAM2));
        LONG(  SIZEOF(.data_RAM2));
        LONG(LOADADDR(.data_RAM3));
        LONG(    ADDR(.data_RAM3));
        LONG(  SIZEOF(.data_RAM3));
        LONG(LOADADDR(.data_RAM4));
        LONG(    ADDR(.data_RAM4));
        LONG(  SIZEOF(.data_RAM4));
        LONG(LOADADDR(.data_RAM5));
        LONG(    ADDR(.data_RAM5));
        LONG(  SIZEOF(.data_RAM5));

/* RUN_FROM_RAM */
        LONG(LOADADDR(.text_ram));
        LONG(    ADDR(.text_ram)) ;
        LONG(  SIZEOF(.text_ram));
/* END RUN_FROM_RAM */

        __data_section_table_end = .;
        __bss_section_table = .;
        LONG(    ADDR(.bss));
        LONG(  SIZEOF(.bss));
        LONG(    ADDR(.bss_RAM2));
        LONG(  SIZEOF(.bss_RAM2));
        LONG(    ADDR(.bss_RAM3));
        LONG(  SIZEOF(.bss_RAM3));
        LONG(    ADDR(.bss_RAM4));
        LONG(  SIZEOF(.bss_RAM4));
        LONG(    ADDR(.bss_RAM5));
        LONG(  SIZEOF(.bss_RAM5));
        __bss_section_table_end = .;
        __section_table_end = . ;
        /* End of Global Section Table */
       

        *(.after_vectors*)

/* RUN_FROM_RAM */   
        *cr_startup_lpc43xx.o ( .text* .rodata .rodata.* .constdata .constdata.*)
        *(.text.main)       
/* END RUN_FROM_RAM */
       
    } >SPIflash

/* RUN_FROM_RAM */   
    .text_ram : ALIGN(4)
/* END RUN_FROM_RAM */    
    {
         __vectors_start__ = ABSOLUTE(.) ;
        *(.isr_vector*)
         *(.text*)
        *(.rodata .rodata.* .constdata .constdata.*)
        . = ALIGN(4);
       
        /* C++ constructors etc */
        . = ALIGN(4);
        KEEP(*(.init))
       
        . = ALIGN(4);
        __preinit_array_start = .;
        KEEP (*(.preinit_array))
        __preinit_array_end = .;
       
        . = ALIGN(4);
        __init_array_start = .;
        KEEP (*(SORT(.init_array.*)))
        KEEP (*(.init_array))
        __init_array_end = .;
       
        KEEP(*(.fini));
       
        . = ALIGN(4);
        KEEP (*crtbegin.o(.ctors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .ctors))
        KEEP (*(SORT(.ctors.*)))
        KEEP (*crtend.o(.ctors))
       
        . = ALIGN(4);
        KEEP (*crtbegin.o(.dtors))
        KEEP (*(EXCLUDE_FILE (*crtend.o) .dtors))
        KEEP (*(SORT(.dtors.*)))
        KEEP (*crtend.o(.dtors))
        . = ALIGN(4);
        /* End C++ */
/* RUN_FROM_RAM */         
    } > RamLoc128 AT>SPIflash
/* END RUN_FROM_RAM */

    /*
     * for exception handling/unwind - some Newlib functions (in common
     * with C++ and STDC++) use this.
     */
    .ARM.extab : ALIGN(4)
    {
    *(.ARM.extab* .gnu.linkonce.armextab.*)
/* RUN_FROM_RAM */         
    } > RamLoc128 AT>SPIflash
/* END RUN_FROM_RAM */

    __exidx_start = .;
   
    .ARM.exidx : ALIGN(4)
    {
    *(.ARM.exidx* .gnu.linkonce.armexidx.*)
/* RUN_FROM_RAM */         
    } > RamLoc128 AT>SPIflash
/* END RUN_FROM_RAM */

    __exidx_end = .;
   
    _etext = .;
       
   
    /* DATA section for RamAHB32 */
    .data_RAM2 : ALIGN(4)
    {
       FILL(0xff)
       *(.ramfunc.$RAM2)
       *(.ramfunc.$RamAHB32)
    *(.data.$RAM2*)
    *(.data.$RamAHB32*)
       . = ALIGN(4) ;
    } > RamAHB32 AT>SPIflash
   
    /* DATA section for RamLoc72 */
    .data_RAM3 : ALIGN(4)
    {
       FILL(0xff)
       *(.ramfunc.$RAM3)
       *(.ramfunc.$RamLoc72)
    *(.data.$RAM3*)
    *(.data.$RamLoc72*)
       . = ALIGN(4) ;
    } > RamLoc72 AT>SPIflash
   
    /* DATA section for RamAHB16 */
    .data_RAM4 : ALIGN(4)
    {
       FILL(0xff)
       __core_m0app_START__ = .; /* start of slave image */
         KEEP(*(.core_m0app*))
       __core_m0app_END__ = .; /* end of slave image */
       ASSERT(!(__core_m0app_START__ == __core_m0app_END__), "No slave code for _core_m0app");
       ASSERT( (ABSOLUTE(__core_m0app_START__) == __vectors_start___core_m0app), "M0APP execute address differs from address provided in source image");
       *(.ramfunc.$RAM4)
       *(.ramfunc.$RamAHB16)
    *(.data.$RAM4*)
    *(.data.$RamAHB16*)
       . = ALIGN(4) ;
    } > RamAHB16 AT>SPIflash
   
    /* DATA section for RamAHB_ETB16 */
    .data_RAM5 : ALIGN(4)
    {
       FILL(0xff)
       *(.ramfunc.$RAM5)
       *(.ramfunc.$RamAHB_ETB16)
    *(.data.$RAM5*)
    *(.data.$RamAHB_ETB16*)
       . = ALIGN(4) ;
    } > RamAHB_ETB16 AT>SPIflash
   
    /* MAIN DATA SECTION */
   

    .uninit_RESERVED : ALIGN(4)
    {
        KEEP(*(.bss.$RESERVED*))
        . = ALIGN(4) ;
        _end_uninit_RESERVED = .;
    } > RamLoc128


/* Main DATA section (RamLoc128) */
.data : ALIGN(4)
{
   FILL(0xff)
   _data = . ;
   *(vtable)
   *(.ramfunc*)
   *(.data*)
   . = ALIGN(4) ;
   _edata = . ;
} > RamLoc128 AT>SPIflash

    /* BSS section for RamAHB32 */
    .bss_RAM2 : ALIGN(4)
    {
    *(.bss.$RAM2*)
    *(.bss.$RamAHB32*)
       . = ALIGN(4) ;
    } > RamAHB32
    /* BSS section for RamLoc72 */
    .bss_RAM3 : ALIGN(4)
    {
    *(.bss.$RAM3*)
    *(.bss.$RamLoc72*)
       . = ALIGN(4) ;
    } > RamLoc72
    /* BSS section for RamAHB16 */
    .bss_RAM4 : ALIGN(4)
    {
    *(.bss.$RAM4*)
    *(.bss.$RamAHB16*)
       . = ALIGN(4) ;
    } > RamAHB16
    /* BSS section for RamAHB_ETB16 */
    .bss_RAM5 : ALIGN(4)
    {
    *(.bss.$RAM5*)
    *(.bss.$RamAHB_ETB16*)
       . = ALIGN(4) ;
    } > RamAHB_ETB16

    /* MAIN BSS SECTION */
    .bss : ALIGN(4)
    {
        _bss = .;
        *(.bss*)
        *(COMMON)
        . = ALIGN(4) ;
        _ebss = .;
        PROVIDE(end = .);
    } > RamLoc128
       
    /* NOINIT section for RamAHB32 */
    .noinit_RAM2 (NOLOAD) : ALIGN(4)
    {
    *(.noinit.$RAM2*)
    *(.noinit.$RamAHB32*)
       . = ALIGN(4) ;
    } > RamAHB32
    /* NOINIT section for RamLoc72 */
    .noinit_RAM3 (NOLOAD) : ALIGN(4)
    {
    *(.noinit.$RAM3*)
    *(.noinit.$RamLoc72*)
       . = ALIGN(4) ;
    } > RamLoc72
    /* NOINIT section for RamAHB16 */
    .noinit_RAM4 (NOLOAD) : ALIGN(4)
    {
    *(.noinit.$RAM4*)
    *(.noinit.$RamAHB16*)
       . = ALIGN(4) ;
    } > RamAHB16
    /* NOINIT section for RamAHB_ETB16 */
    .noinit_RAM5 (NOLOAD) : ALIGN(4)
    {
    *(.noinit.$RAM5*)
    *(.noinit.$RamAHB_ETB16*)
       . = ALIGN(4) ;
    } > RamAHB_ETB16
   
    /* DEFAULT NOINIT SECTION */
    .noinit (NOLOAD): ALIGN(4)
    {
        _noinit = .;
        *(.noinit*)
         . = ALIGN(4) ;
        _end_noinit = .;
    } > RamLoc128
   
PROVIDE(_pvHeapStart = ADDR(.data_RAM2));
PROVIDE(_vStackTop = __top_RamAHB32);
PROVIDE(_pvHeapLimit = _vStackTop - 0x600);   /* stack is 0x600 bytes or 1.5k  */
}
0 Kudos
Reply
6 Replies

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcxpresso-support on Wed Apr 01 14:44:23 MST 2015
Yes, with respect to the .text input section(s) you are correct.
0 Kudos
Reply

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by charchar on Wed Apr 01 13:06:08 MST 2015
I don't think the line LONG( SIZEOF(.text)-SIZEOF(.isr_vector));  is correct.   SIZEOF(.text) stays the same. 
0 Kudos
Reply

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcxpresso-support on Wed Apr 01 10:29:30 MST 2015

This example provided copies most of your code data to RAM. You may not want this, so you're going to have to be more explicit about the code/data you do copy. To guide the linker to locate specific code/data, it's a good idea to create named sections you can easily identify. You can find predefined macro definitions in cr_section_macros.h which serve this purpose, and support for these is included as a Managed Linker Script.

The output section expression "> RamLoc128 AT >SPIflash" means the VMA (virtual address) is RamLoc128, and the LMA (load address) is in SPIflash.

It may not be clear, but the .after_vectors.handler code is seen as data by the linker, and defaults to RamLoc128. Review the __RAM_FUNC macros in the crt_section_macros.h. If you were to use one such macro when you define your handlers, the .after_vectors.handler statements in the script could be eliminated since these would get copied as data.

Regards,
LPCXpresso Support
0 Kudos
Reply

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by charchar on Wed Apr 01 07:24:20 MST 2015
Thanks -- this helps quite a bit.   Some quick questions though-- shouldn't there be something along the lines of

.text_ram : ALIGN(4)
{
FILL(0xff)
.text*
.rodata
.rodata.*
.constdata
.constdata.*
} > SPIflash

so that the code gets put in flash (originally)?  Or is this unnecessary?

Similarly,

LONG(LOADADDR(.after_vectors.handler));
LONG( ADDR(.after_vectors.handler));
LONG( SIZEOF(.after_vectors.handler));

since .after_vectors_handler isn't declared in the the .text section (the section with > RamLoc128 AT>SPIflash)  --- won't this code stay in flash?

Maybe you left these sections out --- I'm asking just for my own understanding.

thanks!
0 Kudos
Reply

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcxpresso-support on Wed Apr 01 00:09:56 MST 2015

Quote: charchar
I have an LPC43xx platform that boots from flash, but executes from RAM, by copying the code upon startup.  It all works fine, but when I'm writing to/erasing the flash and get an interrupt (and the interrupt vector is fetched from flash), it crashes.


More fundamentally - modifying flash with interrupts enabled is most definitely not recommended! If an interrupt occurs at the wrong moment, your flash update may well fail.

Regards,
LPCXpresso Support


0 Kudos
Reply

3,921 Views
lpcware
NXP Employee
NXP Employee
Content originally posted in LPCWare by lpcxpresso-support on Tue Mar 31 16:43:40 MST 2015

We're working on making this somewhat easier to do in a future release.

Note the exception handlers defined in the start up code can be in one place, SPIflash or RamLoc128, unless you have duplicate copies.The handler code is normally placed in a section called .after_vectors. I change the section name to .after_vectors.handlers to allow easier relocation in the linker script. For example:

__attribute__ ((section(".after_vectors.handler")))
void NMI_Handler(void)
{
    while (1) {  }
}

Note also if you're using the SystemInit() code within LPCOpen, you'll need to change the NVIC VTOR setting to use the "__vectors_start__' linker symbol to set the vector table location. Otherwise, you can do this in the ResetISR code. You have the symbol location correct in the script you've provided.

So, substitute these lines

extern void *__vectors_start__;
SCB->VTOR = (unsigned int) &__vectors_start__;

in place of

extern void(*const g_pfnVectors[]) (void);
SCB->VTOR = (uint32_t) g_pfnVectors;

Here's a linker script which shows one way of going about this:

SECTIONS
{
   .isr_vector : ALIGN(4)
    {
        FILL(0xff)
        KEEP(*(.isr_vector)) /* Flash based vector table */
    } > SPIflash
   
    .flash_resident : ALIGN(4)
    {
        FILL(0xff)
       
        /* Global Section Table */
        . = ALIGN(4) ;
        __section_table_start = .;
        __data_section_table = .;
        LONG(LOADADDR(.isr_vector));
        LONG(    ADDR(.text));/* Reserve space for RAM vector table (see below) */
        LONG(  SIZEOF(.isr_vector));
        LONG(LOADADDR(.text)+SIZEOF(.isr_vector));
        LONG(    ADDR(.text)+SIZEOF(.isr_vector));
        LONG(  SIZEOF(.text)-SIZEOF(.isr_vector));     
        LONG(LOADADDR(.after_vectors.handler));
        LONG(    ADDR(.after_vectors.handler));
        LONG(  SIZEOF(.after_vectors.handler));          
        LONG(LOADADDR(.data));
        LONG(    ADDR(.data));
        LONG(  SIZEOF(.data));
        LONG(LOADADDR(.data_RAM2));
        LONG(    ADDR(.data_RAM2));
        LONG(  SIZEOF(.data_RAM2));
        LONG(LOADADDR(.data_RAM3));
        LONG(    ADDR(.data_RAM3));
        LONG(  SIZEOF(.data_RAM3));
        __data_section_table_end = .;
        __bss_section_table = .;
        LONG(    ADDR(.bss));
        LONG(  SIZEOF(.bss));
        LONG(    ADDR(.bss_RAM2));
        LONG(  SIZEOF(.bss_RAM2));
        LONG(    ADDR(.bss_RAM3));
        LONG(  SIZEOF(.bss_RAM3));
        __bss_section_table_end = .;
        __section_table_end = . ;
        /* End of Global Section Table */
        *(.after_vectors)
        *(.after_vectors.reset)
       
        /* Code Read Protect data */
        . = 0x000002FC ;
        PROVIDE(__CRP_WORD_START__ = .) ;
        KEEP(*(.crp))
        PROVIDE(__CRP_WORD_END__ = .) ;
        ASSERT(!(__CRP_WORD_START__ == __CRP_WORD_END__), "Linker CRP Enabled, but no CRP_WORD provided within application");
        /* End of Code Read Protect */
    } > SPIflash

    /* MAIN TEXT SECTION */   
    .text : ALIGN(4)
    {
      __vectors_start__ = ABSOLUTE(.) ; /* SYMBOL IS LOCATION OF RAM VECTOR TABLE!! */        
      . += SIZEOF(.isr_vector); /* Reserve space for RAM vectors (unused gap in flash) */
      *(.text*)
      *(.rodata .rodata.* .constdata .constdata.*)
      . = ALIGN(4);
    } > RamLoc128 AT>SPIflash
    .
    .
    .
}
0 Kudos
Reply