Jumping from bootloader app to user app.

cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Jumping from bootloader app to user app.

Jump to solution
4,514 Views
Tgomes
Contributor III

Hello,


I'm trying to make a bootloader for my application but am having some problems. I have two files .lcf. One has the addresses of memory configured to record the program starting in the region 0x00010000. This is the user application, which will eventually be updated by the bootloader. The other file .lcf is configured to start recording at position 0x00000000, because in this position the bootloader starts, right? To test, I recorded the user application directly at the position 0x00010000 using the BDM. After I recorded the bootloader  at position 0x00000000, also using the BDM. In the boot program, I try to jump to the position where the user application  has been recorded, but nothing happens. I'm relying on the document AN4367, but still could not get any progress. I've seen some examples from the package installation of MQX 3.8, but they are somewhat complex and not directly apply to my project, since I'm using a totally different motherboard from freescale kits. What can I be doing wrong?


Any help is welcome!

If necessary I can put here my .lcf files for analysis.


 

Best regards.

0 Kudos
1 Solution
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

Sounds like you are close.

Please check to ensure that the RTOS libraries that the application is using really is the RTOS built with MQX_ROM_VECTORS 0 .

 

Should I leave the main task from bootloader running?

//DES OK to leave task.  You just want to disable all interrupts before jumping to the application.


What is the exact point where I should jump on my application?

//DES Any location is fine.  The flow is assumed that you have checked to verify a good application is present at 0x10000.  At 0x10000 should be the vector table that will be copied into RAM.  The first two 32-bit values are the stack pointer 0x10000, and PC 0x10004.  You want to read the PC value and jump to it.  That is exactly what happens out of hard reset (power on reset).  The CPU loads the stack and PC and then jumps to the PC. 

The bootloader example places a jump table at 0x10000 so your code would validate it is good, then jump to 0x10000 which would then jump to the starting point of the application.

Either method is fine.


 

Regards,

David

View solution in original post

0 Kudos
19 Replies
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

Sounds like you are off to a good start.

A general rule of thumb is to make sure when you "jump" from bootloader to your application that any resources you have used are disabled (ex: interrupts, UARTs, GPIO, etc...) as they can cause an appplication to crash.

Second is you need to have the application place its vector table into RAM.  The bootloader are 0x00000000 owns the flash vector table and that area of flash should never be touched by the application.

If you can experiment with your application to load it at 0x10000 and run it and see the vector table copied from flash to RAM and then run you should be ready to try with the bootloader.

When I've done this I set a breakpoint in the bootloader where it will jump to the application then step into the application.  If the first few lines of code (usually seen as assembly code since the debgguer was loaded with the bootloader symbol information) you can then let it run to see if it is working OK.

Conversely you can use debugger with the application but don't run it.  Set a breakpoint at the first line of application code.  Then use the register window to set your PC to the beginning of the bootloader and let it run.  Assuming it is setup to check for valid application in flash at 0x10000 and that test passes OK then you should hit the breakpoint you have at the first line of application code and proceed to debug from that point.

Hope this helps.

Regards,

David

0 Kudos
2,066 Views
Tgomes
Contributor III

Hello David,

 

Thanks for responding. So, I'll give you more details about my problem and I'll make a few more questions. I'm using MQX 3.6 in my application and in the bootloader (do not know if the bootloader can be done with the MQX). According to application notes and examples, to place the vector table in RAM, I must define the macro MQX_ROM_VECTORS as 0 . This has already been done, only for user app. When I record the app in position 0x00010000 using the BDM, it runs only once, immediately after recording and using the debug option. If I restart the hardware, the application does not run, even in debug. If I write the app in position 0x00000000, overwriting the bootloader, it works normally. Now my doubts are:
 

 

1 - Is it possible to use the bootloader with the MQX?

2 - What is the best command to skip the bootloader for the app: asm JUMP or function pointer?

3 - Is it possible to configure BDM to start debugging the application in 0x00010000 or any other position? As I told you, when I record the app in a different position from the beginning of memory, I can debug only once, and in release mode, the app does not work.

 4 - If possible use MQX the bootloader, can I use a version compiled differently in the app? That is, the bootloader using a MQX simple, with few features, without interruptions, etc., and the app using a compiled version complete with all features?

