(C) 2009 Hank Wallace
PREVIOUS – Embedded System Design Library: Interrupts
NEXT – Embedded System Design Library: Open Source Code
This series of articles concerns embedded systems design and programming, and how to do it with excellence, whether you are new to the discipline or a veteran. This article is about using the complex hardware we find prevalent in processors and peripherals today.
Way back when, user manuals for the most advanced processors topped out at 100 pages. There were perhaps 50 registers in a microcontroller, and some hundred thousand transistors on chip. Today, we have micros with millions of transistors and data manuals that run to a thousand pages. Can’t you hear the trees thanking God for PDF files?
All this complexity means that we can accomplish great things in a small space. On the other hand, great blessings come at great cost. How many readers can say truthfully that they have read all 1000 pages of that ARM manual? Not many, I expect. The risk here is that there will be some ‘feature’ of CPU or peripheral that is missed, causing program bugs or total failure in the field.
Some years back, Motorola had a utility that would do basic configuration of CPU and peripheral registers on complicated processors. The problem was, the utility was a pain to use and complicated in its own right. There is no substitute for paging through the manual and understanding all the registers and all the functions of a device.
It is important to initialize all registers. Most manufacturers design micros to come up in a predetermined state after reset, but some minority of registers are usually uninitialized. This leads to ‘working over the weekend syndrome’, where you have an incomprehensible bug that is intermittent or only happens on certain instances of your design.
I had this problem with a simple Microchip PIC design implementing an almost trivial seven-segment temperature display. I had written that code years before, so I copied it from a prior PIC project to a more recent CPU. It took only a day to get all the software running, and the product was tested and released to production. Unfortunately, 25% of the production units failed to work. After a long weekend of scoping and reading through previously debugged code, I could find no cause. I then compared the data sheets for the old and new PIC processors and found that Microchip had added a couple bits in one register, bits that were undefined on reset. They were the cause of my problem.
I’ve had this problem several times with Microchip PIC processors where they added, removed, moved, or changed the functions of bits in registers, forcing me to page through huge data sheets comparing old and new register layouts looking for differences in function. Even the most basic peripherals, such as timers, change from part to part. This foolishness has caused me to avoid PICs wherever possible. The TI MSP430 processors are designed such that a timer is a timer, and the register layouts don’t change from part to part.
Microchip has taken lately to using paged paging, where not only are the registers banked, but some registers within one bank are banked still again, requiring the programmer to set bits in other registers to access them. This prevents even a compile- or assemble-time fix using predefined register names and addresses because access to certain registers requires a certain sequence of operations.
All that points to the necessity of reading every line in every data sheet. I initialize all registers, especially in an unfamiliar processor. This also guards against changes in function between various die revisions. The lazy will ignore me, but if you want rock solid reliability, don’t trust the chip designers or nontechnical technical writers who create those data sheets.
Add to that the over-complication of peripherals on microcontrollers today. Just take a look at the interrupt controller in an ARM processor, or the timer/counter module on any Motorola/Freescale device. Do you just need a simple 10ms timer interrupt? Sorry, but you will have to initialize many registers to get it. BUT, if you need a timer interrupt that counts out the fibonacci sequence in hardware, that’s easy!
The documentation of these overly complicated peripherals is also suspect. You must read the data sheet defensively. It is highly likely that a revision A or B or C data sheet will lack information or contain some unintentional lies. Many times I’ve been stumped by a peripheral, only to read the data sheet on a sister device to determine what the real operation of a perhipheral should be.
And several times I’ve had to write routines to load all possible values into an 8-bit control register to determine how to accomplish a task because the documentation was incomplete!
To cope with this mostly unneeded complexity, use the simplest code possible for the task. Turn off all unused features in the CPU and peripherals. I doubt that 10% of applications use even 50% of the peripherals on any ARM processor. Turn the rest off.
I have a Lexmark printer, most certainly containing a ?00MHz ARM processor or something similar. I have had to start unplugging it when it’s not in use because of strange behavior. It sits right beside my desk, and several times during the day I would hear it go through a reset cycle. The program was apparently crashing while idle. That, and numerous other USB and UI lockup problems caused me to conclude that the graduate students who wrote its code did not understand how to manage such a complex processor.
Sometimes you can find example code in data sheets to speed development, or sample projects on the manufacturer’s web site. But be careful! On that temperature display I mentioned above, I used a Microchip multiple precision multiply routine I found in an application note from around 1990. Then my customer calls me and says, “the temperature steps linearly from 30 to 36 degrees, then jumps to 40 degrees!” Turns out there was a bug in the example code. I fixed it and simulated all temperature values from -200 to +200 to be sure the algorithm was linear.
Understand that much of the sample code published in data sheets and on web sites is likely written by co-op students who have just about zero industry experience. Fully test and validate all sample code you use.
As in all things, the Keep It Simple Stupid principle is your friend in embedded design. If you can use an ARM7 instead of an ARM9, do it. If you can copy a memory region manually instead of using a DMA controller, do it. If you can write a simple software timing method instead of using a timer peripheral, do it. The hardware is great, when necessary, but the less your design relies on someone else’s team of graduate students, the better.
Author Biography
Hank Wallace is the owner of Atlantic Quality Design, Inc., a consulting firm located in Fincastle, Virginia. He has experience in many areas of embedded software and hardware development, and system design. See www.aqdi.com for more information.