Working on the SPLD to use as an I/O decoder

LATICE GAL22V10D Programmable Logic Device

I have GAL20V8Z’s available and the free WinCUPL from ATMEL. I needed five chip selects; UART, PIO, DISK, RAM1 and RAM2. I would like to implement a wait-state generator in case I need to use it at the higher Z80 clock speeds. My first stab at it was a simple I/O decoder splitting the ROM up to access at 0x0000 to 0x00FF and 0xE000 to 0xFFFF, RAM1 at 0x0100 to 0x7FFF and RAM2 at 0x8000 to 0xDEFF. I needed to use A[15..8] along with the MREQ and IORQ lines. Oops! WinCUPL failed because I needed too many product terms. I then tried the same with a GAL22V10 and it fit, so I ordered some but they are 3 to 4 weeks away coming from China.

Solderless bread-board to test SPLD functionality.

Next I committed myself to nix the ROM, which allowed me to use just A[15..12], thereby simplifying my design to fit into the GAL20V8Z’s I have. I really wanted to test my code in a live Z80 environment so I wired up a solderless bread-board with a simple ATmega328P, a Z80 with all data lines held low (to execute NOP’s) and the GAL20V8Z. The AVR conrols the RESET and CLOCK lines so I wrote a quick program to initialize them and drive the Z80.

Note on the GAL20V8Z; it has a nice power-saving feature wherein it looks at all the input pins for activity. If all of them do not change state for a certain time period, the part goes into a low-power standby mode util it sees activity again. Since I am using it as an I/O decoder, I don’t see that it will ever get to switch into power-saving mode unless the Z80 executes a HALT or I am running the AVR/Z80 in “single-step” mode.

I own a ZEROPLUS Logic Cube LAPC-16016) 16-channel USB logic analyzer, which was imperative for verifying the functionality and timing of this PLD design.

I had some problems getting the GAL20V8Z to function properly. The UART select kept toggling with the MREQ line. Wierd since the simulation showed all test vectors with correct results! After many hours of re-coding CUPL and re-burning GAL20V8Z’s, I started to get the impression that it was the programmer because even the simplest boolean equations burned in the SPLD did not function correctly. Since the programmer would let me select GAL20V8, GAL20V8A ,GAL20V8B, GAL20V8C and GAL20V8D parts, tried the GAL20V8D device and it started to work, I was using the GAL20V8 setting, which turned out to be the culprit. Odd since all five parts have the same number of bits in their fuse map.

After getting the I/O decoding to work, I set out to design in the wait-state generator since I had a few output pins left on the GAL20V8Z. I used the 74xx74 D-flip-flop reference right out of the Z80 data sheet. The simulation worked correctly, hi-z until it drove the Z80 WAIT pin low. In reality, I could not get it to work as the WAIT pin never went low. Grounding the OE pin (13) on the GAL20V8Z allowed me to see the states of the internal “wait 1” and “wait 2” signals from the registers and I found the problem, which was corrected. So after several hours diddling with the wait-state generator, I had it working at the 8MHz Z80 clock speed. Time to try lower clock speeds. After switching gears to 4MHz, I could see that the Z80 was held in a perpetual WAIT cycle. Switching back to 8MHz and the problem cleared. Hmm… This should not be happening as the internal flip-flops are clocked by the Z80 clock and driven by the MREQ line. A few more hours of diddling and I decided that if I was going to need the wait-states, they would be at the 8MHz clock speed so I added a WS-EN (wait-state enable) pin to be driven by the AVR. If the AVR is generating 8MHz for the Z80 then it will enable the wait-states, if not, then it disables the wait-state generator and now the Z80 functions fine at the lower clock speeds, all the way down to 31.250KHz. After musing over the RAM IC data sheets for the IDT and CYPRESS 32Kx8 static RAMS I have, the cycle time is 150ns between sequential reads. It is likely that I will never need wait-states for the RAM … but I may need them to interface with the AVR UART and DISK emulation.

Z80 memory/IO decoder SPLD with wait-states enabled
Z80 memory/IO decoder SPLD without wait-states enabled