Thanks for the help!
Best regards.

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

If you have a bootloader in flash at 0x0 and application at 0x10000, can you debug the application OK?

Sometimes the debugger interface doesn't work if the bootloader space is all "F"s.

As long at your application has the MQX_ROM_VECTORS 0 (and you have re-compiled the BSP/PSP) you should be able to have your application anywhere in the flash space (including 0x10000).

A1) It is possible to use MQX as the bootloader but in general it is overkill.

A2) I've used the ASM JUMP to get to the application.  I usually do a test at 0x10000 (stack) and 0x10004 (program counter) to see if the memory is none "F"s.  Then I can use the program counter value to know where to jump in the application to get to the first instruction.

A3) Yes.  It basically is the linker file that defines where your code  will be located.  So with the vector table in SRAM, the linker should be able to point to anywhere in the flash space.

A4) Yes and No :-)  Yes because in general the two applications (bootloader and application) are independent.  So you can configure each however you want.  The No is related to the MQX RTOS does require the system tick as an interrupt.  All other drivers can operate in polled fashion.

 

What device are you working on?  ColdFire?  Kinetis?

What development tool chain?

 

Best Regards,

David

0 Kudos
2,066 Views
Tgomes
Contributor III

Hi David,


Yes, if the bootloader is stored in the flash position 0x0, I can debug the application at location 0x00010000. In my bootloader, I will need the MQX only to get the firmware from SD card, since all functions to do that are already implemented and working normally, so I would the MQX in bootloader to be lighter, with features that will not be used disabled. However, when I jump to the application, the MQX restarts? I mean, the function _mqx ((MQX_INITIALIZATION_STRUCT_PTR) & MQX_init_struct) in mqx_main.c will be called again (after being called in bootloader)? This does not cause the crash of the application?



I'm using coldfire MCF52259 with the CodeWarrior 10.1 and 3.6 MQX

 

 

Best regards.

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

Since the bootloader and application are totally independent projects, once the bootloader jumps to the application, the applications version of the _mqx() function will be called.

The bootloader and the application have their own flash space and do not share any code.  The bootloader has its vector table in flash.  The application will copy its vector table from flash, place into SRAM, and then MQX starts using it and not the flash vector table the bootloader has.

Reference file init_bsp.c function _bsp_enable_card()

    /* set possible new interrupt vector table - if MQX_ROM_VECTORS = 0 switch to
    ram interrupt table which was initialized in _psp_int_init) */
    _int_set_vector_table(BSP_INTERRUPT_VECTOR_TABLE);

 

So it sounds like you are able to debug as long as you have the bootloader loaded to flash.  Correct?

Best Regards,

David

0 Kudos
2,066 Views
Tgomes
Contributor III

Hello David,

Okay, so I can have two versions of MQX, one in the bootloader and another in the application. But now I have other questions:


Should I leave the main task from bootloader running?
What is the exact point where I should jump on my application?


vectorrom (RX): ORIGIN = 0x00010000, LENGTH = 0x00000400
or
rom (RX): ORIGIN = 0x00010400, LENGTH = 0x0006FC00
In the examples of bootloader that I saw, was reserved an area of the flash in .lcf file, to store parameters. Is this necessary?


When I have already written the bootloader in flash, I can debug the application at the address in which I recorded. When I try to debug the bootloader, I noticed the following situation: After jumping to the application, both address "vectorrom" as in "rom", some assembly commands are executed, and then the program jumps to address 0x00000628, which seems to be the _int_kernel_isr() interruption. After that, the program gets into an infinite loop in this region. This interrupt is essential or is possible to disable it? Before leaving the bootloader, I call the function _int_disable (). The MQX at bootloader is configured with MQX_ROM_VECTORS = 1 and in user app MQX_ROM_VECTORS = 0, and I have compiled separate libraries for each of these configurations.

 

 

 

Best regards.


0 Kudos
2,067 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

Sounds like you are close.

Please check to ensure that the RTOS libraries that the application is using really is the RTOS built with MQX_ROM_VECTORS 0 .

 

