Author Topic: How to add in-field firmware upgrade  (Read 5419 times)

0 Members and 1 Guest are viewing this topic.

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
How to add in-field firmware upgrade
« on: July 02, 2022, 08:46:05 am »
Hey all

So I was wondering how you guys handle in-field firmware upgrade for your MCUs?

I have a GD32 MCU which has USB Device, and ideally I would want the end-users to be able to simply connect to the product over USB and be able to run a program on their computers to upgrade the firmware.

Unfortunately my MCU's bootloader only supports upgrade over UART and not USB. So what to do and how?

Thanks
Simon
 

Offline GromBeestje

  • Frequent Contributor
  • **
  • Posts: 279
  • Country: nl
Re: How to add in-field firmware upgrade
« Reply #1 on: July 02, 2022, 09:14:28 am »
There are various project on github that might be usefull

https://github.com/devanlai/dapboot
https://github.com/IntergatedCircuits/DfuBootloader
https://github.com/trueserve/stm32f103-bootloader

If you are running a GD32F103, a project targetting the  STM32F103 might work out of the box,
otherwise some adjustments might be needed.

In any case, I'd recommend going with something that implements the USB DFU standard as it allows for using existing tools to perform the update.
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #2 on: July 02, 2022, 09:54:59 am »
That's just what I needed, thank you!

But so the DFU code should be there together with the rest then?
 

Offline GromBeestje

  • Frequent Contributor
  • **
  • Posts: 279
  • Country: nl
Re: How to add in-field firmware upgrade
« Reply #3 on: July 02, 2022, 10:37:34 am »
Yes, it will be below the firmware like this   (The size of 8K in these examples is taken from dapboot)
Code: [Select]
[ Firmware     ]  [0x08002000]
[ DFU          ]  [0x08000000]
You will have to adjust the linker file of you firmware to move to make space for the bootloader.

When your current linker file is like
Code: [Select]
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 128K
RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 20K
}
you will have to adjust like
Code: [Select]
MEMORY
{
FLASH (rx)      : ORIGIN = 0x08002000, LENGTH = 120K
RAM (xrw)       : ORIGIN = 0x20000000, LENGTH = 20K
}

 
The following users thanked this post: Saimoun

Offline dkonigs

  • Regular Contributor
  • *
  • Posts: 107
  • Country: us
Re: How to add in-field firmware upgrade
« Reply #4 on: July 12, 2022, 01:49:08 am »
One of the bigger problems with using a DFU bootloader is that there don't seem to be any good user-friendly applications out there to implement DFU from the host computer side.  Something like dfu-util works pretty well for a technical user, but you'll need to write some sort of foolproof application to wrap it if you expect normal end users to install updates.

This is why for my project, despite my MCU having DFU support built in, I actually threw together a UF2 bootloader.  The advantage of UF2 is that your device basically just shows up as a USB mass storage device, and the user just has to drag-and-drop a file into it. That's all it takes. Very easy.

I used the TinyUF2 project as a basis for my bootloader:
https://github.com/adafruit/tinyuf2

(However, I did a fair bit of work on top of that to better integrate with my platform and to provide the experience I wanted.)

So I'm using the built-in DFU support as a way of installing my bootloader (or recovering from corrupted flash situations), then my bootloader uses UF2 to install the real device firmware.
 
The following users thanked this post: Someone, Saimoun, tellurium

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #5 on: July 12, 2022, 05:05:23 am »
Usually, writing to FLASH is not rocket science; just writing a few magic register values to enable access, trigger erase cycle, and then writing to memory.

Thus, if you can write your actual firmware which communicates with user and does things, you definitely can write the flasher; as a part of the application, or as a separate bootloader project. The upside is, you can fully define the protocol as you wish, using any interface.

As always, I will encourage you to open up the reference manual and do it yourself.

If you write flash as part of the application, the only "weird" thing you need to remember is to make sure the flasher code (all of it; including compiler-generated memset/memcpy calls etc.) must be in RAM (or in another flash sector you are not erasing), so that you don't lose your flasher code while it is running. This will trigger the learning process of how to put code in RAM, which will be a valuable lesson in any case.
 
The following users thanked this post: Saimoun

Offline Peabody

  • Super Contributor
  • ***
  • Posts: 1993
  • Country: us
