UART bootloader with checksum capability (project attached)

Tip / Sign in to post questions, reply, level up, and achieve exciting badges. Know more

cross mob
lock attach
Attachments are accessible only for community members.
User14604
Level 4
Level 4
First solution authored
Hello!

I guess a lot of people wrote their own bootloaders for the XMC4x00. Attached you'll find a DAVE 4 project of an UART bootloader which parses Intel .HEX files and transfers it to the application flash.

specs:
- no DAVE CE, just plain code
- parses Intel .hex file and flashes it to application flash
- for XMC4300, but will also run on XMC4800 with small changes (already ran it there)
- supports custom .hex record with checksum (optional)
- UART on P0.0 (RX) and P0.1 (TX), USIC 1, ch. 1
- baudrates supported from 115200 to 1200000 baud
- incoming data is buffered into RAM during flash operation to allow sending the .hex file in burst mode
- can be controlled by simple terminal program, if UART-to-USB chip is used (FTDI/Microchip and others provide those chips)
- bootloader is triggered by pin 15.3: high -> bootloader, low -> application (if it exists)
- bootloader requires application to include ABM 1 header
- optional bootloader-to-application bridge may be integrated into application to call specific PCB initialization code when switching to bootloader

The code is documented (please read header file comments first). If you find bugs or have questions/recommendations, please reply here.

Make sure to relocate your application to the 2nd flash page (starting at 0x4000) and include an ABM 1 header, because that's what the loader is looking for. If you need an example for that, leave a reply.

Best regards,
Ernie T.
0 Likes
5 Replies
User15148
Level 1
Level 1
Thanks for the well documented example.

But what's the advantage of the code running completely in RAM?
That it can update itself?

and why is
#if defined(DEBUG_BUILD)
void BOOTLOAD_startApp();
#endif
?

