Skip to content

Phase 1 - Pico Supervisor

VS Code tasks

Choose Tasks: Run Task in the Z80ROMlessSBC workspace.

Diagnostic Build task Deployment task
Phase 1 supervisor Build Pico - Phase 1 supervisor Flash Pico - Phase 1 supervisor

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 0 pass gate. Install: Pico 2 W only; all other active sockets empty.

Wiring - Pico 2 W

Program Phase 1 off-board, disconnect USB, fit its soldered headers with USB toward row 1, then wire every connection in the Pico table. Continuity-check actual empty socket contacts and adjacent pins, not only wire ends. Keep the clock path short with a nearby GND return; do not add loops to equalize wire lengths.

Pico Control Outputs

Done Signal Pico connection Destination socket contact
CLK Pico GP2 - header pin 4 U4 AHCT244 input - pin 2
RESET# Pico GP3 - header pin 5 U1 Z80 RESET# - pin 26
BUSREQ# Pico GP4 - header pin 6 U4 AHCT244 input - pin 4
BOOT_READ_DISABLE Pico GP5 - header pin 7 U4 AHCT244 input - pin 6
PICO_DATA_UP_OE# Pico GP6 - header pin 9 U3 HCT32 input - pin 10
DATA_DOWN_OE# Pico GP7 - header pin 10 U10 LVC245 OE# - pin 19
IO_RELEASE Pico GP9 - header pin 12 U3 HCT32 input - pin 5

Wire all Pico connections from the table above with other active devices absent. Continuity-check every destination socket contact and adjacent pins. Tick each row after checking its connection; progress is saved in the Phase 1 checklist.

Phase 1 Firmware

Build z80_stage01_supervisor; load build/src/stage01_supervisor/z80_stage01_supervisor.uf2 using the firmware/console procedure. Maintained application.

Done Command Behavior Measurement
s Sample input/data levels Monitor inputs may float with U7 absent
w Walk seven control outputs, 250 ms each Scope the correct empty socket contact and neighbors
r Hold RESET# alternately HIGH/LOW GP3/header 5 and Z80 socket 26: 3.20-3.40 V / near zero
d Hold one data GPIO HIGH; advance D0-D7 Probe U9/U10 A pins 2-9
x Restore defaults and data inputs Both OE requests HIGH; RESET LOW

Single-Step Data-Pin Test

Type d without Enter. One GP10-GP17 output stays HIGH until the next d; all others remain LOW. The ninth step restores input mode. If the initial inputs are not all LOW, firmware refuses the test: check the eight individual 10 kOhm GP10-GP17 pull-down resistors. Use x to stop. USB disconnect restores any held data/reset test.

Test Plan

Apply external +5 V before USB. Check Pico 3.3 V and that no GPIO sees +5 V.

At startup GP6/GP7/GP4/GP5 must be HIGH; GP2/GP3/GP9 LOW.

Scope each walking output at its empty destination; software PASS only means the sequence ended. Use held r/d levels for meter checks. Do not run these commands with CPU, SRAM, or any other active device fitted.

Pass Gate

  • Correct routing and no neighboring pin activity.
  • Isolated defaults.
  • Valid rail voltages.

Floating monitor readings are not a pass or a fault by themselves.

Maintained Source

These complete files are included from the repository at documentation build time, not copied into this page.

Phase 1 application - src/stage01_supervisor/main.c
#include <stdio.h>

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

static const uint TEST_OUTPUT_PINS[] = {
  PIN_CLK,
  PIN_RESET_N,
  PIN_BUSREQ_N,
  PIN_BOOT_READ_DISABLE,
  PIN_DATA_UP_OE_N,
  PIN_DATA_DOWN_OE_N,
  PIN_IO_RELEASE,
};

static const uint DATA_HEADER_PINS[] = {14, 15, 16, 17, 19, 20, 21, 22};
static int data_step = -1;
static bool reset_high = false;

static uint print_data_levels(void) {
  uint high_mask = 0;
  printf("Data GPIO levels:");
  for (uint bit = 0; bit < 8; ++bit) {
    bool level = gpio_get(PIN_DATA_0 + bit);
    high_mask |= (uint)level << bit;
    printf(" D%u/GP%u=%u", bit, PIN_DATA_0 + bit, (unsigned)level);
  }
  printf("\n");
  return high_mask;
}

static void restore_safe_levels(void) {
  z80_safe_startup();
  data_step = -1;
  reset_high = false;
}

static void toggle_reset_output(void) {
  reset_high = !reset_high;
  gpio_put(PIN_RESET_N, reset_high);
  printf("RESET# GP3 commanded %s: Pico header pin 5, Z80 socket pin 26\n",
         reset_high ? "HIGH" : "LOW");
    printf("Expected voltage at both contacts: %s (measure relative to common GND)\n",
      reset_high ? "3.20-3.40 V" : "near 0 V");
    printf("RESET# must never exceed the Pico 3.3 V rail; no 5 V pull-up permitted\n");
  printf("r: toggle RESET#, x: restore safe levels (Phase 1 only); measure the voltage\n");
}

