Issue
150 January 2003
Construct
an ATA Hard Drive Controller
THE
FIRMWARE
I
wanted the ATA hard drive controller to be capable of
interfacing to any standard ATA device. With that design
requirement mind, I wrote the hard drive controller’s
AVR firmware with ImageCraft’s ICCAVR C compiler and
guidance from the ATA-3 specification. What I ended
up with was a basic set of routines that allows you
to exercise the standard ATA command set, query the
hard drive register set, and exchange data with the
attached ATA hard drive.
As
soon as I had access to the hard drive’s register set
and data, I set out to write code to move the data that
was harvested from the hard drive to the outside world.
The first logical choice of data transport was a serial
port. After thinking it over, I decided that an Ethernet
interface would be an excellent way to move data in
and out of the hard drive controller. The Ethernet port
would allow the hard drive controller to be networked
and provide a high-speed data connection for transfer
rates beyond the capabilities of a serial port. In either
case (serial or Ethernet), you could use any of the
Visual (e.g., Visual Basic, Visual C) or Borland compilers
to build an embedded or PC interface to the ATA hard
drive controller.
The
first order of business as I started to develop the
AVR firmware was to define all of the functions that
would run against the hard drive. With respect to the
software, a hard drive looks like an 8- or 16-bit I/O
port that leads to an internal register set. Register
I/O is normally achieved in 8-bit mode, while data transfers
are typically performed in 16-bit operations.
If
you review Figure 1, you’ll see that a 16-bit data bus
is pinned out on the 40-pin hard drive I/O connector.
The data bus signals are supported by a set of I/O read
and write signals. Access to the internal hard drive
register set is accomplished using the I/O read/write
signals and data bus signals in conjunction with the
address and select signals found on the 40-pin hard
drive I/O connector. The control block is the core set
of hard drive internal registers. Listing
1 is my definition of how the control block registers
are addressed.
If
you take a close look at the control block register
definitions in Listing 1, you’ll
notice that the basic components (in register form)
of addressing data on a hard drive are represented.
Cylinder head sector (CHS) addressing is implied in
the control block register names; however, in the ATA
hard drive controller firmware, I’ll use these same
CHS-based registers to perform logical block address
(LBA) mode addressing operations. LBA mode is a means
set forth by the ATA standards to allow for the linear
addressing of sectors. LBA addressing is derived from
the CHS addressing format as follows:
LBA
= ((cylinder × heads_per_cylinder + heads) × sectors_per_track)
+ sector – 1
For
instance, cylinder 0, head 0, sector 1 is LBA address
0. Therefore, for LBA mode to function, the hard drive
must support LBA mode internally, and that’s the case
for the 540-MB laptop drives as well as the larger 850-MB
3.5" drives.
The
ultimate goal is to use LBA mode to read and write to
sectors on the hard drive. To do this, you must first
be able to read and write to the hard drive’s register
set. A good place to start with the firmware description
of this process is with the hard drive initialization
routine, whose source code is included in Listing
2.
The
first register access occurs when the while(!ready &
busy) statement executes. Note that ready and busy are
macros that call the ata_rdy(void) and ata_busy(void)
functions. The ata_rdy(void) and ata_busy(void) functions
are identical with the exception of the status bit they
check. In both cases the AVR data bus pins are put in
Input mode, the status register is addressed, the I/O
read pin is toggled, and the status register data is
read (8 bits). Additionally, the hard drive I/O port
is put into a high-impedance state, the status condition
is determined, and a return code is generated. Note
that the external buffer SRAM is not used by these functions.
After
the hard drive has done its own power-on reset, the
ready bit will show that the hard drive is ready for
a command, and the busy bit will indicate a "not
busy" status. At this point, a hard reset is toggled
using the RESET pin on the hard drive I/O bus, and time
is marked to allow the physical and electrical hard
drive reset process to finish. Because I was attaching
a single hard drive that’s strapped as master drive
0, I selected drive 0 in LBA mode using the select_drive_0
macro. The next step was to issue a recalibrate command
and check the error status register. In instances like
this, a "drive ready" banner is sent to the
serial port if all is well.
At
that point, I wasn’t ready to start reading hard drive
sectors, because I needed to make sure I could address
and command the hard drive interface accurately. The
easiest way to verify this was to execute an ATA Identify
Device command.
Basically,
the Identify Device command instructs the hard drive
to divulge its factory-loaded identifiers, and 255 words
are returned. All I had to do was pick up the words
from the hard drive I/O port, parse them, and send the
results to the serial port. All 255 words weren’t needed.
As you can see in Table 1, the
first 46 words tell you if things are working correctly.
Photo 3 is a HyperTerminal shot showing you what the
little Hitachi drive had to say about itself.
|

