SparkFun Forums 

Where electronics enthusiasts find answers.

Have questions about a SparkFun product or board? This is the place to be.
By Vraz
#11760
I recently decided to branch out from using microcontrollers and design a simple microprocessor board. My first effort is a simple Z-80 using modern parts. Thought I had it figured out, but ran into a problem (after spending considerable time laying out the board). The Z-80 has a notion of "I/O ports" (versus memory mapped I/O). My board makes the I/O port operations read/write to a special area of memory instead of to external hardware.

During an I/O operation, the Z-80 puts the 8-bit I/O port address onto address bits A0-A7. However, it puts random data onto A8-A15 (my design assumed they were high-Z). I therefore need a method to force A8-A15 to either all zeroes or ones during an I/O operation without impacting their ability to go high-Z at other times.

The Z-80 (z84c00) and 128K SRAM (68128) are both CMOS devices. My original plan (when I believed the pins went high-Z) was to use 8 signal diodes (1N4148) tied to a gate that went logic-low at the appropriate time. However, since the address lines are driven during this time, I am concerned this would either damage the drivers in the Z-80 and/or simply not work.

I have very limited board space for a solution. While I could possibly add an 8-bit buffer with high-Z outputs plus my diodes it would be a difficult fit. I do have room for an additional 8 resistors. Would putting a resistor inline between the Z-80 & SRAM limit current such that connecting the address line to ground (while its driven) produce logic-low to the SRAM without damaging the Z-80?
By jasonharper
#11761
If your I/O operations are just going to write to memory anyway, why not forget about I/O operations completely and use only memory operations? That would appear to be the simplest solution.

While A8..A15 are officially undefined during I/O, on the original Z80 there were some circumstances where they had predictable values. Specifically, the in & out instructions that specified the port via the C register (rather than a literal constant) were known to place the B register's value on the high address lines. (This was one of many Z80 anomalies exploited by the Sinclair ZX80/81 computers.) I don't know if this is still present on current Z80 parts, however.

Hmm, it's been many years since I've thought about this, but I seem to recall that the trick only works on input - for output, there's no way to separately specify values for the data bus and A8..15. I suspect that makes it useless for you.

Series resistors would definitely allow you to safely pull the SRAM side of the high address bus to a known value via diodes. It will reduce the maximum speed of your SRAM somewhat (since there's now an additional RC delay before the high address lines reach their threshholds), and will increase your power consumption by a mA or two in the worst case.
By Vraz
#11763
If your I/O operations are just going to write to memory anyway, why not forget about I/O operations completely and use only memory operations? That would appear to be the simplest solution.
I was attempting to keep I/O support potential compatibility with existing code. My stretch goal was to port CP/M with MMC for storage (would take less space than my S-100 system ;-)). With the AVR as an I/O processor, it should be possible to simulate external devices. As you note, A8-A15 are not actually random but cannot be set to useful values during out instructions.
Series resistors would definitely allow you to safely pull the SRAM side of the high address bus to a known value via diodes. It will reduce the maximum speed of your SRAM somewhat (since there's now an additional RC delay before the high address lines reach their threshholds), and will increase your power consumption by a mA or two in the worst case.
So this is where my electronics knowledge runs out. Any guess on the required series resistor size? Looks like the Z-80 is spec-ed around 2ma per pin with 2.4v logic high. My (hopefully wrong) guess is 1200 ohms. Total capacitance is 15pf for Z-80, 6pf for SRAM. Would that make RC delay around 25ns? Was hoping to run at 10mhz using 70ns SRAM.
By jasonharper
#11780
If the Z80 pins are rated at 2 mA, and there is possibly a 0-5V difference across each series resistor, the resistors would have to be at least 2500 ohms.

I assume that 6 pf figure is for the SRAM chip itself - there will be more capacitance due to the wiring between the resistor and the chip. I'll make a rough guess of 5 pf more - that makes the RC time constant about 27 ns.

Note that the time constant is the time for the voltage to get 2/3 of the way to its final value - if an address line was at 5V, it's going to take nearly two time constants for it to get below the TTL low threshhold. So, 10 MHz operation with a 70 ns SRAM just isn't going to work with this scheme, at least not reliably. You'd need 8 channels of 2:1 mux or 8 gates of some sort to force the high address lines to a known state in well under 30 ns.

I guess I'm still not seeing how this is supposed to work. Ok, you've redirected an I/O output operation to a memory location - how does your AVR I/O processor fit into this? How does it read that location, and how does it know that it's been written?
By Vraz
#11797
If the Z80 pins are rated at 2 mA, and there is possibly a 0-5V difference across each series resistor, the resistors would have to be at least 2500 ohms.
Makes sense. I misread the datasheet and thought 2.4v was the max logic high-- it was the minimum (no max specified) and thus must assume Vcc (5v) and thus 2500 ohms.
I assume that 6 pf figure is for the SRAM chip itself - there will be more capacitance due to the wiring between the resistor and the chip. I'll make a rough guess of 5 pf more - that makes the RC time constant about 27 ns.
Interesting. So the capacitance of the processor is not included? I assumed the capacitance of all devices on the bus would be.

Regardless, its clearly not possible to solve this with passive components at my performance target. Therefore, I will eliminate the diodes all together and modify my software approach. Not optimal, but the most practical. The following I/O operations work when using 16-bit I/O addressing:
Code: Select all
xor a,a   ; zero a8-a15
in a,(n)   ; input from address n (0-255)

ld bc,n    ; setup i/o address
in r,(c)   ; input from address in bc

ld bc,n    ; setup i/o address
out (c),r  ; output to address in bc
I guess I'm still not seeing how this is supposed to work. Ok, you've redirected an I/O output operation to a memory location - how does your AVR I/O processor fit into this? How does it read that location, and how does it know that it's been written?
The AVR is connected to the SRAM via expansion port along with IOREQ, BUSRQ, BUSAK. By becoming bus master, it can write to normal memory and i/o memory at will (they are just mapped to different 64K areas).

In terms of notifying the AVR of I/O activity, I plan to use a simple address decoding scheme connected to an AVR interrupt pin. Because communication between the Z-80 & AVR is via shared memory, there is no requirement for the AVR to respond within a particular timeframe (other than Z-80 software wanting its data). The AVR will also be connected to the Z-80's interrupt pin should I want to notify it.

The AVR can provide reset control, bootstrap code, serial i/o and non-volatile storage (mmc card on SPI), etc. The idea is to keep the Z-80 hardware simple and leave the I/O complexity to the AVR.
By jasonharper
#11798
The capacitance on the Z80 side of the series resistors would be charged/discharged through a lesser resistance (2500 ohms less, assuming you're using the minimum value resistors), and therefore the RC time constant would be shorter. I was assuming that it was enough shorter to be negligible. To accurately determine the value, you'd need to know the effective resistance of the Z80's outputs. This is probably not going to be on the datasheet, but can be approximated from things that are - for example, Vol/Iol would give you one data point.

I don't know enough about AVRs to recommend a specific solution to you. On a sufficiently large PIC, I'd be tempted to use the Parallel Slave Port to latch the I/O value (completely ignoring the I/O port address), and build an I/O request protocol around that.
By Vraz
#11846
I don't know enough about AVRs to recommend a specific solution to you. On a sufficiently large PIC, I'd be tempted to use the Parallel Slave Port to latch the I/O value (completely ignoring the I/O port address), and build an I/O request protocol around that.
Most of the larger AVRs have direct support for external memory (requires a single latch to demux d0-d7/a0-a7). Even cooler, you can software program the size of the address bus (8-16 bits) and wait states (0-3).

ANDing together IOSEL+A16+WR and connecting to INT provides an interupt indicator. I plan to have the Z-80 setup data in low I/O space (anything below 32K) and then signal an int for a particular "device":
Code: Select all
LD BC,$ffff ; address $ffff is int selector
IN A,(C)    ; read current selector
SET A,4     ; indicate device 4 needs service
OUT (C),A   ; store to memory -- triggers INT on AVR
When the AVR gets an INT, it becomes bus master and then reads address $ffff and stores zero. The bits indicate which devices require service (reading the appropriate low-memory I/O addresses).

I think the scheme will be fairly efficient. With a 10mhz Z-80 and 16mhz AVR, I suspect it will outperform my 4mhz S-100 system even with the extra I/O latency.