Re: How to add in-field firmware upgrade
« Reply #6 on: July 12, 2022, 02:27:50 pm »
You could add a USB-to-UART adapter and a USB port to your board.  Maybe the CP2102N.  That would give you the UART input.  But of course you would still need software for the computer to do the uploading.
« Last Edit: July 12, 2022, 02:32:40 pm by Peabody »
 
The following users thanked this post: Saimoun

Offline tellurium

  • Regular Contributor
  • *
  • Posts: 226
  • Country: ua
Re: How to add in-field firmware upgrade
« Reply #7 on: July 12, 2022, 04:42:22 pm »
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #8 on: July 13, 2022, 08:51:21 am »
Thank you all for the replies  ;D

I have indeed given some thought about it since, and I think I am going to do it like @Siwastaja suggested - my product already has a USB-MIDI interface so actually I can save time in USB-dev time and simply get MIDI packages (called "Sysex" where you can basically send whatever) and write these to the flash. Because learning to implement USB DFU correctky will for sure take a lot longer than learning to write to flash.

And actually Siwastaja I was literally thinking about what you mentioned - if I want to be able to write the whole flash I need to copy my code in RAM. How does that work? I imagine something like this:
  • User selects "upgrade mode"
  • The code finds a suitable area of the RAM (I was thinking right after the last global variable, as far away from the stack as possible)
  • Code (or DMA) copies the whole USB code there (the hard part here will be to make sure I know what to copy...)
  • Then I simply jump to the correct address in RAM - and that's it? (nothing special to do other than jumping?)
 

Offline tellurium

  • Regular Contributor
  • *
  • Posts: 226
  • Country: ua
Re: How to add in-field firmware upgrade
« Reply #9 on: July 13, 2022, 09:12:23 am »
  • Code (or DMA) copies the whole USB code there (the hard part here will be to make sure I know what to copy...)
  • Then I simply jump to the correct address in RAM - and that's it? (nothing special to do other than jumping?)

It's not that easy. Your code uses variables and calls functions. When it gets compiled, a compiler creates object files where those places - variables usage and function calls, marked as "placeholders", like  "here I am calling function X". You can see those by running "nm my_object_file.o". Then a linker combines (links) all those object files together, it "resolves" those references by assigning addresses to those placeholders. All functions are put in the "code" region, and data is put in the "data" region. Where those regions are, is defined in a linker script. The "code" region usually resides in flash, and "data" region in RAM. Since everything, a code and data of your firmware, is kept on flash, then a startup code upon boot copies the data part to RAM.

Now, your code calls other functions that reside in the code region - that means, their addresses are in the code region. Once you copy a part of your code to RAM, the very first function call would bring control back to the code region (flash). Another issue is that code/data references may be compiled using a distance from code's current position, i.e. using a relative offset from the PC register in which the very first call/variable usage would just crash the relocated code.
« Last Edit: July 13, 2022, 09:19:21 am by tellurium »
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #10 on: July 13, 2022, 09:42:52 am »
I'm aware of how an LD script and compiling works  :)  I did not think about the function calls though  :palm:

Actually the relative calls (i.e. PC+xxx) are great because these will work when I copy everything. But looking at the assembly listings (for the final linked file) all function calls seem to be absolute.

So how to solve this? I guess the only way is to write a special bootloader function which is independent, not calling anything else, which I can copy to RAM? But that's a lot of duplicated code (the whole USB stack :/ ).
 

Offline tellurium

  • Regular Contributor
  • *
  • Posts: 226
  • Country: ua
Re: How to add in-field firmware upgrade
« Reply #11 on: July 13, 2022, 09:47:57 am »
So how to solve this? I guess the only way is to write a special bootloader function which is independent, not calling anything else, which I can copy to RAM? But that's a lot of duplicated code (the whole USB stack :/ ).

Compiling to a position-independent code might help. I've never done that myself though. Maybe some extra steps would be required, like relocating ELF offset table manually.
Open source embedded network library https://mongoose.ws
TCP/IP stack + TLS1.3 + HTTP/WebSocket/MQTT in a single file
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #12 on: July 13, 2022, 10:10:13 am »
I have just used linker script to make a section that goes to RAM (ITCM if available for high-performance functions throughout the program; or just normal RAM), and then just use the section attribute in every function called by the flasher code, including shared communication code.