static void step_data_output(void) {
  if (data_step < 0) {
    restore_safe_levels();
    if (print_data_levels() != 0) {
      printf("NOT STARTED: data inputs must all be LOW; check RN3 and wiring\n");
      return;
    }
    for (uint bit = 0; bit < 8; ++bit)
      output_with_initial_level(PIN_DATA_0 + bit, false);
  } else {
    gpio_put(PIN_DATA_0 + (uint)data_step, false);
  }

  ++data_step;
  if (data_step == 8) {
    restore_safe_levels();
    printf("DONE: data steps finished; safe levels restored, GP10-GP17 inputs\n");
    return;
  }

  uint bit = (uint)data_step;
  gpio_put(PIN_DATA_0 + bit, true);
  printf("D%u HIGH: GP%u, Pico header pin %u, AHCT245/LVC245 A%u pin %u\n",
         bit, PIN_DATA_0 + bit, DATA_HEADER_PINS[bit], bit + 1, bit + 2);
  printf("Other data pins LOW; d: next, x: restore safe levels (Phase 1 only)\n");
}

static void print_status(void) {
  printf("BUSACK#=%u IORQ#=%u RD#=%u WR#=%u\n",
         gpio_get(PIN_BUSACK_N), gpio_get(PIN_IORQ_N),
         gpio_get(PIN_RD_N), gpio_get(PIN_WR_N));
  print_data_levels();
}

int main(void) {
  z80_safe_startup();
  stdio_init_all();

  printf("\nZ80 ROMless SBC - Stage 1 supervisor\n");
  printf("r: toggle RESET# HIGH/LOW (Phase 1 hardware only)\n");
  printf("w: walking output test (Phase 1 hardware only)\n");
  printf("d: step D0-D7, hold one HIGH (Phase 1 hardware only)\n");
  printf("x: restore safe levels and return data GPIOs to inputs\n");
  printf("s: sample input status\n");

  while (true) {
    if (!stdio_usb_connected()) {
      if (data_step >= 0 || reset_high)
        restore_safe_levels();
      tight_loop_contents();
      continue;
    }

    int command = getchar_timeout_us(0);
    if (command == 'r') {
      toggle_reset_output();
    } else if (command == 'w') {
      restore_safe_levels();
      printf("walking %u outputs\n",
             (unsigned)(sizeof(TEST_OUTPUT_PINS) / sizeof(TEST_OUTPUT_PINS[0])));
      z80_walking_output_test(TEST_OUTPUT_PINS,
                              sizeof(TEST_OUTPUT_PINS) / sizeof(TEST_OUTPUT_PINS[0]),
                              250);
      printf("PASS: safe levels restored\n");
    } else if (command == 'd') {
      step_data_output();
    } else if (command == 'x') {
      restore_safe_levels();
      printf("DONE: safe levels restored, GP10-GP17 inputs\n");
    } else if (command == 's') {
      print_status();
    }
    tight_loop_contents();
  }
}
Shared startup and isolation - src/common/supervisor.c
#include "z80sbc/supervisor.h"

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

static bool banked_memory_available;
static z80_memory_bank_driver_t bank_driver;

void z80_memory_bank_set_driver(z80_memory_bank_driver_t driver) {
  bank_driver = driver;
}

void __not_in_flash_func(z80_memory_bank_select)(unsigned bank) {
  if (bank_driver != NULL)
    bank_driver(bank & 1u);
  else
    gpio_put(PIN_MEMORY_BANK, bank & 1u);
}

void z80_memory_bank_set_available(bool available) {
  banked_memory_available = available;
}

unsigned __not_in_flash_func(z80_memory_bank_status)(void) {
  return banked_memory_available ? BANKED_ABI_STATUS | gpio_get(PIN_MEMORY_BANK) : 0;
}

void output_with_initial_level(uint pin, bool level) {
  gpio_init(pin);
  gpio_put(pin, level);
  gpio_set_dir(pin, GPIO_OUT);
}

void input_with_no_pull(uint pin) {
  gpio_init(pin);
  gpio_set_dir(pin, GPIO_IN);
  gpio_disable_pulls(pin);
}

void z80_isolate_buses(void) {
  gpio_put(PIN_DATA_UP_OE_N, 1);
  gpio_put(PIN_DATA_DOWN_OE_N, 1);
}

void z80_safe_startup(void) {
  output_with_initial_level(PIN_DATA_UP_OE_N, 1);
  output_with_initial_level(PIN_DATA_DOWN_OE_N, 1);
  output_with_initial_level(PIN_RESET_N, 0);
  output_with_initial_level(PIN_MEMORY_BANK, 0);
  bank_driver = NULL;
  banked_memory_available = false;
  output_with_initial_level(PIN_BUSREQ_N, 1);
  output_with_initial_level(PIN_BOOT_READ_DISABLE, 1);
  output_with_initial_level(PIN_IO_RELEASE, 0);
  output_with_initial_level(PIN_CLK, 0);
  for (uint pin = PIN_DATA_0; pin <= PIN_DATA_7; ++pin)
    input_with_no_pull(pin);
  input_with_no_pull(PIN_BUSACK_N);
  input_with_no_pull(PIN_IORQ_N);
  input_with_no_pull(PIN_RD_N);
  input_with_no_pull(PIN_WR_N);
  input_with_no_pull(PIN_PORT_A0);
  input_with_no_pull(PIN_PORT_A1);
  input_with_no_pull(PIN_PORT_A2);
  input_with_no_pull(PIN_PORT_A4);
}

void z80_walking_output_test(const uint *pins, size_t count,
                             uint32_t dwell_ms) {
  for (size_t index = 0; index < count; ++index)
    output_with_initial_level(pins[index], false);

  for (size_t active = 0; active < count; ++active) {
    for (size_t index = 0; index < count; ++index)
      gpio_put(pins[index], index == active);
    sleep_ms(dwell_ms);
  }

  z80_safe_startup();
}