Skip to content

Instantly share code, notes, and snippets.

@davidrea
Last active February 18, 2019 18:05
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save davidrea/497453600877d59957c4f2eb343bd0ae to your computer and use it in GitHub Desktop.
Save davidrea/497453600877d59957c4f2eb343bd0ae to your computer and use it in GitHub Desktop.
NXP's MCUXpresso IDE doesn't support allocating a Bootloader Configuration Area (BCA) segment out-of-the-box. Here's how to do it.

Background

Since NXP acquired Freescale and the Kinetis line of microcontrollers, they have been (gradually) migrating the Kinetis ecosystem from the (Eclipse+GNUARM-based) Kinetis Design Studio (KDS) to the (Eclipse+GNU Make-based) MCUXpresso IDE. While many MCUs within the Kinetis family are now supported by the MCUXpresso IDE, the Kinetis Bootloader (KBOOT) is not yet supported.

MCUXpresso is actually a constellation of 3 tools: The MCUXpresso IDE, the MCUXpresso config tools (which are incorporated into the IDE, and installable separately if a different IDE - such as MDK or IAR - is used), and the MCUXpresso SDK (formerly the Kinetis SDK or KSDK). The SDK tool is actually an online SDK generator.

Caution: Unsupported Devices

The Kinetis SDK (KSDK) and Kinetis Bootloader (KBOOT) do not support the entire line of Kinetis microcontrollers. Before designing a microcontroller into a project, check for KSDK and KBOOT support. Search for part family support at the MCUXpresso SDK online builder to check KSDK coverage, and download the KBOOT distribution package to check for bootloader support in the targets/ directory.

If a microcontroller is not supported by KSDK or KBOOT, NXP provide some porting instructions. This Community Forum Post links to a PDF with a terse explanation of how to port KSDK, while the KBOOT 2.0 Reference Manual (Chapter 10) explains porting of the Kinetis Bootloader.

Problem Statement

