HARDWARE PROJECT 1

profileJohn_matt
chapter_10_reading_.docx

Chapter 10: Topics in Embedded 10 Systems

[General-purpose] microprocessors will [someday cease to exist]. You will not see sockets anymore. This will happen first in the embedded space. It's about delivering a system, not a processor.

—Greg Papadopoulos

Sun Microsystems Chief Technology Officer at the 2003 Microprocessor Forum, San Jose, California

10.1 Introduction

Greg Papadopoulos is among many who believe that embedded processors are the next big thing in computer hardware. Proponents of general-purpose microprocessors may find this statement provocative—if not downright inflammatory. It is, however, easy to see how someone could come to this conclusion, given that practically every device that interacts with the physical world includes some sort of computerized control. As we conduct our daily activities, we encounter literally scores of embedded processors, whereas we might use only one or two general-purpose systems.

An exact definition of embedded systems is hard to come by. In every sense, they are real computers, having a CPU, memory, and some sort of I/O capabilities. But they differ from general-purpose computers because they carry out a limited set of tasks within the domain of a larger system. Most often, the larger system is something other than a computer. Embedded systems can be found in devices as simple and innocuous as coffeemakers and tennis shoes, and as complex and critical as commercial aircraft. Many of today's automobiles contain dozens of embedded processors, each of which manages a particular subsystem. These subsystems include fuel injection, emissions control, antilock brakes, and cruise control, to name only a few. Moreover, automotive processors communicate with one another so that their efforts are coordinated and appropriate to the state of the automobile. For example, the cruise control disengages when the antilock brakes engage. Embedded systems are also found in other computers. A disk drive controller is an example of a computer within a computer. The controller positions the disk arm and encodes and decodes data as it is written and read from the disk surface.

The design and programming of embedded systems requires us to think in new dimensions. We see first that the distinction between hardware and software is fuzzy and mutable. In general-purpose computing, we know in advance the capabilities of the hardware that run the programs we write. In embedded systems design, this is not always the case. The capabilities of the hardware can change while the system is under development. The principle of equivalence of hardware and software becomes a profound guiding tenet of system development. The functional partitioning of the hardware and software is a major—and sometimes contentious—issue.

The second way in which embedded system development differs greatly from general-purpose computing is that a deep comprehension of the underlying hardware is required. A person who writes application programs in high-level languages such as Java or C++ may never know or care about the endianness of a system's data storage, or whether an interrupt occurs at any particular time. Yet these considerations are paramount in the mind of an embedded systems programmer.

Along these same lines are the tremendous constraints under which many embedded systems must function. These constraints include limited CPU speed, limited memory, weight restrictions, limited power consumption, uncontrolled and often harsh operating environments, limited physical space, and unforgiving requirements as to the responsiveness demanded of the embedded system. Moreover, embedded systems designers constantly struggle under the burden of stringent cost requirements for both design and implementation! Enhancements or bug fixes made to an embedded system's programming may cause the entire system to go over budget with respect to power consumption or memory footprint. Today's embedded systems programmer, much like his or her grandfather who programmed a mainframe in the 1950s, may spend a good part of the day counting machine cycles— because every machine cycle counts.

Of the aforementioned constraints, limited power consumption is often at the forefront during the design process. Low power consumption means lower heat dissipation, which in turn means fewer heat sinks—which means less money for components and a smaller overall size. Lower power consumption also means battery backup is more feasible, thus providing uninterruptible and reliable operation.

Most embedded systems can be divided into three categories, based on their power requirements: battery-operated, fixed power, and high-density systems. Battery-operated systems, such as those found in portable audio devices, need to maximize battery life but minimize size. Fixed power systems, such as those found in payphones and caller ID boxes, have limited power supplies (such as phone lines), and the goal is to offer maximal performance within the constraints of the limited power available. High-density systems (high-performance and multiprocessor systems) are more concerned with power efficiency primarily because of heat dissipation issues. For example, voice over IP (VOIP) systems integrate data with voice signals and require a significant number of components, and this translates into significant heat generation. These systems often have unlimited power supplies, but must limit power consumption so they do not overheat.

The fourth, and possibly most difficult, aspect of embedded systems is the matter of signal timing. Embedded systems designers hold a precise and unambiguous awareness of the interrelationship between signals generated by events in the outside world and signals routinely taking place within the embedded system. Any set of events can take place at any time and in any order. Moreover, the reactions to these events often take place within milliseconds. In hard real-time systems, a late reaction is tantamount to failure.

Embedded systems must be functional and flexible, yet small and inexpensive (both to develop and to manufacture). Power consumption is always a major concern. We expand on all these ideas in the sections that follow, beginning with a look at embedded hardware.

10.2 An Overview of Embedded Hardware

The embedded processors that control airliner telemetry are radically different from those that control the quality of a cup of coffee produced by a sophisticated coffeemaker. These applications differ in both their complexity and their timing models. Accordingly, they require vastly divergent hardware solutions. For the simplest control applications, off-the-shelf microcontrollers are often quite suitable. Applications of higher complexity can exceed the capabilities of standard components in terms of performance, power consumption, or cost. Possibly, a configurable manufactured circuit might be adapted to the task. In cases where the application is highly specialized, or responsiveness is critical, a chip must be designed from scratch. We therefore place embedded processors into three broad classifications: standardized processors, configurable processors, and full-custom processors. After describing each type, we will examine the trade-offs that are considered in selecting one approach over another.

10.2.1 Off-the-Shelf Embedded System Hardware

Advances in VLSI technology are most obvious to us when we think about the ever-increasing power available to us in desktop, laptop, and PDA systems. The computing power of yesteryear's million-dollar water-cooled mainframe fits into a shirt pocket today, and this computational power costs less than a good business suit. But some of the most striking applications of VLSI technology go practically unnoticed, despite being quite literally under our noses every day.

Microcontrollers

Many of the embedded processors that we routinely (and unwittingly) rely on for our daily convenience and safety are derivatives of yesterday's leading-edge general-purpose processors. Although these early processors are not powerful enough to run today's common desktop software, they possess more than enough power for simple control applications. Furthermore, these processors are now sold at a fraction of their original prices, because their makers long ago recovered the costs of developing them when they were widely used in personal computers. Motorola's enormously popular 68HC12 shares common ancestry with its 6800, the chip that was at the heart of the first Apple computers. Motorola has sold billions of the 68HC12 and its less-sophisticated predecessors. In large quantities, the MC68H12 can today be purchased for less money than the first Apple users shelled out for a decent printer cable. Intel's 8051 is an offspring of the 8086, the processor that was the heart of the first IBM PC.

Microcontrollers have a great deal in common with general-purpose processors. Like a general-purpose processor, a microcontroller is programmable and can access a variety of peripherals. Unlike a general-purpose processor, a microcontroller runs at a much slower clock speed, has much smaller memory address space, and its software cannot be changed by the consumer.

A simplified example of a microcontroller is shown in Figure 10.1. It consists of a CPU core, memory for programs and data, I/O ports, controllers for I/O and the system bus, a clock, and a watchdog timer. Of these components, the watchdog timer is the only one that we haven't discussed at length in earlier chapters.

Figure 10.1: A Simplified Microcontroller

As the name implies, watchdog timers keep an eye on things within the microcontroller. They provide a fail-safe mechanism that engages when a problem is detected. General-purpose computers do not need watchdog timers because they interact directly with human beings. If the system hangs or crashes, a human being corrects the situation, usually by rebooting the computer. It is impossible for humans to pay such close attention to the myriad embedded systems all around us. Moreover, embedded systems are often installed in places where we cannot get to them. Who can hit the reset button of a deep space probe? How would one reset a cardiac pacemaker? Might it take too long to realize such action is necessary?

