SLTF Consulting
Technology with Business Sense

 Home | Bio | Contact Us | Site Map


System initialization requires as much care as the rest of your design

Scott Rosenthal
March, 1994

One of the last things many system designers consider is how to initialize a system. Yet this start up probably takes more time getting right than almost anything else in a design. The difficulty lies in software first dealing with hardware vagaries and then initializing low-level data structures. In addition, software must also handle inadvertent system resets, interrupts, stacks (or lack of) and proper initialization sequences. To complicate matters further, each embedded system is unique, so what worked before might not function on another system. This column summarizes some initialization techniques that have worked for me during almost two decades of system design.

No magic allowed

The goal of the initialization process is to enable you to just flip on power to an embedded system so it starts running, as if by magic. Unfortunately, it's not magic, and you're not a magician. In brief, microcomputer databooks state that when a circuit releases the RESET signal, a microcomputer starts executing instructions at a specified memory location. This explanation is the same simplistic response I get from my kids when I ask what happened, and they say, "It broke." In both cases, there's a whole lot more to the story.

The databook is correct in that the microcomputer begins executing at a particular memory address called the reset address. The problem is that each chip designer has a different idea of what's the best reset address, so you'll find them all over the memory map. For example, the Z80 reset address is 0000H, the 80C196 resets at 2080H with some assistance from information at 2018H, and the 80X86 resets 16 bytes down from the top of its memory space. From a software viewpoint, an address is an address and the physical location means nothing. Yet to hardware, the reset address' location is of vital importance.

To see why, consider the miserable example of the 80C196, which is a memory-design nightmare. Not only is its reset address in the middle of a memory map, its first 100H memory bytes are devoted to onboard registers, and 2000H to 207FH are off limits to program code. If you locate ROM at the bottom of memory, you lose the first 100H bytes, and a ROM gap exists between 2000H to 207FH, with initialization code starting at 2080H. Alternately, you can fill the bottom of memory with RAM (except, of course, for the first 100H bytes), switch to ROM at 2000H and then switch back to RAM at a higher address. It's a good thing that each user doesn't need to keep this addressing straight!

Databooks typically instruct you to place a jump to program code at the reset address. Nowadays, programmers typically store the first instruction in ROM, but things didn't work that way not that long ago when UVEPROMs held only 256 bytes. For example, I first learned about programming microprocessors on the original 8-bit engine, Intel's 8008. The chip wasn't very powerful and didn't integrate any peripherals or memory to speak of, and fancy emulators didn't exist yet. The chip's reset address was 0000H, but the target system I was working on had RAM at that location. Each time I powered up this computer, I had to toggle in-through front-panel switches-a jump instruction to branch to the built-in monitor program. I can still remember toggling switches and entering the jump instruction: 44H, 00H, 38H (JMP 3800H). It's still possible to see a jump instruction at the reset address in every PC. Just run the DOS DEBUG program and type uffff:0,1. This instruction sits at the reset address and is therefore the first one the processor executes on power up.

Start your engines

Armed with knowledge of a microcomputer's reset address, it's time to examine its effect on an application. The most simplistic way to construct a program is to put its first instruction at the reset address and build forward from there. Another technique puts a jump instruction at the reset address that branches to the actual program code. A strange technique I once used when I needed every memory byte to stick an RST instruction at location 0. This 1-byte call to a fixed location allowed me to save 2 bytes in ROM-and you don't need a stack if the code never returns. Sometimes, every bit counts.

My preference is to place a short piece of program code starting at the reset address (see nearby listing). This code snippet first disables all interrupts. When a CPU resets, it disables its interrupts so that the program code has a chance to start running and initialize the system. However, I've seen situations where the program, through a coding error, somehow manages to work its way back to its own beginning. In this case, interrupts are still active. If the program is going to start over, I'd rather it do so in a proper sequence. This feature also makes debugging easier because finding out why the program restarted is generally easier than trying to debug a strange program path. In short, keep interrupts off until both software and hardware are capable of supporting them.

Listing: Getting started               
ResetAddress:	di			; turn off all interrupts
		ld	sp,StackTop	; setup the stack pointer
;  clear RAM to zero (always assume even number of bytes to clear)	
		ld	1ch,#02000h	; amount of RAM	
		sub	1ch,1ch,#mem	; byte of uninitialized memory	
		shr	1ch,#1		; words to clear	
		ld	1eh,#mem	; starting address to clear from
up:		st	0,[1eh]		; store a 0 word at pointer	
		add	1eh,#2		; increment pointer	
		dec	1ch		; decrement word counter	
		jne	up		; jump if more words to clear
		jmp	Main		; go do the rest

The next step in my initialization code is to clear all RAM to zero. Obviously, oodles of RAM might preclude this step, but in most embedded systems the process is fast and easy. This step guarantees that the program always has a fixed starting point. This point is important for two reasons: First, one of the most difficult software problems to find is an uninitialized variable. With this type of error-depending on how long system power was off, the phase of the moon and maybe even your biorhythms-RAM sometimes powers up with the right value, sometimes not. Further, emulators aren't always helpful in troubleshooting this problem because some always load their overlay memories with a particular bit pattern. Setting RAM to 00H guarantees that each time the program starts, it always knows what's in every variable.

The second reason for clearing RAM is that with every uninitialized variable set to a known value, you can use this information to allow local initialization of functions. Some compilers with their own initialization code perform this task, whereas others don't. It only takes a few bytes of code to implement this safeguard, so I do it just to make sure.

After clearing memory, the startup code sets up a stack for the processor to use. Although this step might seem obvious, you must first ensure that the RAM is accessible and functional. If the system uses static RAM with simple address decoding, you probably don't have to perform any initialization to start using it. On one board I designed, I had to set the ROM/RAM border to divide memory between these two types. If your system uses DRAM, precharge it before storing information in it. Until the RAM is ready, you can't use any local variables, subroutine calls or stack space.

With accessible RAM in place, it's possible to set up a stack pointer. In most processors this task is simply a matter of loading a memory pointer with a location in RAM. However, be careful of this step. I once had a linker that supplied a RAM location for the stack top. That feature was great, but this linker supplied the stack pointer as an odd-numbered memory location that didn't work properly with the 16-bit bus. I ended up anding the supplied address with 0FFFEH. This technique guarantees that the stack always starts on an even address at the cost of maybe losing 1 byte of RAM. PE&IN

About Us | What We Do | SSM | MiCOS | Search | Designs | Articles

Copyright © 1998-2014 SLTF Consulting, a division of SLTF Marine LLC. All rights reserved.