Phase 6 - CPU Reset, Fetch and Bus Grants¶
VS Code tasks¶
Choose Tasks: Run Task in the Z80ROMlessSBC workspace.
| Diagnostic | Build task | Deployment task |
|---|---|---|
| Phase 6 bus grants | Build Pico - Phase 6 bus grants | Flash Pico - Phase 6 bus grants |
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: Complete the loader and RAM checks in Phase 6. Install: No additional chips; CPU was fitted in Phase 6.
This diagnostic provides the unique bus-grant and receiving-pin measurement checks. Phase 6's arithmetic and SRAM tests already provide much stronger execution coverage than this small loop, but they do not request/release the CPU bus or prove analogue reset/clock margins.
Do not flash this diagnostic during another running test. It is a separate, manual low-rate fixture, not a new mode in the arithmetic soak.
Build z80_stage06_bus_grant; UF2 lives in build/src/stage06_sram_dma/.
Maintained application.
The test image is NOP; NOP; JP 0000. It is injected, read back, then reset
again before execution from SRAM.
| Done | Command | Test |
|---|---|---|
l |
Load/verify for stepping; hold RESET afterward | |
s |
Release reset on first step, clock one manual cycle with 50 ms minimum half-pulses | |
0/1/2/3 |
Reload and run at 10 Hz / 1 kHz / 100 kHz / 1 MHz | |
r |
Reload/run 1 MHz | |
q |
BUSREQ/BUSACK round-trip; no Pico memory drive | |
z |
Reset/restart current run rate | |
x |
Assert reset, disable paths, stop clock |
Commands are single characters; Enter is not required. q requires a
free-running mode, not manual stepping. A request or release timeout fails
closed and clears the run state: start a fresh run before retrying.
The diagnostic does not enable an I/O trap; its Z80 loop contains no port I/O.
On a successful grant, the Pico never takes over the memory address/data bus.
Build from the repository root with the firmware toolchain:
cmake -S . -B build -G Ninja -DPICO_BOARD=pico2_w
cmake --build build --target z80_stage06_bus_grant
Test Plan¶
Capture reset, first fetch at 0000, M1, CE/OE/WE and data. Both Pico data enables stay HIGH during SRAM execution and boot inhibit stays LOW.
Verify RESET# rises while CLK is stopped LOW, with at least 1 us before PWM starts. The first clock edge must meet the Z80 reset setup requirement. Verify normal fetches differ from the injected boot stream; observe the NOP/JP loop.
Measure the loaded clock at Z80 pin 6. Progress through slow rates before
1 MHz. q must float the CPU bus while BUSACK LOW without activating either
Pico path; rearm I/O before releasing a grant in integrated firmware.
Test a missing BUSACK using a test-only isolated input fixture, not by shorting the CPU output. Timeout must not authorize memory access; release timeout must assert RESET and isolate. Power off before fault wiring changes.
Pass Gate¶
- Verified reset-to-SRAM fetch.
- Repeated cold loads.
- Correct instruction loop.
- Safe bus-grant timeout behavior.
Higher rates need the separate qualification plan.
Maintained Source¶
These complete files are included from the repository at documentation build time, not copied into this page.
Phase 6 bus-grant diagnostic - src/stage06_sram_dma/bus_grant_test.c
#include <stdio.h>
#include "pico/stdlib.h"
#include "z80sbc/clock.h"
#include "z80sbc/cpu.h"
#include "z80sbc/pins.h"
#include "z80sbc/supervisor.h"
static const uint8_t CPU_TEST_PROGRAM[] = {
0x00, /* NOP */
0x00, /* NOP */
0xC3, 0x00, 0x00, /* JP 0000h */
};
static bool step_image_loaded;
static uint32_t running_clock_hz;
static void stop_test(void) {
z80_cpu_fail_closed();
step_image_loaded = false;
running_clock_hz = 0;
}
static bool load_and_start(uint32_t clock_hz) {
stop_test();
if (!z80_cpu_load_and_verify(CPU_TEST_PROGRAM, sizeof(CPU_TEST_PROGRAM)))
return false;
if (!z80_cpu_release_reset_and_run(clock_hz))
return false;
running_clock_hz = clock_hz;
return true;
}
static bool load_for_single_step(void) {
stop_test();
step_image_loaded = z80_cpu_load_and_verify(
CPU_TEST_PROGRAM, sizeof(CPU_TEST_PROGRAM));
running_clock_hz = 0;
z80_reset_with_clock_cycles(6, 1);
return step_image_loaded;
}
static bool single_step(void) {
if (!step_image_loaded)
return false;
if (!gpio_get(PIN_RESET_N)) {
gpio_put(PIN_BOOT_READ_DISABLE, 0);
z80_isolate_buses();
gpio_put(PIN_BUSREQ_N, 1);
gpio_put(PIN_RESET_N, 1);
}
z80_clock_one_cycle(50000);
return true;
}
static bool reset_running_cpu(void) {
if (running_clock_hz == 0)
return false;
uint32_t clock_hz = running_clock_hz;
stop_test();
z80_reset_with_clock_cycles(3, 1);
if (!z80_cpu_release_reset_and_run(clock_hz))
return false;
running_clock_hz = clock_hz;
return true;
}
static bool bus_round_trip(void) {
if (running_clock_hz == 0)
return false;
if (!z80_cpu_request_bus(500000) || !z80_cpu_release_bus(500000)) {
stop_test();
return false;
}
return true;
}
int main(void) {
z80_safe_startup();
stdio_init_all();
printf("\nStage 6: CPU reset/fetch and bus-grant qualification\n");
printf("l=load for stepping, s=single cycle (50ms minimum half-pulses), "
"0=run 10Hz, 1=run 1kHz, 2=run 100kHz, 3/r=run 1MHz, "
"q=BUSREQ round-trip, z=reset/restart, x=fail closed\n");
while (true) {
int command = getchar_timeout_us(0);
if (command == 'l') {
printf(load_for_single_step() ? "PASS: image loaded; RESET# held\n"
: "FAIL: image load\n");
} else if (command == 's') {
printf(single_step() ? "step complete\n"
: "FAIL: use l before stepping\n");
} else if (command == '0') {
printf(load_and_start(10) ? "PASS: CPU running at 10Hz\n"
: "FAIL: load/start\n");
} else if (command == '1') {
printf(load_and_start(1000) ? "PASS: CPU running at 1kHz\n"
: "FAIL: load/start\n");
} else if (command == '2') {
printf(load_and_start(100000) ? "PASS: CPU running at 100kHz\n"
: "FAIL: load/start\n");
} else if (command == '3' || command == 'r') {
printf(load_and_start(1000000) ? "PASS: CPU running at 1MHz\n"
: "FAIL: load/start\n");
} else if (command == 'q') {
printf(bus_round_trip() ? "PASS: bus round-trip\n"
: "FAIL: start a run mode first or grant failed; inspect state\n");
} else if (command == 'z') {
printf(reset_running_cpu() ? "PASS: reset and restart\n"
: "FAIL: start a run mode first\n");
} else if (command == 'x') {
stop_test();
printf("CPU held in reset\n");
}
tight_loop_contents();
}
}
Shared CPU reset and bus grants - src/common/cpu.c
#include "z80sbc/cpu.h"
#include "pico/stdlib.h"
#include "z80sbc/clock.h"
#include "z80sbc/pins.h"
#include "z80sbc/sram.h"
#include "z80sbc/supervisor.h"
void z80_cpu_fail_closed(void) {
z80_isolate_buses();
gpio_put(PIN_RESET_N, 0);
gpio_put(PIN_BUSREQ_N, 1);
gpio_put(PIN_BOOT_READ_DISABLE, 1);
gpio_put(PIN_IO_RELEASE, 0);
z80_clock_stop();
}
bool z80_cpu_prepare_loader(void) {
z80_memory_bank_set_available(false);
z80_cpu_fail_closed();
z80_reset_with_clock_cycles(3, 1);
if (!gpio_get(PIN_BUSACK_N) || !gpio_get(PIN_IORQ_N) ||
!gpio_get(PIN_RD_N) || !gpio_get(PIN_WR_N))
return false;
return z80_sram_prepare_loader();
}
bool z80_cpu_load_and_verify(const uint8_t *image, uint32_t length) {
if (image == NULL || length == 0 || length > 65536u ||
!z80_cpu_prepare_loader())
return false;
bool loaded = z80_sram_load(0, image, length) &&
z80_sram_verify(0, image, length);
if (!loaded)
z80_cpu_fail_closed();
return loaded;
}
bool z80_cpu_release_reset_and_run(uint32_t clock_hz) {
if (clock_hz < Z80_CLOCK_MIN_HZ || clock_hz > Z80_CLOCK_MAX_HZ) {
z80_cpu_fail_closed();
return false;
}
z80_isolate_buses();
z80_reset_with_clock_cycles(6, 1);
z80_memory_bank_select(0);
gpio_put(PIN_BOOT_READ_DISABLE, 0);
gpio_put(PIN_IO_RELEASE, 0);
gpio_put(PIN_BUSREQ_N, 1);
gpio_put(PIN_RESET_N, 1);
busy_wait_us_32(1);
if (!z80_clock_set_hz(clock_hz)) {
z80_cpu_fail_closed();
return false;
}
return true;
}
static void (*bus_wait_hook)(void);
void z80_cpu_set_bus_wait_hook(void (*hook)(void)) { bus_wait_hook = hook; }
bool z80_cpu_request_bus(uint32_t timeout_us) {
absolute_time_t deadline = make_timeout_time_us(timeout_us);
z80_isolate_buses();
gpio_put(PIN_BUSREQ_N, 0);
while (gpio_get(PIN_BUSACK_N)) {
if (time_reached(deadline)) {
gpio_put(PIN_BUSREQ_N, 1);
return false;
}
if (bus_wait_hook)
bus_wait_hook();
else
tight_loop_contents();
}
return true;
}
bool z80_cpu_release_bus(uint32_t timeout_us) {
absolute_time_t deadline = make_timeout_time_us(timeout_us);
z80_isolate_buses();
gpio_put(PIN_BUSREQ_N, 1);
while (!gpio_get(PIN_BUSACK_N)) {
if (time_reached(deadline)) {
z80_cpu_fail_closed();
return false;
}
tight_loop_contents();
}
return true;
}