z80

Troubleshooting Grounding on a Z80 System


Lately I've been working on a Z80 system with 3 expansion chips (74HC138), 1 RAM chip, a 20MHz clock and a 40-pin connector for connecting to external systems.

However, when debugging the Z80 with an ESP32 using a code that writes 0b00000000 on the data bus, reads the address and prints it on the serial port, it does not work correctly, the addresses increment randomly, but when touching the GND it increments correctly . Most likely there is something wrong with the board's grounding.

NOTE:

  1. The board has a manual "switch" for the clock, allowing you to switch between internal clock (20MHz) and external clock (external system = ESP32)
  2. The board was soldered with thin enameled copper wires.

Z80 System schematic

Z80 + ESP32

ESP32 debug code

#include <Arduino.h>

int pinAddress[] = {17, 5, 18, 19};
int pinData[] = {13, 12, 14, 27, 26, 25, 33, 32};
int pinClock = 16;

void writeData(byte data)
{
  for (int i = 0; i < sizeof(pinData) / sizeof(pinData[0]); i++)
  {
    digitalWrite(pinData[i], (data << i) & 1);
  }
}

byte readAddress()
{
  byte addr;
  for (int i = 0; i < sizeof(pinAddresss) / sizeof(pinAddresss[0]); i++)
  {
    addr |= digitalRead(pinAddresss[i]) << i;
  }

  return addr;
}

void setup()
{
  Serial.begin(115200);
  pinMode(pinClock, OUTPUT);

  for (int i = 0; i < sizeof(pinAddresss) / sizeof(pinAddresss[0]); i++)
  {
    pinMode(pinAddresss[i], INPUT);
  }

  for (int i = 0; i < sizeof(pinData) / sizeof(pinData[0]); i++)
  {
    pinMode(pinData[i], OUTPUT);
  }

  writeData(0x00);
}

void loop()
{
  digitalWrite(pinClock, HIGH);
  delay(250);
  digitalWrite(pinClock, LOW);
  delay(250);

  Serial.println("Address: 0b" + String(readAddress(), BIN));
}

ESP32 output

Address: 0b1111
Address: 0b1111
Address: 0b1111
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b0
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b1
Address: 0b10
Address: 0b10
Address: 0b1
Address: 0b1
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b11
Address: 0b1100
Address: 0b1100
Address: 0b10
Address: 0b10
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b1101
Address: 0b110
Address: 0b110
Address: 0b11
Address: 0b11
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b111
Address: 0b0
...

Solution

  • The Z80 is not a single-clock CPU. Please consult your reference manual to learn how and when the Z80 reads from memory.

    You need to synchronize on one edge of nMREQ to see when a new memory cycle occurs.

    To provide the "program" of endless NOPs correctly, observe nRD and nMREQ. Only if both of them are active, which is low, drive the data bus with data. However, in this primitive setup, you can get away with a statically driven data bus.

    Helpful details

    This is the relevant timing diagram from the Z80 Family CPU User Manual straight from Zilog:

    enter image description here

    Change you sketch such that it reads the lines before the rising edge of the clock. Then it shall fetch the instruction address when it sees nMREQ = low AND nRD = low the first time after both being high. This is the start of the clock state T2. Please note that the start of clock state T3 has the same pattern.

    This suffices for your most simple experiment. For a correct sampling add nM1, which signals by low that the memory cycle is the first opcode fetch cycle. As you will learn, different instructions have different numbers of bytes. Instructions with more than one byte read their additional bytes without an active nM1, please be aware of this.