Thursday, 26 September 2013

SPI - the Key to the Project

Using SPI to Output Track Data

I looked at several floppy disk projects for ideas on how to make the Raspberry Pi work as a virtual floppy disk. On in particular has been quite useful - the SVD. It was based on a MicroChip PIC controller and bit-banged the output line representing the floppy disk track data. The difficulty with this approach is that it relies on assembler code and very tight timing where each instruction time is measured and accounted for. This is not compatible with an operating system where interrupts could stop the flow of execution and cause gaps in the output data.

Let the Hardware do the Heavy Lifting

After a bit of investigation I decided to try the SPI bus instead. This would let the specialized hardware of the Raspberry Pi do a lot of the work for me. Put simply, I'm ignoring the SPI protocol and asking the SPI hardware to perform parallel to serial conversions and to do it according to a specified clock rate. The logic analyzer screen shot below shows the SPI clock on channel 1 and the SPI output on channel 0. The first two output bytes are 0xFF and the third byte is 0x00.

Note that the SPI clock is not continuous. There are eight clock pulses for each output byte followed by a one clock cycle pause. So what we actually have on the output are eight bits that can be toggled on and off followed by a ninth 'spacer' bit which is always zero. The challenge is to use this capability to output a signal that emulates the floppy drive data output.

The initial attempt set the SPI clock so that the total time for the eight bits plus the ninth 'spacer' bit equaled 8 microseconds. A value of 0x80 was transmitted for a data bit 0 which only has the clock bit set and a value of 0x88 was transmitted for a data bit 1 which has both clock and data bits set. Therefore each byte of track data was translated into eight bytes of SPI data where each byte of SPI data represents a single bit of track data. The screenshot below shows the timing.

The output signal deviated from the ideal signal because of the spacer bit, but the floppy disk controller was still able to decode this correctly as the bits appeared within the expected timing window.

A more accurate approach doubled the SPI clock speed and represented each clock or data bit with a single SPI byte using a bit pattern of 11000000b or 0xC0 in hexadecimal for a '1' and zero for a '0'. (Clock bits are always '1' except for the special address marks noted in the previous blog post.) Note that because the clock speed is doubled we have to set two bits instead of one for each output '1'. Therefore each byte of track data was translated into sixteen bytes of SPI data where the even bytes of SPI data represented the clock bits and the odd bytes represented the data bits. The screenshot below shoes the timing. The clock and data bits are still .888 ┬Ás but the output pulses and gaps are now much closer to the ideal signal.

With the double-rate scheme a single-density track becomes 50,000 bytes of SPI data which must be transmitted every 200 milliseconds or one byte every four microseconds. You may wonder what happens if the software is interrupted for a period greater than four microseconds. Will there be a gap in the SPI output signal? Fortunately the Raspberry Pi SPI has a 64 byte input buffer which will take 256 microseconds to empty using the double-rate scheme. The current software runs on Risc OS where the recommended maximum time to disable interrupts is 100 microseconds so even in the worst case situation the buffer is unlikely to be emptied while the operating system is handling an interrupt. If I ever figure out how to enable DMA for the SPI even faster data rates would be possible.

More on SPI and GPIO programming in later blog posts.

Wednesday, 25 September 2013

Floppy Disk Format From Bytes to Bits

Floppy Disk Track Format

The original TRS-80 Expansion Interface supported single-sided, single-density floppy disks with 35 tracks and ten sectors per track with 256 bytes per sector. The floppy disk controller supported many more format variations, but this was the standard format used on the first systems sold.

The actual track data contains padding bytes at the start of the track and between sectors. Each sector starts with a header that contains a special address byte followed by the track number, the head number (used in later drives), the sector number, a sector length code and a two byte CRC.