Conceptually, the design and operation of watchdog timers are simple. The timer is initialized with an integer value from which 1 is subtracted after a given number of milliseconds elapse. The application program running in the microcontroller periodically "kicks" or "tickles" the timer, causing the count to revert to its initial value. Should the timer ever reach zero, the watchdog circuit may issue a system reset signal. Timer tickling is the responsibility of the application program running in the microcontroller. The program may have a structure that resembles the following:

main() {

boolean error = false;

do while not (error) {

resetWatchdog(); // Reinitialize watchdog timer count

error = function1(); // Perform first user function

if (error)

exit while;

endif;

resetWatchdog(); // Tell the timer we are still alive

error = function2(); // Perform second user function

if (error)

exit while;

endif;

resetWatchdog();

error = function3(); // Perform third user function

}

}

If any one of the functions, function1 through function3, becomes stuck in an infinite loop, or just hangs, the watchdog timer count will run out. One of the tricks involved in this type of programming is to ensure that no function runs longer than the watchdog timeout interval, including any interrupts that might take place along the way.

There are literally hundreds of different kinds of microcontrollers, some of which are built with specific applications in mind. Some of the most popular are Intel's 8051; Microchip's 16F84A, one of the PIC (Programmable Intelligent Computer) family; and Motorola's 68HC12, one of the many chips in the so-called 68000 (68K) series. Microcontrollers can have analog interfaces that are suitable for nondiscrete physical control applications and I/O buffers designed for continuous high-volume throughput. Microcontroller I/O control may be through any of the methods described in Chapter 7. Unlike general-pur-pose systems, however, programmed I/O control is feasible for embedded control applications.

Microcontroller CPUs can be as small as 4 bits and as large as 64 bits, with memory ranging from a few kilobytes to several megabytes. To make their circuitry as small and as fast as possible, microcontroller ISAs are generally stack based and can be optimized for a particular area of specialization. Despite their "old technology," microcontrollers continue to be widely used in a vast array of consumer products and industrial machinery. One has every reason to believe that this will continue to be the case for many years to come.

Watchdog Timer Engineering Decisions

Embedded systems designers may spend a good amount of time debating whether an expired watchdog timer should immediately issue a reset or whether it should instead invoke an interrupt routine. If an interrupt is issued, valuable debugging information can be captured prior to issuing the reset. Debugging information often exposes the root cause of the timeout, permitting the problem to be analyzed and corrected. If the system is simply reset, most of the diagnostic information is lost.

On the other hand, it is possible for a failing system to become so hobbled that it cannot reliably execute any code, including interrupt processing code. Thus, the reset might never be issued by the interrupt routine. Such irrecoverable errors can be caused by something as simple as a corrupted stack pointer, or an errant signal in an I/O port. If we simply issue a reset, we are certain that—barring a catastrophic hardware failure—the system will return to a consistent state within milliseconds.

Unlike personal computer reboots, embedded controller resets take very little time. In fact, resets can happen so quickly that it is not always possible to know whether a reset has occurred. This problem lends weight to the argument that the interrupt approach is superior to the reset approach. The other side of the argument is that there is a hardware solution: Prudent engineers connect a latch and a light-emitting diode (LED) to the processor's reset line. After a reset, the LED will stay illuminated to indicate that a problem has occurred. Assuming that the LED is situated in a location where a human can see it, the reset event will sooner or later be noticed.

It is, of course, possible to combine both approaches. We might initialize the timer with a small value prior to invoking the debugging interrupt. If the count expires, it is certain that the system is in trouble, and a reset would subsequently be issued by the watchdog. A company that seeks to deliver a "quality" product would do well to give this technique some consideration.

However, a company that seeks to go beyond delivering a "quality" product to produce a "high-reliability" system has to think a little deeper. Watchdog timers usually share the silicon space and resources of the systems they are supposed to be watching. What happens if a catastrophic hardware failure occurs in the chip itself? What if the system clock stops? Engineers plan for this contingency by providing a backup watchdog timer on a separate chip. The extra circuit adds only a few dollars to the cost of a system, but may save thousands of dollars—or human lives—if it is ever needed. So now the discussion moves to whether the backup watchdog timer should issue a reset or whether it should instead process the timeout through an interrupt routine.

Systems on a Chip

We have observed that microcontrollers are computer systems in miniature. They consist of a CPU, memory, and I/O ports. They are not, however, called systems on a chip. That designation is usually reserved for devices that are much more complex.

Systems on a chip (SOCs) are distinguished from microcontrollers by their complexity and increased on-chip resources. Microcontrollers often require supporting circuits such as signal processors, decoders, and signal converters, to name only a few. A system on a chip is a single piece of silicon that contains all circuits required to deliver a set of functions. A system on a chip can even consist of more than one processor. These loosely coupled processors do not necessarily share the same clock or memory space. The individual processor functions can be specialized to the extent that they are provided with ISAs designed specifically for efficient programming in a particular application domain. For example, an Internet router may have several RISC processors that handle communications traffic, and a CISC processor for configuration and management of the router itself. Although microcontroller memory is usually measured in kilobytes, SOCs can include memory on the order of megabytes. With their larger memories, SOCs accommodate full-featured real-time operating systems (which we discuss in Section 10.3.2). The great advantage of SOCs is that they are faster, smaller, more reliable, and consume less power than the several chips they replace.

Although there are many off-the-shelf SOCs available, customization is sometimes required to support a particular application. Rather than absorb the cost of designing an SOC from scratch, a semicustom chip may be assembled from intellectual property (IP) circuits licensed from companies that specialize in creating and testing circuit designs. Licensed IP module designs are combined with customized circuits to produce a circuit mask. The completed mask is subsequently sent to a circuit fabrication facility (a fab) to be etched in silicon. This process is very expensive. Mask costs are now approaching $1 million (US). The economic benefits of this approach must therefore be considered carefully. If a standard SOC provides the same functionality—even with lesser performance—the $1 million expenditure is not justifiable. We return to this idea in a later section.

10.2.2 Configurable Hardware

Some applications are so specialized that no off-the-shelf microcontroller can do the job. When designers find themselves in this situation, they can choose between two alternatives: They can elect to create a chip from scratch, or they can employ a programmable logic device (PLD). When speed and die size are not primary concerns, a PLD may be a good choice. PLDs come in three general varieties: programmable array logic, programmable logic arrays, and field-programmable gate arrays. Any of these three types of circuits can be used as glue logic, or customized circuits that interconnect prefabricated IP circuit elements. Programmable logic devices are usually presented along with the combinational logic components we described in Chapter 3. We have deferred this discussion until now because it is helpful to understand the functional context in which these chips are most useful.

Programmable Array Logic

Programmable Array Logic (PAL) chips were the first logic devices that could be configured to provide a variety of logic functions. PALs consist of a set of inputs and a set of outputs that are connected by an array of programmable AND gates and a fixed array of OR gates, as shown in Figure 10.2. A PAL chip's outputs give a sum-of-products function of its inputs. The circuit is programmed by blowing fuses or throwing switches within a conductor grid that interconnects the inputs and the AND gates. When a PAL fuse is blown, the AND gate input to which it is connected becomes a logical 0.[1] OR gates collect the outputs from the AND gates to complete the function.

Figure 10.2: Programmable Array Logic (PAL) a) Detailed logic diagram for a PAL b) A shorthand version of the same PAL

