So I'm looking to bulletproof an application that will be updated in place against failed uploads. However, experience has shown, to my horror, that interrupted uploads sometimes leave the code functional enough that the bit that resets the WDT (which forces it back into the bootloader so the upload can be reattempted) keeps ticking!
Obviously, this defeats (part of) the point of using the WDT.
Is there a way to tell the compiler/linker to put a certain function at the end of the application code? That way, as long as the size of the code uploaded isn't the same as the previous version, the function to reset the WDT would be in a different place, and so the call to the function where I put the WDT reset won't work.
(While upload failures shouldn't happen, historically they occasionally have due to network conditions beyond our control, so I'd like to make sure we're resiliant against that)
Are you uploading through a bootloader?
You can put code in separate sections, and re-position the sections with a customer linker map or link command line; like optiboot does for the version number. I think that code references to an absolutely positioned code segment will still be resolved by the linker (although optiboot doesn't do that.)
It doesn't need to be absolutely positioned - we actually don't WANT it absolutely positioned, as it wouldn't solve the problem then.
The idea is that we want this function to be the last thing written, so if the whole thing isn't written, this function won't be there. An interrupted upload would still have the old application code in in the pages that hadn't been erased - but since the new and old firmware likely wouldn't have the same size, this function wouldn't be in the same location, so the calls to it would do something other than what they're supposed to, the WDT wouldn't be reset, and it would then reboot into the bootloader and we could try uploading to it again.
Where is the linker map used by the IDE for compiling for an AVR?
Note - at this point, I'm more interested in how this sort of thing is done, than whether it's actually a proper solution to this problem (I'm not sure it is, and I'm not sure the problem really still exists)
The correct way to handle failed uploads is to not run the application. The usual strategy is a two-phase commit and a CRC. If the upload has not been committed or the CRC is wrong the bootloader does not start the application; the bootloader just keeps waiting for an upload attempt.
I will look into this, thanks. That's is definitely a better way of handling it.
Original question still stands from curiosity perspective.
Original question still stands from curiosity perspective.
To some extent you can get away with locating things in the image by pinning them to an existing section. For example, putting a function in the ".fini1" section places it nearly at the end of the image. As long as main never returns nothing bad will happen (I've done that sort of thing. I can't remember why.) If returning from main is an issue, put halt-like code in .fini1 and your stuff in .fini0. The syntax is simple...
void my_shutdown_portb (void) __attribute__ ((section (".fini1")));
void my_shutdown_portb (void)
{
PORTB = 0xff;
DDRB = 0xff;
}
You do have to have an explicit prototype like I've shown. (The Arduino IDE auto-prototyping may cause trouble if you put the function in a sketch.)
As @westfw mentioned that basic idea can be tweaked and tuned to your delight if you are willing to modify the linker script / linker command-line.
Bear in mind that clean-up code (like global destructors) is put in .fini sections which are placed in order at the end of the image.
I will look into this, thanks. That's is definitely a better way of handling it.
You are welcome. If you have questions or want to discuss it just say so. It is not the first time I've been down the N-phase commit rabbit hole.
Thanks a lot! Cool stuff.