We are running a survey to help us improve the experience for all of our members. If you see the survey appear, please take the time to tell us about your experience if you can.
I've never programmed a bootloader before, but need have one that does firmware reflashing over UART. Shouldn't be that bad, but I do need some pointers...
There are 7 questions below, but they all should be very easy...
1. I want to start the code at 0, have a 4k bootloader and the real code past that. This is about all I know for sure. I do not PLAN on using interrupts in my bootloader and instead will make a simple polling machine for what it's going to do.
2. - The bootloader should check the a hash of userflash, if something is wrong with a previous update, I want to stay in the bootloader and wait for another firmware update over UART. This means I need a table somewhere with the correct hash of the user flash, the bootloader hash, and maybe some settings hash to make sure nothing was altered somehow. Is there a common way to lay this out? My plan was to just make the bootloader a hundred bytes short of 4k in size, then use that remaining size to store the hashes as a fixed location and format. Will be a pain for debugging I'm sure!!! But it's something I think I want to see for production. Issues with this?
3. I'm using UART for the physical connection. But since I won't be running RTX during the bootloader, I'll need a separate library for this low level communication and not the RTX UART code. I'll also need anything else the bootloader will use - BUT - Can I reuse these functions and libs in my user code? I saw an NXP AppNote that said you want to keep anything your user code needs outside of the bootloader because it'll span sectors otherwise, not sure if this is really an issue - or why they said that.
4. How do I lay this out in Keil!?! My thought was to do a multi-project one called Bootloader and another call Application, but I'm not sure how to get my Segger unit to flash the bootloader to 0x00 everytime and the flash project to 0x4000 or wherever. Is there a way I can have both open, flash both at the same time? Because...
5. I want to distribute the bootloader and userflash and maybe another package separately. I want the application to be able to update any of these separately. So I THINK I want the raw hex to be generated separately. Not sure how to do this in Keil.
6. On that note... It's obviously risky to update the bootloader. And I clearly don't want to run code while I'm also overwriting it. So... Is there a method where I'd take my WriteNewBootloader() and move it in to ram and execute there? Is it popular to have a bootloader backup in place that never gets overwritten, that way I know if bootloader hash fails I have a fully functioning backup in place I can switch over to?
7. On the ARM parts, Can I set my own Reset Reason/Code? My reasoning is that I see no reason to put an UpdateFlash() in the bootloader and again in the user flash while it's running normally. Instead, my idea was to reboot back to the bootloader - but I need some way of knowing the context. So that I know I just rebooted from normal mode back into bootloader to do a reflash - verus - this is a new boot or power cut out or whatever other reason I had to do a reset.
WOW! Excellent info. Thanks a ton!
Yea, #1 was supposed to talk about interrupt vectors for before and after the context. I've seen enough about that where I think I get the idea.
I don't want to get into a position where I release a firmware update that seems good in ram (CRC valid) but didn't transfer to flash correctly due to exceptional bug. The idea is bootloader verifies itself, the flash, and the NVROM/Settings, if everything is OK it transfers context to application - otherwise, error, and it hangs out and waits for UART connection to deliver a correct firmware. In this case I'll need a table of hashes or CRCs for my ROM sections, constant location, and updated on firmware upgrades. I like the idea of CRC because it's fast and built-in, but I think I'll need more than a 32bit result for security. This is what I was getting at with "lists" of hashes stored in specific locations. I'm still kinda wondering if I'm planning something with a much more elegant solution?
Good points about dependencies. I think I'll just use a basic UART and couple of libs for the bootloader, and use more/better/expanded versions for the real application. I like the idea of being able to at least for debug just flash the application to a fresh chip and let it roll. I'll be stacking duplicate code in some places. But you're right, I should keep these separate.
.
This part though.... Do it as two project, change the IROM scope to match the basis/size of the region in question, create a scatter file if you want to get clever. You could perhaps do it as a multi-project, consider if you, or people coming after you will want to deal with that, or split it later if the app forks into dozens of custom builds.
Explain? I can do this as two separate projects, then configure my JLink to erase whole chip, program bootloader, switch over to application project, use J-link to erase only that area (Can I do this?) then program the application in. Am I close on this? So basically, I'd write the bootloader once, but what about debugging? I'd have no context while the chip was in the bootloader section?
If I use a multi-project, what is the difference? I won't have many forks off this. And for the foreseeable future, I'm the only software engineer. But I'm not really comprehending the difference if I did. Would multi-project allow me to debug both the bootloader and the application in the same session? .
Good notes about hex file / fromelf, flashing while running, and keeping RAM between sessions. I suppose I could put a zero-initialized single byte somewhere protected that I call RESET_CODE or something.
8.</8> Is it possible for a bootloader to use X amount of ram at Y location and then when I switch to the application I can re-use that? I want to entirely (or partially) drop the context of the bootloader when I leave it. Not sure how to explain this to the compiler however. I suppose I could memalloc if I need something large in the bootloader then clear that when done?
With the CRC you don't need a separate table to store CRC values. Just store the CRC of the binary directly after the binary itself. But you would like to be able to know the size of the binary unless you always force the binary to the full size of the area where it is expected to be stored.
If can have two tarets in the same project. But you want zero source code reuse between them to avoid accidents. That's why it's best with two projects in differnt directories to make sure you don't get tempted to change the wrong file.
With two targets that doesn't share the same source, you will still not get debugging for both application and boot loader, since the debugger will load the symbols for the project you debug - and not the symbols for the other target in the project file.
The boot loader can use any amount of RAM. The application will not care - it will take whatever the project configuration said it should take.
So to send a message from application to boot loader, it's enough to reserve maybe 32 byte of RAM at top or bottom of RAM. But the boot loader itself can consume 32 kB RAM if it wants - RAM that gets reused by the application.
Most everything makes sense. I'm not entirely clear on the IROM1/2 and etc. How to set the Jlink up to program the secific sectors while also using code NoRead protection eventually. Or what happens when I switch context. Nor specifically how I'll make sure this is "safe" for the field (I'll be panicking if I ever push out a bootloader update! I'll have to look at the rom and see if I can have a backup-bootloader just in case)
I see on the separate projects. I'm not sure which code I'll re-use and which I won't.
Not being able to debug both in context is going to make designing the bootloader slightly difficult :\ Like I'm really not thrilled about that. There is no way to import all of the symbols to have out-of-project contextual debugging? Seems very limiting.
Thanks again!
The debugger can step through code whether you have source code or not. When you load the application in the debugger it will "run to main()" that by break pointing it, once the boot loader hands off control the app will stop in main() like it always does. I have a protocol to flag to the loader it's got a debugging image to work with and it expedites into the app.
Or you can debug the loader and walk through the transition to the app. You'll lose source code visibility, but assembler is usually adequate, and it's mainly going to be start up code setting up the arena or C run time stuff, and calling whatever platform initalization code you have in SystemInit().
The RAM is a clean sheet every time the code starts, whether it's in the loader or app. You can play games with the RAM allocations using IRAM basis/size or with a scatter file if you want too. The heap doesn't transfer, a fixed address block is a common way to pass a command line or structure between the two.
Not much for symbolic debuggers, if I couldn't figure out what my own code was doing I'd code a tool to merge two ELF/AXF files.
I suspect you could create a debugger script (.ini) which would LOAD two separate AXF files. I haven't had occasion to try it myself.
I normally debug boot loader separately from application.
When debugging the application, a debugger script can perform the same "hand-over" as the boot loader would do. This works well as long as the boot loader doesn't have errors where it leaves interrupts etc still active after the handover. In that case, the processor can jump to a random address after the boot loader or application switches interrupt vector table.
Any comments about how this looks in Keil? How to set this up?
Have a look at the commands supported by the debugger.
You can write own ini files to run when you start the debugging. These allows you to read/write memory addresses or to set register content.
This is a bit similar to how some sample projects has a RAM target, where the application isn't stored in flash but is instead stored in RAM - the processor itself doesn't know how to start a program in RAM.