Figure 10.3 illustrates a 3-input, 2-output PAL. Standard commercial PALs provide several inputs and dozens of outputs. In the figure, it is easy to see that it is not possible to provide an output for every possible function of the inputs. It is also not possible to use the same product term in more than one OR gate, so a product term may have to be repeated across more than one OR gate.

Figure 10.3: A Programmed PAL

Figure 10.3 shows a PAL programmed to provide the functions . and . The connections between the logic devices are indicated as "blown" using an ×. (Sometimes we see PAL illustrations use 3 to indicate a connection, that is, a fuse that is not blown.) As you can see, the minterm is repeated. It would seem that we could make more efficient use of our gates if arbitrary connections were also allowed between the OR gates. Programmable logic arrays are devices that provide this additional level of configurability.

Programmable Logic Arrays (PLAs)

It is easy to see that the outputs of a PAL are always the logical sum of all the logical products provided by the AND gates in the array. In our illustration above, each output function consists of two minterms, regardless of whether we need them both. PLAs remove this restriction by providing fuses or switches for the OR gates and the AND gates, as shown in Figure 10.4. The blown fuses or thrown switches provide logical 0s as inputs to the AND gates and to the OR gates.

Figure 10.4: a) A programmable logic array b) A PLA reusing a minterm

PLAs are more flexible than PALs, but they also tend to be slower and more costly than PALs. Thus, the occasional redundancy among PAL minterms is only a minor inconvenience when speed and cost are important considerations. To maximize functionality and flexibility, PLA and PAL chips usually include several arrays on one silicon die. Both types are referred to as complex program-mable logic devices (CPLDs).

Field-Programmable Gate Arrays

A third type of PLD, the field-programmable gate array (FPGA ), provides programmable logic using lookup tables instead of changing the wiring of the chip.

Figure 10.5 shows a typical FPGA logic element consisting of memory cells and multiplexers. The inputs of the multiplexers (labeled MUX) are selected according to the values of the logic function inputs, x and y. These inputs trigger multiplexer selection of the appropriate memory cell, labeled "?" in the diagram. The value stored in the memory cell is then raised on the appropriate output of the multiplexer.

Figure 10.5: Logic Diagram for a Field Programmable Gate Array Logic Element

As a simple example, consider the XOR function discussed in Chapter 3. The truth table for a two-input XOR is shown at the left in Figure 10.6a. The table has been programmed into the FPGA as shown at the right. You can see the direct correspondence between the values in the truth table and the values in the memory cells. For example, when the y signal is high (logical 1), the lower memory cell value of each pair is selected by the leftmost pair of multiplexers. When the x value is high, the input from the lower y multiplexer is selected, giving a logical 0 for the value on the output of the x multiplexer. This agrees with the truth table: 1 XOR 1 = 0. Figure 10.6b shows FPGA programming for the AND function.

FPGA logic elements are interconnected through a routing architecture that consists of switches and connection multiplexers. A typical configuration, called an island architecture, is shown in Figure 10.7. Each connection multiplexer and switch block is programmed to connect the outputs of one logic element to the inputs of another. A good portion of the chip area, usually about 70%, is devoted to connection and routing functions. This is one of the many things that make FPGAs slow and costly.

Figure 10.6: Logic Diagram for a Field Programmable Gate Array

Figure 10.7: An "Island Style" FPGA Configuration

To help reduce cost and improve speed and functionality, some FPGAs are also equipped with microprocessor cores. It would seem that such chips give designers the best of everything: The microprocessor can carry out the functions that it does best and fastest. Where the microprocessor functionality falls short, the FPGA can be programmed to make up the difference. Moreover, the programming process can be iterative. An FPGA permits continual changes while the circuit is debugged and tuned for performance.

FPGAs provide a multitude of logic functions at a reasonable cost with acceptable performance in many applications. Because they can be reprogrammed as many times as needed, FPGAs are especially useful for prototyping customized circuit designs. In fact, FPGAs can even be programmed to reprogram themselves! Because of this, FPGA-based systems are considered by many to be the first of a new breed of computers that can dynamically reconfigure themselves in response to variations in workload or component failures. They have the potential to bring new levels of reliability to critical embedded processors that operate in hostile or safety-critical environments. They can repair themselves whenever a human can't get to them—or can't get to them quickly enough to avert catastrophe.

Reconfigurable Computers

It hardly escapes anyone's notice that our world is awash in electronic gizmos and gadgets. The well-equipped college student is laden with a notebook computer, a cellular telephone, a personal digital assistant, a CD player, and possibly an electronic book reader. So how does one deal with having so much electronic stuff to lug around?

Clothing manufacturers have addressed the problem by providing clothing and backpacks with ample specialized pockets. At least one clothier has gone so far as to create garments having built-in "personal area networks"!

The "engineering solution" to the problem of the gadget glut is just to make fewer gadgets, giving each greater functionality. The greatest challenges lie in overcoming the barriers presented by the power and cost constraints of portable consumer electronics. Certainly, a notebook computer can do just about anything we want it to, but it all happens at the expense of weight and high power consumption. Thus, we have the Holy Grail of reconfigurable electronics: a single, affordable, electronic device that can be a cell phone, a PDA, a camera, and even a music playback device, with sufficient battery life to be useful.

There is no doubt that this vision lies far past today's horizons. The reconfigurable devices of today consist mainly of FPGAs. Today's FPGAs are too slow, too power hungry, and much too expensive for wide deployment in consumer electronics. Research in this area is following several tracks. The first approach concerns the routing architecture of FPGA chips. Different topologies have been found to be more effective than others in certain applications. Most notably, linear configurations are better for shifters, because bit shifting is inherently a linear operation. A three-dimensional approach is also under study by several companies. This topology places the control elements beneath the logic elements, making the entire package denser and the routes shorter.

Another concern related to topology is inconsistent signal response within the chip. Signals traveling a longer distance through the routing structure take longer to get where they're going than signals traveling only a short distance. This tends to slow down the whole chip, because there seems no way to know in advance whether an operation will use a logic element that is nearby or one that is far away. Clock speed is limited by the worst case. Electronic design language compilers (see below) optimized for FPGA implementations are helping to make progress in this area.

Logic cell memory power consumption is an active area of FPGA research. This memory is usually SRAM, which is volatile. This means that the FPGA must be configured each time it is powered on, and the configuration has to be stored in ROM or flash. Various nonvolatile memories have been created from materials that are (for now) too exotic for the general market. If these new memories show promise in the general market, mass production will eventually drive down their cost.

It is also possible to create hybrid FPGA chips that contain islands of generic circuits (such as adders or shifters) that are common to all functions served by the device. The rest of the chip is then available for function-specific logic that can be paged on and off the circuit as needed.

In the short term, reconfigurable circuits may be incorporated in specialized applications such as cell phone communications protocol modules. Cell phones equipped with flexible modules could operate efficiently across areas that employ differing protocols within a national (or international) cell phone network. Although this idea is certainly appealing to the consumer's wallet, it also means fewer discarded phones ending up in solid waste landfills, ameliorating an environmental problem of growing importance.

10.2.3 Custom-Designed Embedded Hardware

To command the largest market for their products, embedded processor manufacturers load their chips with as many features as possible. The chief disadvantage in purchasing these complex, ready-made processors is that some of the functionality of the chip may not be needed. Unused circuits not only cause the chip to run slower, but they also generate heat and waste precious power. In a tightly constrained application, programmable logic devices are not much help because they tend to be slow and power hungry. In short, there are situations where the only reasonable approach is to create a fully customized application-specific integrated circuit (ASIC) .

