cancel
Showing results for 
Search instead for 
Did you mean: 

basic bootloader for M0

stenasc
Senior
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
43 REPLIES 43
stenasc
Senior
Posted on July 04, 2016 at 11:57

Tried this over the weekend. Compiled, linked and converted a binary from a standalone file and can get it to work for the simplest functions as before. There are a lot of flash access functions that I need to integrate into this code, so this is going to be quite time consuming.

Before working on the standalone version, I had compiled the loader into an object file as part of the main app compilation process. I thought I could use this object file and generate a standalone binary from it that I could convert to const char. However, when I run the linker on it, I get a load of link errors where it looks to reference functions in another file. Any way to get around this or will I have to pull all these functions into the local file?

Regards

Bob

 

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
Up vote any posts that you find helpful, it shows what's working..
stenasc
Senior
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;

}

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
Up vote any posts that you find helpful, it shows what's working..
stenasc
Senior
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

 

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
Up vote any posts that you find helpful, it shows what's working..
stenasc
Senior
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

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
Up vote any posts that you find helpful, it shows what's working..
stenasc
Senior
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
Senior
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