A typical track layout would look something like this:

  • 16 bytes of 0xFF
  • 6 bytes of 0x00
  • 1 byte ID Address Mark - 0xFE (with special bit pattern)
  • 1 byte track number (0x00 for track zero)
  • 1 byte head number (always 0x00 for original TRS-80 drives)
  • 1 byte sector number (sector numbers start at zero)
  • 1 byte sector length code (always 0x01 for standard format)
  • 2 byte CRC (calculated by the floppy disk controller)
  • 256 bytes of sector data
  • 11 bytes of 0xFF for sector 0, 12 bytes of 0xFF for all other sectors
  • 6 bytes of zero
  • 1 byte Data Address Mark - 0xFB (with special bit pattern)
  • 256 bytes of sector data.
  • 2 byte CRC (calculate by the floppy disk controller)
  • 12 bytes of 0xFF
  • 6 bytes of 0x00
  • Then nine more sectors as above, starting with another ID Address Mark
  • Finish with a bunch of 0xFF until the sector is finished. Typically around 130 bytes.
The extra zeros and 0xFF between the sectors headers and sector data allow the floppy disk controller to synchronize to the data stream while waiting for the special address marks. Since the drive speed can vary slightly between drives, especially on older models drives, sectors can be slightly shorter or longer. The padding allows room for these speed variations and on a disk that's been written on different drives there may be slight variations in the number of padding bytes.

Soon after the introduction of the original floppy disk drives, third-party manufacturers started to offer disks with 40 and even 80 tracks. 80 track drives had twice as many tracks per inch and therefore floppy disks formatted on those drives could not be read or written in older 35/40 track drives.

Data Encoding

The actual data on a single density floppy disk is written using a scheme called FM - Frequency Modulation. In FM encoding each byte is represented by a stream of clock bits and data bits.

The picture above is from an actual floppy disk as captured by a Saleae Logic Analyzer with the data bits colored red and the clock bits colored blue. The picture represents a single byte of data. Since all the data bits are present, the byte is 0xFF. If none of the data bits were present and only the clock bits appeared, the byte would be 0x00. On the TRS-80 Model I with single-density the clock and data bits are 1 microsecond wide and the gaps between clock and data bits are 3 microseconds wide. That corresponds to 8 microseconds per bit.

For all normal data bytes from 0x00 to 0xFF the clock bits are always present and the data bits are present or missing according to what byte value is represented. However, there are seven special address mark exceptions to this rule which are used by the 1771 floppy disk controller. These are 0xF8 through 0xFE but with certain clock bits missing. The missing clock bits allow the floppy disk controller to find these address marks in the stream of bits coming from the drive.

The Address Marks look like this:

'Deleted' Address Mark 0xF8

Non Standard Address Mark 0xF9 (only used by 1771 FDC)

Non Standard Address Mark 0xFA (only used by 1771 FDC)

Data Address Mark 0xFB
'Index' Address Mark 0xFC

Address Mark 0xFD
'ID' Address Mark 0xFE

Two file formats that are commonly used to store TRS-80 floppy disk as files are described here. When turning these formats into raw bit data the correct bit patterns for both normal data bits and the address marks need to be generated. Therefore knowledge of the address marks is important for correct emulation. The emulator will also have to add the various padding bytes to create a complete bit image of a track. Since the drive rotates at a speed of 300 rpm an ideal track should contain 50,000 bits of raw data (including both clock and data bits) which will take 0.2 seconds to transmit at a data rate of 4 microseconds per bit.

Tuesday, 24 September 2013

Introduction to the project

Last winter I started a project to create a Virtual Floppy disk emulator for the TRS-80 Model I using a Raspberry Pi. The original project description is here.

My TRS-80 Model 1

The project was inspired by several similar projects based on Arduino and other micro-controllers and because my ancient TRS-80 Model I was having problems reading and writing floppy disks as the electronics and hardware aged. It's also rather difficult to transfer software to and from the TRS-80 without such a system.

The advantage of the Raspberry Pi is that it is a complete system with video, USB, keyboard, mouse and storage support. The disadvantage is that the current available operating systems are not ideal for real-time. But the hardware features of the processor help overcome that limitation as will be seen later.

