This discussion has been locked.
You can no longer post new replies to this discussion. If you have a question you can start a new discussion

Code Relocation - Revisited

I'm looking for some ideas (or solution) to the following problem :-

1. I using an STM32F207VG and I want to operate from 2 x separate code executables. An "old" version, and the "new" uploaded version.
2. Loading a binary executable to the upper-half of the FLASH (0x0808 0000) from an old dongle is easy. That's not the problem.
3. The problem lies in "reseting" to start execution from 0x0808 0004 (and not from 0x0800 0004).
4. I really don't see a way around this other than to copy the "new" executable code over the default FLASH boot space (0x0800 0000).
5. I've searched the Cortex-M3 manual and there is no register whereby you can change the default reset vector (i.e. from 0x0800 0000) to another value. Unless it's hidden.
6. Using a FLASH based "system memory bootloader" IAR is not an option.
7. That should make some sort of sense, if not, then I haven't explained it very well.

All positive comments and ideas are most welcome.

Parents
  • I'm not sure what you meant by point 6, but in my view there has to be some kind of a ROM monitor (a bootloader or whatever you want to call it.) It would be the first to run after reset and decide which of the two programs should be allowed to run. By the way, this functionality can be incorporated into the app that resides in lower part of the flash (or both.)

Reply
  • I'm not sure what you meant by point 6, but in my view there has to be some kind of a ROM monitor (a bootloader or whatever you want to call it.) It would be the first to run after reset and decide which of the two programs should be allowed to run. By the way, this functionality can be incorporated into the app that resides in lower part of the flash (or both.)

Children
  • Hi Mike,

    Yes I agree, there has to be some sort of ROM-Monitor/Bootloader function that runs immediately after reset. I was looking for a cheat in order to make this very simple (i.e. on uploading a new executable, issue a reset that vectors "here" as opposed to "there"). That way the binary that has just be uploaded starts execution from RESET.

    My issue is that on a reset, the ROM-monitor/bootloader function that is executed will ALWAYS be the one in the LOWER part of the FlASH. Do you agree ? Is that a valid concern ?

    I'm just wondering out loud because I'm going into unknown territory.

    Any other suggestions or comments or hints are most welcome.

    Best, Rich.

  • You should have 3 memory regions. A small boot loader, and two application areas. And possibly letting the boot loader compute CRC on the memory regions to verify that the one flagged as "new" isn't corrupt.

  • Hi Rich

    I just worked through this general multi-app topic for a system I'm working on so I can share some summary tidbits.
    Per previous thread comments, it is correct that there isn't a way to directly boot into a different area of internal flash. The ARM at reset always goes to the 0x08000004 reset vector. So you have to have some code there no matter what. What that code does is up to you (e.g. simple boot manager, full bootloader/downloader, single application, etc). So I agree that for your scenario you'd need some boot code at flash base, and then your 2 different applications which get located in flash sectors offset from the base (and each other). Really each of those entites is a separate "application" in that they are independently built by a separate Keil project and targeted to their particular location in flash. How you get them into the flash is another topic, though it sounds like you have some kind of loader scheme for application code.
    For the boot code, if all you wanted to do was decide which of two different applications to jump to you could have something as simple as configuring a gpio input pin and reading to see if the pin is pulled high or low and use that to jump to one or the other of your app locations. As mentioned from other posts, its a good idea to do some sanity check on flash contents before jumping. In our application we check the offset app's vector table for "sensible" initial Stack Pointer and Reset Vector values BEFORE jumping, and then after jumping do a CRC32 of the application flash very early in the app init (by doing the CRC in the app after the jump its easier to know the end of the app's flash space).

    The couple of key things in a Keil environment about targeting an application to an offset flash address are:
    - In the Target tab of the project Options dialog, set the Start address for on-chip IROM1 to the offset address (e.g. 0x08004000 instead of the default 0x08000000). This then links the app to start at somewhere other than the flash base. Adjust the Size parameter as needed there as well.
    - In early startup code you have to write to the VTOR (Vector Table Offset Register) so that its pointing to your offset application's vector table and not to the reset default base of 0x08000000. Assuming you're using the ST supplied startup code there is a #define VECT_TAB_OFFSET you can change. If you forget this step you'll end up using the "boot" code's vector table when running the application code, which very quickly won't turn out so well.
    - To get the debugger (assuming the Keil ULINK) to startup properly you need to add some code to the STM32F2xx_TP.ini (or similar) file. This file controls startup behavior for the debugger and is specified in the Debugger tab of the project Options dialog (Initialization file). You'll need code to read the vector table from the offset location. Put the following two lines of code within the DebugSetup() function:
    PC = _RDWORD(0x08004004); // load Program Counter from offset reset-vector address
    SP = _RDWORD(0x08004000); // load Stack Pointer from offset initial-SP address

    The boot code must know the location(s) of the application code, and then jump to that address to transition from the boot "application" to the real "application". In C the jump itself is simply invoked as a function call to the known absolute address. But prior to jumping you'll need to set the stack pointer with the initial SP value (1st entry) from the offset app's vector table. We're using the library routine __set_MSP() to do this.

    Hope that helps.

  • Hi Paul,

    Thanks very much for the info. What you write all makes perfect sense. In fact it's what I've been working towards for the past week, and what you've written confirms that there might be some method to my madness. Thanks for that.

    I've implemented a simple boot scheme that will be built into all of the executables (i.e. the same boot mechanism will always be in each released executable) :-

    1. Restart & initialise based on the restart type (H/W, or S/W).
    2. Check which FLASH sector we're going to vector off to and perform some rudimentary checks.
    3. Setup the SP and vector off to the reset vector of the selected executable.

    As I say, this code will be in every executable. I don't need a sophisticated stand-alone bootloader executable.

    The info on setting up the STM32F2xx_TP.ini file for the debbuger is particularly useful, that's something that I hadn't thought of.

    Once again thanks for your input, it's been a great help. And if you have any other useful tips on this matter then I am a welcome audience.

    Best, Rich.

  • "I don't need a sophisticated stand-alone bootloader executable."

    Sounds like you are doing it wrong.

    You shouldn't try to integrate the boot loader in the application. It may sound like a good idea to just have two binaries but in reality it is a more complex solution because how do you handle the situation when you need to erase the flash for one application? What boot loader will you then have? How do you get the processor to look for the boot loader in the other application binary - the boot loader that is at the "wrong" address? How can you then guarantee that a power loss at any arbitrary state of a firmware update will not leave your processor as toast?

    With a 3-binary solution, you have a boot loader that is never erased. That is always there to check the two application areas and decide which of them may be selected between. That have a fixed address - always where the processor expect to find initial code.

    A separate boot loader isn't "sophisticated". It is the standard way to make sure you don't brick your units accidentally. Google for bricked units, and you will know that the programmers involved have thought they could do it smart. Or that they felt the suggestions they received was too complex. So they cheated.

  • Just a footnote: If the dedicated boot loader have transfer capability, then it's possible to have just one application binary. So that application binary can be erased by the boot loader and a new application retrieved.

    Still no danger of bricking. But the unit will have a service downtime until the new application have been correctly downloaded into the unit and it has rebooted.

    With a two-application + bootloader solution, it is possible to retrieve a new application and program into the unit, and then just take a quick reboot to switch. So almost zero service downtime. And the user will know that the new application they downloaded from the net is valid before the reboot - instead of getting a "download error" by the boot loader after the original application had already been erased.

  • Per,

    "Google for bricked units"

    Thanks, I'll have a read.

    Best, Rich.