Something like this:
Code: [Select]
    _TEXT_ITCM_I_BEGIN = LOADADDR(.text_itcm);
    .text_itcm :
    {
        _TEXT_ITCM_BEGIN = .;
        *(.text_itcm)
_TEXT_ITCM_END = .;
    } >ram_itcm AT>rom

Then startup code which does the copy:
Code: [Select]
extern unsigned int _TEXT_ITCM_BEGIN;
extern unsigned int _TEXT_ITCM_END;
extern unsigned int _TEXT_ITCM_I_BEGIN;

uint32_t* text_itcm_begin  = (uint32_t*)&_TEXT_ITCM_BEGIN;
uint32_t* text_itcm_end    = (uint32_t*)&_TEXT_ITCM_END;
uint32_t* text_itcm_i_begin = (uint32_t*)&_TEXT_ITCM_I_BEGIN;

while(text_itcm_begin < text_itcm_end)
{
*text_itcm_begin = *text_itcm_i_begin;
text_itcm_begin++;
text_itcm_i_begin++;
}

LD linker script command NOCROSSREFS is useful as it errors out on any function call from the ram section to flash section, including those nasty implicit memset/memcpy calls the compiler sometimes generates.
 
The following users thanked this post: Saimoun

Offline alexanderbrevig

  • Frequent Contributor
  • **
  • Posts: 700
  • Country: no
  • Musician, developer and EE hobbyist
    • alexanderbrevig.com
Re: How to add in-field firmware upgrade
« Reply #13 on: July 13, 2022, 10:25:57 am »
SysEx is slow. 128k will - best case - be about 16 seconds if you send one byte per message. 13 seconds if you bit pack all 10 bits. I would say don't reinvent the wheel if you do not absolutely depend on it.
As an owner of gear that uses SysEx DFU, I would strongly suggest you do not go that route.

Good luck! :)
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #14 on: July 13, 2022, 10:35:10 am »
I have just used linker script to make a section that goes to RAM (ITCM if available for high-performance functions throughout the program; or just normal RAM), and then just use the section attribute in every function called by the flasher code, including shared communication code.
ITCM - what does that mean?

But ok I think I get it, the fact that you tell the linker that this section is in RAM means the calls will be to the RAM space (so branch to 0x2000xxxx instead of 0x0800xxxx), even though the actual code section will be placed somewhere in the flash - correct? Is that done by the "AT>rom" at the end?

The copying code makes perfect sense.

My problem with this is still the fact that I need a complete separate boatloader code which has to include the whole USB stack, while I was hoping reusing most of the already running code.

Just thinking, maybe I can make it hybrid, i.e. putting the USB stack at the beginning of the flash (say till 0x08000FFF - first 4kB), and only erasing from 0x08001000. That way I'll be able to use the stack in flash and only need a little code in the RAM. Problem is then is the USB stack code cannot be upgraded.  ::)
 

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #15 on: July 13, 2022, 10:37:27 am »
SysEx is slow. 128k will - best case - be about 16 seconds if you send one byte per message. 13 seconds if you bit pack all 10 bits. I would say don't reinvent the wheel if you do not absolutely depend on it.
As an owner of gear that uses SysEx DFU, I would strongly suggest you do not go that route.
Ha ha thanks but how is that slow? My flash is only 128k, and the users will need an upgrade every... 6 months at most? They can wait for 20 seconds I think  ;D
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #16 on: July 13, 2022, 10:58:57 am »
ITCM - what does that mean?

Instruction tightly coupled memory - it's optional fast RAM in the ARM core, so you can place code there so that code execution doesn't need to use the main SRAM bus.

Quote
But ok I think I get it, the fact that you tell the linker that this section is in RAM means the calls will be to the RAM space (so branch to 0x2000xxxx instead of 0x0800xxxx), even though the actual code section will be placed somewhere in the flash - correct? Is that done by the "AT>rom" at the end?

The AT> syntax means the original location - i.e., the flash. > syntax tells the location where the things will be relocated.

Syntax wise, it's exactly the same story as with initialized variables - the content (initialized values) are in the flash (AT>), but after the initial relocation copy, the code will access the > address.

I recommend you look at some example linker scripts and documentation of GNU LD.

