Electronics » the Z80 project » ide interface
Note: There's a newer version of my IDE interface / article here
My Z80 Project was sorely lacking in the local non-volatile storage dept.
I thought about adding a flash memory IC (almost) directly to CPU
address/data bus as I had some old BIOS chips recycled from PC motherboards
that would have done the job. However they wouldn't hold much data - half
a meg - and the only practical "file system" would have been "console memory
card" style block save approach.
 Compact Flash to IDE Adapter & Card. Ta eBay :) |
Thinking of flash RAM in general, I noticed how ridiculously
cheap camera memory cards were getting, especially on ebay
(seeing how people only really want the highest capacity cards nowadays).
Looking at the different types available (MMC, Secure
Digital, Compact Flash, Memory Stick et al) and scanning the
net for interface specs, I found the most commonly used cards
in hobbiest's projects were the Compact Flash type. This is because - as
I soon found out - they can run in "True IDE mode", ie: they can use the
same interface as hard drives, CDROMs etc. All that's needed is an adapter
(check out the Hong Kong based suppliers on ebay) and you can attach 'em to a
normal IDE connector.
I figured the IDE / Compact flash route would be cheap, offer large capacity and
allow me to do all my testing with an old harddrive, rather than risk spooning up
a memory card. (It'd still require a file system for anything more than
random access to thousands of 512 byte sectors, but that could come later..)
Also the IDE interface specs are well documented on the net - people have made
IDE interfaces for the CBM64, data loggers, PIC chips etc so I theres lots
of 'inspiration' out there...
Firstly, here's the IDE pin-outs (not worried about the CF card pin-outs, the adapter takes care of that. Beware though that Compact Flash cards are not TTL level compliant and
require a 4.0V logic high according to the spec sheet.)
/Reset - 1 2 - Ground
Data7 - 3 4 - Data 8
Data6 - 5 6 - Data 9
Data5 - 7 8 - Data 10
Data4 - 9 10 - Data 11
Data3 - 11 12 - Data 12
Data2 - 13 14 - Data 13
Data1 - 15 16 - Data 14
Data0 - 17 18 - Data 15
Ground - 19 20 - No pin (Key)
DMARQ - 21 22 - Ground
/Write - 23 24 - Ground
/Read - 25 26 - Ground
IORDY - 27 28 - CSEL (Cable select pull-up)
DMACK - 29 30 - Ground
IRQ - 31 32 - Obsolete: no connection (formerly "IOCS16")
Addr1 - 33 34 - PDIAG / CBLID (UDMA detect)
Addr0 - 35 36 - Addr2
/CS0 - 37 38 - /CS1
DASP - 39 40 - Ground
Electrically, it's just a 16-bit data bus, with a 3-bit address bus, 2 chip select
signals, a read & a write signal plus a drive reset line. (Compact Flash cards will
work in an 8-bit mode too, but I havent looked into that.) Most home-brew IDE circuits I
have seen leave the DMARQ, DMACK, IORDY, PDIAG, CSEL and IRQ lines unconnected,
however the following may be of interest:
DMARQ is an output from the drive used in high speed DMA transfers so presumably can
be ignored. DMACK is an input at the drive used to acknowledge DMA mode - I noted
that the Compact Flash ATA mode spec sheets say if there is no DMA-mode ability in
the host (as there wouldn't be for a project like this) DMACK should be driven high.
IORDY is an output from the drive which can be used to slow down read/write cycles if they
are too quick (ie: insert wait states) I have not used this signal. The ATA spec sheet
suggests it be pulled high with a 1k resistor min (presumably, that's only if it is to be
used by the host circuit.)
"CSEL" is generally pulled high via a 10K resitor. It can be used for cable select of master
and slave when using a special cable. These cables ground CSEL, and have the conductor cut
beyond the first connector, this way the first device sees itself as the master (pulled down)
and the second sees itself as slave (not pulled down).
The "DASP" pin just an open collector drive activity indicator and can be connected
to a LED from Vcc via a resistor. All other pins, (except the ground lines of course:)
can be ignored and left unconnected.
On the software side the interface protocol has been expanded to very complicated levels
over the years but I didnt need super-fast interrupt-based DMA modes etc. Fortunately its
all pretty straightforward: Basically you tell the drive the sector number you want to
access, give it a command and then transfer the data. Communicating with the drive is
done via 16 IDE registers, the IDE address lines A0-A2 and chip select lines /CS0 and /CS1
dictating which IDE register is being accessed. Data is written to (and appears in) these
registers depending on what the drive is doing (more info later).
Onto the actual interface electronics: An IDE interface would be a direct bus connection
to the Z80 if wasnt for those meddling kids the 16-bit data bus. As it is, you need a
couple of latch ICs, a buffer and some control logic (or a microcontroller with a lot
of ports) to handle the 8-to-16bit conversion. (I used 74HC574 latch chips and a 74HC245
buffer. Another latch was used to hold the IDE address bus and chip select signals but
in other projects these could come direct from the host perhaps.)
 Bytes from the 'Get ID' command. Note the drive name. |
Here's a schematic (hand scribbled - sorry:) of my interface - some of the input lines are
specific to my project - for example "/IOWR3", "/IORD3" and "/IOW2" are the outputs of a
74HC139 port/address demultiplexor on my CPU board that were going begging. There's also
a couple of doubled up inverter gates whose purpose is nothing more than to make absolutely
sure that certain signals arrive at the correct time via propagation delays.
In my implementation of the interface, in order to send a 16bit word to the interface I write the
high byte to (my Z80 board's) port 7, then the low byte to it's port 3 - the circuit
presents to the full 16 bit value to the IDE pins when the second byte is written. To
read a 16bit word from the drive I read the low byte from (my Z80 board's) port 3 (which
also clocks the high byte in a latch) and then read the high byte from its port 7. Writing to
(my Z80 board's) port 2 simply sets the IDE's A0,A1,A2,/CS0 and /CS1 lines (IDE register
select etc) - I could've controlled /RESET from this latch too but just made a resistor/
capacitor timer instead. Note that only the sector data transfers (to reg 0) are 16 bit, commands
and reads to/from the other registers are 8 bit so the high byte can be ignored.
Here's a run down of the IDE registers accessed by A0-A2 and /CS0 and /CS1 - some
simplifications have been made. EG: I'm ignoring all the old head/cylinders/sectors
rubbish as that's ancient history - using the LBA (Logical Block Address) mode
is the way to go. Nowadays each sector is simply addressed sequentially, via the
28 bit address held in registers $3-$6 (Incidentally, this is where the pre-LBA48
137GB drive size limit comes from in Windows: 2^28 sectors * 512 bytes = ~137GB.
I've not looked into the way LBA48 works as I'm not going to be needing anything
like that amount of capacity!).
IDE reg: A0-A2: /CS0: /CS1: Use:
$0 000 0 1 IDE Data Port
$1 001 0 1 Read: Error code
$2 010 0 1 Number Of Sectors To Transfer
$3 011 0 1 Sector address LBA 0 (low byte)
$4 100 0 1 Sector address LBA 1
$5 101 0 1 Sector address LBA 2
$6 110 0 1 Sector address LBA 3 (high nybble in 0:3) **
$7 111 0 1 Read: "Status", Write: Issue command to drive
$8 000 1 0 Not Important
$9 001 1 0 Not Important
$A 010 1 0 Not Important
$B 011 1 0 Not Important
$C 100 1 0 Not Important
$D 101 1 0 Not Important
$E 110 1 0 Not Important
$F 111 1 0 Not Important
** Other bits in register $6:
Bit 4= Select Master (0) or Slave (1) drive
Bit 5= Always set to 1
Bit 6= Always Set to 1 for LBA Mode Access
Bit 7= Always set to 1
Some detail: The most important register is IDE register $7 which accepts
commands when written to and reports the drive status when read.
IDE Register $7 WHEN WRITTEN = Send command to drive.
Key commands (there's loads more besides) :-
$20 - Read sectors with retry
$30 - Write sectors with retry
$EC - Identify drive
IDE Register $7 WHEN READ = drive status:
Bit: Name: Condition:
0 ERR 1 = Previous command ended in an error
1 IDX (not important)
2 CORR (not important)
3 DRQ 1 = Data Request Ready (Sector buffer ready for transfer)
4 DSC (not important)
5 DF 1 = Write Fault
6 RDY 1 = Ready for command
7 BUSY 1 = Controller is executing a command.
IDE Reg $1: Can be read for more detail when the ERR (bit 0)
in STATUS (reg $7) above is set:
Bit: Condition:
0 1 = DAM not found
1 1 = Track 000 not found
2 1 = Command aborted
3 Reserved
4 1 = ID not found
5 Reserved
6 1 = Uncorrectable ECC error
7 1 = Bad block detected
Sending commands
The "Identify drive" command is useful as it tells you your interface
is working (without having to read anything from the disk itself). The
command returns 512 bytes of data containing a lot of info about the
connected/selected drive including the device name in ASCII (40 chars
@ byte offset $36) and the capacity of the drive in sectors (2 words @ byte
offset $72, least significant first).
The procedure for getting a drive's ID is:
1. Make sure BUSY=0 and RDY=1 before proceding.
2. Select the drive master/slave with appropriate write to IDE reg $6.
3. Send "Drive ID Command" ($EC) to Command Register $7
4. Wait until BUSY=0 and DRQ=1
5. Check error flags.
6. Read 256 words from data port 0.
The procedure for reading a sector is:
1. Make sure BUSY = 0 and RDY =1 before proceding.
2. Tell the drive what sector is required (fill in the LBA address regs)
3. Set number of sectors to transfer to 1 (reg $2)
4. Issue read sector command ($20) to reg $7
5. Wait until BUSY=0 and DRQ=1
6. Check error flags.
7. Read 256 words from data port 0.
The procedure for writing a sector is:
1. Make sure BUSY = 0 and RDY = 1 before proceding.
2. Tell the drive what sector is to be written (fill in the LBA address regs)
3. Set number of sectors to transfer to 1 (reg $2)
4. Issue write sector command ($30) to reg $7
5. Wait until BUSY=0 and DRQ=1
6. Write 256 words to data port 0.
7. Wait for BUSY to become clear and RDY to become set.
8. Check error flags.
And that's about for the basics of reading and writing to an IDE drive a
sector at a time. Here's the Z80 source code I programmed to operate
my Z80 interface - as usual its The Z80 Project-specific, not well
optimized, poorly commented and may have bugs. "Educational use only"
as they say.
 The finished board (includes keyboard interface) |
Oh yes, file systems. Small memory cards (and hard drives) tend to use the FAT
file system and this is reasonably well documented on the net. It is
however overkill for what I need (what with its variable cluster sizes,
partition tables and so on) so.. I made my own.
Info here.
Update: The IDE interface circuit was (largely) integrated into a CPLD when I did the
V3 Z80 Project. A newer schematic is here
Further Reading:
CompactFlash.Org - Specs available for download etc
Peter Faasse's 8255 to IDE interface
Compact Flash to IDE adapters on ebay UK
The Alya Project (more CF->IDE stuff)
Hans Summer's IDE interface
(Old) ATA spec text file
Official ATA specs - very detailed (too much so in fact)
|