Should I leave the main task from bootloader running?

//DES OK to leave task.  You just want to disable all interrupts before jumping to the application.


What is the exact point where I should jump on my application?

//DES Any location is fine.  The flow is assumed that you have checked to verify a good application is present at 0x10000.  At 0x10000 should be the vector table that will be copied into RAM.  The first two 32-bit values are the stack pointer 0x10000, and PC 0x10004.  You want to read the PC value and jump to it.  That is exactly what happens out of hard reset (power on reset).  The CPU loads the stack and PC and then jumps to the PC. 

The bootloader example places a jump table at 0x10000 so your code would validate it is good, then jump to 0x10000 which would then jump to the starting point of the application.

Either method is fine.


 

Regards,

David

0 Kudos
2,066 Views
Tgomes
Contributor III

Hi David,

 

 

So how do I disable all interrupts before jumping to the application? Like I said, before leaving the bootloader I call _int_disable (), but after the program start to run at 0x00010000 position, he jumps to the location 0x00000628, which seems to be an interruption (_int_kernel_isr ()). If the bootloader is occupying this space, the application should not jump to that position, right?


Thanks for help!

0 Kudos
2,066 Views
Tgomes
Contributor III

David,


Thanks for the help!I was finally able to jump from bootloader to the user application, and there was no interruption that I reported in the previous post. The problem was the value of the PC. I thought jumping straight to the beginning of the ROM of my application, the PC would be automatically loaded to start the program. In fact I had to cheat to realize it, because I got the value of the PC directly from .lst file from the application, and not from ROM address 0x00010004.


0 Kudos
2,066 Views
Tgomes
Contributor III

Hi David,


Now I need to get this value from the flash. After opening the stream (flashx) and seek to position 0x00010004, I could not read the value from flash, because apparently I'm exceeding the limit of addressable memory. In the configuration file of my board there is the definition BSPCFG_FLASHX_SIZE = 0x4000. Do I have to modify this value to be able to read and write from the position 0x00010000? And in the LCF file, do I need to change ___ FLASHX_START_ADDR?

 

 

Best regards.

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

Awesome.  Glad you are up and running.

I didn't use the flashx to get the PC but you can use the linker file to help by having the linker define the starting address of the MQX application space.  Two ways:  hard code, soft code. 

Hard code meaning something like:

__MQX_APP_START = 0x10000;

Soft code might look something like:

__MQX_APP_START=(_Code_Space_Size + 0xfff) / 0x1000 * 0x1000;

Soft code dynamically changes with your build to increase to the next flash sector size (note the 0x1000 might change with different MCUs)

Then in your MQX Application reference with something like:

extern unsigned long  __BOOT_STACK_ADDRESS[];

 

Then the code __BOOT_STACK_ADDRESS[1] can reference the PC.

 

I often do a bit of a hack in the application like:

{

   unsigned int PC_to_jump_to;

 

  PC_to_jump_to = *(unsigned int *0x10004);

  asm("  jmp PC_to_jump_to");

}

 

Obviously you should have code to test that the PC_to_jump_to is a valid (non 0xffffffff) address.  If not valid remain in the bootloader.

 

Hope this helps.

Regards,

David

 

 

0 Kudos
2,066 Views
Tgomes
Contributor III

Hi David,


After following your recommendations, now I can use the bootloader to write my application at the address I specified in the linker file and everything is working fine. Coincidentally, before reading your post, I had the same idea you mentioned and did a little "hack" to get the PC. I found this the simplest way, but also work in the other way.
Now I'm focusing on stability and recovery (if necessary) of the bootloader, due to the following situation: At the beginning of development, the program constantly fall into an infinite loop in a kernel interruption, as I mentioned before. Fortunately it did not happen more, but if happens again, I wish I could restart the microcontroller, using the watchdog. I tried using _watchdog_create_component and _watchdog_start, to create, start and reset the watchdog value, but this interruption of the kernel seems to have a higher priority, or I'm using the watchdog in the wrong way. Is there any way to reset the chip that is fully guaranteed? I mean, no matter what it is running, like a hardware watchdog?


Thanks again. Your tips have been helpful!
Best regards.

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

