Skip to content

Phase 5 - Data Translation

VS Code tasks

Choose Tasks: Run Task in the Z80ROMlessSBC workspace.

Diagnostic Build task Deployment task
Phase 5 data bus Build Pico - Phase 5 data bus Flash Pico - Phase 5 data bus

Deployment tasks force-update without checking the previous firmware phase. Complete this phase's hardware prerequisites and save work before flashing. See Pico tools.

Tick setup items when complete and tests only after recording the measured result. Progress is saved in this browser; ticks are not automatic verification.

Prerequisite: Phase 4. Install: U9 AHCT245 and U10 LVC245. CPU/SRAM still absent.

Wiring - Bidirectional Data Path

U9 is the SN74AHCT245N Pico-to-5 V translator; U10 is the SN74LVC245AN 5 V-to-Pico translator. Disconnect external power and USB and keep both translators out of their sockets until wiring checks pass. CPU and SRAM remain absent.

Each row below is one shared net with branches, not a series path through a chip. Tick each row after checking every listed contact and isolation from adjacent pins. Use the connection tables below and translator reference.

5 V data-bus branches

Z80 to SRAM

Rows are ordered by SRAM pin number, highest to lowest.

Done Signal Z80 socket → SRAM socket
D7 Z80 pin 13 → SRAM pin 21
D6 Z80 pin 10 → SRAM pin 20
D5 Z80 pin 9 → SRAM pin 19
D4 Z80 pin 7 → SRAM pin 18
D3 Z80 pin 8 → SRAM pin 17
D2 Z80 pin 12 → SRAM pin 15
D1 Z80 pin 15 → SRAM pin 14
D0 Z80 pin 14 → SRAM pin 13

Z80 to U9 AHCT245, then U10 LVC245

Keep the same D7-to-D0 order. Use the second free hole connected to each Z80 data pin for the hop to U9. At U9, use another hole in the same connected breadboard group for the hop to U10. The signal does not pass through U9's circuitry: all four IC contacts for a bit remain on one shared net. Tick each row after checking both hops.

Done Signal Z80 socket → U9 AHCT245 B pin U9 AHCT245 B pin → U10 LVC245 B pin
D7 Z80 pin 13 → U9 pin 11 U9 pin 11 → U10 pin 11
D6 Z80 pin 10 → U9 pin 12 U9 pin 12 → U10 pin 12
D5 Z80 pin 9 → U9 pin 13 U9 pin 13 → U10 pin 13
D4 Z80 pin 7 → U9 pin 14 U9 pin 14 → U10 pin 14
D3 Z80 pin 8 → U9 pin 15 U9 pin 15 → U10 pin 15
D2 Z80 pin 12 → U9 pin 16 U9 pin 16 → U10 pin 16
D1 Z80 pin 15 → U9 pin 17 U9 pin 17 → U10 pin 17
D0 Z80 pin 14 → U9 pin 18 U9 pin 18 → U10 pin 18

Pico-side data branches and pull-downs

Pico pin numbers are physical pin positions; GPIO names are in parentheses. Verify the eight individual 10 kOhm pull-downs fitted in Phase 0, not a second set. These pulls belong on the Pico side, not the 5 V data bus.

Done Signal Pico pin U9 AHCT245 A pin U10 LVC245 A pin Existing pull-down
D0 Pico pin 14 (GP10) U9 pin 2 U10 pin 2 10 kOhm to GND
D1 Pico pin 15 (GP11) U9 pin 3 U10 pin 3 10 kOhm to GND
D2 Pico pin 16 (GP12) U9 pin 4 U10 pin 4 10 kOhm to GND
D3 Pico pin 17 (GP13) U9 pin 5 U10 pin 5 10 kOhm to GND
D4 Pico pin 19 (GP14) U9 pin 6 U10 pin 6 10 kOhm to GND
D5 Pico pin 20 (GP15) U9 pin 7 U10 pin 7 10 kOhm to GND
D6 Pico pin 21 (GP16) U9 pin 8 U10 pin 8 10 kOhm to GND
D7 Pico pin 22 (GP17) U9 pin 9 U10 pin 9 10 kOhm to GND

Supplies, directions and enables

Verify existing Phase 0/1/2 connections rather than adding duplicate wires or capacitors. Supply, ground and DIR links are direct wires, not resistors.

Done Pin / component Required connection
U9 pin 20, VCC +5 V
U9 pin 10, GND Common GND
U9 pin 1, DIR +5 V: A to B
U10 pin 20, VCC Pico-derived +3.3 V, not +5 V
U10 pin 10, GND Common GND
U10 pin 1, DIR Common GND: B to A
U9 pin 19, OE# U3 HCT32 output pin 8; U3 input pin 9 receives Z80 RD# pin 21 and input pin 10 receives Pico pin 9 (GP6)
U10 pin 19, OE# Pico pin 10 (GP7)
C6/C7, 100 nF each C6 near U9 pin 20 across +5 V/GND; C7 near U10 pin 20 across +3.3 V/GND
Installation Confirm no raw 5 V bus reaches Pico pins; verify rails, continuity, adjacent-pin isolation and pin-1 orientation before fitting U9/U10 with power and USB disconnected

Once fitted, apply external +5 V before USB and disconnect USB before removing +5 V. U9 lacks data-port power-off isolation; disabling OE does not make USB-only operation safe. Never enable both data paths at once.

Firmware and Tests

Build z80_stage05_data_bus; UF2 lives in build/src/stage05_data_bus/. Maintained application.

For upward d patterns, temporarily pull raw RD# LOW through 1 kOhm with CPU absent; otherwise the HCT32 correctly keeps U9 disabled. Keep SRAM absent and boot inhibit HIGH.