Designing full-custom ASICs requires thinking of the chip from three perspectives: From a behavioral perspective, we define exactly what the chip is supposed to do. How do we produce the desired outputs as functions of the inputs? From a structural perspective, we determine which logic components provide the desired behavior. From a physical perspective, we think of how the components should be placed on the silicon die to make the best use of chip real estate and minimize connection distances between the components.

Each perspective is a unique problem domain requiring a distinct set of tools. The interrelationship of these ideas is made clear by the logic synthesis Y-chart developed by Daniel Gajski, shown in Figure 10.8.

Figure 10.8: Gajski's Logic Synthesis Y-Chart

Each of the three axes in Gajski's chart is labeled to show the levels of detail for each dimension of chip design. As an example, consider a binary counter. At the highest behavioral level (the algorithmic level), we know that the counter increments its current value, producing a new value. At the next lower level, we understand that to carry out this function, some sort of register is needed to hold the value of the counter. We can state this idea using a register transfer statement such as AC ← AC + 1.

On the structural side, we specify how large the register will be. The register consists of gates and flip-flops, which themselves consist of transistors. To make a silicon chip that contains the counter, each storage cell is positioned and connected to make the best use of die space.

Direct manual synthesis of logic components is feasible only for very small circuits. Today's ASICs consist of hundreds of thousands of gates and millions of transistors. The complexity of these chips escapes human comprehension. Various tools and design languages help manage the complexity, but the constantly increasing gate densities are forcing the design process to a higher plane of abstraction. One of the tools used in dealing with this complexity is the hardware definition language (HDL). Hardware definition languages allow a designer to specify circuit behavior on an algorithmic level. Instead of thinking about the circuit in terms of gates and wires, a designer works with variables and control structures in a manner similar to working with a second- or third-generation programming language. Instead of creating a stream of binary machine code, the HDL source code produces a specification for gates and wires called a netlist. The netlist is suitable input for the software that ultimately produces the silicon die layout.

HDLs have been around for a few decades. The earliest of these were proprietary languages supplied by chip fabricators. One of these languages, Verilog , created by Gateway Design Automation in 1983, became one of the two leading design languages by the end of the twentieth century. In 1989, Cadence Design Systems bought Gateway, and in the next year, it released Verilog into the public domain. Verilog became an IEEE standard in 1995, the latest version of which is IEEE 1364-2001.

Because it was patterned after the C programming language, Verilog is considered an easy language to learn. With C being a popular language for writing embedded systems software, engineers can shift between writing software and designing hardware with relative ease. Verilog produces netlists at two levels of abstraction. The highest level, the register transfer level (RTL), uses constructs akin to any C language program, with variables replaced by registers and signals. When necessary, Verilog can also model circuits at the gate and transistor levels.

The second dominant HDL is VHDL , which is an abbreviation for Very (high-speed integrated circuit) Hardware Design Language. VHDL came into existence under a U.S. Defense Advanced Research Program Agency (DARPA) contract awarded in 1983 to Intermetrics, IBM, and Texas Instruments. The language was released in August 1985, and it became an IEEE standard in 1987. Its latest revision is IEEE 1097-2002.

VHDL syntax is similar to the Ada programming language, which was once the only language authorized for use in any Defense Department projects. VHDL models circuits at a higher level than Verilog. Like Ada, it is a strongly typed language that permits user-defined types. Unlike Verilog, VHDL supports concurrent procedure calls, which are essential for multiprocessor designs.

Increasingly, VHDL and Verilog are strained to keep up with advances in VLSI technology. Modeling and testing the latest multimillion-gate circuits with these languages is tedious, error prone, and costly. It has become evident that the level of abstraction in chip design must be raised once again to allow designers to think in terms of functions, processes, and systems, as opposed to worrying about gates, signals, and wires. Several system-level design languages have been proposed to fill this need. Two competing languages have emerged as leaders of the pack: SystemC and SpecC.

SystemC is an extension of C++ that includes classes and libraries geared specifically for modeling timing, events, reactive behavior, and concurrency in embedded systems. It was the product of the combined efforts of several electronic design automation (EDA) and embedded software companies. Among its strongest proponents were Synopsys, CoWare, and Frontier Design. They garnered support from virtually every other major player in the industry. A consortium of business and academic experts, the Open SystemC Initiative, oversees the evolution and promotion of the language.

The principal aim for the invention of SystemC was to create an architecture-independent specification language that would be widely disseminated and supported within the electronic design community. To this end, anyone agreeing to the terms of the SystemC license may download the language source code and its requisite class libraries. Any ANSI-compliant C++ compiler can produce executable SystemC modules.

SystemC models designs at two levels of abstraction. At the highest level, only the circuit functionality is specified. This specification is sufficient for production of simulation test suites and netlists. If finer granularity is required, SystemC can produce a system or module at the hardware implementation level using either a functional or an RTN style.

SpecC is another system-level design language arousing some interest in the EDA industry. SpecC was originally developed at the University of California at Irvine by Daniel Gajski, Jianwen Zhu, Rainer Dömer, Andreas Gerstlauer, and Shuqing Zhao. It has since been placed under the control of the SpecC Open Consortium, which consists of a number of academic and industry leaders. The SpecC compiler and its related tools and documentation are available from the SpecC website.

The SpecC language differs from SystemC in two fundamental ways. First, SpecC is a modification of the C programming language that is tailored to the needs of embedded system design. Designers of SpecC found that their objectives could not be met through the simple addition of libraries (as is the case with SystemC).

The second—and most remarkable—difference between SpecC and SystemC is that SpecC includes a design methodology as part of the package. The methodology guides engineers through four facets of system development: its specification, architecture, communication channels, and implementation.

Indeed, using any of the several system-level specification languages requires some degree of departure from the embedded system design methodologies that have been in use for decades. The traditional approach typically follows the paths shown in Figure 10.9. The process flows as follows:

Figure 10.9: A Traditional Embedded System Design Cycle

· A detailed specification is derived from a functional description articulated by the designers of the product in which the embedded system will be installed. Product improvement ideas based on customer feedback can be put forth by the marketing department at this stage.

· Based on the specification and sales estimates, engineers decide whether an off-the-shelf processor is suitable, or whether creating a custom-designed processor makes more sense.

· The next step is to partition the system into a hardware component and a software component. Consideration is given to the availability of intellectual property designs and to the footprint and power requirements of the proposed processor.

· With the system partitioning complete, general software and detailed hardware design begins. Software design proceeds to the point where the underlying machine architecture of the finished product makes a difference. After the underlying machine structure is finalized, the programming can be completed.

· A hardware prototype may be created allowing the software to be tested in the prototype. If for some reason a prototype cannot be created, a software simulation of the completed processor is produced. Integration testing usually involves creating test vectors that contain the binary state of the signals on each pin of the processor as it executes its program. Simulation testing consumes many hours of execution time in the simulator, because each clock tick of the new processor must be simulated using the test vectors as inputs and outputs of a complex program.

· When testing is completed to everyone's satisfaction, the finalized design is sent to a fabrication facility, which produces the finished microprocessor.

At any rate, this is how things are supposed to work. However, this is not always the case. The reverse arrows in the diagram are two of the paths that can be taken when an error is discovered. Because hardware and software are developed by two different teams, it is easy for a critical function to be overlooked or for a requirement to be misunderstood. Misunderstandings in requirements can, in fact, ripple back through the whole process, causing redesign after the fabrication step has been completed. In the worst case, a design flaw would be detected after the processor is placed in the finished product. Errors found this late in the process are enormously costly to correct, both in terms of retooling the hardware or software (or both), and in terms of lost opportunity in the marketplace.

