Skip to main content
stenasc
Associate III
June 20, 2016
Question

basic bootloader for M0

  • June 20, 2016
  • 43 replies
  • 5514 views
Posted on June 20, 2016 at 23:21

Hello Forum,

I've been reading on the forums about custom bootloaders stored in flash and I was wondering if there was another possibility (possibly because I want to avoid messing with stack pointers, reset vectors etc.)

In my main app, I have a bootloader function, that copies the new app stored in external flash to processor flash. This bootloader function contains a number of inline functions to read from external flash and copy to processor flash. Checking the .map file, this bootloader function looks to be all located in one place.

My thought process is to copy this bootloader function into RAM at say 0x20000100 and then jump to that address as shown below

void CopyLoaderToRam(void)

{

 //   uint32_t *Ram = (uint32_t *)RAM_ADDR;

    char *Ram = (char *)RAM_ADDR;///////////// Byte Alignment test

    typedef  void (*pFunction)(void);

    pFunction RunCode;

 ptr_bootloader_fn = &bootloader;    // Find the address of the bootloader function.  

   

//    memcpy(Ram, (void *)ptr_bootloader_fn, LOADER_FLASH_SIZE); // Copy bootloader into ram

   

    

      char *boot_byte;

      boot_byte = (char *)&bootloader;

      int i;

      for (i=0; i<= 4*LOADER_FLASH_SIZE; i++)

          {

                Ram[i] = *(boot_byte+i);    

                }

             

    RunCode = (pFunction)Ram[1];       // ResetHandler (PC in second vector)

     

    RunCode();

}

Well the first issue is it doesnt seem to work as I get a hard fault.My fears are that I am overwriting some necessary data in Ram or some alignment issue. I'm not sure. 

However, is it even technically feasible in the first place and if not, why? If it is a non-starter, I would rather know at this stage. Device is a STM32F051.

Kind Regards

