Bootloader for Kinetis K64 (Cortex M4)

cancel
Showing results for 
Search instead for 
Did you mean: 

Bootloader for Kinetis K64 (Cortex M4)

8,493 Views
andersbergström
Contributor II

Hi

 

I´m currently working on a bootloader for Kinetis K64. The reason that we don´t use the Kinetis bootloader is beacuse we nedded to have a custom protocol.

I never done this before so i tought i will make the bootloader in the 4 following steps:

 

1) Make a simple Hello-World program resident in memory at adress: 0x00C28CB0

2) Debug and start the Hello-World program to make sure it is located at the correct memory adress

3) Make a simple "Hello-World application", to be able to jump between to the Hello-World program.

4) Get the new application and write it into flash.

 

Im stuck at 2) and 3) since the program always starts up at adress 0x0, not where i put my second vector table for the "Hello World program" It also ends up in Reset_Handler at 0x0 when i try to jump between bootloader and application.

 

I use different linking files for bootloader and application but the same startup code are used ( See attached files)

 

When i try to jump from the bootloader to the application i use the following code:

 

    // Test to go to application     static void (*JumpToApp)(void) = 0;     JumpToApp = (void (*)(void))APP_ADRESS;      // Set the VTOR to the application vector table address.     SCB->VTOR = (uint32_t)APP_VECTOR_TABLE;       // Set stack pointers to the application stack pointer.     __set_MSP(APP_STACK_PTR_DEF);     __set_PSP(APP_STACK_PTR_DEF);       // Jump to the application.     JumpToApp();      

 

I use the following defines:

 

#define      APP_ADRESS     0x00C28CB0 #define      APP_VECTOR_TABLE APP_ADRESS #define      APP_STACK_PTR_DEF APP_ADRESS

 

Im grateful for help,

 

/ Anders

Original Attachment has been moved to: K64FN1Mxxx12_flash.ld.zip

Original Attachment has been moved to: K64FN1Mxxx12_flash_bootloader.ld.zip

Original Attachment has been moved to: startup_MK64F12.S.txt.zip

Labels (1)
Tags (2)
32 Replies

707 Views
robotjosh
Contributor IV

I just got my bootloader to work on the k22.  You don't need to touch VTOR, this is taken care of in the startup code.  The last sticking point for me was infinite loop in the startup code, matched it up to the map file and it was uart init.  Then I tried to deinit uart and spi by enabling the deinit bean functions, deinit them right before the main app jump and now everything is working.  I was also able to get the bootloadable image to run in the debugger by adding 2 bytes to 0x0 in the linker script that has to be commented out to generate the bootloadable binary.  Maybe this info can help someone.

  m_dummystart (RX) : ORIGIN = 0x0, LENGTH = 0x190

  .dummystart :

  {

  LONG(0x20000000);

  LONG(__thumb_startup);

  } > m_dummystar

0 Kudos

707 Views
jeremyzhou
NXP TechSupport
NXP TechSupport

Hi Anders,

I've had a brief look through your question. I think the the new Freescale bootloader solution-KBOOT could help you make it more easily and effectively.

Please find more detail infromation about it through the link as follows.

KBOOT solution:

Kinetis Bootloader|Freescale
Have a great day,
Ping

-----------------------------------------------------------------------------------------------------------------------
Note: If this post answers your question, please click the Correct Answer button. Thank you!
-----------------------------------------------------------------------------------------------------------------------

0 Kudos

707 Views
andersbergström
Contributor II

Jeremy,

I had a look on the Bootloader you suggested, but it does not come with Ethernet Support ( Thats why we decided to write our own bootloader.)

Also, we use KDS, not IAR.

The only thing remaining is the jump between the application and vice versa.

/ Anders

0 Kudos

707 Views
mjbcswitzerland
Specialist V

Anders

Please post the binary file that you are loading. Errors are usually visible when looking at its content. Just tell me the address of the jump function in the boot loader (look in your boot loader map file to find it).