I didn't get how the Bootloader would then start the actual application through the ABM.
(well, at least I can't locate the function in the code.)

Is there any reason you used ABM1 for the Application and not ABM0?
0 Likes
User14604
Level 4
Level 4
First solution authored
mprt wrote:

But what's the advantage of the code running completely in RAM?
That it can update itself?


Not sure why I ran it from RAM. I was used to do so when working with early Cortex chips, because they did not allow to access the flash while flashing. The XMC series has some nice flash cache, which probably allows fetching a few more instructions from flash before crashing too. But again, I'm not sure about it. Guess it does not make much difference performance wise whether you use RAM or ROM.

Edit: The bootloader currently cannot update itself. I guess this could be made possible, because the XMC device has plenty of RAM to store the received data. Makes it easy to check data integrity before doing the actual flashing. But if flashing fails, there's no way going back, because the start-up code needs to be within first flash page.

mprt wrote:

and why is
#if defined(DEBUG_BUILD)
void BOOTLOAD_startApp();
#endif
?

Seems this some code part from an earlier version, which can be deleted. I had problems running the application in debug mode for several reasons.

mprt wrote:

I didn't get how the Bootloader would then start the actual application through the ABM.
(well, at least I can't locate the function in the code.)

The actual start-up of the application is placed in xmc/system.c in SYSTEM_startAppIfRequested(). This method is called from start-up code and decideds whether to start the app or the bootloader branch. The actual loading of the app is performed within the last block of code in this method:


/* jump to application */
SCU_RESET->RSTCLR = 1 << SCU_RESET_RSTCLR_RSCLR_Pos; /* clear latest reset reason (otherwise debugger will fail) */
SCU_GENERAL->STCON = BOOTLOAD_START_APP << SCU_GENERAL_STCON_SWCON_Pos; /* set boot mode to use Alternate Boot Mode 1 (=application) */

__DSB(); /* ensure completion of outstanding memory accesses before reset */
SCB->AIRCR = 0x5FAU << SCB_AIRCR_VECTKEY_Pos
| 1U << SCB_AIRCR_SYSRESETREQ_Pos;
__DSB();
for(;;) { /* wait until reset */
__asm volatile ("nop");
}



mprt wrote:

Is there any reason you used ABM1 for the Application and not ABM0?


ABM0 is placed earlier in flash than ABM1. Using ABM1 allows to me have a bigger contiguous area in flash for actual code. Saves me from wasting my time on scatter files.
0 Likes
User14604
Level 4
Level 4
First solution authored
About running from RAM:
XMC does not support fetching code from flash while doing erase and/or flash operations. After you uploaded the content and commanded the flash operation to start, the controller cannot fetch the next instructions placed in flash, because it is busy. As said before, the flash has a cache which may help you advancing a few steps in code, if the next few instructions are already cached. If not, your code execution is screwed. (Result may be a bus error or any other hardware interrupt.)

If you decide to not actively wait for the flash operation to finish (as in the example code), you're better off running everything from RAM.
Please read this thread: https://www.infineonforums.com/threads/1854-Bootloader-running-from-Flash
0 Likes
User15148
Level 1
Level 1
Thanks a lot for your detailed answers. that helped me a lot.

There's one more thing I've been wondering about: Where and how do you write the ABM Header?

I'm not sure If I understood this right, but it seems a bit counterintuitive:
If you use ABM1 to start your application, and you update the firmware (i.e. write data starting from 0x0C02000), you also have to modify data that is actually in the BSL region (ABM1 Header).
So I somehow have to make sure my BSL only takes up 112kB space, because when I update the application, I have to update the CRC32 in the ABM1 Header and therefore have to erase the whole last 16Kb logical sector of the BSL Area.

Maybe you can tell me if there's an error in my logic...
0 Likes
lock attach
Attachments are accessible only for community members.
User14604
Level 4
Level 4
First solution authored
Hello mprt,

sorry for the late reply.

The bootloader runs without the actual ABM1 content. On bootloader start-up, it only checks whether there is a valid ABM1 header or not. If there's none, bootloader will wait for .hex file (received on UART).

The ABM1 header is part of the application itself. So yes, I do overwrite the ABM part everytime I upload the application. This is done that way because it eases the uploading/flashing process of the application. Please see documentation in bridge.c.
The BSL section is only 16 kB. This is more than enough to put lots of bootloader stuff into it. We currently use ~1.5 kB of it.
The application has two sections in flash, first one with 240 kB and 2nd one with 128 kB. The gap between those two app sections is the ABM1.

ABM headers contain two CRC32 fields:
1. The first one (headerCrc) is used to validate the ABM header itself. If you plan to relocate the application to a fixed address in flash, this CRC can be easily calculated manually (see bridge.c for example and CRC website link).
2. The second CRC (appCrc) contains the checksum of the whole application flash. Unfortunately that includes the ABM header itself, which makes it impossible to calculate it. (At least my very basic math skills don't provide me a solution to that problem.) That's why I added a checksum to the .hex file which spans the flash programmed by the app and can be easily calculated. The Infoneon guys knew about the drawback of their CRC, so they allowed you to disable this CRC calculation.


__attribute__((section(".bridge"))) const struct BRIDGE_Data BRIDGE_data = {
.magicKey = 0xA5C3E10F,
.appStartAddr = 0x08004000,
.appLength = 0xFFFFFFFF, /* set -1 to disable application flash CRC test */
.appCrc = 0xFFFFFFFF, /* set -1 to disable application flash CRC test */
.headerCrc = 0xA4D18C28,
};


The attached archive contains three essential files (all with comments):
bridge.c contains the ABM header which has been extended by some additional data. (Remember: Only 20 of 32 bytes of the ABM header are actually used, so you are free to use the remaining 12 bytes for your own purposes.)
linker_script_release.ld contains the linker script for the relocated application (starting at 0x0C004000 in flash). It also has a special section for the ABM header (which is called "bridge", because it contains additional information). The application flash is divided into two sections (APP1 and APP2) by the ABM/BRIDGE. If your application has a lot of resources to place in flash, it may be a good idea to manually put some of them into APP2 section, in case APP1 is not sufficient.

FLASH_1_BOOT_uncached(RX) : ORIGIN = 0x0C000000, LENGTH = 0x04000 /* Uncached mapping of bootloader flash. (16 kB) */
FLASH_1_APP1_uncached(RX) : ORIGIN = 0x0C004000, LENGTH = 0x1bfe0 /* Uncached mapping of application flash. (240 kB - 32 byte) */
FLASH_1_BRIDGE_uncached(RX) : ORIGIN = 0x0C01FFE0, LENGTH = 0x00020 /* Uncached mapping of bridge. (32 byte) */
FLASH_1_APP2_uncached(RX) : ORIGIN = 0x0C020000, LENGTH = 0x20000 /* Uncached mapping of application flash 2. (128 kB) */

linker_script_debug.ld is for debugging the software. It places the app at the beginning of the flash section (0x0C000000) otherwise your debugging session will not start.
0 Likes