8-bit AVRs and similar microcontrollers are not OK anymore

From rdiez's Personal Wiki
Jump to navigationJump to search


8-bit CPUs are obsolete and should not be used anymore in most situations.

Unless you manufacture hundreds of thousands of devices, sooner or later you will realise that the software investments by far outweigh the hardware costs. The constraints imposed by 8-bit architectures will make development harder, and speed and space issues will probably force you to write more code in assembly language than otherwise necessary. Furthermore, the tight architectural limitations will probably shorten the product lifetime or hold desired features back in the future.

If you finally decide to migrate to a 32-bit CPU, you will find that sharing code between 8-, 16- and 32-bit platforms is difficult, even without the memory address issues described below. Therefore, you will probably have to leave the existing 8-bit software aside and create a separate branch for the new processor.

CPU architectures with different address spaces are especially nasty

Many 8-bit CPUs, like the Atmel AVR microcontrollers, are not actually compatible with the C/C++ language. The C standard requires a flat memory space, but AVRs provide two of them: program memory and data memory. Therefore, a given memory address like 0x0000 is ambiguous, as it could refer to the first code word in program space (usually flash memory) or to the first data word in data space (usually SRAM). This is what the datasheets mean when they say that the AVRs have a Harvard architecture, although the architecture itself has actually nothing to do with the ambiguous addressing issues.

An AVR memory address needs to be qualified at C language level in order to disambiguate it. This is what PROGMEM, PGM_P, PSTR and the like do in the 8-bit AVR world, and that is why there are 2 or even 3 versions of many C runtime library routines, such as strlen(), strlen_P() and strlen_PF().

Other CPU architectures have even more memory spaces, there are DSPs out there with X, Y and Z address spaces. Avoid such CPUs like the plague! With such architectures, you cannot copy C/C++ source off the Internet and use it directly on your CPU, you need to carefully inspect all the code and add the memory space qualifiers to the pointers that need it. You may need to call strlen() in one place, and strlen_PF() in another one. If you miss one of those, you may not notice straight away, but your program may crash later on. And you will not be able to easily share the code with other CPUs, because those tricks are usually platform specific. Nowadays, this is just not worth the trouble.

No memory protection at all

Many microcontrollers have no memory protection whatsoever, not even the most basic tricks. It would be really easy to leave the first and last 4 KBytes of memory space unused, and trigger some sort of access violation if somebody tries to access them. That would catch most NULL pointer errors in C/C++, which would help prevent many nasty bugs and save a great deal of debugging time. Furthermore, any unmapped or unused memory region should also trigger an access violation, which would also help catch bugs like trying to dereference an uninitialised pointer.

However, most microcontrollers just ignore invalid addresses. Or worse, most architectures place critical data structures, like interrupt vectors or I/O addresses, at the first memory addresses, where they are most likely to be hit by NULL pointer bugs.

Drawbacks specific to the AVR architecture

Address space hacks

At the beginning, 16 bits probably seemed enough for memory addressing purposes, so strlen_P() and friends sufficed. Remember that an AVR program space address is 16-bit wide, instead of the usual 8 bits, so with a 16-bit address you can access 64 K x 16 bits = 128 KBytes of program memory (Flash). But then AVRs with 256 KBytes of Flash came along, and then you suddenly needed more than 16 bits to address all of it, so Atmel came up with hacks like strlen_PF() and the RAMPZ register.

In C/C++, 24-bit integers are rare, so the new uint_farptr_t type got mapped to uint32_t. Addresses are now 4-byte long, but the CPU can only read and compute 8 bits at a time, so the code becomes bigger and slower. Smaller AVRs do not support those address extensions, which is the reason why the code needs to be recompiled for smaller chips. The conditional compilation for all those hacks has rendered AVR Libc's source code unreadable. Note that the AVRs need a separate libc, as sharing such code with other CPUs is impractical.

And there is yet another addressing hack: if the AVR has more than 256 bytes of SRAM, the stack pointer becomes 16-bit wide, but the AVR can only write 8 bits at a time, which means each function prolog and epilog needs to disable interrupts for a short time in order to save and restore the stack frame pointer. Otherwise, an interrupt could trigger at the wrong time and corrupt the stack pointer.

Disregard for Linux

Atmel Studio only runs on Windows. Atmel sells 2 families of JTAG adapters, one for AVRs and another one for ARM-based chips, but they tend to keep the protocol documentation secret, so it is hard to use them under Linux. It is also hard to use other JTAG probes, because they also don't like publishing the JTAG commands that they chips support. To top it all, the Atmel adapters are grossly overpriced.

Atmel Studio 6.0 SP2 is slow and still buggy

Most hackers I know stay with outdated and buggy version 4.0 if they can, because the more recent versions are worse. The GCC versions they install are not the same across all supported platforms, and the AVR and AVR32 ones have not been updated for quite some time. The official support tests your patience's limits, you are better off asking in AVR Freaks forums.

The bootloader address moves if the flash memory increases

If a particular chip becomes discontinued in the future, you will probably have to recompile at least the bootloader, so you will end up having to maintain 2 versions. The main problem is that the bootloader area is located at the end of the flash memory. Atmel could have placed it at the beginning, or mapped it at a high memory address. At least when the RAMPZ hack was introduced (see above), Atmel could have taken the opportunity to fix this issue.

The bootloader cannot use interrupts and is hard to update on the field

The interrupt table is not moveable, and the bootloader address is always near the flash end (see above). Therefore, the bootloader cannot use interrupts and tends to be written in assembly.

You cannot partition the flash space into 2 equally-sized parts and implement a fully-featured emergency firmware (the way some BIOSes provide 2 versions, in case the main one fails). If you implement a firmware update feature, but the update gets interrupted, you have to implement some sort of recovery with a seriously-limited bootloader.

Getting the bootloader right the first time is next to impossible, and it is difficult (but not impossible) to update the bootloader on the field without a JTAG connection. The CPU's datasheet does not mention the necessary tricks to achieve that.