Bob
    This topic has been closed for replies.

    43 replies

    Tesla DeLorean
    Guru
    July 4, 2016
    Posted on July 04, 2016 at 14:46

    The linker is usually pretty good at detecting the dependencies and dead code.

    You can either add the files it needs or you can make a monolithic file that has all the code you need in it.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    stenasc
    stenascAuthor
    Associate III
    July 14, 2016
    Posted on July 14, 2016 at 23:24

    Hi Clive,

    I created a standalone project and was able to compile, link and generate a standalone binary. From the binary I generated an array of const char, which was copied into ram in my main app as described earlier in this thread. I can run the bootloader  function to just before I initialize the USART. After that it hardfaults. I've linked in all the usart dependencies and there are no errors.

    ArmLink --entry bootloader app_bootloader.o stm32f0_discovery.o stm32f0xx_flash.o stm32f0xx_misc.o system_stm32f0xx.o stm32f0xx_syscfg.o stm32f0xx_pwr.o stm32f0xx_gpio.o stm32f0xx_spi.o stm32f0xx_rcc.o stm32f0xx_usart.o usarts.o -o app_bootloader.axf

    Am I missing anything in the link command or can you explain why it hardfaults?

    Bob

     

    int8_t bootloader(void)

    {

        

        

      RCC_ClocksTypeDef RCC_Clocks;

     

     

        

        // Switch ON all clocks to the ports

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOA, ENABLE);

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOB, ENABLE);

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOC, ENABLE);    

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOD, ENABLE);

      RCC_AHBPeriphClockCmd(RCC_AHBPeriph_GPIOF, ENABLE);

             

        // SysTick end of count event each 1ms

      RCC_GetClocksFreq(&RCC_Clocks);

     

    // return 22;  //Value returned correctly to main app

        

          // Enable SYSCFG clock

      RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);

    // return 33;  //Value returned correctly to main app

                          

        // Initialize UART   

        Usart1Init();    //

    // return 44;  //Hard Fault Occurs at this point

      

      // SPI configuration ------------------------------------------------------

      SPI_Config();

                

      puts(''\rHello From Bootloader'');  

    return 1;

    }

    Tesla DeLorean
    Guru
    July 14, 2016
    Posted on July 15, 2016 at 00:53

    Am I missing anything in the link command or can you explain why it hardfaults?

    It Hard Faults because you do something, or jump somewhere, which the CPU objects too. Most likely an absolute address.

    I'm not sure of why it is necessary to guess. Do a disassembly of the generated code, review that, and step through the code with a debugger. Have a handler that outputs clear and specific failure information. FromELF can disassemble AXF or ELF files.

    The source doesn't really provide enough clarity about what is happening internally.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    stenasc
    stenascAuthor
    Associate III
    July 16, 2016
    Posted on July 16, 2016 at 22:09

    Hi Clive,

    Tried stepping through the code. However, the constant char array doesn't disassemble into assembly language instructions, so I cannot step through it. The bootloader needs access to spi flash and the  usart1. Without access to these modules I am in hot water.

    Another thought I had was to have a separate loader module as part of my main app and load its object file into ram using the scatter file. I don't know if I will have any better success.

    Really stuck here, so all ideas greatly appreciated.

    Regards

    Bob

     

    Tesla DeLorean
    Guru
    July 16, 2016
    Posted on July 17, 2016 at 00:35

    Ok, but you can surely disassemble the stand-alone object you created, and you should be able to step the code and jump code in the disassembly via of the debugger. I have ZERO visibility into what you've created.

    And yes, a far more practical approach is to let Keil built the whole loader as a stand-alone app with a vector table, etc, but instead of building it for 0x08000000 you set IROM to 0x20000000[0x4000] and IRAM to 0x20004000[0x1000] or whatever fits you specific M0 part. Then take the .HEX from the linker, create an include file arrayed version of it, and add some code to ''memcpy'' that into RAM and start it, ideally in the Reset_Handler path.

    Would perhaps take a couple of hours of my time to frame up and demonstrate.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    stenasc
    stenascAuthor
    Associate III
    July 18, 2016
    Posted on July 18, 2016 at 10:05

    Hi Clive,

    I would appreciate your help here. I've tried two different approaches so far

    1...

    I did build a standalone loader consisting of a number of functions to read from SPI flash and write to micro flash. However, I didn't have a vectot table included in the loader as the plan was was for the loader to copy the new app from SPI to address 0x80000000 (completely overwrite the original app). The new app should already contain the vector table so I'm wondering why I need one in the loader.

    Anyway I used a scatter file to set the partitions to IROM to 0x20000000[0x4000] and IRAM to 0x20004000[0x1000] as you suggested, but I get a linker error. From what I read the linker expects a startup.s file included in the project. Modifying the scatter file gives me a warning so I was able to generate a const char array, but the bottom line is...it is still not working.

    2...I also integrated the loader within the main app. In the scattr file, I place loader.o in the RAM region. When the main app boots up, I can see the address of the loader function are in RAM. When I call the loader it starts to erase the micro flash as expected. However, it only erase micro flash from 0x80000000 to 0x80003FFF and no more. The micro contains 256K flash so I would have expected all to be erased.

    I'm wondering id even though I have placed loader.o in RAM, are some of the flash functions I call still in Flash memory and haven't been loaded to RAM

    Basically Clive, I am learning a lot about bootloaders as I do this but there is still big gaps in my knowledge. so any help appreciated. If you want to move this offline that is OK with me.

    Bob

    Tesla DeLorean
    Guru
    July 18, 2016
    Posted on July 18, 2016 at 12:08

    I would use the vector table approach because its orders of magntude simpler than the way you insist on approaching this.

    I could copy a monolithic block of code into RAM and execute it, but I would do so with a clear appreciation of what is going into it.

    At the moment you are fighting against how the processor and linker want to function, and without a sufficient depth of understanding of either. It is a battle you are going to lose.

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..
    stenasc
    stenascAuthor
    Associate III
    July 18, 2016
    Posted on July 18, 2016 at 14:14

    Hi Clive,

    I'm all ears. Initially, I tried to use an approach I understood in my mind, but the details of implementing this are very difficult...I know I've lost the battle with my current stratagies. Up to now, I've had no dealing with bootloaders to now, so now that I've been through a lot of pain with it, if there is an easier way, I'm all for it.

    Now, at this stage, I would use the vector table approach if I understood it. Can you give me an indication of how to use it.

    The main app will be able to determine if an update is available and copy it to external SPI flash. Then the main app should then call the bootloader

    My main criteria with the loader is that I can add puts statements to the console for debugging, and be able to copy from SPI flash to micro flash.

    I have also a couple of basic questions I need to clear up.

    1...The original app resides at 0x80000000. Can I copy the new app completely over this address range. If not, why not?

    2...Based on the above answer, where should the new bootloader be stored (in flash or in Ram)?

    3...Can you explain the remapping of the vector table to RAM? Why is this required?

    Clive, I appreciate that these are silly questions, but if the vector table approach is a lot simpler, I'd like to use it.

    Do you have some basic steps that I can follow to implement this method?

    Bob

     

    stenasc
    stenascAuthor
    Associate III
    July 18, 2016
    Posted on July 18, 2016 at 17:08

    Hi Clive,

    Are the following steps OK?

    1...Create a standalone bootloader project(including a startup.s file which contains the reset vectors. These reset vectors will specifically be for the bootloader. Ensure that the bootloader is mapped to 0x8000000. Q...A startup.s file is definitely required here ????

    2...Integrate this into the main app using const_chars/memcpy etc. Qustion, if my bootloader section is mapped to 0x8000000, what is the location of the current main app?

    0x8001000

    Can I get away with having only one ROM region in the scatter file here or do I need two? Can you provide an example to suit the above?

    The startup process...

    My interpretation....

    On reset, the bootloader is always the first to start. It checks some key to find if a new update is available. If not, the bootloader maps the vector table to 0x20000000, which is then mapped to 0x00000000. The vector table contains the location of the main app. We jump then to the main app. As long as we dont reset all vectors should function as expected.

    If a new app is required , the bootloader copies the new app to 0x8001000. Is there a requirement at this stage to remap the vectors to 0x2000000, as this will have been done previously.

    What does that sound like. Am I anyway close?

    Regards

    Bob

    Tesla DeLorean
    Guru
    July 18, 2016
    Posted on July 18, 2016 at 19:22

    Your primary application resides at 0x08000000

    Your loader image is built for 0x20000000, and held within your app as a ''static const'' stream of bytes. It thus resides within FLASH until deployed.

    Your app needs a mechanism to copy the loader into RAM, and transfer control via the SP/PC vector entries. This in my opinion should be done using the Reset_Handler mechanism I've demonstrated repeatedly on the forum, and in doing so won't tread on the RAM used by the normal app execution.

    You use the remapping, because the Cortex-M0 does not support changing the vector table address.

    You use a vector table because it solves multiple problems, and creates less problems, and you can use loader code with interrupts, fault handlers, etc.

    If you erase FLASH and the Hard Fault handlers, and vector table is still in that FLASH, you device is going to crash-and-burn.

    You build your app and loader as separate projects. In the same way you build all your other projects, with a vector table, start up code, USART, GPIO and SPI code/libraries, etc. The difference is that the IROM load region is going to be defined at a RAM address, and your IRAM is going to advance and shrink to accommodate the space the code portion is taking up.

    I'm not sure where the 0x08001000 stuff is coming from unless you want a FLASH based loader at 0x08000000 implementing an IAP model with the app at 0x08001000

    Tips, Buy me a coffee, or three.. PayPal Venmo (See Profile) Up vote any posts that you find helpful, it shows what's working..