Regards

Mark

0 Kudos

707 Views
andersbergström
Contributor II

Mark, i just got it to work, using your function.

It seems like calling the __set_MSP and __SetPSP functions caused a HardFault for some reason. When I read the assembler code and commented those function calls, i got it to work. Any reason why those will cause the hard fault?

    // Set the VTOR to the application vector table address.

    SCB->VTOR = (uint32_t)APP_VECTOR_TABLE;

    // Set stack pointers to the application stack pointer.

    //__set_MSP(APP_STACK_PTR_DEF);

    //__set_PSP(APP_STACK_PTR_DEF);

    // Jump to the application.

    start_application(APP_ADRESS);

Thanks for the help!

/ Anders

0 Kudos

707 Views
mjbcswitzerland
Specialist V

Anders

I don't know what __set_MSP() and __set_PSP() do but they are certainly not needed for a jump to a new application; the new application enters as if it had just been started after a normal reset so assumes that nothing has been set up for it to work from.

SCB->VTOR = (uint32_t)APP_VECTOR_TABLE; also doesn't belong in the boot loader. If the application needs it it should do it itself, as any application would do anyway.

The only thing that you may need to do before the jump is if the boot loader has been using peripherals is to deinitialise them; if not you may find that peripherals have certain settings and pending interrupts that then cause problems when reused by the application.

Below is the de-init I use with the K64 when jumping to an application after bootloading with SD card, USB, UART and Ethernet, which ensures no potential problems.

Regards

Mark

POWER_DOWN(4, (SIM_SCGC4_UART0 | SIM_SCGC4_UART1 | SIM_SCGC4_UART2 | SIM_SCGC4_UART3 | SIM_SCGC4_USBOTG));

POWER_DOWN(1, (SIM_SCGC1_UART4 | SIM_SCGC1_UART5));

POWER_DOWN(2, SIM_SCGC2_ENET);

POWER_DOWN(3, (SIM_SCGC3_SDHC));

SIM_SOPT1_CLR(SIM_SOPT1_USBREGEN, SIM_SOPT1CFG_URWE);

IRQ0_31_CER  = 0xffffffff;

IRQ32_63_CER = 0xffffffff;

IRQ64_95_CER = 0xffffffff;

IRQ0_31_CPR  = 0xffffffff;

IRQ32_63_CPR = 0xffffffff;

IRQ64_95_CPR = 0xffffffff;

0 Kudos

707 Views
robotjosh
Contributor IV

But be aware I have not tested my binary yet because I have not found a way to flash a binary using this fancy multilink fx, too fancy to deal with bin files!  I'll have to finish the bootloader and test the binary and bootloader at the same time.

0 Kudos

707 Views
ndavies
Contributor V

Josh, the Multilink doesn't care what format the executable file is in. It's the Development environment that specifies the file formats. The multilink doesn't know what a file format is. It only accepts the individual commands to physically flash the files onto the target. The development environment accepts various file formats and then converts it into flashing commands for the multilink.

There is a separate forum for KDS:

Kinetis Design Studio

And there is a thead on Programming binary files.

Flash from File: Downloading a file without the sources

Norm

0 Kudos

707 Views
robotjosh
Contributor IV

Yes, I have attempted the procedure outlined in the Flash from File page after adding the kinetis.cfg option mentioned in this page KDS Debug Configurations (OpenOCD, P&E, Segger)

I used the elf file since that is in the example and should work.  It produces this error:

Open On-Chip Debugger 0.8.0-dev-dirty (2014-08-20-11:07)

Licensed under GNU GPL v2

For bug reports, read

  http://openocd.sourceforge.net/doc/doxygen/bugs.html

Info : only one transport option; autoselect 'cmsis-dap'

Error: unable to open CMSIS-DAP device

Error: unable to init CMSIS-DAP driver

Error: Error selecting 'cmsis-dap' as transport

Runtime Error: C:/Freescale/KDS_1.1.1/openocd/bin/..//scripts/kinetis.cfg:3:

in procedure 'script'

at file "embedded:startup.tcl", line 58

in procedure 'interface' called at file "C:/Freescale/KDS_1.1.1/openocd/bin/..//scripts/kinetis.cfg", line 3

Keep in mind that I want the multilink fx to flash a binary, this procedure appears to require the opensda chip included in the dev kit.

0 Kudos

707 Views
mjbcswitzerland
Specialist V

Josh

I am still not really fully clear about your set up:

- IDE (KDS I believe)

- Debugger ( Multilink I believe)

- Processor (K22 or K64?)

- Board (evaluation board or custom board?)

Is the problem that KDS doesn't support CMSIS-DAP? (Since I don't think that it does) I believe that it only works with P&E OpenOCD mode. I expect that the debugger has different modes (may be needs a different firmware loaded to it?)

I have only worked with CMSIS-DAP with Rowely Crossworks. You could get the evaluation version from them to see.

Regards

Mark

0 Kudos

707 Views
robotjosh
Contributor IV

KDS with multilink fx.  Processor is K22FN512, dev kit is FRDM-K22F.

I can probably do what I need to do without flashing binaries but it would be nice if I could

0 Kudos

707 Views
mjbcswitzerland
Specialist V

Josh

As I wrote before you can simply copy binaries by dragging them onto the hard drive seen when connecting the board via USB.

You can then debug by connecting the debugger with the Flash programming step disabled. The binary you loaded is there and you can debug as normal.

I suppose you use the Multilink FX because it is faster than the on-board OpenSDA (?) Otherwise the OpenSDA debugger works adequately with KDS.

Regards

Mark

P.S I don't think that I used the FRDM-K22F with the debugger since I always load binaries via the pre-installed mdeb loader. I do all development (and debugging) in the uTasker Kinetis simulator so rarely need to debug after loading to the target.

0 Kudos

707 Views
mjbcswitzerland
Specialist V

Josh

Just drop your binary onto the hard drive that appears when connecting the FRDM-K64F (assuming you are using, it or similar).

You can also convert your binary file into other formats using GCC's objcopy if that makes it easier for the tools.

Regards

Mark

0 Kudos

706 Views
ndavies
Contributor V

Did you modify the linker descriptor file to have your application and its vector table start at the new address? The boot loader and Applications linker descriptor files must specify different code addresses from each other.

You also need to make sure you are jumping to an odd address when you jump from the boot loader to the application. It should be APP_START+1. The Arm core Uses the Even Vs Odd address to decide which instruction set to run when it gets to the new address. All instructions are on even addresses. A jump to an odd address keeps the MCU in THUMB instruction mode, Jumping to an even address causes a hard fault on the Kinetis since it only supports the Thumb instruction set.

0 Kudos

706 Views
andersbergström
Contributor II

Norm,

I modified my linker file. Please see the one I attached for the bootloader. (I never written a linker file either, so there might be something missing in it.)

To merge the two binary files together, i use the following shell script:

`cat HELLO_WORLD_BL.bin > bootloader.bin`

`cat bootloader.bin HELLO_WORLD.bin > combined.bin`

`rm -f tmp.bin bootloader.bin`

I tried both my function, and the  one Mark provided to perform the Jump without any luck.

0 Kudos

706 Views
robotjosh
Contributor IV

I think you can just cat HELLO_WORLD_BL.bin HELLO_WORLD.bin > combined.bin.  If this helps, here are my modifications to the linker scripts in order to be able to cat the two binaries:

For the main app add to the starting address of each memory location, I added 0x10000 to give the bootloader 64kb of space, and subtract that amount from the section size, and comment out this memory section:

/*  m_cfmprotrom  (RX) : ORIGIN = 0x00000400, LENGTH = 0x00000010 */

/*

  .cfmprotect :

  {

    . = ALIGN(4);

  KEEP(*(.cfmconfig))

  . = ALIGN(4);

  } > m_cfmprotrom

  */