Today's chip complexity is exponentially greater than it was when Verilog and VHDL were invented. The traditional model for embedded system development is increasingly error prone and unsuited for today's shrinking product development time frames. The potential competitive edge offered by processor-enabled consumer products is lost when the time to market increases because of protracted processor design time frames. This gives similar products without complex processors command of the market until the more sophisticated product is introduced. In order for manufacturers of processor-enabled products to stay competitive, it is clear that the embedded systems methodology must change. System-level languages like SystemC and SpecC are the facilitators of this change. Not only do these languages raise the level of design abstraction, but they also remove many of the barriers that are so vexing in traditional embedded processor development projects.

The first limitation inherent in the traditional design life cycle is that development teams do not all speak the same language. The "idea people" state their requirements in plain English. A structured system-level design is then derived from their requirements. The specification is next translated into Verilog or VHDL for the hardware design. The hardware design is then "thrown over the wall" to the software designers, who are usually doing their work in C, C++, or assembly. Multiple iterations through this disjointed process can result in reworking the design in at least two different languages.

How much simpler life would be if everyone spoke the same language, and if everyone were working on the same models throughout the design life of the processor! Indeed, one of the greatest benefits offered by SystemC or SpecC is that they both present a uniform, high-level view of the system. Such consistency permits codevelopment, where hardware and software are designed simultaneously. The product design life cycle can thus be shortened appreciably while improving processor design quality.

A streamlined codesign methodology life cycle is shown in Figure 10.10. As you can see, fewer distinct steps are involved:

Figure 10.10: Embedded Processor Codevelopment

· A system-level processor simulation is drawn up in SpecC or SystemC, using the inputs, outputs, and other constraints derived from the detailed specification. This is the behavioral model of the system. The behavioral model validates the designers' understanding of the functions of the processor. This model will also be used to validate the behavior of the completed design.

· Next, during the system codesign phase, software development and virtual hardware development proceed concurrently through stepwise decomposition and refinement of the system specification. Partitioning decisions take into consideration the usual array of concerns, including time–space trade-offs. However, because the hardware design is only simulated at this point, partitioning becomes a dynamic activity instead of one where the objective is to force the software to work in a (fairly) static hardware prototype.

· Through each refinement step, the behavioral model of the system is checked against the simulated processor in a process called coverification. Design corrections are expedited because the hardware design is only a computer simulation of the final product. Additionally, because of the close proximity of the development teams, the number of iterations is usually decreased.

· Once the programming and virtual hardware models are complete, their functions are tested simultaneously using a virtual model of the processor. This activity is called cosimulation.

· With the functionality of both the hardware and software verified against the original behavioral model, a netlist can be synthesized and sent for layout and fabrication.

Although they were the first, neither SystemC nor SpecC is the last word on system-level development languages. Efforts are also under way to adapt Java and UML for this purpose. Support-tool vendors and their customers will determine which of these wins out. Clearly, the sun is setting on the era when circuits are designed exclusively with VHDL or Verilog. Moreover, if a standard language could be agreed upon, intellectual property designs could be exchanged economically. Vendors should be relieved of the risk in choosing a single design language, or the costs of supporting several of them. Intellectual effort could be spent in developing better designs, rather than translating designs from one language to another.

Embedded System Trade-offs

The costly task of designing a full-custom die is undertaken only when no off-the-shelf chip has the desired functionality, power consumption profile, or speed—or, if such a chip is available, the cost of using it cannot be justified.

Because embedded systems are sold as an unnoticed part of some other product, the decision to build or buy is rarely made without considerable market analysis. Two questions weigh heavily in the decision: What is the expected market demand for the product, and how long will it take to bring the product to market if a custom processor is built?

Although it is enormously expensive to design and fabricate a processor, in very large quantities, the economics may favor a customized solution. This idea is captured by the following processor cost formula:

· Total processor cost = Fixed cost + (unit cost × number of units)

Fixed costs are the nonrecurring engineering (NRE) expenses that include designing and testing the processor, producing the masks, and production setup. Unit cost is the amount of money required to produce each unit after all the setup is complete.

As an example, suppose we are in the video game business. Let's say that the hottest new game is expected to be spawned by a movie version of Gulliver's Travels to be released in June. (One plays the game by controlling the antics of the Lilliputians.) Our marketing staff believes that with timely release of our game, we can sell 50,000 units, which will be priced to give us a profit of $12 per unit. From previous experience, we know that our total NRE expenses for such a product would be $750,000, and each processor would cost $10 to produce. Thus, the total cost for the game unit processors is:

· Game unit processor cost = $750,000 + (50,000 × $10) = $1,250,000

The alternative is to purchase a processor that is a little slower and more expensive (per unit) than the custom processor. If such processors cost $30 each in lots of 10,000, the total cost for the processors in the game is:

· Game unit processor cost = $0 + (50,000 × $30) = $1,500,000

Thus, the custom-design approach would save $250,000 in processor costs. This means we could charge the customer less per unit, or increase our per-unit profit, or have some combination of both.

Clearly, our cost model rests on the assumption that we will sell 50,000 units. If that assumption holds true, the choice seems obvious. One thing we have not considered, however, is the length of time it will take to build the processor.

Suppose that the marketing department has come up with monthly sales projections as shown in the chart below. Based on their experience with movie-themed games, the marketing staff knows that some units will be sold before the movie is released. Sales will peak the month following the movie release and will then decline steadily over the subsequent six months.

To sell 50,000 units, the product must be on store shelves by May 1. If the design and manufacture of the customized processor causes us to miss the May product release by three months or more, the best choice becomes one of using an off-the-shelf processor despite its higher unit cost and lower performance.

Our (greatly) simplified example illustrates the trade-offs that take place when the timing of product release is essential to profitability. Another reason for choosing an off-the-shelf processor or an FPGA might also be when sales data are unclear. In other words, if market demand is uncertain, it makes sense to minimize the initial investment by using a standard part rather than incur the NRE expenses of a customized processor. If the product proves to be a hot seller, the NRE expenses would be justified in releasing a subsequent "new and improved" model of the product (containing a faster, customized processor) that would generate even more sales.

[1]There are two types of PLD fuses. The first type requires a higher than normal voltage on the fuse that causes it to blow, severing any connection. The second type of fuse is sometimes referred to as an antifuse because its initial state is open. When an antifuse is blown, a conductive path is created by melting a thin insulating layer between two conducting lines, thus completing a circuit. This is the opposite behavior that we normally expect from a fuse. In our discussions in this chapter, we refer to the first type of technology—so blowing a fuse indicates severing the connection.

10.3 An Overview of Embedded Software

Thanks to virtual memory and gigahertz processor clocks, a general applications programmer typically doesn't have to give much thought to the hardware that runs the programs. Moreover, when he or she writes an application program, perhaps in C or Java, performance problems are usually addressed only after several user complaints have been made. Sometimes the problem is solved by an adjustment to the program code; sometimes it is solved by adding hardware components to the machine that is running the slow application. The embedded systems programmer typically writes programs exclusively in assembly language and does not have the luxury of after-the-fact performance tuning. Performance problems must be identified and corrected long before the consumer sees the program in operation. Algorithms are intimately intertwined with the hardware that runs the program. In short, embedded systems programming requires us to think differently about the code we write, and to think deeply about where and how it will be run.

10.3.1 Embedded Systems Memory Organization

There are two principal ways in which embedded system memory organization differs from the memory organization of general-purpose computers. First, embedded systems rarely employ virtual memory. The main reason for this is that most embedded systems are time constrained. Proper system operation requires that all activities take place within clearly defined time frames. Given that virtual memory access times can vary by several orders of magnitude, the timing model becomes unacceptably nondeterministic for embedded systems implementations. Furthermore, virtual memory page table maintenance consumes precious memory and machine cycles that could be put to better use.

