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
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
Up vote any posts that you find helpful, it shows what's working..
stenasc
Senior
Posted on July 19, 2016 at 09:42

Hi Clive,

Will give that a go.

Yes, the 0x08001000 stuff is based on the IAP flash loader. I was considering implementing it that way. However, I will use the RAM loader, as I've completed a lot of it already.

Regards

Bob 

stenasc
Senior
Posted on August 08, 2016 at 23:33

The original post was too long to process during our migration. Please click on the provided URL to read the original post. https://st--c.eu10.content.force.com/sfc/dist/version/download/?oid=00Db0000000YtG6&ids=0680X000006I6kP&d=%2Fa%2F0X0000000buY%2FdbwivSiFjtZB05IBw6czz60hV_Hoay4KgRR6_C6S0ig&asPdf=false
Posted on August 09, 2016 at 01:55

One of your key problems is you take simple concepts and then add a layer of unnecessary complications. Understand and apply the basic concepts, and it will be much simpler.

RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE);     // Enable the SYSCFG peripheral clocks ** CLOCK not RESET **

The remapping can map FLASH 0x08000000 at zero, or RAM 0x20000000 at zero. If you have vectors already at 0x08000000 they don't need to be copied. They need to be copied if they are at 0x08001000 etc, because you can't relocate the vector table with a VTOR register.

Yes, both your projects need their own main(), they are separate projects, they don't share the same name-space, and your main() function is called by the __main code after it has initialized the statics for the C runtime environment.

 

The value for the initial SP is stored in the first vector word. The way I'd handle this is to have the first instructions in the RAM based code to load SP for you.

  LDR R0,__initial_sp

  MOV SP, R0

I can write an example in under an hour, framing in an app note would take longer.

Tips, Buy me a coffee, or three.. PayPal Venmo
Up vote any posts that you find helpful, it shows what's working..
Posted on August 09, 2016 at 06:01

Ok, so you have to adapt the code to match what you are doing. You can't call this like it is a function anymore, as what you have built is a firmware image, with a vector table. You are going to transfer control to this loader, and it isn't going to come back to you.

To copy and call the loader image you're going to want something like this

void RamLoader(void)
{
static const uint8_t Loader[] = {
0x40, 0x44, 0x00, 0x20, 0xd5, 0x00, 0x00, 0x20, 0x01, 0x04, 0x00, 0x20,
0x25, 0x03, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// ........................... etc 
};
pFunction RunLoader;
RCC_APB2PeriphClockCmd(RCC_APB2Periph_SYSCFG, ENABLE); // Enable the SYSCFG peripheral clocks 
memcpy(0x20000000, Loader, sizeof(Loader)); // Copy loader image to SRAM
SYSCFG_MemoryRemapConfig(SYSCFG_MemoryRemap_SRAM); // Remap SRAM at 0x00000000
RunLoader = (pFunction)*((uint32_t *)0x20000004); // Pull Initial PC 
RunLoader(); // Isn't going to return here
} // sourcer32@gmail.com

For startup.s

...
; Reset handler routine
Reset_Handler PROC
EXPORT Reset_Handler [WEAK]
IMPORT __main
IMPORT SystemInit
LDR R0, =__initial_sp ; Ensure SP set as expected
MOV SP, R0 
LDR R0, =SystemInit ; Setup clocks and buses
BLX R0
LDR R0, =__main ; Initialize C runtime, and then go to main()
BX R0
ENDP ; Reset_Handler
...

If you want to read up on the mechanics at work here, I'd strongly recommend Joseph Yiu's book on the Cortex-M0, and the ARM TRM for the core.
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 August 09, 2016 at 15:37

Hi Clive,

Thanks very much. That worked a treat. Thats a big step for me and I will purchase Joseph's book. I should be able to make decent progress from here, but I likely will be back with more questions at some stage. A big thanks to you Clive for your patience and expertise.

Bob

stenasc
Senior
Posted on August 14, 2016 at 20:48

Hi Clive,

A quick question to ask you about programming processor flash on the M0

My bootloader is required to overwrite the old app beginning at 0x08000000. is this a restricted area, as any of the other bootloaders (e.g. IAP) I've seen always program flash away from this address at 0x08003000 for example.

While looking through the IAP app code, I've also noticed that the flash address increments by 16. I would have expected it to increment by 4.

*FlashAddress += 1 increments the address by 4. Any ideas?

Regards

Bob

uint32_t FLASH_If_Write(__IO uint32_t* FlashAddress, uint32_t* Data ,uint16_t DataLength)

{

  uint32_t i = 0;

  for (i = 0; (i < DataLength) && (*FlashAddress <= (USER_FLASH_END_ADDRESS-4)); i++)

  {

    /* the operation will be done by word */

    if (FLASH_ProgramWord(*FlashAddress, *(uint32_t*)(Data+i)) == FLASH_COMPLETE)

    {

     /* Check the written value */

      if (*(uint32_t*)*FlashAddress != *(uint32_t*)(Data+i))

      {

        /* Flash content doesn't match SRAM content */

        return(2);

      }

      /* Increment FLASH destination address */

      *FlashAddress += 4;

    }

    else

    {

      /* Error occurred while writing data in Flash memory */

      return (1);

    }

  }

  return (0);

}

Posted on August 14, 2016 at 22:12

You are passing a pointer to a 32-bit variable, you are then adding to the variable, not the pointer.

ie

uint32_t Addr = 0x08000000;

FLASH_If_Write(&Addr, ... );

You should be able to write the entire flash array, starting from 0x08000000. You should be able read/write protect specific sectors, but I haven't reviewed the F0 models.

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 August 15, 2016 at 00:23

Hi Clive,

That was the original IAP code from app note AN4065. I didn't modify it at all.

However, now when I try to write a word into Address 0x08000000, I get a flash error...

FLASH_ERROR_PROGRAM. I have successfully erased the flash so I'm not sure why I can't write to this location.

I've traced it to the following where the flash programming error flag is set

      if((FLASH->SR & (uint32_t)(FLASH_SR_PGERR)) != (uint32_t)0x00)

      {

        FLASHstatus = FLASH_ERROR_PROGRAM;

      }

I read at the following that it was caused by ROM being mapped over the page that was being flashed.

http://www.keil.com/forum/61044/stm32f030-read-write-flash-throwing-hardfault/

I know I am doing this as I'm trying to overwrite the original app, but is there any way around it.

Bob

Posted on August 15, 2016 at 00:47

Point is you are adding 4 to the variable via the pointer, you are not advancing the pointer by 16

If you don't want to get into a situation where the FLASH doesn't contain any executable code, then have a sector or two committed to be a loader and don't erase them, ever.

I would generally avoid stepping this kind of thing in the debugger. Instrument it, and check that the memory is blank, in-line

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