First of all, I hope I'm not asking something that has already been asked before. I've searched as much as I can but I haven't found an answer to my specific problem or something useful.
I'm working on a FRDM-KL82Z board which runs a Cortex M0+ core. I'm using MCUXpresso IDE v10.0.2 and a Segger J-Link programmer, although I think this is not relevant for this question.
This project will need a custom bootloader and app coded by different developers, each block with its own flash memory space: 8K for the bootloader and 120K for the app (this may change in the future but it's no big deal at the moment).
Once the bootloader is completed, it will manage the jump to App space and the app will change de Vector Table Offset Register (VTOR) so that the Interrupt Vector Table changes form the Boot IVT to the App IVT. This has already been tested successfully.
My aim is to set up the linker script file so that the app developers can build and debug their project on the board before the bootloader is completed, as they will be developed at the same time. The reason for this is that they can work with the App space as it will be in the final version.
I think the Reset vector and the Config bits must be at their default position because the hardware will go to the same position every time it needs to read them.
My first idea consist in disabling the automatic linker script generation and modifying the MyProject_Debug.ld file.
What the script automatically generates:
INCLUDE "LEDTest_Debug_library.ld"
INCLUDE "LEDTest_Debug_memory.ld"
ENTRY(ResetISR)
SECTIONS
{
/* MAIN TEXT SECTION */
.text : ALIGN(4)
{
FILL(0xff)
__vectors_start__ = ABSOLUTE(.) ;
KEEP(*(.isr_vector))
/* 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));
__data_section_table_end = .;
__bss_section_table = .;
LONG( ADDR(.bss));
LONG( SIZEOF(.bss));
LONG( ADDR(.bss_RAM2));
LONG( SIZEOF(.bss_RAM2));
__bss_section_table_end = .;
__section_table_end = . ;
/* End of Global Section Table */
*(.after_vectors*)
/* Kinetis Flash Configuration data */
. = 0x400 ;
PROVIDE(__FLASH_CONFIG_START__ = .) ;
KEEP(*(.FlashConfig))
PROVIDE(__FLASH_CONFIG_END__ = .) ;
ASSERT(!(__FLASH_CONFIG_START__ == __FLASH_CONFIG_END__), "Linker Flash Config Support Enabled, but no .FlashConfig section provided within application");
/* End of Kinetis Flash Configuration data */
} >PROGRAM_FLASH
.text : ALIGN(4)
{
*(.text*)
*(.rodata .rodata.* .constdata .constdata.*)
. = ALIGN(4);
} > PROGRAM_FLASH
/*
* for exception handling/unwind - some Newlib functions (in common
* with C++ and STDC++) use this.
*/
.ARM.extab : ALIGN(4)
{
*(.ARM.extab* .gnu.linkonce.armextab.*)
} > PROGRAM_FLASH
__exidx_start = .;
.ARM.exidx : ALIGN(4)
{
*(.ARM.exidx* .gnu.linkonce.armexidx.*)
} > PROGRAM_FLASH
__exidx_end = .;
_etext = .;
/* USB_RAM */
.m_usb_data (NOLOAD) :
{
*(m_usb_bdt)
*(m_usb_global)
} > USB_RAM
/* possible MTB section for USB_RAM */
.mtb_buffer_RAM2 (NOLOAD) :
{
KEEP(*(.mtb.$RAM2*))
KEEP(*(.mtb.$USB_RAM*))
} > USB_RAM
/* DATA section for USB_RAM */
.data_RAM2 : ALIGN(4)
{
FILL(0xff)
PROVIDE(__start_data_RAM2 = .) ;
*(.ramfunc.$RAM2)
*(.ramfunc.$USB_RAM)
*(.data.$RAM2*)
*(.data.$USB_RAM*)
. = ALIGN(4) ;
PROVIDE(__end_data_RAM2 = .) ;
} > USB_RAM AT>PROGRAM_FLASH
/* MAIN DATA SECTION */
/* Default MTB section */
.mtb_buffer_default (NOLOAD) :
{
KEEP(*(.mtb*))
} > SRAM
.uninit_RESERVED : ALIGN(4)
{
KEEP(*(.bss.$RESERVED*))
. = ALIGN(4) ;
_end_uninit_RESERVED = .;
} > SRAM
/* Main DATA section (SRAM) */
.data : ALIGN(4)
{
FILL(0xff)
_data = . ;
*(vtable)
*(.ramfunc*)
*(.data*)
. = ALIGN(4) ;
_edata = . ;
} > SRAM AT>PROGRAM_FLASH
/* BSS section for USB_RAM */
.bss_RAM2 : ALIGN(4)
{
PROVIDE(__start_bss_RAM2 = .) ;
*(.bss.$RAM2*)
*(.bss.$USB_RAM*)
. = ALIGN (. != 0 ? 4 : 1) ; /* avoid empty segment */
PROVIDE(__end_bss_RAM2 = .) ;
} > USB_RAM
/* MAIN BSS SECTION */
.bss : ALIGN(4)
{
_bss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4) ;
_ebss = .;
PROVIDE(end = .);
} > SRAM
/* NOINIT section for USB_RAM */
.noinit_RAM2 (NOLOAD) : ALIGN(4)
{
*(.noinit.$RAM2*)
*(.noinit.$USB_RAM*)
. = ALIGN(4) ;
} > USB_RAM
/* DEFAULT NOINIT SECTION */
.noinit (NOLOAD): ALIGN(4)
{
_noinit = .;
*(.noinit*)
. = ALIGN(4) ;
_end_noinit = .;
} > SRAM
.heap : ALIGN(4)
{
_pvHeapStart = .;
. += 0x1000;
. = ALIGN(4);
_pvHeapLimit = .;
} > SRAM
.heap2stackfill :
{
. += 0x1000;
} > SRAM
.stack ORIGIN(SRAM) + LENGTH(SRAM) - 0x1000 - 0: ALIGN(4)
{
_vStackBase = .;
. = ALIGN(4);
_vStackTop = . + 0x1000;
} > SRAM
}
I've tried to find information in this guide about de GNU linker but my ideas haven't worked so far. What I've tried:
Setting the location counter to a different value after the Config Words and copying the ISR_vector code snipped before the text section:
... /* End of Kinetis Flash Configuration data */ } >PROGRAM_FLASH .text : ALIGN(4) { /* MODIFIED CODE */ . = 0x2000; /* First position of App code */ FILL(0xff) __vectors_start__ = ABSOLUTE(.) ; KEEP(*(.isr_vector)) /* END OF MODIFIED CODE */ *(.text*) *(.rodata .rodata.* .constdata .constdata.*) . = ALIGN(4); } > PROGRAM_FLASH ...
When I do this and I open the .hex file, the space between the Config Words (0x400) and the start of the App space (0x2000) is effectively empty (full of 0xFF) but the code after 0x2000 is nothing like the IVT table.
If I move location counter to 0x2000 before the IVT code lines it effectively moves the IVT adresses to the 0x2000 position. To do this, I move the Config Words part before the IVT part because de location counter can't move backwards.
I've tried creating a Bootloader section in the memory map, with the correct starting and length positions, and copying every line that by default gets placeD in the PROGRAM_FLASH section into a new one that goes to BOOTLOADER (the same code with ">BOOTLOADER" at the end). In this case de IVT only appears in the Boot space.
Is it possible that the linker script places de IVT only in the first place it is indicated and then ignores every other call? What am I doing wrong? Should I try another way to achieve this?
Thank you very much, I know it's quite a long!
I don't think it's possible to make a copy of the vector table using only linker shenanigans. The linker script will not let you match the same symbol multiple times so that you can output it twice.
From the binutils 2.29 manual:
I tested it without using any wildcard patterns at all with similar results, so I don't think the linker will ever let you output the same symbol twice.
I also tried using objcopy to create a renamed copy of the vector table that could referenced from the linker script but that table ended up as all zeroes and the whole approach was rather convoluted, so I don't think that's worth pursuing.
If you want to keep the application code as similar as possible between now and when the bootloader is completed, I would suggest a different approach:
Make use of the
__vectors_start__
symbol provided by the existing linker script so that your code always knows where the vector table is placed, even if you make changes to the linker script.This will allow the same code to work with your current configuration (no bootloader, ROM starting at 0x0) and your eventual bootloader configuration (ROM starting at 0x2000).