The second distinctive aspect of embedded system memory is the variability and diversity of memory architectures. Unlike general systems programmers, embedded programmers are continually aware of the sizes and types of available memory. A single embedded system can contain random access memory (RAM), ROM, and flash memory. Memory space is not always continuous, so some memory addresses may be invalid. Figure 10.11 shows a model execution environment, consisting of several predefined address blocks. The reserved system memory space contains interrupt vectors that hold interrupt service routine (ISR) addresses. ISR programs are invoked in response to various signals as they are raised on the pins of the processor. (The programmer has no control over which pins are connected to which vector.)

Figure 10.11: An Embedded System Memory Model

You will also notice that our memory space distinguishes between program memory, writeable data memory, and read-only memory. Read-only memory contains constants and literals used by the embedded program. Because small embedded systems usually run only one program, constants can be stored in ROM, thus protecting them from accidental overwrites. In many embedded systems, the designer determines the placement of program code, and whether the code will reside in RAM, ROM, or flash memory. The stack and the heap are often implemented in static RAM to eliminate the need for DRAM refresh cycles. An embedded system may or may not use a heap. Some programmers avoid dynamic memory allocation altogether for the same reasons that virtual memory is avoided: Memory cleanup incurs overhead that can cause unpredictable access delays. More importantly, memory cleanup should be performed routinely to avoid the risk of running out of heap space. Embedded systems memory leaks are serious business because some systems may operate for months or years without rebooting. Without explicit cleanup or restarts, even the smallest memory leak will eventually consume the entire heap, causing a system crash that could be catastrophic.

10.3.2 Embedded Operating Systems

Simple microcontrollers typically run a single application that tends to be of low to medium complexity. As such, microcontrollers require no task or resource management beyond that which is provided by its program. As the capabilities of embedded hardware continue to grow, embedded applications become more diverse and complex. Today's high-end embedded processors are capable of supporting multiple concurrent programs. As we explained in Chapter 8, it is difficult to exploit the capabilities of today's powerful processors without sophisticated multitasking and other resource management that is routinely provided by an operating system—but not just any operating system. Embedded operating systems differ from general-purpose operating systems in two important ways. First, embedded operating systems allow direct hardware access, unlike general-purpose operating systems, which aim to prevent it. Second and more importantly, an embedded operating system's responsiveness to events is clearly defined and understood.

Embedded operating systems are not necessarily real-time operating systems. In Chapter 8, we mentioned that there are two types of real-time systems. "Hard" real-time means that system responses take place within strictly defined timing parameters. "Soft" real-time systems have more lenient timing constraints, but a soft real-time system is still reactive to its environment within clearly defined bounds.

Embedded devices that carry out activities based solely on human interactions with them are no stricter in their timing requirements than ordinary desktop computers. A sluggish response from a cell phone or PDA is an annoyance, but usually not a crisis. In a real-time system, however, responsiveness is an essential factor in determining its correct behavior. In short, if the response from a real-time system is late, it's wrong.

Two of the most important metrics in the evaluation of real-time operating systems are context switch time and interrupt latency. Interrupt latency is the elapsed time between the occurrence of an interrupt and the execution of the first instruction of the interrupt service routine.

Interrupt handling lies at the heart of real-time systems design. Programmers must be continually mindful of the fact that interrupts can occur at any time, even while other critical activities are taking place. Thus, if the highest-priority user thread is executing when a high-priority interrupt occurs, what should the operating system do? Some systems continue to process the user thread while keeping the interrupt in queue until the user thread processing is complete. This approach may or may not present a problem depending on the application and the speed of the hardware.

In hard real-time applications, the system must quickly respond to external events. Therefore, a hard real-time scheduler must be preemptive. The best real-time embedded operating systems allow a wide range of values in the assignment of task and interrupt priorities. However, priority inversion can be problematic. Thread priority inversion occurs when a higher-priority task needs the services provided by a task that runs at a lower priority, or when a lower-priority task holds a resource that a higher-priority task requires in order to complete. When one of these conditions exists, the higher-priority task can do nothing but wait for the lower-priority task to complete. Conceivably, the system could deadlock. Some embedded operating systems deal with priority inversion through priority inheritance: Once the situation is recognized, the priorities are equalized so that all competing tasks can complete.

The second type of priority inversion pertains to interrupts. Interrupts can be disabled during interrupt processing. This avoids a situation called interrupt nesting , where interrupt service routines are interrupted to process other interrupts, potentially causing stack overflow in the extreme case. The problem arises with this approach whenever a high-priority interrupt occurs while a low-priority interrupt is being serviced. When interrupts are disabled, the high-priority interrupt ends up sitting in the queue until the lower-priority interrupt service routine completes. High-quality systems allow interrupt nesting and will recognize the occurrence of a high-priority interrupt, subsequently preempting the lower-priority interrupt service routine.

After responsiveness, the next most important consideration in operating system selection is usually memory footprint. Using a small, efficient operating system kernel means that less memory has to be installed in each device. This gives us a cost model that pits operating system license fees against memory chip prices. If memory is expensive, you can afford to pay more for a small, optimized operating system. If memory is very expensive, or most of it is needed for the application program, the effort of hand coding a totally customized operating system may be justified. With cheap memory, a larger operating system with good tool support may be the better choice.

Embedded operating systems vendors aim to strike a balance between offering a variety of features and small footprint by making their systems as modular as possible. The kernel typically consists of little more than a scheduler, an interprocess communications manager, primitive I/O facilities, and a collection of protection mechanisms, such as semaphores. All other operating system features, such as file management, network support, and a user interface, are optional. If they are not needed, they aren't installed.

The extent to which an operating system adheres to standards can be critical in selecting one operating system over another. One of the most important standards is the IEEE 1003.1-2001 Portable Operating System Interface, POSIX (pronounced paw-zicks), which is a standardized Unix specification that now includes real-time extensions. POSIX real-time specifications include provisions for timers, signals, shared memory, scheduling, and mutexes. A POSIX-compliant real-time operating system must implement these functions through interfaces as specified in the standard. Many major embedded operating systems manufacturers have had their products certified as POSIX compliant by the IEEE. This certification is required for work performed under U.S. government and military contracts.

There are literally dozens of embedded operating systems available. Among the most popular are QNX, Windows 8 Embedded, and Embedded Linux. QNX is the oldest of this group, and at less than 10 kilobytes, it has the smallest memory footprint. QNX is POSIX compliant with installable GUI, file system, and I/O systems, including PCI, SCSI, USB, serial ports, parallel ports, and Internet communications. For more than 20 years, QNX has been running scientific and medical systems on PowerPC, MIPS, and Intel architectures.