But hey, I missed you use a GD32 MCU. I haven't used them, but big kids on the streets talked about these things shadowing the whole flash into RAM at boot and run out of RAM. If this is really the case, then you would not need to worry at all, just write code that does whatever with the flash.

If you want safe end-user firmware update procedure with least risk of brickage, you should have a separate bootloader (with full communication stack) which can always write the application sector in case something goes wrong.
 
The following users thanked this post: Saimoun

Offline SaimounTopic starter

  • Frequent Contributor
  • **
  • Posts: 548
  • Country: dk
Re: How to add in-field firmware upgrade
« Reply #17 on: July 13, 2022, 11:19:20 am »
Thanks for the explanation - that makes sense.

But hey, I missed you use a GD32 MCU. I haven't used them, but big kids on the streets talked about these things shadowing the whole flash into RAM at boot and run out of RAM. If this is really the case, then you would not need to worry at all, just write code that does whatever with the flash.
This sounds crazy - never heard about this... You mean the MCU itself would do that? I've spent a lot of time going through many GD32 Manuals, never saw something like this.
How would that even work since the RAM is much smaller (especially if your variables already use 90% of it)?


If you want safe end-user firmware update procedure with least risk of brickage, you should have a separate bootloader (with full communication stack) which can always write the application sector in case something goes wrong.
Fully agree with this. But then the bootloader will be in flash, never overwritten, correct? (i.e. what I said about only erasing from 0x08001000 f.x.)
« Last Edit: July 13, 2022, 11:24:47 am by simonlasnier »
 

Offline pqass

  • Frequent Contributor
  • **
  • Posts: 725
  • Country: ca
Re: How to add in-field firmware upgrade
« Reply #18 on: July 13, 2022, 12:27:43 pm »
So I was wondering how you guys handle in-field firmware upgrade for your MCUs?

If you can squeeze an spi flash onto your board, it can be used to hold one or more copies of a firmware update; keeping the bootloader code simple.  Your application handles the user interaction and complex stacks (TCP/IP, USB, FAT, etc.) and just copies the new firmware to this spi flash.  On reset, the bootloader checks if the current firmware version matches the spi flash and if they differ does a main flash update.  No dedicated update software external to your board is needed.

Mike explains it here:
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #19 on: July 13, 2022, 12:59:21 pm »
If you can squeeze an spi flash onto your board, it can be used to hold one or more copies of a firmware update; keeping the bootloader code simple.  Your application handles the user interaction and complex stacks (TCP/IP, USB, FAT, etc.) and just copies the new firmware to this spi flash.  On reset, the bootloader checks if the current firmware version matches the spi flash and if they differ does a main flash update.  No dedicated update software external to your board is needed.

What a weird idea, usually you do this with multiple FLASH pages/sectors - external chip offers no benefit. But of course there are microcontrollers (for example the STM32H750 "budget model" of H743) on the market with only one page/sector, and in those cases such board-level workaround might be still favorable over just using a suitable microcontroller.

Even then, the external chip won't provide full safety against power-loss during erase-write cycle.

But most MCUs do have flash sectioned into multiple pages because this is a very common requirement.
« Last Edit: July 13, 2022, 01:01:33 pm by Siwastaja »
 

Offline pqass

  • Frequent Contributor
  • **
  • Posts: 725
  • Country: ca
Re: How to add in-field firmware upgrade
« Reply #20 on: July 13, 2022, 02:15:22 pm »
If you can squeeze an spi flash onto your board, it can be used to hold one or more copies of a firmware update; keeping the bootloader code simple.  Your application handles the user interaction and complex stacks (TCP/IP, USB, FAT, etc.) and just copies the new firmware to this spi flash.  On reset, the bootloader checks if the current firmware version matches the spi flash and if they differ does a main flash update.  No dedicated update software external to your board is needed.

What a weird idea, usually you do this with multiple FLASH pages/sectors - external chip offers no benefit. But of course there are microcontrollers (for example the STM32H750 "budget model" of H743) on the market with only one page/sector, and in those cases such board-level workaround might be still favorable over just using a suitable microcontroller.

Even then, the external chip won't provide full safety against power-loss during erase-write cycle.

But most MCUs do have flash sectioned into multiple pages because this is a very common requirement.

Weird?  It does have a cost advantage. 
WRT STM32F103, going from 128KB to 512MB is a CA$7 difference.  Whereas, a 512KB (4Mb) spi flash is CA$0.64 (See Digikey: W25X40CLSNIG)

