3

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:

  1. 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.

  1. 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.

  2. 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!

MAF
  • 41
  • 5
  • 4
    "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" Why can't they simply use a JTAG in-circuit debugger until then? And use the vector table as is? All of this sounds needlessly complicated. – Lundin Sep 13 '17 at 11:33
  • I only did that on AVR and not on M0 so I will not post a complete answer, but "you cannot put 2 vector tables unsing linker script". For me the solution was : compile 2 FW (boot and appli) with different linker scripts and have the correct memory location used thanks to linker script. Then either use `srec_cat` to make this one hex file to flash, or flash the one after the other providing you do not erase the other part before programming – Julien Sep 14 '17 at 06:07
  • Thanks for your answers @Julien and Lundin. – MAF Sep 14 '17 at 09:39
  • so @Julien I can successfully do what you are saying: paste two .hex together and flash them. The bootloader just jumps to app and I get the two IVTs. But this process is a bit too long, I was looking for a way where the developer can just click on "Debug". I will search information of what you are saying to see if I can set up the flasher to flash one .hex after the other. – MAF Sep 14 '17 at 09:47
  • @MAF find a way to use srec_cat for concatenation in Makefile and configure the debug button to flash the output file. If my understanding is correct your real problem is you want to debug applicative and boot during the same debug session. I don't think this is possible (you will be missing half of the debug infos) but this could be a good question. – Julien Sep 14 '17 at 11:51

3 Answers3

3

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:

If a file name matches more than one wildcard pattern, or if a file name appears explicitly and is also matched by a wildcard pattern, the linker will use the first match in the linker script.

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.

void relocate_vector_table(void) {
    extern unsigned __vectors_start__;
    SCB->VTOR = (unsigned)&__vectors_start__;
}

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).

Devan
  • 106
  • 2
3

My experience with M4 application and bootloader shows that it is enough to set the Flash start at some offset address, and then in the application to initialize VTOR to this address.

From linker script:

#

/* Specify the memory areas */

MEMORY
{
CLASSBRAM (rw)   : ORIGIN = 0x20000000, LENGTH = 0x80
/*RAM length = 192K - CLASSBRAM-length */
RAM (xrw)      : ORIGIN = 0x20000080, LENGTH = 0x2FF80 
CCMRAM (rw)      : ORIGIN = 0x10000000, LENGTH = 64K
/* FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 512K */
FLASH (rx)      : ORIGIN = 0x08010000, LENGTH = 448K  /*in case of 64K for Bootloader*/
}

/* Define output sections */
SECTIONS
{
  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(4);
    PROVIDE( _Rom_Start = . );
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(4);
  } >FLASH

.....

#

code:

extern const uint32_t _Rom_Start;
....
#define ROM_START ((uint32_t *)&_Rom_Start)
...
SCB->VTOR = (uint32_t)ROM_START;
1

My MCU projects typically have Makefile targets that will actually flash the chip (with building as a dependency of course), so what I did for this was make a special target that flashes the main firmware "solo".

My openocd-driven programmers can flash flat binaries and not just hex files, so I was able to do this by using dd to copy just the vector table off the start of the main firmware binary into its own file. I then write this to the start of flash, and the main firmware to its usual location in separate operations. The chip boots, gets the reset and stack addresses out of the copied vector table, starts the main firmware, and that then repoints the vector table address to its own copy at the higher address.

If your programmer doesn't support flat binaries you can use objdump or some other tool to turn a flat binary back into a hex file, or likely to change the base address of a hex file / fragment.

Chris Stratton
  • 39,853
  • 6
  • 84
  • 117