circuitcellar.com
Magazine Support   Digital Library   Products & Services   Suppliers Directory 
 
 





 

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.