Safety from power loss is accomplished by the bootloader checking if the newest firmware in spi flash has a valid checksum (at a known offset in the binary) and if not, skipping it.  Once in the app again, the user downloads and updates the spi flash and reboots to try to apply the update again.  The trick is to make sure there is at least one valid version in sp flash.  The bootloader can also check the MCU flash checksum and force a replace if that got corrupted.  The bootloader itself never needs updating.
« Last Edit: July 13, 2022, 02:22:49 pm by pqass »
 

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #21 on: July 13, 2022, 02:42:11 pm »
Weird?  It does have a cost advantage. 
WRT STM32F103, going from 128KB to 512MB is a CA$7 difference.  Whereas, a 512KB (4Mb) spi flash is CA$0.64 (See Digikey: W25X40CLSNIG)

OK, we are getting confused by mixing two totally orthogonal concepts here.

STM32F103 with 128KB of flash divides the internal flash into 512 pages (if I read the reference manual correctly - so many parts; in any case, all have way more than 3 pages!)

Hence, external flash chip does not offer anything regarding bootloader / firmware updating functionality which already isn't possible with the internal flash. You totally can erase and write only part of the flash within the bootloader.

But now you are talking about not having enough FLASH - then obviously it becomes a cost/availability optimization between a MCU with too little memory + external expansion, vs. larger MCU with more memory. This is not weird.

I find it quite rare to use a budget tier STM32F1 class device and run out of 128KB, even if you divide that to two copies for firmware switching. What makes the application so large? Sometimes it is bloat or poor design - and if this is the case, getting it down to more manageable size would enable ultimate cost reduction by being able to use a smaller microcontroller, without the complexity of adding external memory.
« Last Edit: July 13, 2022, 02:45:37 pm by Siwastaja »
 

Offline pqass

  • Frequent Contributor
  • **
  • Posts: 725
  • Country: ca
Re: How to add in-field firmware upgrade
« Reply #22 on: July 13, 2022, 04:06:42 pm »
@Siwastaja

Yes I'm aware that the user app can erase/update just part of its program flash.  And therefore, one can apply the same technique I'd described soley with internal flash.   However, I didn't see the technique described in earlier posts so I wanted to be more explicit (via the video and my description).

Also, desire to keep the BOM to a minimum is understandable but even so, there's a CA$3 difference going to 256KB. It's OPs call.


« Last Edit: July 13, 2022, 05:10:30 pm by pqass »
 

Offline ejeffrey

  • Super Contributor
  • ***
  • Posts: 3713
  • Country: us
Re: How to add in-field firmware upgrade
« Reply #23 on: July 13, 2022, 04:56:27 pm »
Fully agree with this. But then the bootloader will be in flash, never overwritten, correct? (i.e. what I said about only erasing from 0x08001000 f.x.)

That is how I prefer to do it.  It's possible to make the bootloader updatable but ideally that should not be necessary.  It's much easier to avoid bricking if the bootloader is never overwritten.
 
The following users thanked this post: Siwastaja

Offline Siwastaja

  • Super Contributor
  • ***
  • Posts: 8168
  • Country: fi
Re: How to add in-field firmware upgrade
« Reply #24 on: July 13, 2022, 05:22:02 pm »
Fully agree with this. But then the bootloader will be in flash, never overwritten, correct? (i.e. what I said about only erasing from 0x08001000 f.x.)

That is how I prefer to do it.  It's possible to make the bootloader updatable but ideally that should not be necessary.  It's much easier to avoid bricking if the bootloader is never overwritten.

Of course that is the way to do it.

In a few projects, I have been pushing my luck by doing in-application flashers with no separate bootloader and no double firmware functionality. Of course I receive the whole new firmware in RAM (also a reason to make it small), do CRC check, and only after successful verification, do quick erase-write step, which cannot fail, and does not need any further communication. This only leaves around 0.5 second of critical time during which power loss bricks the thing. Coupled with the instruction of "do not power off", we have seen zero complaints on returns, over maybe ~100 units.

But latest project to utilize such scheme did use STM32H750 with a single(!) flash page anyway, so there is no safe way to do it at all.
 


Share me

Digg  Facebook  SlashDot  Delicious  Technorati  Twitter  Google  Yahoo
Smf