KBOOT is generally flashed to address 0x000000 in memory, executing after the microcontroller comes out of reset. While many KBOOT instances can run just fine with their configuration defaults, some (particularly those using USB and I2C host interfaces) require configuration. This configuration is provided by a Bootloader Configuration Area (BCA) placed at a fixed location in Flash relative to the start of the regular application (the binary that's loaded by the bootloader).

KDS used static, pre-defined linker script (.ld) files to define and assign memory regions, while MCUXpresso uses a much more advanced template-based "managed linker script" system which generates a new linker script with each project build. There is no facility in the managed linker script settings to explicity allocate a segment of flash for the BCA.

We wish to move the regular application to an offset location in flash, making room for KBOOT at address 0x000000. We also wish to place the regular application's interrupt vectors and startup code at the offset address, reserve 0x40 bytes of flash for the BCA at its designated location, and place the balance of the regular application content in the remaining flash space.

Approach

The KBOOT binary, built with support for a single peripheral using the GNU toolchain, uses about 25KB of flash. To leave reasonable headroom, we wish to place the regular application at a 32KB offset, or at an address offset of 0x8000. On a 128KB part (such as MK02F128xxx) this will leave 96KB of flash for the regular application.

Thus, the desired memory map is:

Segment Start Address Length Note
PROGRAM_FLASH 0x8000 0x3c0 Contains interrupt vectors and startup code
Rm_bootloader_config 0x83c0 0x040 BCA placed at fixed offset from vector table
REMFLASH 0x8400 0x17c00 Remainder of flash available for regular application

We can readily configure these regions in MCUXpresso's project settings: Right-click on the project, select "Properties", then select "MCU Settings" under "C/C++ Build". The "Edit..." button near the bottom allows configuration of the regions.

Attempting to build the project at this point, however, will result in an "insufficient space" error. By default MCUXpresso attempts to link the entire regular applicaton into the first flash region it encounters. Since the PROGRAM_FLASH region now only has enough space for the vector table and startup code, this is impossible.

Remapping the Application

Breaking with time-honored Kinetis tradition, the MCUXpresso template-driven managed linker script system is very well-documented in the MCUXpresso IDE User Guide. Sections 14.10 and 14.11 provide a detailed explanation of how the managed linker script system generates linker scripts, where the content comes from, and how to override the default linker script templates with application-specific templates.

To remap the regular application (except for the vector table and startup code) into our REMFLASH region, we need to instruct the linker to place all .text, .rodata and .constdata segments except for those associated with the startup file there. Opening the top-level linker script for the project, we can see that the managed linker script system has already created 2 new sections for the new flash regions we created in the settings dialog:

.text_Flash2 : ALIGN(4)
{
   FILL(0xff)
    *(.text_Flash2*) /* for compatibility with previous releases */
    *(.text_Rm_bootloader_config*) /* for compatibility with previous releases */
    *(.text.$Flash2*)
    *(.text.$Rm_bootloader_config*)        *(.rodata.$Flash2*)
    *(.rodata.$Rm_bootloader_config*)    } > Rm_bootloader_config

.text_Flash3 : ALIGN(4)
{
   FILL(0xff)
	*(.text*)
	*(.rodata .rodata.* .constdata .constdata.*)
    *(.text_Flash3*) /* for compatibility with previous releases */
    *(.text_REMFLASH*) /* for compatibility with previous releases */
    *(.text.$Flash3*)
    *(.text.$REMFLASH*)        *(.rodata.$Flash3*)
    *(.rodata.$REMFLASH*)    } > REMFLASH

This leaves us with 2 tasks: isolate the startup file's objects and keep them in the existing .text section, and place the remaining regular application objects into the .text_Flash3 section.

Overriding Linker Script Templates

We're going to need to override some of the default linker script templates with our own. Since MCUXpresso first looks for template files in a linkscripts/ directory in the project tree (before fetching them from its own default template directory), create this folder to store our alternative linker script templates.

The linker script templates used by MCUXpresso are highly hierarchical. The User's Guide provides a detailed explanation of the hierarchy of the template files in section 14.10.2.

Isolating the Startup code

Section 14.11 in the MCUXpresso IDE User Guide providese an example of "Relocating majority of application into RAM", which overrides some of the linker script templates to move all .text, .rodata and .constdata segments into the SRAM regions of the memory map. Using this as a guide, create the following files to override the default templates:

/* linkscripts/main_text.ldt */
*startup_*.o (.text.*)
	*(.text.main)
	*(.text.__main)

/* linkscripts/main_rodata.ldt */
*startup_*.o (.rodata .rodata.* .constdata .constdata.*)
	. = ALIGN(${text_align});

These templates modify which portions of the regular application are linked into the PROGRAM_FLASH (alias: Flash) region. Rather than linking the entire

(.text)
segment into PROGRAM_FLASH, we now only link in the startup code. The linker configuration that places the vector table into PROGRAM_FLASH is unchanged.

Relocating the Remainder of the Regular Application

Now, we wish to place the remainder of the regular application into the REMFLASH region (alias: Flash3). Create the following file to override the default template:

/* linkscripts/text.ldt */
<#if memory.alias == "Flash3">
	    *(.text*)
	    *(.rodata .rodata.* .constdata .constdata.*)
</#if>
*(.text_${memory.alias}*) /* for compatibility with previous releases */
*(.text_${memory.name}*) /* for compatibility with previous releases */
*(.text.$${memory.alias}*)
*(.text.$${memory.name}*)

This included template is run within the text_section.ldt template for each additional region of flash created in the project configuration. Since we only want to add the regular application's .text, .rodata, and .constdata segments to Flash3, we include a FreeMarker conditional block preventing inclusion of these segments in Flash2.

The relocation is accomplished in the resulting generated top-level linker script:

SECTIONS
{
    .text_Flash2 : ALIGN(4)
    {
       FILL(0xff)
        *(.text_Flash2*) /* for compatibility with previous releases */
        *(.text_Rm_bootloader_config*) /* for compatibility with previous releases */
        *(.text.$Flash2*)
        *(.text.$Rm_bootloader_config*)        *(.rodata.$Flash2*)
        *(.rodata.$Rm_bootloader_config*)    } > Rm_bootloader_config

    .text_Flash3 : ALIGN(4)
    {
       FILL(0xff)
        *(.text*)
        *(.rodata .rodata.* .constdata .constdata.*)
        *(.text_Flash3*) /* for compatibility with previous releases */
        *(.text_REMFLASH*) /* for compatibility with previous releases */
        *(.text.$Flash3*)
        *(.text.$REMFLASH*)        *(.rodata.$Flash3*)
        *(.rodata.$REMFLASH*)    } > REMFLASH

    /* 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*)



    } >PROGRAM_FLASH

    .text : ALIGN(4)
    {
    *startup_*.o (.text.*)
    *(.text.main)
    *(.text.__main)
    *startup_*.o (.rodata .rodata.* .constdata .constdata.*)
    . = ALIGN(4);

    } > PROGRAM_FLASH

Reviewing the build output, we see that the majority of the regular application is linked into the REMFLASH region:

Building target: Application.axf
Invoking: MCU Linker
arm-none-eabi-gcc -nostdlib -Xlinker -print-memory-usage -Xlinker --gc-sections -Xlinker [...] -T Application_Debug.ld [...]   
Memory region             Used Size  Region Size  %age Used
       PROGRAM_FLASH:         512 B        960 B     53.33%
Rm_bootloader_config:          0 GB         64 B      0.00%
            REMFLASH:       35112 B        95 KB     36.09%
          SRAM_UPPER:        1284 B         8 KB     15.67%
          SRAM_LOWER:          0 GB         8 KB      0.00%
Finished building target: Application.axf

Relocating the Interrupt Vector Table

Since the bootloader's interrupt vector table (IVT) will be loaded in the default vector table location of 0x0000000, we need to instruct the MCU to use a different location as the IVT when executing the application. Normally, the bootloader does this just before calling the main application entry function.

However, when debugging via JTAG or SWD, the bootloader may not be present, and is usually bypassed by the debugger configuration, which sets the entry point to the application's IVT and runs to main(). In this case, the VTOR (Vector Table Offset Register) must be changed to point to the beginning of the application vector table:

SCB->VTOR = 0x8000;

TODO: Defining and Placing the BCA Contents in the Source Code

After creating the segments and relocating the balance of the regular application, we still need to define the content of the Bootloader Configuration Area (BCA) region.

See Also

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment