Skip to content

Hardware Design Notes

J.B. Langston edited this page May 20, 2018 · 19 revisions

Several other people had the idea to combine an AVR with a Z80 before me, but none of these projects provided all the capabilities that I wanted, and some of them had not shared any code. I have taken inspiration from these projects but I designed this specific circuit and software implementation myself. The design choices I've made are described below.

Bus Interface

The ATmega1284p doesn't have enough I/O to interface with all of the Z80's bus lines, so I used an I/O expander to provide two additional 8-bit I/O ports. I have tried to optimize performance by connecting the most frequently-changed signals directly to the AVR, and connecting those less-frequently changed to the I/O expander. The AVR has direct connections for the LSB of the address bus, the data bus, and following control lines: MREQ, IORQ, RD, WR, M1 and HALT.

The MSB of the address bus and the remaining control lines--INT, NMI, RESET, BUSRQ, and BUSACK--are on the I/O expander. This arrangement allows the AVR to do faster DMA than would be possible if the entire address bus were on the I/O expander. With just the upper 8 bits of the address on the expander, the AVR only has to use the slower SPI interface to change the MSB of the address bus once every 256 bytes of memory instead of for every byte.

PB0 on the AVR is used as an output to flash an LED by the bootloader, so it cannot safely be connected to an output signal on the Z80. Therefore, is connected to the IOACK line on the 74HCT74, which can be safely used as an output during the boot process.

Clock Generation

The AVR produces a 10MHz clock signal for the Z80 using hardware PWM. The PWM clock can be divided if desired to prevent time-sensitive software from running too quickly. When debugging, the clock is brought under software control of the clock so that the AVR can sample the bus for each clock cycle. Under software control, the clock can achieve a maximum frequency of about 4MHz but when tracing information is being logged, the attainable clock rate slows down to several hundred KHz.

IORQ Handling

Because the AVR cannot respond to an I/O request quickly enough to satisfy the timing demands of the Z80, the IORQ line also sets a flip-flop that asserts the WAIT line on the Z80 to add wait states until the AVR can respond. The AVR polls the IORQ line in a tight loop and services the request when IORQ goes low. When the AVR has finished servicing the I/O request, it brings the reset line on the flip-flop low to deassert the WAIT line on the Z80.

However, care must be taken to avoid bus contention when switching the bus directions. Therefore, after setting the data bus port to output in response to an IO read, the AVR asserts the BUSRQ line before releasing the WAIT line. The Z80 samples this before the next machine cycle begins and tristates the data bus to avoid any bus contention. The AVR releases the BUSREQ signal after it has switched its data port back to an input and it's safe for the Z80 to resume control of the bus. I got this idea from the AVR/Z80 project by just4fun.

Previously, was bringing the clock under software control to perform cycle-exact timing when handing the bus back after an I/O read. However, I discovered that this interfered with other peripherals on the bus, such as a YM2149 sound card, that relied on having a stable clock frequency.

To prevent the z80ctrl from adding wait states when other peripherals on the bus are being addressed, I have added an address decoder to allow the AVR to be assigned to none, all, or a selectable block of 64 I/O ports ($00-$3F, $40-$7F, $80-$BF and $C0-$FF). The flip flop will only trigger a wait state if the I/O address is within the selected range. I'm using half of a 74HC139 dual 2-to-4 decoder to do this.

Halting

The HALT input on the AVR is connected to the Z80 through a diode so that it can also be grounded using a push button without shorting the halt pin on the Z80. Thus, it is possible to return to the AVR-based monitor system automatically when the Z80 executes a halt instruction, or manually by pressing the halt button. The HALT pin on the AVR has the internal pullup resistor enabled so that the halt signal is high (inactive) when neither the HALT signal is asserted by the Z80 nor the button is pressed.

SPI Bus

The SPI signals are exported to the user pins on the RC2014 bus. This should allow additional SPI-based peripherals to be added to the RC2014 bus and the AVR can act as a bridge between them and the Z80. The RC2014 bus lines USER1-6 are connected to SCK, MISO, MOSI, IO expander chip select, and 2 unassigned chip select lines, respectively. The other half of the 74HCT139 is used as an SPI chip select multiplexer to allow the AVR to control up to 4 SPI peripherals with just 2 I/O pins. The traces to these pads can be cut or the pins can be omitted if this is not desired. The MCP23S17 I/O expander has a 3-bit serial addressing scheme so it should be possible to add up to 7 additional I/O expanders to the bus with only a single chip select line shared between all 8 I/O expanders. The I/O expander on the z80ctrl board is hardcoded to address 0.

Clone this wiki locally