Scope 00/FF/55/AA and walking-bit patterns on all eight bus lines. Verify U10 remains disabled throughout upward drive.

Remove the temporary RD pull. For i, apply weak external test pulls to D0-D7 and sample the downward path; U9 must remain disabled.

For e, reinstall the 1 kOhm raw RD# pull-down at the empty Z80 socket pin 21, keeping CPU/SRAM absent and boot inhibit HIGH. Fit external 0xAA test pulls: D1/D3/D5/D7 each through 10 kOhm to +5 V, and D0/D2/D4/D6 each through 10 kOhm to GND. Do not attach another push-pull bus driver. Disconnect USB and external power before changing any test pull, then restore external +5 V before USB.

e repeats 1000 drive/isolate/receive cycles. Capture U9 OE# pin 19 and U10 OE# pin 19: require each to go LOW in its respective phase, an interval with both HIGH between phases, and no overlapping LOW enables. Without the RD# pull-down, U9 stays disabled and the test can report PASS just by reading the external 0xAA pulls. Even with the correct fixture, byte readback alone does not prove upward drive or exclusion; retain the upward d captures and the actual-OE captures as separate pass evidence.

x isolates both paths. The software result does not measure voltage.

Pass Gate

  • Correct 5 V upward and 3.3 V downward levels.
  • Stable disabled paths and no simultaneous OE assertion.
  • No excessive current.
  • Remove every temporary test pull before fitting CPU/SRAM in Phase 6.

Maintained Source

These complete files are included from the repository at documentation build time, not copied into this page. The shared bus module also implements the four-bit port sampling used in Phase 3.

Phase 5 application - src/stage05_data_bus/main.c
#include <stdio.h>

#include "pico/stdlib.h"
#include "z80sbc/bus.h"
#include "z80sbc/supervisor.h"

static void output_data_patterns(void) {
  static const uint8_t patterns[] = {0x00, 0xFF, 0x55, 0xAA};
  for (size_t index = 0; index < sizeof(patterns); ++index) {
    z80_data_bus_drive(patterns[index]);
    printf("data=%02x\n", patterns[index]);
    sleep_ms(250);
  }
  for (unsigned int bit = 0; bit < 8; ++bit) {
    z80_data_bus_drive((uint8_t)(1u << bit));
    sleep_ms(100);
    z80_data_bus_drive((uint8_t)~(1u << bit));
    sleep_ms(100);
  }
  z80_data_bus_isolate();
  printf("PASS: patterns complete; verify on analyzer\n");
}

static bool data_endurance_test(void) {
  const uint8_t expected = 0xAA;
  for (unsigned int cycle = 0; cycle < 1000; ++cycle) {
    z80_data_bus_drive(expected);
    z80_data_bus_isolate();
    z80_data_bus_prepare_input();
    uint8_t actual = z80_data_bus_sample();
    z80_data_bus_isolate();
    if (actual != expected) {
      printf("FAIL: data cycle %u expected=%02x actual=%02x\n",
             cycle, expected, actual);
      return false;
    }
  }
  return true;
}

int main(void) {
  z80_safe_startup();
  stdio_init_all();
  printf("\nStage 5: data transceivers; CPU/SRAM absent\n");
    printf("d=output patterns, i=sample data, e=1000 cycles with external "
      "AA test pulls, x=isolate\n");

  while (true) {
    int command = getchar_timeout_us(0);
    if (command == 'd') {
      output_data_patterns();
    } else if (command == 'i') {
      z80_data_bus_prepare_input();
      printf("data=%02x\n", z80_data_bus_sample());
      z80_data_bus_isolate();
    } else if (command == 'e') {
      printf(data_endurance_test() ? "PASS: 1000 data interlock cycles\n"
                                   : "FAIL: data endurance\n");
    } else if (command == 'x') {
      z80_isolate_buses();
    }
    tight_loop_contents();
  }
}
Shared data paths and port sampling - src/common/bus.c
#include "z80sbc/bus.h"

#include "pico/stdlib.h"
#include "z80sbc/pins.h"

enum { DATA_MASK = 0xFFu << PIN_DATA_0 };
_Static_assert(PIN_DATA_7 == PIN_DATA_0 + 7, "data GPIOs must be contiguous");
_Static_assert(PIN_PORT_A1 == PIN_PORT_A0 + 1 &&
                   PIN_PORT_A2 == PIN_PORT_A0 + 2 &&
                   PIN_PORT_A4 == PIN_PORT_A0 + 3,
               "port GPIO snapshot mapping changed");

uint8_t __not_in_flash_func(z80_port_bus_sample)(void) {
  return z80_port_decode(gpio_get_all() >> PIN_PORT_A0);
}

void __not_in_flash_func(z80_data_bus_isolate)(void) {
  gpio_set_mask((1u << PIN_DATA_UP_OE_N) | (1u << PIN_DATA_DOWN_OE_N));
}

void __not_in_flash_func(z80_data_bus_drive)(uint8_t value) {
  z80_data_bus_isolate();
  busy_wait_us_32(1);
  gpio_put_masked(DATA_MASK, (uint32_t)value << PIN_DATA_0);
  gpio_set_dir_masked(DATA_MASK, DATA_MASK);
  busy_wait_us_32(1);
  gpio_put(PIN_DATA_UP_OE_N, 0);
}

void __not_in_flash_func(z80_data_bus_prepare_input)(void) {
  z80_data_bus_isolate();
  gpio_set_dir_masked(DATA_MASK, 0);
  busy_wait_us_32(1);
  gpio_put(PIN_DATA_DOWN_OE_N, 0);
}

uint8_t __not_in_flash_func(z80_data_bus_sample)(void) {
  return (uint8_t)(gpio_get_all() >> PIN_DATA_0);
}