If anyone has any input on why the wait-state generator only functions when the clock speed is 8MHz, I have posted the CUPL code below and I would appreciate some feedback and/or tips.

Name Z80_IOdecode-1V10;
Partno Z80C;
Revision 01;
Date 9/20/2016;
Designer Quest, Johnny;
Assembly Z80 Computer;
Location U4;
Device g20v8a;

 *               ______________
 *              | Z80_IOdecode |
 *  sys_clk x---|1           24|---x Vcc 
 *    !mreq x---|2           23|---x !rst
 *    !iorq x---|3           22|---x UART_sel 
 *    ws_en x---|4           21|---x PIO_sel 
 *          x---|5           20|---x !DSK_sel 
 *          x---|6           19|---x !RAM1_sel
 *          x---|7           18|---x !RAM2_sel
 *      a12 x---|8           17|---x !WAIT 
 *      a13 x---|9           16|---x wait1 
 *      a14 x---|10          15|---x wait2 
 *      a15 x---|11          14|---x !m1 
 *      GND x---|12          13|---x !oe 
 *              |______________|
/* This device generates chip select signals for two */
/* 32Kx8 static RAMs. */
/* It also drives the system WAIT line to insert a */
/* wait-state of at least one cpu clock for all memory */
/* accesses. 1 wait-state is internally generated by */
/* the Z80 for I/O accesses */

/** Inputs **/
PIN 1 = sys_clk; /* System Clock */
PIN 2 = mreq; /* Z80 MREQ (active low) */
PIN 3 = iorq; /* Z80 IORQ (active low) */
PIN 4 = ws_en; /* Wait-state enable (active high) */
PIN [8..11] = [a12..a15]; /* upper 4 address bits */ 
PIN 23 = rst; /* System reset (active low) */
PIN 14 = m1; /* Z80 M1 (active low) */
PIN 13 = !oe; /* Output Enable (active low) */
/** Outputs **/
PIN 22 = UART_sel; /* 82C51A UART (active high) */ 
PIN 21 = PIO_sel; /* 82C55A PIO (active high) */ 
PIN 20 = DSK_sel; /* SDmemory interface (active low) */ 
PIN 19 = RAM1_sel; /* 32Kx8 RAM (active low) */ 
PIN 18 = RAM2_sel; /* 32Kx8 RAM (active low) */ 
PIN 17 = WAIT; /* insert 1 WAIT-STATE (active low) */ 
PIN 16 = wait1; /* Internal Use (combinatorial logic out only) */
PIN 15 = wait2; /* Internal Use (combinatorial logic out only) */

/** Declarations and Intermediate Variable Definitions **/
FIELD address = [a15..12]; /* upper 4 addresses */

uart_eqn = !iorq & !m1 & address:[0000]; /*******************************/
pio_eqn = !iorq & !m1 & address:[1000]; /* */
dsk_eqn = !iorq & !m1 & address:[2000]; /* I/O Address */
 /* Ranges */
ram1_eqn = !mreq & !m1 & address:[0XXX..7XXX]; /* */
ram2_eqn = !mreq & !m1 & address:[8XXX..FXXX]; /*******************************/

/** Logic Equations **/

/* UART/PIO select (active high) */
UART_sel = uart_eqn; /* high for addresses 0000h-0FFFh */
PIO_sel = pio_eqn; /* high for addresses 1000h-1FFFh */

/* DSK select (active low) */
DSK_sel = !dsk_eqn; /* low for addresses 2000h-2FFFh */

/* RAM select (active low) */
RAM1_sel = !ram1_eqn; /* low for RAM addresses 0100h-7FFFh */
RAM2_sel = !ram2_eqn; /* low for RAM addresses 8000h-DFFFh */

/* wait state generator */
wait1.d = (!mreq # !iorq); /* Synchronous Wait-state */
wait2.d = wait1; /* wait1 delayed */
WAIT.oe = ws_en & wait1 & !wait2; /* Turn Buffer ON */
WAIT = !(ws_en & wait1 & !wait2); /* End Wait */

Next up; Interfacing with the 74HC299.


Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s