Linux was POSIX compliant from the start, but it was unsuitable (without modifications) for hard real-time systems because it did not have a preemptive scheduler. In September 2003, Linus Torvalds and Andrew Morton released version 2.6 of the Linux kernel. This version includes a more efficient scheduling algorithm that is preemptive. Some kernel processes (i.e., "system calls") can also be preempted. Virtual memory is an installable option. (Many embedded systems designers won't install it.) Its minimal footprint for basic functionality is only 250KB. A full-featured installation requires 500KB. Nearly everything outside the Linux kernel is installable—even support for a keyboard, mouse, and display. Embedded Linux distributions are available for every major processor architecture, including several popular microcontrollers.

Microsoft offers several embedded operating systems, each aimed at different classes of embedded systems and mobile devices. In a break with its past, the Windows Embedded suite supports ARM as well as x86-class processors. Adding ARM support was a crucial step in order to keep Microsoft competitive in this arena. Microsoft's enormous investments in the embedded space have brought a dizzying array of products and licensing terms to market. These offerings include the following:

· Windows Embedded 8 Standard, a modular version of Windows 8 that allows designers to select only the features that are needed for any particular application. Selectable features include networking, multimedia support, and security.

· Windows Embedded 8 Pro, a full Windows 8 version with a modified licensing model suited to the embedded market.

· Windows Embedded 8 Industry, a Windows 8 version tailored specifically to the needs of the retail, financial services, and hospitality industries. Enhanced support is provided for point-of-sale (POS) devices, branding, and security.

· Windows Phone 8, a lean version of Windows 8 with features specific to supporting the Windows phone platform.

· Windows Embedded 8 Handheld, a version of Windows Phone 8 tailored to industrial handheld devices, such as scanners and manufacturing control products.

· Windows Embedded Compact 2013, a modular version of Windows 8 that is even more streamlined from Windows 8 Compact. The entire operating system is typically stored in ROM.

· Windows Embedded Automotive 7, a specialization of Windows 7 Compact specifically aimed at automotive applications.

When not operating in highly resource-constrained applications, the Windows Embedded 8 series provides traditional software developers an easy transition into the embedded space. The tools, to which developers have become accustomed, such as Visual Studio, can be used for Windows Embedded 8 development without modification. Microsoft has thus opened the door for many traditional applications to be ported to mobile environments, a space that heretofore had been dominated by Android and iOS. In theory, an embedded Windows-based product could be brought to market sooner than competing systems because Windows tools make the programming much easier. Getting to market faster means cash flow starts sooner. This implies that more money is available for memory chips and operating system licensing fees. Of course, cost models are never quite that simple.

Another operating system that is widely used in embedded systems is MS-DOS. Although it was never intended to support real-time hardware, MS-DOS provides access to underlying hardware and responsiveness that is adequate for many applications. If multitasking is not required, its small memory footprint (approximately 64KB) and inexpensive licensing fees make MS-DOS a very attractive embedded operating system option. Additionally, because MS-DOS has been around for so long, tool and driver support is readily available. After all, it stands to reason that if obsolescent desktop processors find new life in embedded systems, there is nothing wrong with bringing their operating systems along for the ride.

10.3.3 Embedded Systems Software Development

The development of general application programs tends to be a highly iterative process. Some of the most successful development methodologies involve proto-typing wherein a semifunctional mockup of the application is produced and presented to the system users for their review and comment. In accordance with the user input, the prototype is then modified and presented once again. This process can repeat itself for quite some time. What makes this approach so successful is that it eventually produces a set of unambiguous functional requirements that can be stated in a manner suitable to software implementation.

Although iterative approaches are good for general applications development, embedded systems development requires a much more rigorous and linear path. Functional requirements must be spelled out in great detail before work begins on either the hardware or the software. To this end, formal languages are useful in specifying embedded program behavior, because these languages are unambiguous and testable. Furthermore, unlike general applications, where a little schedule "slippage" is tolerable, embedded development schedules must be carefully planned and monitored to ensure coordination with the hardware development schedule. Unfortunately, even with the most rigorous development efforts, requirements can be overlooked, causing a flurry of last-minute "crunch time" programming activity.

Developing software for complex general applications usually requires the work to be partitioned and delegated to teams of programmers. Without this division of labor, it would be nearly impossible for a large project to meet its deadlines. Assuming that the system and its modules have been properly analyzed and specified, it is usually not too hard to figure out which modules to assign to which teams.

By contrast, embedded software is difficult to partition into team-sized chunks. Many embedded programs "jump to main()" right after the system boots. The programs then run "forever" in a super loop (or grand loop) polling for signals raised by external events.

One area of perennial debate among embedded programmers concerns the use of global variables and unstructured code. As you probably know, global variables are named memory space that is visible from any point in the program. The risk in using global variables is that of side effects: They may be updated in ways that the programmer does not expect through variations in the program's execution path. Global variables make it practically impossible to be certain of the value of a variable at any given time.

The advantage offered by global variables is that of performance. When the scope of a variable is limited to a particular procedure or function, every time the subprogram is invoked, the variable is created in the program's namespace. This activity causes negligible execution delay in a general-purpose system, but when every cycle counts, the overhead of local variable creation can be problematic. This is especially true if a function or subroutine is called frequently, as in processing a routine event.

The same people who advocate the use of global variables are often the same people who condone the use of unstructured ("spaghetti") code in embedded systems. Structured programs consist of a mainline that calls a sequence of procedures or functions, which in turn may call more procedures or functions. The program fragment given in our discussion of microcontrollers in Section 10.2.1 is an example of a structured program mainline. In unstructured code, most—if not all—of the code provided by each of the modules is inside the main loop of the program. Instead of controlling execution by calling modules, unstructured code controls execution using branch (goto) statements.

The problem with unstructured code is that it can be hard to maintain. This is especially true if the program is long and involves a lot of branching. Mistakes are easy to make, especially if there is no obvious way to discern the execution path for any particular block of code. Multiple entrance and exit points can create a debugger's nightmare.

Advocates of unstructured embedded programming point out that subprogram calls involve considerable overhead. The return address is placed on a stack, the subprogram address is retrieved from memory, local variables (if any) are created, and the return address is popped from the stack. Furthermore, if program address space is large, subprogram and return addresses can span two or more bytes, causing multiple stack pushes and pops at both ends of the call. In hearing these arguments, one can't help but wonder that if resources are that tight, why wouldn't one just move up to a more powerful processor? Software engineering instinct implores us to find some way other than global variables and spaghetti code to help us to optimize performance.

One of the greatest challenges to the embedded programmer is dealing with events. Events can happen asynchronously and can occur in any order. It is virtually impossible to test all possible sequences of events. There is always the risk that after successfully processing n events, the arrival of event n + 1 will cause a failure. Consequently, embedded systems designers carefully craft formal test plans to facilitate rigorous and detailed testing. To the extent possible, proper operation of the product must be ensured before it is released to the field. It is difficult enough to apply patches to standard desktop software, but it is nearly impossible to patch software that conceivably could be running on millions of devices, the whereabouts of which may be unknown and untraceable.

Debugging embedded systems usually requires more than setting a few breakpoints and stepping through code. Indeed, when event timing is suspected as a problem, stepping through code may not be the best way to approach the problem. Many embedded processors are provided with integrated debugging facilities that reveal some of the inner workings of the chip. Motorola provides Background Debug Mode (BDM) for its embedded processors. The output of BDM can be connected to a diagnostic system via a special "n-wire" connector. The IEEE 1149.1 Joint Test Action Group (JTAG) interface samples signals through a serial loop connected to all signals of interest on a chip. Nexus (IEEE-5001) is a specialized debugger for automotive systems. People who debug embedded software often find themselves working with oscilloscopes and logic analyzers equally as much as they use traditional debugging tools.

An in-circuit emulator (ICE) has helped many embedded system designers out of tight spots. An ICE is a test instrument that integrates microprocessor execution control, memory access (read/write), and real-time trace, among other things. It consists of a microprocessor, shadow memory, and logic that provide control over the emulator's operations. ICE boards vary in their design and capabilities. They can be expensive to buy and build, and hard to learn to use. In the long run, however, they can pay for themselves many times over in the time and frustration they save.

As in general applications development, tool support in the form of compilers, assemblers, and debuggers is important in embedded system work. In fact, tool support can be a major consideration in selecting one processor over another. Because of their varied architectures, each processor offers a unique set of tools that is not necessarily compatible across processor types. Furthermore, simply porting a compiler from one processor to another might not produce a compiler that makes optimal use of the underlying architecture in order to produce optimized code. For example, porting a compiler from a CISC system to a RISC system will probably not result in a compiler that takes advantage of the larger register set of RISC systems.

There may be no tool support at all for custom-developed processors. These systems tend to be the most resource constrained, requiring considerable hand optimization to squeeze maximum work out of every cycle. If the processor is power constrained, its designers will want the clock to run as slowly as possible, thus making optimization even more important to the success of the implementation. Accordingly, creating systems without the benefit of a compiler, assembler, or source-level debugger is the most tedious and costly approach to embedded software development. The results, however, can be magnificently efficient and robust if done properly.

Chapter Summary

We encounter hundreds of embedded systems every day. Their diversity defies any effort to define them. The design and programming of these systems require us to think in different ways about hardware, software, and operating systems. The principle of equivalence of hardware and software gives designers maximum flexibility in engineering for performance and economy. Programming embedded systems requires a deep understanding of hardware, and the ability to think in terms of timing and events. Embedded systems designers draw on every aspect of their computer science or engineering education. Mastery of the principles of computer organization and architecture is essential to success in this field. A deep understanding of operating systems concepts helps in resource management and event handling. A good education in the discipline of software engineering provides a foundation for writing solid code and ensuring its quality. A firm foundation in the study of algorithms helps an embedded system designer to write efficient programs without resorting to dangerous shortcuts, such as global variables. Embedded systems require a change in thinking about all aspects of computing, including the division between hardware and software.

Many processors and operating systems long considered "obsolete" for desktop computing continue to live on within embedded systems. They are easy to program and inexpensive. The simplest of these devices is the microcontroller, which usually consists of a CPU core, memory (ROM and RAM), I/O ports, controllers for I/O, and a system bus, clock, and watchdog timer.

More sophisticated embedded processors, called systems on a chip (SOCs), contain more than one CPU. These processors don't necessarily share the same clock or instruction set. Each processor can be specialized to a particular role within the system. Some SOCs are assembled from intellectual property (IP) circuits licensed from companies that specialize in creating and testing special-purpose circuit designs.

The cost of embedded system development and prototyping can be reduced by using programmable logic devices. They come in three general varieties: programmable array logic (PAL), programmable logic arrays (PLAs), and field-programmable gate arrays (FPGAs). PALs and PLAs are programmed by blowing fuses. Their outputs are sum-of-products and product-of-sums functions of their respective inputs. FPGAs, consisting of memory elements and multiplexers, can implement any logic function based on the values supplied in their memory cells. FPGAs can even be programmed to reprogram themselves.

It can be more economical to buy than to build the microcontroller or microprocessor for an embedded system. It usually makes more sense to build rather than buy when large quantities of the chip are required or responsiveness or power constraints are so tight that only a customized ASIC will suffice. Traditionally, hardware and software design activities followed different paths in the development life cycle. Software was designed using C, C++, or assembly. Hardware was designed using Verilog or VHDL. New system-level specification languages are enabling collaborative codesign and coverification by cross-functional teams of hardware and software designers. A dominant system-level language has yet to emerge, although SystemC and SpecC are gaining widespread support.

The memory organization of an embedded system can be quite different from the memory organization of a general-purpose computer. In particular, large blocks of memory addresses may not be valid. Virtual memory is rarely used. Programs and constant values are usually stored in read-only memory.

Not all embedded systems require an operating system. For those that do, the important considerations in operating system selection include memory footprint, responsiveness, licensing fees, and adherence to standards. Hard real-time embedded systems require operating systems with clearly defined responsiveness limits. This implies that they employ preemptive scheduling and make provisions for priority manipulation, particularly with regard to interrupt processing. There are literally dozens of embedded operating systems on the market. Among the most popular are QNX, Windows CE, Windows Embedded 8, and Embedded Linux.

Embedded system software development is typically more controlled and linear than general application software development, and it requires a thorough understanding of the underlying hardware architecture. Embedded systems are subjected to rigorous testing because the costs associated with their failure are so great.

Further Reading

Many books provide detailed coverage of embedded design and development. Berger (2002) is an excellent and comprehensive beginner's introduction to embedded design activities. Heath (2003) is strong in its coverage of embedded programming. Vahid and Givargis (2002) is noteworthy in its exacting descriptions of embedded system components, including an entire chapter on embedded control applications (PID controllers) that is remarkably well done.

Embedded Systems Programming magazine published three very good watch-dog timer articles. Two were written by Jack Ganssle (2003), and the third is by Niall Murphy (2000). All three are worth reading.

Reconfigurable and adaptive systems loom large in the future of embedded systems as well as the future of general-purpose computers. Among many good articles on this subject are those by Andrews et al. (2004), Compton and Hauck (2002), Prophet (2004), Tredennick and Shimato (2003), and Verkest (2003).

Berger (2002) provides a detailed account of the traditional embedded system design life cycle. His exposition describes the many ways in which the development process can go off track. Later chapters describe testing and debugging activities that are beyond the scope of this chapter. Articles by Goddard (2003),Neville-Neil (2003), Shahri (2003), and Whitney (2003) present clear pictures of two of the most challenging aspects of programming customized processors: hardware-software partitioning and the lack of development tool support. Similarly, Daniel Gajski (1997) provides a great deal of information regarding the details of circuit creation.

Owing to ever-increasing circuit complexity, embedded system codesign and coverification is a growing area of interest. Good introductory papers are those by De Micheli and Gupta (1997) and Ernst (1998). Wolf (2003) traces a ten-year history of hardware/software codesign, and Benini et al. (2003) describe the use of SystemC in codesign. Information on the specifics of the various hardware definition languages can be obtained from the following websites:

SpecC: http://www.specc.org

SystemC: http://www.systemc.org

Real-time UML: http://www.omg.org

Verilog and VHDL: http://www.accellera.org

Bruce Douglass (1999, 2000) has written the definitive real-time UML books. Reading them will give you a new and deeper understanding of real-time software development and a new perspective on UML. Stephen J. Mellor (2003) describes how Executable and Translatable UML can be used in the development of real-time systems. Lee's (2005) article outlines the ways in which computer science thinking is challenged by embedded systems development.

The best sources for up-to-the-minute embedded systems information is found on vendor websites. Among the best are:

ARM: www.arm.com

Cadence Corporation: http://www.cadence.com

Ilogix: http://www.ilogix.com

Mentor Graphics: http://www.mentor.com

Motorola Corporation: http://www.motorola.com

Synopsis Corporation: http://www.synopsis.com

Wind River Systems: http://www.windriver.com

Xilinx Incorporated: www.xilinx.com

Good general information sites include:

EDN (Electronic Design News): http://www.edn.com

Embedded Systems Journal: http://www.embedded.com

Embedded operating systems sites include:

Embedded Linux: http://www.embeddedlinux.com

Microsoft operating systems: http://www.embeddedwindows.com and http://www.microsoft.com

POSIX: http://www.opengroup.org

If you find that the subject of embedded systems sufficiently excites you to pursue it as a career, in his article "Breaking into Embedded," Jack Ganssle (2002) provides a number of steps that you should take to get yourself started along this path. One activity that he stresses as the best way to learn embedded programming is to do embedded programming. He urges aspiring embedded system designers to visit http://www.stampsinclass.com for information on purchasing microcontroller kits and manuals that are designed for educational purposes. A recent addition to their offerings is the Javelin Stamp, which is a PIC microcontroller programmable in Java. (Other languages are also available.)