I'm working on a custom bootloader and I'm having some problems with the debug configuration for my application. I've got a binary build set up and I've tested that I can jump to it from the bootloader.
I started with the Processor Expert linker files and have adapted them for my application. The target micro is an MK64FN1M0xxx12. I've got a config working with the application located at 0x0, and have had no problems with debugging it before moving it to make room for my bootloader. I'm using a Segger J-Link.
The problem I'm having is with my debug build configuration. When I start the debugger (flash & debug), the PC is at 0x0, unless I set a program counter in the debug configurations/start options. My application lives at 0x5800. The vector table starts at 0x5800, and is 0x198 bytes long, application starts at 0x5C00. I _think_ my problem might be setting the VTOR register, but GDB doesn't seem to know what that is, reporting "unsupported register !" when I try to write to vtor and scb->vtor. I've also tried writing it on startup manually using the EmbSys register plugin, to no avail. The debugger doesn't help once the application's started, the PC seems to jump from one end of the memory space to the other.
Summary:
What I've tried so far:
Questions:
Please let me know if you need anything else from me.
Thanks!
Linker file:
/* Entry Point */ ENTRY(__thumb_startup) /* Highest address of the user mode stack */ /*_estack = 0x20030000; had to move this because of the shared variable.*/ _estack = 0x2002FFF0; __SP_INIT = _estack; __stack = _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 = 0x00005800, LENGTH = 0x00000198 m_text (RX) : ORIGIN = 0x00005C00, LENGTH = 0x000FA3FF m_data_1FFF0000 (RW) : ORIGIN = 0x1FFF0000, LENGTH = 0x00010000 m_data (RW) : ORIGIN = 0x20000000, LENGTH = 0x00030000 m_cfmprotrom (RX) : ORIGIN = 0x00000400, LENGTH = 0x00000010 } /* Define output sections */ SECTIONS { /******************************************************************************/ /*******************************app sections***********************************/ /*data shared between application and bootloader. The NOLOAD parameter is used to prevent jumps between applications from causing re-initialization of the data.*/ .BootloaderSharedData 0x2002FFF8 (NOLOAD): { KEEP(*(.BootloaderSharedData)) /* keep my variable even if not referenced */ } > m_data /******************************************************************************/ /* 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_1FFF0000_ROMStart = ___ROM_AT + SIZEOF(.data); .m_data_1FFF0000 : AT(___m_data_1FFF0000_ROMStart) { . = ALIGN(4); ___m_data_1FFF0000_RAMStart = .; *(.m_data_1FFF0000) /* This is an User defined section */ ___m_data_1FFF0000_RAMEnd = .; . = ALIGN(4); } > m_data_1FFF0000 ___m_data_1FFF0000_ROMSize = ___m_data_1FFF0000_RAMEnd - ___m_data_1FFF0000_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_1FFF0000); .romp : AT(_romp_at) { __S_romp = _romp_at; LONG(___ROM_AT); LONG(_sdata); LONG(___data_size); LONG(___m_data_1FFF0000_ROMStart); LONG(___m_data_1FFF0000_RAMStart); LONG(___m_data_1FFF0000_ROMSize); LONG(0); LONG(0); LONG(0); } > m_data text_end = ORIGIN(m_text) + LENGTH(m_text); data_init_end = ___m_data_1FFF0000_ROMStart + SIZEOF(.m_data_1FFF0000) + SIZEOF(.romp); ASSERT( data_init_end <= text_end, "region m_text overflowed with text and data") /* User_heap_stack section, used to check that there is enough RAM left */ ._user_heap_stack : { . = ALIGN(4); PROVIDE ( end = . ); PROVIDE ( _end = . ); PROVIDE ( __end__ = . ); __heap_addr = .; __HeapBase = .; . = . + __heap_size; __HeapLimit = .; . = . + __stack_size; . = ALIGN(4); } > m_data .ARM.attributes 0 : { *(.ARM.attributes) } }
Solved! Go to Solution.
Hi George,
You are not able to debug your application because when a reset occurs the program counter goes to the address 0x0 where it finds the first vector which loads the stack pointer. Then the program counter moves to next address 0x4 and the second vector jumps to the startup routine. The reset vector must always be stored in address 0x4. In startup routine all the hardware initialization is performed and the vector can be copied to RAM. This is recommended as interrupts will work faster in RAM than in Flash. In this case you must change VTOR register to point to the address in RAM where the vectors are copied.
So I would recommend you to try debugging the application and bootloader at the same time, here is a document that explains how to do this:
Debugging Bootloader and Application using Kinetis Design Studio
or do not relocate the m_interrupts section if you want to debug your application without the bootloader.
You can find more information about debugging with gdb on the following link:
https://sourceware.org/gdb/current/onlinedocs/gdb/
And for detailed information on the GCC Linker please refer to “The GNU Linker” by Steve Chamberlain and Ian Lance Taylor.
Regarding the register monitoring, are you using the EmbSys Register view?
Hope it helps!
Best Regards,
Carlos Mendoza
Technical Support Engineer
I followed the instructions at the link and it's working OK now, with a huge caveat: I discovered after about an hour of confusion that some of the variables in the application, in code common with my bootloader (i.e. the same variables exist in both applications), are showing up with completely unexpected values; all 0's in one struct, and the reset address of the stack pointer in some of my ints. When I close the bootloader project and remove it from the application's debug args(apparently giving up the ability to debug it), my vars are as I expect them to be.
This isn't a huge problem for me, now that I'm aware of it, as I don't really need to debug both applications together.
Hi George,
You are not able to debug your application because when a reset occurs the program counter goes to the address 0x0 where it finds the first vector which loads the stack pointer. Then the program counter moves to next address 0x4 and the second vector jumps to the startup routine. The reset vector must always be stored in address 0x4. In startup routine all the hardware initialization is performed and the vector can be copied to RAM. This is recommended as interrupts will work faster in RAM than in Flash. In this case you must change VTOR register to point to the address in RAM where the vectors are copied.
So I would recommend you to try debugging the application and bootloader at the same time, here is a document that explains how to do this:
Debugging Bootloader and Application using Kinetis Design Studio
or do not relocate the m_interrupts section if you want to debug your application without the bootloader.
You can find more information about debugging with gdb on the following link:
https://sourceware.org/gdb/current/onlinedocs/gdb/
And for detailed information on the GCC Linker please refer to “The GNU Linker” by Steve Chamberlain and Ian Lance Taylor.
Regarding the register monitoring, are you using the EmbSys Register view?
Hope it helps!
Best Regards,
Carlos Mendoza
Technical Support Engineer