TRS-80 Floppy Disk Interface (Model I)

The TRS-80 Model I expansion interface (which supported the first floppy drives for the TRS-80) was created around 1978. It was designed to use existing floppy disk drives (with perhaps some minor customizations) connected by ribbon cable to the expansion interface. It used a Western Digital 1771 floppy disk controller IC which supported single density only, running at a clock speed of 1 MHz. (The timing diagrams on the 1771 data sheet are for 2 MHz so you have to adjust them for the slower speed of the Model I.)

Interface Signals

For this discussion I will refer to output signals as signals output from the Raspberry Pi emulator that input to the TRS-80 expansion interface and input signals as signals coming into the Raspberry Pi emulator from the expansion interface. Because several floppy drives could be connected to the same line the TRS-80 used open collector TTL interfacing with 150 ohm pull-up resistors to 5 volts. This meant that 'active' signals pulled the voltage down to zero.

Note: The connectors on the TRS-80 Expansion Interface are flat two-sided card-edge connectors but the schematic in the technical reference manual refers to "Pin numbers" so "Cable pin" refers to the pin numbers from the schematic.

The output signals are:
  1. Index Pulse. Cable Pin 8. This short (4 millisecond) pulse is sent at the beginning of every track. On a real floppy drive this is generated by a photocell pointed at a LED shining through the index hole on the floppy disk.
  2. Track Zero. Cable pin 26. Active when the R/W/ head is at track zero (the home position). This is used by the floppy disk controller when seeking.
  3. Write Protect. Cable pin 28. Also generated by a photocell/LED combination when the write protect notch on the disk is covered.
  4. Read Data. Cable pin 30. This is the data from the disk R/W head that has been transformed from the raw analog signal to digital pulses by circuitry in the floppy disk drive. Because drives may vary slightly in speed from the ideal 300 rpm the floppy disk controller is somewhat tolerant of variations from the ideal signal. My system has an add-on data separator board from Percom that improves the performance in this area.
Because more than one floppy drive can be connected to the cable, a drive should only pull an output signal low when it has been made active (selected) by the drive select signal. The TRS-80 supported up to four drives, numbers from 0 to 3.

The input signals are:
  1. Drive Select 0. Cable pin 10. Selects drive 0 when active. In an actual drive this would cause the R/W head to press against the disk surface. DS0 on the schematic.
  2. Drive Select 1. Cable pin 12. Selects drive 1 when active. DS1 on the schematic.
  3. Drive Select 2. Cable pin 14. Selects drive 2 when active. DS2 on the schematic.
  4. Drive Select 3. Cable pin 32. Selects drive 3 when active. DS3 on the schematic.
  5. Motor On. Cable pin 16. Turns all drive motors on. The emulator should only be transmitting track data on the Read Data pin and accepting data on the Write Data pin when both Motor On and the appropriate Drive Select signals are active.The expansion interface contained a timer circuit that would automatically shut off this signal after a few seconds if the operating system was not using the drive interface.
  6. Direction Select. Cable pin 18. Selects the direction of movement of the R/W head when the step signal is pulsed.
  7. Step. Cable pin 20. This short (about 8 microseconds) pulse indicated that the R/W head should be moved one step in the direction indicated by the Direction Select pin. Fortunately the Raspberry Pi has programmable edge detect triggers that will watch for this and set a bit when the line is pulsed. Since an actual floppy disk takes a couple of milliseconds (or more) to move the physical head from one track to the next but the emulator can move the virtual R/W head almost instantly, the timing is not critical. I have been able to poll the edge detect signal rather than trying to use interrupts (which are not well documented).
  8. Write Data. Cable pin 22. Not yet implemented. This would contain the data to be written to the disk surface.
  9. Write Gate. Cable pin 24. Not yet implemented. Signals that the write data is valid and should be written to the disk. Stays low for the duration of the write operation.

Interface Circuitry

