4
\$\begingroup\$

I am writing an ethernet capable bootloader for an AVR ATmega328 MCU. I am currently running over the 4k size limit and need some design advice.

Bootloader requirements:

  • Needs to be able to update the application via a simple UDP based messaging protocol (similar to TFTP).
  • As the device operates in a variety of LANs, it should support DHCP.

With my current code, I need about 4k for the Ethernet driver, DHCP, ARP and UDP support. Plus 1.5k for the actual messaging and app programming.

Compiler optimizations are turned on.

My current considerations to proceed are:

  1. The code is not particularily optimized, so I guess I could save some bytes by following AVR035. I doubt I can save enough code size going this route though.
  2. I could simply extend the bootloader area by reserving 2k from the top app memory area for the bootloader. I need to be extra careful then to not overwrite that area when programming though. And, I would end up with a large, complex bootloader which probably has bugs, which I cannot update in the field.
  3. I could move some functions from the bootloader to the app. DHCP could be a good candidate: The app obtains a IP address via DHCP and stores it into EEPROM. The bootloader then can simply use the stored IP address and does not need to do DHCP itself. This could be dangerous though if the app fails e.g. by software bug or random corruption.

Which route would you suggest? Are there other ideas I should consider?

\$\endgroup\$
  • 1
    \$\begingroup\$ The 4K is the maximum size you can configure the bootloader section to (NRWW section) on the Atmega328. I understand it is technically possible to have a bootloader larger that size. You need to make extra considerations though as I outlined in option 2. \$\endgroup\$ – henning77 Apr 27 '12 at 12:32
  • 1
    \$\begingroup\$ As for the ethernet chip, I am using an ENC28J60. \$\endgroup\$ – henning77 Apr 27 '12 at 12:33
  • 2
    \$\begingroup\$ @henning77 - Without access to your current bootloader code, it's awful hard to guess at where you can reduce the code size. If you post your code in a Github repository or something, I'd put a bounty on the question to encourage people to download the code and give it a try. \$\endgroup\$ – Kevin Vermeer May 1 '12 at 20:27
  • 1
    \$\begingroup\$ Have you considered implementing something simpler, without the need to hardcode? For instance, you could have it speak an ethernet-based protocol directly, instead of using UDP, or you could implement an even simpler system than DHCP to assign it an IP. \$\endgroup\$ – Nick Johnson May 14 '12 at 3:07
  • 1
    \$\begingroup\$ @henning77 Routing raw ethernet packets is all switches do - UDP and TCP packets are encapsulated and sent in ethernet frames. As long as your host and client are on the same subnet, it should work fine. \$\endgroup\$ – Nick Johnson May 14 '12 at 23:39
3
\$\begingroup\$

I managed to reduce the size of the bootloader to around 3k. Here is what I did, maybe it helps someone with a similar task:

I removed DHCP and ARP support from the bootloader. I make the assumption that I can always reach the device via UDP broadcast, so I don't need it to obtain an IP address when running the bootloader.

I also did a lot of code tweaking, which helped reduce the size some more. The removal of DHCP/ARP was the biggest chunk, however.

\$\endgroup\$
1
\$\begingroup\$

One approach that might sometimes be useful (even not for the original asker, for others who need tiny boot loaders) is to have a two-part bootloader. One part would be in "permanent" memory, and would basically just have enough smarts to send a broadcast packet, listen for a reply, and throw a suitable-looking received reply into memory and execute it. Otherwise, it would perform a checksum on the second part, executing it if it looks valid. The second part would be in writable storage and could contain the logic for ARP and other such things. That part could in turn be used to perform application updates.

Note that the second part may not fit entirely within a 1.5K Ethernet packet, but there should be no difficulty fitting enough logic within a 1.5K Ethernet packet to load a few more packets worth of boot-loader code (especially since the hardware will already be set up for that purpose). The Apple II uses this type of approach to load from floppy. A 256-byte PROM sets up a 128-byte table to decode 7-bit GCR data into 4-bit nybbles, advances the head mechanism outward a bunch of times, looks for a sector-zero header, and then reads 256 bytes of data to address $0800. The code there is large enough to read the next 15 sectors from the same track (since the table it requires has already been produced), and the code within those 15 sectors can finish loading the OS.

Setting up a boot loader to work this way may be somewhat less convenient than having a "load everything in one step" loader, but it offers the advantage of allowing the unchangeable part of the boot loader to be very tiny.

\$\endgroup\$
1
\$\begingroup\$

I made a bootloader for the atmega328 that is updated via ethernet using TFTP. I have DHCP in the application space and the results go into eeprom. The bootloader reads the eeprom, so the bootloader effectively has DHCP as long as the application runs at least one time in the network.

\$\endgroup\$
0
\$\begingroup\$

Without seeing your code, or at least the layout of some of it, it is hard to say what you can change and omit/recycle.

Have you tried using different optimization's? On a 4k program I think I have seen variations of almost 1k by just changing the optimization used, (this was a while back, it may not have been that dramatic.) Although I doubt that would get you the ~33% reduction you would need. I would try to write or find some asm examples that may be smaller than what to compiler would make and use them in place of some of your code then recompile and see how much of a difference that makes.

You could also compile, step through the asm, pull out sections you like swap that in your code, then change the optimization, compile and repeat. But this would be tedious!

Your best bet is probably extend the bootloader area and make sure the bootloader won't overwrite this when it is being programmed.

\$\endgroup\$
  • \$\begingroup\$ My personal optimization tip for avr-gcc is: Always use -Os, and compile the whole program from source in one gcc call with -fwhole-program. That permits it to remove unused code even if not static or separate compilation units, inline anything used in just one place, etc. \$\endgroup\$ – Yann Vernier Aug 30 '12 at 12:14

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.