For the bootloader linker script, it must be changed so that the binary is exactly the size of the bootloader flash space so that the concatenated main app binary starts at the correct address.  Change the length of each memory section as if it were a 64kb flash device or however large your bootloader space is. Then add this section so that it generates a binary exactly the right size.  .romp is the last section that is added to flash so it is used to calculate the starting address of the fill section.

  .fill LOADADDR(.romp) + SIZEOF(.romp) :

  {

  FILL(0xAAAAAAAA);

  . = ORIGIN(m_text) + LENGTH(m_text) - 1;

    BYTE(0xAA)

  } > m_text

You have to turn off linker file generation in processor expert after making these changes.

706 Views
andersbergström
Contributor II

Josh. I will try those modifications tomoroww morning.

Do you have the possibility to attach the modified linker scripts, so I can see them in their whole context? :smileyhappy:

Do i need to set the Entry Point as well in the application linker file? Right now it is set to Reset_Handler.

0 Kudos

706 Views
robotjosh
Contributor IV

You shouldn't have to change the reset handler, leave defaults in the bootloader.  For the main app, those interrupts and reset vector will work the same at location 0x10000 after you set the SCB_VTOR = 0x10000

These are default processor expert generated linker scripts with the changes I mentioned.  I do not see how to attach the file but I can paste the bootloader linker script. The main app linker script has the simple changes I mentioned.

/* Entry Point */

ENTRY(__thumb_startup)

/* Highest address of the user mode stack */

_estack = 0x20000000;    /* end of m_data */

__SP_INIT = _estack;

/* Generate a link error if heap and stack don't fit into RAM */

__heap_size = 0x00;                    /* required amount of heap  */

__stack_size = 0x0400;                 /* required amount of stack */

MEMORY {

  m_interrupts (RX) : ORIGIN = 0x00000000, LENGTH = 0x00000198

  m_text      (RX) : ORIGIN = 0x00000410, LENGTH = 0x0000FBF0

  m_data      (RW) : ORIGIN = 0x1FFF0000, LENGTH = 0x00010000

  m_data_20000000 (RW) : ORIGIN = 0x20000000, LENGTH = 0x00010000

  m_cfmprotrom  (RX) : ORIGIN = 0x00000400, LENGTH = 0x00000010

}

/* Define output sections */

SECTIONS