There were two main considerations when choosing what ICs to interface between the Rasperry Pi and the TRS-90 expansion interface. The first was the TTL logic (5v) used by the TRS-80 which is not compatible with the lower voltage (3.3v) logic on the Raspberry Pi. The second was the very low resistance (150 ohm) of the pull-up resistors. This results in a fairly high current and I needed to use 74LS06 to drive the output signals. Also, many level translation chips, especially bi-directional ones, would not work with such low resistance pull-up values. Fortunately I found I was able to avoid level translation chips in my design. While I could have used a 74LS05 for the input signals I found it was simpler to use a 74LS06 for both input and output so I could chose the IC that was most convenient.

Originally I put a level translation between the Pi and the 74LS06 for the output signals but it turned out not to be necessary and the Pi seems to be able to drive the 74LS06 directly. The input signals use the open collector of the 74LS06 (could be a 74LS05, but easier to use the same IC for both) and rely on the pull-up resistors on the GPIO pins to perform the level translation to the correct voltage. The Pi has programmable pull-up resistors on the GPIO pins so I just program them in the initialization code. Two GPIO pins also have pull-up resistors on the board itself, so it's convenient to use these two as input pins for the floppy drive interface.

Note: all Raspberry Pi pins refer to the Rev 2 pins as seen here.

Current configuration is shown below.

Read Data must be connected to GPIO pin 10.
Write Data must be connected to GPIO pin 9.
GPIO pin 11 (which is the SPI clock) should be left alone. I connect it to a pin for use with a logic analyzer to help me see what's happening.
I also leave GPIO pins 7 and 8 alone as they are part of the SPI interface and unavailable as I am using that interface for read and write data. GPIO 2 and 3 have pull-up resistors on the board itself so I use them as inputs.

Note: All outputs are pulled up to 5v by 150 ohm resistors on the Expansion Interface.

Write Protect (output) connected to GPIO 18 - Header pin P1-12.
Track 0 (output) connected to GPIO 23 - Header pin P1-16.
Read data (output) connected to GPIO 10 - Header pin P1-19.
Index Pulse (output) connected to GPIO 24 - Header pin P1-18.

Drive Select 0 (input) connected to  GPIO 2 - Header pin P1-03. (has 1K8 pull-up on the board)
Drive Select 1 (input) connected to GPIO 25 - Header pin P1-22.
Motor On (input) connected to GPIO 4 - Header pin P1-07.
Direction Select (input) connected to GPIO 17 - Header pin P1-11.
Direction Step (input) connected to GPIO 27 - Header pin P1-13.
Drive Select 2 (input) connected to GPIO 22 - Header pin P1-15.
Write Gate (input) connected to GPIO 15 - Header pin P1-10.
Write Data (input) connected to GOIP 9 - Header pin P1-21.

Update Sept 30. I found that using GPIO 3 as an input caused Rasbpian to hang while booting so I have changed Drive Select 1 to use GPIO 25, header pin P1-22 instead. This problem doesn't exist under RISC OS so perhaps a driver needed to be disabled.

One of the drives connected to the cable should have 150 ohm pull-up resistors to 5v. Only one drive should have the pull-up resistors and generally it is the last drive on the cable. I put a set of pull-up resistors on my interface board and a jumper between the resistors and 5v from the Pi power supply. The TRS-80 drive cable does not provide +5 volts so it's necessary to use +5 volts from the Pi board.

Not listed here are the ground signals from the cable which should be connected to ground on the Pi.

 This is a picture of the top of the original prototype (plugged into the Raspberry Pi beneath it). The IC on the left is a level translation board which I found was not necessary. The connector to the Pi is at the bottom left. The cable that connects to the Expansion Interface is at the top. The resistors are pull-up resistors and the yellow jumper connects them to +5v. The board itself is just a plain prototype board with a cutout for one of the Pi connectors that is too high for the pin header.

This is a picture of the bottom of the original prototype. The connector that is plugged into the Pi is at the left. The floppy disk connector is at the right.