Topic : Jumping back and forth between Bootloader and App (Reposted)

Forum : ST7/STM8

Original Post
Post Information Post
September 4, 2012 - 7:31pm
Guest

(End of first post was deleted. Reposting)

I am working on a custom Bootloader for IAP through CAN Communication with a STM8S208RB.

I have been using the "Bootloaders for STM8" AN61, which has been very helpful, although I am running into an issue with jumping back and forth between Bootloader and Application code. My Bootloader is reserved between 0x8080 and 0x9000 (I kept Interrupts as-is and share them with the BL and App). Application code starts at 0x9000 with an "at 0x9000 void appmain(void)", which works fine. When the Bootloader starts, I can jump to the Application if the Application is present. If the Application area is empty, the chip sits in the Bootloader and waits for an Application to be sent over the CAN.

This setup works fine if I am programming the Flash for the first time, but if I want to erase the Application area and add in some Updated Code, I run into an issue.

- I would like the STM8S to stay in the Bootloader if the Application area is empty.
- I would like the STM8S to automatically and instantly jump to the Application if the Application area is filled.
- I would also like to be able to somehow return to the Bootloader, from the Application, with a CAN command, when I choose to do a firmware update.

The first two parts I have down, but I cannot figure out the third. My main() for the Bootloader has no absolute address. Should I add one in? As in "at 0x8080 void main(void)"? Resetting obviously starts in the Bootloader section, but it will return me right back to the Application if the Application area is already filled.

Is there an elegant way to do this? Or something obvious that I am not thinking of?

Thank you for the help,

-Dan

Replies
Post Information Post
+1
0
-1
September 5, 2012 - 1:56pm
Raisonance Support Team

Hi,

Yes you can use "at" on your bootloader's main. (or rather, another bootloader function, for SP and global init, see below)

Or you could use the CAN interrupt for this, if the CAN is reserved for the bootloader.

And there are other solutions.

In any case, beware that the stack pointer and bootloader global variables are not reinitialized by a jump or call. So the stack used by the application will be there "under" the bootloader's stack, with a risk of stack overflow. And your bootloader's global variables will not be (re)initialized. There are many easy ways to solve these problems. Or maybe they can be ignored. (if you use very little stack and no global variables, for example) Just make sure you don't forget about them.

I hope it helps.

Vincent

+1
0
-1
September 12, 2012 - 9:59pm
Guest

I tried this out, and I am able to initialize in the Bootloader, jump to the Application, and jump back to the Bootloader, but during my return to the Bootloader, my program loses its place at the _rim_() enable interrupts command. To explain:

I have my main bootloader program:

... In Bootloader ...

at 0x8080 void main(void)
{
      _sim_();         //disable interrupts
      ....Initialize...
      _rim_();         //enable interrupts


      ...Program waits for a command.  If its the GO command, jump to Application with...
      _jmp_((_fctptr_t)0xFA00);  //This is where the application is located
}

.. In Application ..

at 0xFA00 void appmain(void)
{
       _sim_();
       ... Initialize ...
       _rim_();

       while(1) {}  //Infinite loop, waiting for interrupts which lead to actual function of application

}

...Later in Application, after an interrupt is generated and a message is received through CAN...

if (msg == jumptoBootloader)
{
        _jmp_((_fctptr_t)0x8080);  //Jump back to the Bootloader from the Application
}

Now the Bootloader by itself works. It has a lot more code than I showed, but it will successfully program the chip with the Application over the CAN system. With the Application loaded it, I can reset the chip, initialize in the Bootloader, jump to the Application, and the Application will run fine at this point as well. However, if I ever want to reprogram the MCU, I want to jump back to the Bootloader.

The "_jmp_((_fctptr_t)0x8080)" command works, and I end up back in the Bootloader. "_sim_()" runs, and everything initializes, but once I get to that second iteration of the "_rim_()" command, the MCU will lose its place on the stack, or will no longer continue. Any command after "_rim_()" will not run. I can generate interrupts, but after the interrupt, I will be back at "_rim_()", frozen.

For example, since its difficult to run the Debugger with 2 applications, I usually just use LEDs to tell me where I am at. If the code in the Bootloader is:

...
SetLEDs(LED_RED_PIN);
_rim_();
SetLEDs(LED_GREEN_PIN);
...wait for command...
...

The first initial run, the Bootloader starts, the Red LED flashes quickly, but then interrupts are enabled, and the board waits for a command with the Green LED on. I send the command to jump to the Application, then jump back to the Bootloader. The Bootloader initializes again, the Red LED turns on, interrupts are enabled, but then the stack never reaches the Green LED code. I am stuck with the Red LED on, interrupts work, but the program will not continue.

There's something about returning to the Bootloader, and reinitializing interrupts, which causes this issue.

Hopefully I've explained it sufficiently, but is this a known problem, or is there anything about the _rim_() command that can cause this?

Thanks,

Dan

+1
0
-1
September 13, 2012 - 10:01am
Raisonance Support Team

Hi,

There are very few things that could prevent the CPU from executing. A stack overflow might do it, or maybe an interrupt that comes so often that it comes back before it is finished servicing, leaving no time for anything else to execute, or it could be a problem of clocking...