I think the subject of watchdog implementation is equally complex as benchmarking!

There are multiple levels of watchdog capability with ColdFire and FSLMQX.  To date I have not found any one to be the best solution but rather a combination of watchdog's being implemented to help make the design more robust.

ColdFire:

The core watchdog must be serviced regularly and the most regular event that occurs is the BSP_TIMER incrementing the system tick.  When the core watchdog timers times out (your code is good example) an ISR routine can be used to save off system information (i.e.. log stuff) and then issue a software rest (as your code does).  FSLMQX does implement this but by default does not enable/active it as it prevents debugging capability as core watchdog does not stop decrementing when processor is HALTed.

There is also a backup watchdog timer that once it times out the system does a hard rest.  This watchdog isn't directly supported other then having its registers defined as well as structure to access/enable it.  It is defined in the mcf5225.h header of the PSP...look for MCF5225_WATCHDOG_STRUCT.

FSLMQX:

Lastly MQX has a capability to implement task watchdog timers (reference ~mqx/examples/watchdog) that will ensure task do not fail and if they do then an ISR routine is called and decision as what to do can be done (ex: reset device, kill task and try reset, log stuff, etc.).  The example simply logs to terminal which task failed.  I enhanced the code to work with multiple tasks as originally it was working with only one. 

 

Placing the watchdog resetting code is a system engineering design verses one method that fits all designs.

 

I've attached several examples I have played with in the past but not lately that should help as well.

 

Hope this helps.

Best Regards,

David

0 Kudos
2,066 Views
Tgomes
Contributor III

David,


I was able to simulate the situation in which it was not possible to recover the system using the watchdog. My bootloader has only one task, which makes access to SD cards and other peripherals. I realized that the problem happens when the stack reserved for this single task is small. When accessing the SD card, to create a subdirectory, the application "dies", and I need to turn off and on the board. The problem does not occur when a larger amount of stack is reserved. How can I recover from a deadlock like this?

 

 

Best regards.

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Tgomes,

The attached file has baremetal code (so need to port into MQX) to enable the BWT (backup watchdog timer).  The BWT is the ultimate wrapper to reset the ColdFire MCU should a stack go bad and cause lock up.

The BWT is not kicked/serviced, will cause a hard reset.

Please review the clock mode setting at top of file.

In general I have used combinations of watchdogs.  With MQX the task watchdog should have short timeout.  The core watchdog should have a medium timeout.  The BWT should have long timeout.

Hope this helps.

Regards,

David

0 Kudos
2,066 Views
MarkP_
Contributor V

Hi,

better way to generate reset:

_int_disable();

{ asm ("dsb 0"); }

SCB_AIRCR = ( SCB_AIRCR_VECTKEY(0x05fa) | (SCB_AIRCR & SCB_AIRCR_PRIGROUP_MASK  ) |

                                ( SCB_AIRCR_SYSRESETREQ_MASK ) );

{ asm ("dsb 0"); }

while(1)

{

}

 

Maybe MQX should have a reset-function like NVIC_SystemReset, as other platforms have.

 

~Mark

0 Kudos
2,066 Views
DavidS
NXP Employee
NXP Employee

Hi Mark,

Yes that is a good software reset solution for the Kinetis architecture.  ColdFire is different in that respect.

Good idea to have a MQX SystemReset function.  I will pass that along.  Should be a simple addition.

Regards,

David

 

0 Kudos
2,066 Views
Tgomes
Contributor III

Hi David,


I was able to reset using the backup watchdog. However, how can I set the time of this reset to occur at intervals greater than 7seconds (corresponds to 0xFFFF in WMR approximately, in my environment)?

When a reset like  "reg_ptr> CCM.RCR | = 0x80" happens, the value of WMR is also maintained?

 

 

 

Best regards.

0 Kudos
2,066 Views
Tgomes
Contributor III

Hello David,

Thanks for the help. I finally got to configure the watchdog to a range that meets my needs. I set the clock source of watchdog to be the relaxation oscillator, which gave me a maximum delay of approximately 33s, which is perfect for my application. I have other doubts related to FTP. I will consider this topic closed and start another in  RTCS  session.


Best regards!

0 Kudos