{

  /* The startup code goes first into INTERNAL_FLASH */

  .interrupts :

  {

    __vector_table = .;

    . = ALIGN(4);

    KEEP(*(.vectortable)) /* Startup code */

    . = ALIGN(4);

  } > m_interrupts

  .cfmprotect :

  {

    . = ALIGN(4);

  KEEP(*(.cfmconfig)) /* Flash Configuration Field (FCF) */

  . = ALIGN(4);

  } > m_cfmprotrom

 

  /* The program code and other data goes into INTERNAL_FLASH */

  .text :

  {

    . = ALIGN(4);

    *(.text)           /* .text sections (code) */

    *(.text*)          /* .text* sections (code) */

    *(.rodata)         /* .rodata sections (constants, strings, etc.) */

    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */

    *(.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 */

  } > m_text

   .ARM.extab   : { *(.ARM.extab* .gnu.linkonce.armextab.*) } > m_text

    .ARM : {

    __exidx_start = .;

      *(.ARM.exidx*)

      __exidx_end = .;

  } > m_text

.ctors :

  {

    __CTOR_LIST__ = .;

    /* gcc uses crtbegin.o to find the start of

       the constructors, so we make sure it is

       first.  Because this is a wildcard, it

       doesn't matter if the user does not

       actually link against crtbegin.o; the

       linker won't look for a file to match a

       wildcard.  The wildcard also means that it

       doesn't matter which directory crtbegin.o

       is in.  */

    KEEP (*crtbegin.o(.ctors))

    /* We don't want to include the .ctor section from

       from the crtend.o file until after the sorted ctors.

       The .ctor section from the crtend file contains the

       end of ctors marker and it must be last */

    KEEP (*(EXCLUDE_FILE (*crtend.o ) .ctors))

    KEEP (*(SORT(.ctors.*)))

    KEEP (*(.ctors))

    __CTOR_END__ = .;

  } > m_text

  .dtors :

  {

    __DTOR_LIST__ = .;

    KEEP (*crtbegin.o(.dtors))

    KEEP (*(EXCLUDE_FILE (*crtend.o ) .dtors))

    KEEP (*(SORT(.dtors.*)))

    KEEP (*(.dtors))

    __DTOR_END__ = .;

  } > m_text

  .preinit_array     :

  {

    PROVIDE_HIDDEN (__preinit_array_start = .);

    KEEP (*(.preinit_array*))

    PROVIDE_HIDDEN (__preinit_array_end = .);

  } > m_text

  .init_array :

  {

    PROVIDE_HIDDEN (__init_array_start = .);

    KEEP (*(SORT(.init_array.*)))

    KEEP (*(.init_array*))

    PROVIDE_HIDDEN (__init_array_end = .);

  } > m_text

  .fini_array :

  {

    PROVIDE_HIDDEN (__fini_array_start = .);

    KEEP (*(SORT(.fini_array.*)))

    KEEP (*(.fini_array*))

    PROVIDE_HIDDEN (__fini_array_end = .);

  ___ROM_AT = .;

  } > m_text

 

 

  /* Initialized data sections goes into RAM, load LMA copy after code */

  .data : AT(___ROM_AT)

  {

    . = 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 */

  } > m_data

  ___data_size = _edata - _sdata;

  ___m_data_20000000_ROMStart = ___ROM_AT + SIZEOF(.data);

  .m_data_20000000 : AT(___m_data_20000000_ROMStart)

  {

     . = ALIGN(4);

     ___m_data_20000000_RAMStart = .;

     *(.m_data_20000000) /* This is an User defined section */

     ___m_data_20000000_RAMEnd = .;

     . = ALIGN(4);

  } > m_data_20000000

  ___m_data_20000000_ROMSize = ___m_data_20000000_RAMEnd - ___m_data_20000000_RAMStart;

  /* Uninitialized data section */

  . = ALIGN(4);

  .bss :

  {

    /* This is used by the startup in order to initialize the .bss section */

    __START_BSS = .;

   PROVIDE ( __bss_start__ = __START_BSS );

    *(.bss)

    *(.bss*)

    *(COMMON)

    . = ALIGN(4);

    __END_BSS = .;

   PROVIDE ( __bss_end__ = __END_BSS );

  } > m_data

  _romp_at = ___ROM_AT + SIZEOF(.data) +SIZEOF(.m_data_20000000);

  .romp : AT(_romp_at)

  {

    __S_romp = _romp_at;

    LONG(___ROM_AT);

    LONG(_sdata);

    LONG(___data_size);

    LONG(___m_data_20000000_ROMStart);

    LONG(___m_data_20000000_RAMStart);

    LONG(___m_data_20000000_ROMSize);

    LONG(0);

    LONG(0);

    LONG(0);

  } > m_data

 

  /* User_heap_stack section, used to check that there is enough RAM left */

  ._user_heap_stack :

  {

    . = ALIGN(4);

    PROVIDE ( end = . );

    PROVIDE ( _end = . );

    __heap_addr = .;

    __HeapBase = .;

    . = . + __heap_size;

    __HeapLimit = .;

    . = . + __stack_size;

    . = ALIGN(4);

  } > m_data

 

  .ARM.attributes 0 : { *(.ARM.attributes) }

/*  .fill 0x2a0c : same as below */

  .fill LOADADDR(.romp) + SIZEOF(.romp) :

  {

  FILL(0xAAAAAAAA);

  . = ORIGIN(m_text) + LENGTH(m_text) - 1;

    BYTE(0xAA)

  } > m_text

 

 

}

0 Kudos

706 Views
andersbergström
Contributor II

Alright. I have tested to merge the two files now, and filled the space up to the start adress for the application.

The application start adress is set to 0xC530. This is also the binary adress from which the application is Added to the bootloader.

( I have diffed the bootloader binary with Beyond Compare, and the application.)

The memory configuration for the bootloader looks like this:

MEMORY

{

  m_interrupts          (RX)  : ORIGIN = 0x00000000, LENGTH = 0x00000400 /* FLASH */

  m_flash_config        (RX)  : ORIGIN = 0x00000400, LENGTH = 0x00000010 /* FLASH */

  m_text                (RX)  : ORIGIN = 0x00000410, LENGTH = 0x0000C120 /* FLASH */

  m_application (RX)  : ORIGIN = 0x0000C530, LENGTH = 0x000F3AD0 /* FLASH, Reserved for application */

  m_data_1FFF0400       (RW)  : ORIGIN = 0x1FFF0400, LENGTH = 0x0000FC00 /* RAM */

  m_data                (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000 /* RAM */

}

The memory configuration for the application looks like this:

MEMORY

{

  m_interrupts          (RX)  : ORIGIN = 0x0000C530, LENGTH = 0x00000400

  m_flash_config        (RX)  : ORIGIN = 0x0000C930, LENGTH = 0x00000010

  m_text                (RX)  : ORIGIN = 0x0000C940, LENGTH = 0x000F36C0

  m_data_1FFF0400       (RW)  : ORIGIN = 0x1FFF0400, LENGTH = 0x0000FC00

  m_data                (RW)  : ORIGIN = 0x20000000, LENGTH = 0x00030000

}

I use the same startup-file for both projects ( Please see the attached file.)

I also modifed the code as mark suggested, with the application Jump. I set the application adress and the stack pointer.

Still the same result, the MCU ends up in Reset_Handler.

Edit: I found out that it ends up in Hard_Fault_Handler, and then goes to reset.

0 Kudos

706 Views
mjbcswitzerland
Specialist V

Anders

If you use the jump code that I posted, don't use your entry address (0x00C28CB0) but instead use the address where you linked your application code to (eg. 0x8000 - it will usually be aligned to a 4k boundary).

When you combine your two binaries make sure that you also have the application locate at the address that it is linked to be at. I think that the cat function you use will simply add the two together which is not suitable since the program MUST be at the correct (physical) address otherwise nothing can work.

If the cat utility doesn't prove to be suitable simply use the uTaskerCombine that I linked to. It can be called with

uTaskerCombine bootloader.bin  HELLOW_WORLD.bin 0x8000 combined.bin

where 0x8000 is the (example) address where the second file should be appended to and the gap between if filled with 0xff.

Another potential problem with combining binaries (if output from CW using metroworks compiler) is that they are sometimes not RAW binary but are in MOTOROLA binary format (that means that they are divided into chunks and each chunk has a small header informing where its address is). Using simple binary tools may result in the output being in a format that can't be loaded using most tools (it will be loaded but the content can't run).

uTaskerCombine will also recognise MOTOROLA binary format and always generate output file in RAW binary so that it can be loaded to the processor with any tools.

Don't forget that there is already a complete general purpose solution for the K64 in the uTasker serial loader project http://www.utasker.com/docs/uTasker/uTaskerSerialLoader.PDF so you could simply use that and then get started with the actual customised protocol. It works out-of-the-box for the K64 with CW, KDS, IAR, Keil, Atollic, Rowley Crossworks, CooCox or standalone GCC and the Kinetis can be simulated (in approx. real-time) in VisualStudio so also custom developments parts are must simpler to develop, test and debug.
There are all tools and configurations needed as well as Flash drivers, peripheral drivers and communication suites (serial, Ethernet, USB) allowing flexible loaders to be configured or constructed with minimum effort/work/investment (the project is optimised for efficiency of resource usage and so a web server based boot loader, for example, occupies only 18k of Flash).

Regards

Mark

0 Kudos