O'Reilly Network    
 Published on O'Reilly Network (http://www.oreillynet.com/)
 See this if you're having trouble printing code examples

Designing Embedded Hardware

Powering Up Your Home-Brewed Computer

by John Catsoulis, author of Designing Embedded Hardware

Creating new computer hardware is a process of precise engineering and design. It involves careful selection of chips and then a thorough analysis to ensure that those chips will work (and interact) in the way in which you intend. Once the design is complete, the first prototype must be built, and built in a logical (pardon the pun) and systematic manner, such that any design or construction faults are easily found.

A virgin computer, the engineering term for such machines, has a lot of unknowns, and these need to be discovered step by step. While this process is heavily based on engineering ground rules, there's an art to doing it effectively. It's an art that is slowly (and sometimes painfully) learned by the novice. In this article, I want to share with you some of the basic techniques that I have acquired over the years. Here's how I go about producing a small computer system.

Read the Technical Data

First, obtain data sheets and books for all major components in the system. These provide detailed information not only on the functionality of the components, but also their electrical characteristics. They also provide programming information vital for software development.

Related Reading

Designing Embedded Hardware
By John Catsoulis

Read the technical data from beginning to end. This may seem arduous, as these documents can be lengthy and are not, shall we say, in the best-selling-novel category of reading. Think of it as a character-building exercise that will develop your patience and stamina for the debugging sessions that lie ahead. It is important that you understand how the devices work. When you are debugging your system, you have to know what to look for to know whether different parts of your computer are functioning, and the only way to gain this information is by reading the data thoroughly. Don't assume anything about the functionality of the devices. Read and check everything carefully, including voltage levels, basic timing, and anything else that may be relevant to the system.

Once this is done, you should then work out a basic schematic of the system. The schematic shows how devices are connected together functionally. It should include your initial design of the basic computer, including processor, logic, external memory (if used), and whatever peripherals are needed. The outputs of a given chip will be driving the inputs of other chips. It is important to ensure that the timing of these output signals match the requirements of the other chips. A signal that arrives too late, or ends too soon, will result in a nonfunctioning computer. Therefore, you should do an analysis of your design and confirm the timing of your system. This can be a real pain, but it is necessary and will save you a lot of time later on, when it comes to debugging your system. Faults are easier to fix before a computer is built than afterward.

Design Your Schematic

When designing the schematic, also consider the system's power consumption and power requirements. Will it be battery powered, or will you plug it into a wall socket? What sort of connectors are required to connect this computer to the outside world? Also think about any hardware that you can add that will aid in debugging. These may not be part of the primary application, but their inclusion could save you a lot of time (and pain) during the debugging process. I always add in an LED that shows when the system is powered. Two other such "tools" I always add are a bank of status LEDs and a serial port, whether or not they will be needed in the end system. The LEDs can be driven under software control to provide valuable status information. For example, a bank of LEDs can indicate the system's progress through a power- up sequence, long before the rest of the computer is operational. A serial port extends this even further; at its basic level, it can be used to output status and debugging messages to a terminal or host computer, providing precious insight into the flow of software execution.

