Phase 2 - Output Buffer, Fixed Logic, and Clock¶
VS Code tasks¶
Choose Tasks: Run Task in the Z80ROMlessSBC workspace.
| Diagnostic | Build task | Deployment task |
|---|---|---|
| Phase 2 buffers and clock | Build Pico - Phase 2 buffers and clock | Flash Pico - Phase 2 buffers and clock |
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.
Phase Setup Checklist¶
| Done | Requirement |
|---|---|
| Phase 1 pass gate completed | |
| CPU, SRAM and data translators remain absent | |
| U4 SN74AHCT244N fitted after the wiring checks below pass, with external power and USB disconnected | |
| U3 SN74HCT32N fitted after the wiring checks below pass, with external power and USB disconnected |
Wiring - Output Buffer and Fixed Logic¶
Disconnect external power and USB. Keep U3/U4 out of their sockets while wiring and checking the tables below. CPU, SRAM and later-phase devices also remain absent. Connections already fitted in Phases 0/1 need checking, not a second wire. The buffer and gate reference explains the channel functions.
U4 AHCT244 input connections¶
| Done | Signal | Source socket/header contact | Destination socket contact |
|---|---|---|---|
| CLK | Pico GP2 - header pin 4 | U4 AHCT244 input - pin 2 | |
| BUSREQ# | Pico GP4 - header pin 6 | U4 AHCT244 input - pin 4 | |
| BOOT_READ_DISABLE | Pico GP5 - header pin 7 | U4 AHCT244 input - pin 6 | |
| MREQ# | U1 Z80 MREQ# - pin 19 | U4 AHCT244 input - pin 8 | |
| WR# | U1 Z80 WR# - pin 22 | U4 AHCT244 input - pin 11 | |
| RD# | U1 Z80 RD# - pin 21 | U4 AHCT244 input - pin 13 | |
| SRAM bank select | Pico GP8 - header pin 11 | U4 AHCT244 input - pin 17 |
U4 AHCT244 outputs to CPU and SRAM¶
| Done | Signal | Source socket contact | Destination socket contact |
|---|---|---|---|
| SRAM WE# | U4 AHCT244 output - pin 9 | U2 SRAM WE# - pin 29 | |
| SRAM A16 | U4 AHCT244 output - pin 3 | U2 SRAM A16 - pin 2 | |
| SRAM CE# | U4 AHCT244 output - pin 12 | U2 SRAM CE# - pin 22 | |
| BUSREQ# | U4 AHCT244 output - pin 16 | U1 Z80 BUSREQ# - pin 25 | |
| CLK | U4 AHCT244 output - pin 18 | U1 Z80 CLK - pin 6 |
The remaining U4 outputs, pins 7 and 14, connect to U3 in the next table.
U3 HCT32 gate connections¶
Rows are ordered by U3 pin number. Inputs and outputs are identified explicitly.
| Done | Signal | Source socket/header contact | Destination socket contact |
|---|---|---|---|
| Buffered RD# | U4 AHCT244 output - pin 7 | U3 HCT32 input - pin 1 | |
| Buffered BOOT_READ_DISABLE | U4 AHCT244 output - pin 14 | U3 HCT32 input - pin 2 | |
| SRAM OE# | U3 HCT32 output - pin 3 | U2 SRAM OE# - pin 24 | |
| Raw IORQ# | U1 Z80 IORQ# - pin 20 | U3 HCT32 input - pin 4 | |
| IO_RELEASE | Pico GP9 - header pin 12 | U3 HCT32 input - pin 5 | |
| WAIT# | U3 HCT32 output - pin 6 | U1 Z80 WAIT# - pin 24 | |
| DATA_UP_OE# | U3 HCT32 output - pin 8 | U9 AHCT245 OE# - pin 19 | |
| Raw RD# | U1 Z80 RD# - pin 21 | U3 HCT32 input - pin 9 | |
| PICO_DATA_UP_OE# | Pico GP6 - header pin 9 | U3 HCT32 input - pin 10 |
Supplies, grounded pins and unused outputs¶
These supply and ground connections are direct links, not resistors. The local capacitors are additional to the direct supply/ground wires. R24 is a separate 10 kOhm pull-down from Pico physical pin 11 (GP8) to GND; U4 pin 17 and SRAM A16 pin 2 must not be directly grounded.
| Done | Device | Pin / component | Required connection |
|---|---|---|---|
| U4 AHCT244 | Pin 1, OE# | Common GND | |
| U4 AHCT244 | Pin 5, unused output | Leave open | |
| U4 AHCT244 | Pin 10, power GND | Common GND | |
| U4 AHCT244 | Pin 15, unused input | Common GND | |
| U4 AHCT244 | Pin 19, OE# | Common GND | |
| U4 AHCT244 | Pin 20, VCC | Core +5 V rail | |
| U4 AHCT244 | C4, 100 nF | Close to pin 20, between +5 V and nearby common GND | |
| U3 HCT32 | Pin 7, power GND | Common GND | |
| U3 HCT32 | Pin 11, unused output | Leave open | |
| U3 HCT32 | Pin 12, unused input | Common GND | |
| U3 HCT32 | Pin 13, unused input | Common GND | |
| U3 HCT32 | Pin 14, VCC | Memory +5 V rail | |
| U3 HCT32 | C3, 100 nF | Close to pin 14, between +5 V and nearby common GND |
With power and USB disconnected, tick each row after checking continuity at both actual socket contacts and isolation from adjacent pins. Progress is saved in the Phase 2 checklist. Keep the CLK connection entirely on Core.
Final checks before inserting U3 and U4¶
| Done | Check |
|---|---|
| All listed signal and supply connections complete; U4 pin 18 to Z80 socket pin 6 stays short and entirely on Core | |
| Both U4 OEs grounded, all unused inputs grounded, unused outputs left open | |
| Continuity checked at every actual socket contact, no neighboring shorts, correct supply rails and pin-1 orientation verified before inserting either device |
Firmware and Tests¶
Keep the CPU, SRAM and data translators absent throughout this phase.
| Done | Test | Procedure and pass condition |
|---|---|---|
| Firmware | Build z80_stage02_buffers_clock and load its UF2 from build/src/stage02_buffers_clock/. See the maintained application |
|
| Buffer levels | Press d for each LOW/HIGH step and probe U4 pairs 2/18, 4/16 and 6/14. Inputs are near 0/3.3 V; outputs are near 0/+5 V, LOW below 0.3 V and HIGH at least 4.4 V. The seventh step or x restores defaults |
|
| Clock | Use 1, 2, 3 for 1 kHz, 100 kHz and 1 MHz. At U4 pin 18 and Z80 socket pin 6 verify frequency, 45-55% duty, and no runt pulses or threshold-crossing ringing. HIGH must reach measured CPU VCC minus 0.5 V: at 5.0 V, at least 4.5 V |
|
| Inhibit and WAIT | Drive raw RD# and IORQ# LOW one at a time through 1 kOhm. SRAM OE stays HIGH while boot inhibit is HIGH; WAIT is LOW only while IORQ and IO_RELEASE are both LOW |
OR-gate truth-table fixture¶
For every gate, test LOW/LOW, LOW/HIGH, HIGH/LOW and HIGH/HIGH. The expected outputs are LOW, HIGH, HIGH and HIGH respectively.
Use current-limited fixture outputs referenced to common circuit GND and limited to 0 V through measured U3 VCC. Disconnect external power and USB before every fixture change, and define both inputs before powering up. Never drive a connected Pico GPIO or another active output.
| Done | Step |
|---|---|
| Set the fixture ground, voltage range and current limit; keep Phase 2 loaded and do not use the Phase 1 walking test | |
| Gate 1: disconnect U4 output 7 from U3 input 1 | |
| Gate 1: disconnect U4 output 14 from U3 input 2 | |
| Gate 1: attach fixture output A to U3 input 1 | |
| Gate 1: attach fixture output B to U3 input 2 | |
| Gate 1: test all four combinations at output 3, SRAM OE# | |
| Gate 2: disconnect Pico GP9/header 12 from input 5, attach fixture outputs to inputs 4/5, then test all four combinations at output 6, WAIT#. CPU-absent IORQ# at input 4 has no active driver | |
| Gate 3: disconnect Pico GP6/header 9 from input 10, attach fixture outputs to inputs 9/10, then test all four combinations at output 8, DATA_UP_OE#. CPU-absent RD# at input 9 has no active driver | |
| Unused gate: leave inputs 12/13 grounded and output 11 unconnected |
Restore normal wiring¶
| Done | Step |
|---|---|
| Disconnect external power and USB, then remove the fixture outputs and ground | |
| Reconnect U4 output 7 to U3 input 1 and U4 output 14 to U3 input 2 | |
| Reconnect Pico GP9/header 12 to U3 input 5 and Pico GP6/header 9 to U3 input 10 | |
Check continuity and adjacent-pin isolation, power up, then press x to restore defaults |
Pass Gate¶
- Buffer tests and all three used-gate truth tables passed; unused gate inputs grounded and output open.
- Clock levels measured correctly.
Start later execution at 1 MHz, not the CPU's 20 MHz rating. This phase qualifies the unloaded socket clock only; repeat with the CPU fitted.
Maintained Source¶
These complete files are included from the repository at documentation build time, not copied into this page.
Phase 2 application - src/stage02_buffers_clock/main.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "z80sbc/clock.h"
#include "z80sbc/pins.h"
#include "z80sbc/supervisor.h"
static const uint BUFFER_INPUT_PINS[] = {PIN_CLK, PIN_BUSREQ_N, PIN_BOOT_READ_DISABLE};
static const uint BUFFER_OUTPUT_CONTACTS[] = {18, 16, 14};
static const uint BUFFER_INPUT_CONTACTS[] = {2, 4, 6};
static int held_step = -1;
static void restore_test_outputs(void) {
z80_clock_stop();
z80_safe_startup();
held_step = -1;
}
static void step_buffer_output(void) {
int next_step = held_step + 1;
restore_test_outputs();
if (next_step >= 6) {
printf("DONE: safe levels restored\n");
return;
}
held_step = next_step;
unsigned index = (unsigned)held_step / 2;
bool high = held_step % 2 != 0;
gpio_put(BUFFER_INPUT_PINS[index], high);
printf("GP%u -> AHCT244 input %u -> output %u: HELD %s; verification=unmeasured\n",
BUFFER_INPUT_PINS[index], BUFFER_INPUT_CONTACTS[index],
BUFFER_OUTPUT_CONTACTS[index], high ? "HIGH" : "LOW");
}
int main(void) {
restore_test_outputs();
stdio_init_all();
printf("\nStage 2: AHCT244 clock and boot-read inhibit; CPU/SRAM absent\n");
printf("d=held LOW/HIGH steps, 1=1kHz, 2=100kHz, 3=1MHz, x=safe restore\n");
while (true) {
int command = getchar_timeout_us(0);
if (command == 'd') {
step_buffer_output();
} else if (command >= '1' && command <= '3') {
restore_test_outputs();
uint32_t hz = command == '1' ? 1000 : command == '2' ? 100000 : 1000000;
printf(z80_clock_set_hz(hz) ? "clock_actual=%lu verification=unmeasured\n"
: "FAIL: clock configuration %lu\n",
(unsigned long)z80_clock_get_hz());
} else if (command == 'x') {
restore_test_outputs();
}
tight_loop_contents();
}
}
Shared clock generation - src/common/clock.c
#include "z80sbc/clock.h"
#include "pico/stdlib.h"
#include "z80sbc/pins.h"
#if defined(Z80_BANKED_PIO)
#include "z80sbc/banked_pio.h"
#include "z80sbc/gpio_mux.h"
#else
#include "hardware/clocks.h"
#include "hardware/pwm.h"
#endif
static volatile uint32_t actual_clock_hz;
void __not_in_flash_func(z80_clock_prepare_stepping)(void) {
z80_clock_stop();
gpio_put(PIN_CLK, 0);
gpio_set_dir(PIN_CLK, GPIO_OUT);
if (gpio_get_function(PIN_CLK) != GPIO_FUNC_SIO) {
// Extend a frozen clock HIGH before the mux can create a falling edge.
busy_wait_us_32(1);
#if defined(Z80_BANKED_PIO)
z80_gpio_mux_select(PIN_CLK, GPIO_FUNC_SIO);
#else
gpio_set_function(PIN_CLK, GPIO_FUNC_SIO);
#endif
}
}
uint32_t z80_clock_get_hz(void) {
return actual_clock_hz;
}
#if defined(Z80_BANKED_PIO)
bool z80_clock_set_hz(uint32_t hz) {
if (!z80_banked_pio_clock_start(hz))
return false;
actual_clock_hz = hz;
return true;
}
void __not_in_flash_func(z80_clock_stop)(void) {
z80_banked_pio_clock_stop();
}
#else
bool z80_clock_set_hz(uint32_t hz) {
if (hz < Z80_CLOCK_MIN_HZ || hz > Z80_CLOCK_MAX_HZ)
return false;
uint slice_num = pwm_gpio_to_slice_num(PIN_CLK);
uint channel = pwm_gpio_to_channel(PIN_CLK);
uint32_t sys_clk = clock_get_hz(clk_sys);
uint32_t best_divider = 0;
uint32_t best_count = 0;
for (uint32_t divider = 1; divider <= 255; ++divider) {
uint64_t denominator = (uint64_t)hz * divider;
uint64_t count = ((uint64_t)sys_clk + denominator - 1u) / denominator;
count = (count + 1u) & ~UINT64_C(1);
if (count < 2)
count = 2;
if (count > 65536)
continue;
uint64_t product = (uint64_t)divider * count;
uint64_t best_product = (uint64_t)best_divider * best_count;
if (best_divider == 0 || product < best_product ||
(product == best_product && count > best_count)) {
best_divider = divider;
best_count = (uint32_t)count;
}
}
if (best_divider == 0)
return false;
z80_clock_prepare_stepping();
busy_wait_us_32(1);
pwm_set_clkdiv_int_frac4(slice_num, (uint8_t)best_divider, 0);
pwm_set_wrap(slice_num, (uint16_t)(best_count - 1u));
pwm_set_chan_level(slice_num, channel, (uint16_t)(best_count / 2u));
pwm_set_counter(slice_num, 0);
gpio_set_function(PIN_CLK, GPIO_FUNC_PWM);
pwm_set_enabled(slice_num, true);
uint64_t product = (uint64_t)best_divider * best_count;
actual_clock_hz = (uint32_t)(((uint64_t)sys_clk + product / 2u) / product);
return true;
}
void __not_in_flash_func(z80_clock_stop)(void) {
pwm_set_enabled(pwm_gpio_to_slice_num(PIN_CLK), false);
}
void __not_in_flash_func(z80_clock_resume)(void) {
pwm_set_counter(pwm_gpio_to_slice_num(PIN_CLK), 0);
gpio_set_function(PIN_CLK, GPIO_FUNC_PWM);
pwm_set_enabled(pwm_gpio_to_slice_num(PIN_CLK), true);
}
#endif
void z80_clock_one_cycle(uint32_t half_period_us) {
z80_clock_prepare_stepping();
z80_clock_step_prepared(half_period_us);
}
void __not_in_flash_func(z80_clock_step_prepared)(uint32_t half_period_us) {
busy_wait_us_32(half_period_us);
gpio_put(PIN_CLK, 1);
busy_wait_us_32(half_period_us);
gpio_put(PIN_CLK, 0);
// Let CPU controls, translators and input synchronizers settle before return.
busy_wait_us_32(1);
}
void z80_reset_with_clock_cycles(unsigned int cycles,
uint32_t half_period_us) {
gpio_put(PIN_RESET_N, 0);
for (unsigned int cycle = 0; cycle < cycles; ++cycle)
z80_clock_one_cycle(half_period_us);
z80_clock_stop();
}