Are you sure that the stack pointer is reinitialized some time before or after jumping back to the bootloader? This is mandatory but it does not appear in your code. I don't understand what you could mean by "the stack never reaches the Green LED code"... are you sure you know what "stack" is? This is critical.

If checking the stack pointer does not help, it would be interesting to ask the debugger to perform assembler steps (set the focus in the disassembly view before executing StepInto) when you are blocked after the rim, to see it the execution goes in an IRQ that has no debug information, and therefore might not be displayed when performing C-level step or run.

I also suggest you disable everything from the init except the LEDs, and see if the green LED turns ON. If yes, then reactivate the parts of your init one after another and try to determine which one generates the problem...

If the green LED does not turn ON when your init is empty, then probably there is a problem related to an interrupt that has been activated by the application _before_ jumping to the bootloader. At the beginning of your bootloader's init, just after the sim, you must make sure that your bootloader deactivates all interrupts and clears all the pending interrupt requests.

Concerning clocking problems, just make sure that the application did not select a CPU clock that the bootloader init disables. Seeing your report, I don't think that your problem is related to clock, but it will be worth checking if the other hints give no result.

Finally, note that for debugging a 2-parts application, you just need to add the aof of one application in the other application's project, like if it was a source file. Then you should be able to debug both parts without issues.

I hope it helps.

Best Regards,

Vincent

+1
0
-1
September 13, 2012 - 4:41pm
Guest

Hi Vincent,

Thank you for the reply. You are probably right, and I may be a bit confused with my terminology.

When you say that the Stack Pointer needs to be reinitialized, are you referring to the _rsp_() command? I had originally thought to include it, but it seems that this command was not available on STM8S. Going into the intrinsic functions by Raisonance, "instrist7.h":

#if defined(__STM8__) && (__STM8__!=1)  /* RSP not available on STM8 */
extern void          _rsp_              (void) reentrant;
#endif

Is there an alternate way to reset the stack pointer?

As for deactivating and clearing interrupts, I believe this, along with resetting the stack pointer, are probably my issue, considering this is the first time I'm realizing they are required. I inherited most of the interrupt code, but is there documentation on reseting the stack pointer, and clearing the interrupts, besides the "ST7/STM8 Compiler Manual"? This lists the sim, rim, rsp commands, but nothing about clearing interrupts.

When I jump from the Application back to the Bootloader, I am in an interrupt which I never leave, so for example:

void CAN_RX_IRQHandler (void) interrupt 8
{   
    // An interrupt generated by activity on CAN_RX pin.
    CAN_GetData();

}

And somewhere in CAN_GetData, if its a command to jump back to the Bootloader, I have my "_jmp_((_fctptr_t)0x8080)" to return to the main() of the BL. So I never leave that interrupt, indefinitely. Now that I think about it, when I'm back in the Bootloader, and use _sim_() and _rim_(), I still have never finished the interrupt listed above. Could you point me in the right direction to resolve this?

As for Debugging both applications together, I have never been able to get that to work properly. I can add both applications to a project, creating a single HEX file, and everything works fine while loading that HEX onto my STM8, but whenever I run Ride7's Debug with both applications, I always get different behavior in the code. As in, the Bootloader works, but won't jump to the Firmware, or the debugger will freeze at random times after so many CAN messages processed. It is behavior that is markedly different than just programming the MCU and running it without a debugger. I have not spent much time trying to fix this, since in AN61 on Bootloaders for STM8, section 4.2, warns against using the debugger with IAP. I am not actually using IAP when testing these issues, but I will spend some more time on this.

I'll also review our clock init.

Thanks again,

-Dan

+1
0
-1
September 13, 2012 - 5:13pm
Raisonance Support Team

Hi,

What you need to do is reproduce a complete reset, and this includes much more than PC=0.

To reinit the stack pointer there is no STM8 instruction. You must use some assembler code that will write the initial value (which depends on which STM8 you are using) to the SP register. Keep in mind that if your main is too complex, you probably cannot do that in C because you don't know what the compiler has put in the stack that it will need in the future. So instead of placing your bootloader's main at 8080, you could place there a piece of assembler code that will reinit SP, and maybe also the other things below, even though these other things could be reinitialized in C, and then it will call your main, or rather it should call your bootloader's startup code (right after where it checks if there is an application), so that your bootloader's global variables are reinitialized too...
See the main function of the bootloader example provided by ST here:
http://www.st.com/stonline/stappl/resourceSelector/app?page=resourceSelector&doctype=APPLICATION_NOTE&SubClassID=1244

You'll see (line 113 of main.c) a section of inline assembler which reinitializes SP. I personnaly advise to avoid inline assembler and prefer real assembler in asm files, in all situations, but especially when meddling with the stack like here.

To disable interrupts and clear pending interrupt request you'll have to write appropriate values to some control registers of the CPU. (IT controller, peripherals, etc.) There is no single instruction for this, it's just normal data writes at dedicated addresses. See your STM8 device documentation to know which values must be written to which registers.

Anoter way to do all this in one time is to make the hardware trigger a real reset, for example using a watchdog. But for this you would need to change the way your bootloader decides if it launches the application or goes in IAP mode. For example you'd need to see if you come from normal reset or from watchdog reset. Of course if your application and/or bootloader already use the watchdog, it might be more complex.

I hope it helps.

Vincent