Beyond this, a simple command interpreter (or better, an interactive language such as Forth, gives you control of the computer hardware. To be able to interactively execute snippets of code, or "manually" tweak control registers, can be an incredibly useful debugging tool, if used intelligently.

Lay Out Your PCB Design

Once you have your schematic and timing analysis completed, then you begin laying out your printed circuit board (PCB) design. When laying out a PCB, consider not only what connections have to be made, but all the physical, electrical, and noise environments in which your system will be operating. You need to consider the physical size of the system and where it will live. Will it be enclosed in a case, or will it need mounting in some special assembly? Does it need holes for mounting screws? What is the best way to place components within the circuit board so as to optimize signal routing within the system? Will the system draw a lot of current, and therefore have a lot of heat to dissipate? If so, how will you cool the machine? How will electrical noise (crosstalk, EMI, ground bounce, etc.) affect the computer, and will it require shielding? Add anything that may help debugging, such as a test point that allows you to easily monitor a particular signal using an oscilloscope or logic analyzer. Another useful thing that seasoned engineers add is a simple pad that is connected to ground. This provides a convenient place to anchor the ground lead of an oscilloscope probe.

When the PCB design is completed, you must carefully check it over, making sure that there are adequate clearances between tracks, pads, vias, and fills. Also ensure that the hole sizes are correct for the components that will be mounted. If a pin is too large for a hole, then the component can't be mounted on the PCB. It is useful to print out the PCB design, because sometimes faults will stand out easily on paper that you simply won't notice when staring at a computer screen. After you've checked everything thoroughly and you're happy with the design, then send it to a PCB fabrication house for manufacture.

Once the PCB has been fabricated and you get it back, check it carefully to ensure that all pads and tracks are intact and properly etched. It is not unusual for problems to occur in the fabrication process, and it is better to find these problems before you solder on the components than after. With the power turned off, do a continuity test between the ground pads and the ground pin on the power connector. This ensures that good ground connections are present where they should be.

Construct Your Computer

Do the construction a step at a time and check everything as you go. Start with soldering in the power connector, "power" LED, resistor, and protection diode. Check that these are operational and also that you have power on every pad on the board where you expect power to be. Check the ground pads and signal pads to make sure that there is not power where you expect no power to be. Solder the reset circuitry for the processor and confirm that it is operational -- observe the reset line with an oscilloscope. Does the reset signal line go active when you press the reset button? Solder in the power-decoupling capacitors for the chips. Add in the processor's oscillator and decoupling capacitors. For a prototype system, the use of sockets to hold the chips is a good idea. If there is a problem and you need to remove a chip, it is a simple matter of popping it out of its socket. This is much easier than having to desolder a multi-pinned chip.

When you are sure that the processor socket has power and ground connections, plug the processor into its socket and power it up. Check the processor's clock and confirm that it is oscillating.

If the processor has external buses, check these for activity. With no ROM (or flash) plugged in, the processor will be executing garbage from nonexistent memory. (It is, in effect, crashing, but you should still be able to see that it is trying to do something.) With processors that support externally-generated wait states (such as 68000s), the wait state input on the processor may be tied active (with a piece of wire between two pins of the wait-state generator's socket, for example) such that the processor is in a permanent wait state. When exiting reset, the processor will therefore be locked in its first access -- the fetching of the reset vector from ROM. This allows you to check whether the addresses and control signals coming from the processor are appropriate and also reaching all devices as they should. Confirm this on all appropriate pins. Don't assume that just because a signal is present at one end of a trace that it is present at all points along the trace. Check everywhere with an oscilloscope probe.

If your system uses programmable logic, program the chip with appropriate equations and plug this chip into your system. Use an oscilloscope to confirm that it is generating the signals you expect. Use the "permanent wait state" technique described previously to verify that the PAL is generating a chip select to the ROM as the processor comes out of reset.

In assembly language, write a program that does nothing but loop back on itself. This program should be as simple as possible. An example of such a program in pseudo-assembly language is shown below.

    org ROM     ; start code at ROM

loop    BRA loop    ; just a simple program that
            ; loops back on itself.
            ; this is used to check the
            ; ROM chip select and output enable

    org VECTORS ; vector table

    fdb ROM     ; allocate all vectors equal
    fdb ROM     ; to RESET for the time being
    fdb ROM     ;
    fdb ROM     ;
    fdb ROM     ;
    fdb ROM     ;
    fdb ROM     ;
    fdb ROM     ; this is the REAL RESET vector
            ; this points to start of code

Burn this program into a ROM and plug the ROM into your system.

If your system is working correctly, the processor will be running this program from ROM. This means that there will be a chip select to the ROM. There should be no other chip selects being generated by your address decode logic. There should be an output enable (read enable) going active at the same time as the ROM's chip select (as the processor fetches each instruction in turn). There should be no write-enable active, since your program isn't storing anything to memory.

Consider the addresses in memory at which your program lies. The activity on the address bus should reflect the processor running through this address range. The state of the address bits should correspond to these addresses and no others. Check each of these aspects of your system in turn. For example, if the following program lies at address 0xC000 in ROM:

loop    BRA loop

You should see the processor reading from address 0xC000 as it fetches the BRA (branch) instruction, then from 0xC001 as it fetches the offset. The processor will then branch back to 0xC000 and begin the loop again. Therefore, the only activity seen by this processor is a read from 0xC000, followed by a read from 0xC001, followed by a read from 0xC000, and so on. Thus, if the processor is executing this program correctly, when the processor is performing a read cycle (when the read strobe is active), the address bits should correspond to 0xC000/1. If the address bus looks random, then you have a problem that needs to be solved.

Once you have confirmed that the processor is executing code from ROM, add the components that compose the serial interface (if you've included it in your system). Confirm that the serial interface chip's oscillator is working by looking at the crystal inputs with an oscilloscope.

Write a program that initializes the serial port and then continuously outputs a single character to a terminal. Don't try to be too adventurous. If it doesn't work and you need to debug, you will appreciate a really simple program without lots of complexity and unknowns.

Once the serial interface is operational, plug in your RAM and write a program to confirm that it is functioning correctly. For example, read a character from a terminal (via the serial interface), store this to the RAM, read it back from the RAM, then print it back to the terminal. If the character printed back is the same as the one that was typed, this shows that the processor was successfully able to write to, and read back from, the RAM. Once this is done, you have a basic operational computer system, and you can move on to debugging the rest of your peripherals.

Prior to doing this, replace your simple diagnostic software with something more useful, such as a simple monitor program or a bootloader. This will allow you to more easily change the code on your computer and allow you to debug any other hardware in the system that remains to be tested. The time spent porting Forth to your system (and it's not that hard) is well worth the effort for the debugging capabilities it affords.

And after that, well there's always Linux to be ported ....

John Catsoulis is an electronics engineer, programmer and physicist who specializes in advanced computer architectures.

O'Reilly & Associates will soon release (November 2002) Designing Embedded Hardware .

Return to the O'Reilly Network.

Copyright © 2009 O'Reilly Media, Inc.