(Click
here to enlarge)
|
Photo
3—Things are good when the numbers in this photo
match the numbers written on the hard drive. |
Now
that you know how to get data from the hard drive, I’ll
show you how to read a sector. Before the code is tested,
however, there’s work to be done on the hard drive,
and you’ll need a way to verify your results.
Because
I plan to develop AVR firmware to manipulate FAT32 formatted
drives, it would be logical to format the hard drives
that will be used with MSDOS by way of Windows 98. Formatting
in this way puts master boot records, partition tables,
and data in predictable places on the drive. Two drives
should be formatted: one is used on the ATA hard drive
controller, and the other is used on a PC for verification
and as an aid in debugging.
The
verification program for the PC is called WinHex. Normally,
WinHex is used to inspect and repair files on PC hard
drives. This program does it all as far as hard drives
are concerned; it understands FAT12, FAT16, FAT32, NTFS,
and CDFS. In addition, WinHex includes a disk editor
that allows you to become a dangerous hard drive technician.
You can also use WinHex to create templates that automatically
parse known data areas of the hard drive.
Photo
4 is a screen shot of an actual WinHex panel that’s
aimed at the 2.5" Hitachi drive attached to the
PC. I’ve dialed in the MBR master boot record (MBR),
which resides at cylinder 0, head 0, and sector 1 or
LBA 0. If all goes well with the sector read on the
hard drive controller, the data in the Hyper-Terminal
window should be identical to the bytes found in the
WinHex window.
|

(Click
here to enlarge)
|
Photo
4—There isn’t much about a hard drive you will want
to know that WinHex won’t tell you. |
The
HyperTerminal readout in Photo 5 matches the numbers
picked up by WinHex from the clone drive in Photo 4.
As Spock would say, "Random chance seems to have
operated in our favor." The ata_read_sector() function
shown in Listing 3 works as designed.
Reading a sector in LBA mode entails loading the Device/Head
register with the LBA mode bit set, loading the cylinder
High/Low and Sector registers, and issuing the Read
Sectors command.
|

(Click
her to enlarge)
|
Photo
5—The format of the data may not be pretty, but
the data itself is beautiful because it matches
the clone drive’s MBR data read by WinHex. |
Here’s
where that big chunk of external 16-bit SRAM is handy.
Instead of pulling the data directly into the AVR, I
used the AVR to generate address information for the
SRAM and manipulate the SRAM’s write enable and chip
select lines to store the incoming data in the external
64 KB of SRAM.
I divided the SRAM into logical pages of 256 words each
and wrote routines to read and write these pages. Each
page of external SRAM holds one sector of hard drive
data, allowing up to 256 sectors to be buffered. That
pretty much takes care of verifying the ATA hard drive
controller’s read functionality.
Writing
to the hard drive is a similar process. The external
SRAM is filled with a sector’s worth of data (256 words),
and then that particular SRAM page is written to the
hard drive I/O port’s data bus. Instead of performing
an ATA I/O read, an ATA I/O write is performed when
the data is presented on the external SRAM data pins.
I tested the write sector code successfully on random
sectors of the hard drive attached to the ATA hard drive
controller. Additionally, I verified the writes by moving
the hard drive to the PC and reading the sectors I wrote
using WinHex.