Hotdry.

Article

RP2350 捕获 Z80 总线:从 GPIO 轮询到 PIO+DMA 实时逻辑分析

利用 RP2350 的 48 路 GPIO 和 PIO+DMA 引擎,构建低成本 Z80 总线逻辑分析仪,实现复古 CPU 的实时调试与单步追踪。

2026-06-05embedded-systems

复古计算爱好者在调试 Z80 系统时,常常面临一个尴尬困境:现代示波器和逻辑分析仪虽然功能强大,但价格不菲,且与 8 位时代的总线协议交互时往往显得笨重。Raspberry Pi Pico 2(RP2350)的出现为这一场景提供了新的解题思路 —— 利用其可编程 IO(PIO)和 DMA 引擎,以不到 10 美元的硬件成本,构建一个能够实时捕获 16 位地址总线、8 位数据总线及全部控制信号的嵌入式逻辑分析方案。

Z80 总线架构与关键时序参数

Z80 的并行总线包含 16 根地址线(A0-A15)、8 根数据线(D0-D7)以及多根控制信号线,包括 /MREQ(存储器请求)、/IORQ(I/O 请求)、/RD(读使能)、/WR(写使能)、/M1(取指周期标志)等。理解这些信号的时序关系是实现可靠捕获的前提。

Z80 的时钟特性值得特别关注。虽然常见型号标称运行频率为 4MHz 或 10MHz,但其时钟周期存在严格的上下限约束。根据原始 Z80A 数据手册,时钟低电平脉冲宽度不得超过 2μs,而高电平脉冲宽度理论上可以任意延长(手册保证功能正常的高电平宽度上限为 200μs)。这意味着 Z80 的最低运行频率约为 5kHz—— 这一特性为 "单步执行" 调试提供了理论基础。此外,RESET 信号需要保持至少 3 个时钟周期才能确保可靠复位。

Z80 采用机器周期(M cycle)和时钟周期(T state)两级时序概念。一条指令通常需要 3 到 6 个 T 周期完成一个 M 周期,而完整指令可能包含多个 M 周期。外部设备可通过拉低 /WAIT 信号请求 CPU 等待,这为慢速存储器与高速 CPU 的同步提供了机制。

RP2350 GPIO 映射策略

RP2350B 型号提供 48 路 GPIO,恰好能够完整覆盖 Z80 的全部总线信号。一个实用的映射方案如下:

RP2350 GPIO Z80 信号
0-15 A0-A15
16-23 D0-D7
24 /RD
25 /WR
26 /BUSACK
27 /HALT
28 /M1
29 /RFSH
30 /MREQ
31 /IORQ
32 /BUSRQ
33 /INT
34 /WAIT
35 CLK
36 /NMI
38 /RESET

这一映射方案与 picoZ80 项目保持一致,便于复用现有的硬件设计资源。

需要注意的是,RP2350 相比 RP2040 引入了 GPIO base 的概念。在标准 SDK API 中,base 0 对应 GPIO 0-31,base 1 对应 GPIO 32-47。然而 PIO 外设的处理方式略有不同:PIO 的 base 0 对应 GPIO 0-31,而 base 1 对应 GPIO 16-47(而非 32-63),这是为了在两个 base 配置下都能实现 32 路并行采样。这种不一致性在跨模块编程时需要特别留意。

从软件轮询到 PIO+DMA 的演进

项目的实现可分为三个阶段,每个阶段对应不同的技术复杂度和捕获精度。

阶段一:基础 GPIO 轮询

最简单的实现方式是使用 gpio_get_all() 轮询读取 GPIO 状态。RP2350 的 SDK 提供了批量操作 API,如 gpio_init_mask()gpio_set_dir_in_masked(),可以一次性配置多个引脚。然而,软件轮询方式存在明显的采样率瓶颈 —— 即使以 150MHz 主频运行,也难以可靠捕获 10MHz Z80 的总线活动。

// 基础轮询示例
uint32_t gpio31 = gpio_get_all();
uint8_t data8 = (GP_DATA_MASK & gpio31) >> GP_DATA_START;

这种方式适合验证硬件连接和基础功能,例如将捕获的数据总线值实时显示到 LED 上,或检测特定 I/O 地址的写入操作。

阶段二:条件触发捕获

在轮询基础上增加条件判断,可以实现简单的总线监控功能。例如,检测 /IORQ 和 /WR 同时为低电平(表示 I/O 写操作),且地址总线低 8 位匹配目标地址时,捕获数据总线值。

if (((gpio32 & GP_CTRL_MASK) == ((1<<GP_RD)|(0<<GP_WR)|(1<<GP_M1)|
    |(1<<GP_MREQ)|(0<<GP_IORQ))) && ((gpio32 & 0xFF) == 0)) {
    uint8_t data8 = (GP_DATA_MASK & gpio32) >> GP_DATA_START;
    // 处理捕获的数据
}

这种方式本质上是用一颗 150MHz 的双核 Cortex-M33 模拟几片 TTL 逻辑芯片的功能,虽然可行,但显然不是最优解。

阶段三:PIO+DMA 实时捕获

真正的解决方案是利用 RP2350 的 PIO 和 DMA 外设。PIO 是可编程状态机,能够以确定的时钟周期采样 GPIO 状态,不受 CPU 中断和任务调度的影响。DMA 则负责将 PIO 捕获的数据批量传输到内存,实现零 CPU 开销的数据采集。

一个典型的 PIO 程序会配置为在 Z80 时钟边沿触发采样,同时捕获地址总线、数据总线和控制信号的状态。采样数据以 32 位字的形式存储,每个字包含一个时钟周期的完整总线快照。DMA 通道配置为循环模式,将采样数据流式传输到环形缓冲区,供后续分析或实时上传。

PIO 的 FIFO 深度为 8 级,配合 DMA 的突发传输模式,可以轻松应对 Z80 的总线速率。以 10MHz Z80 为例,每个时钟周期 100ns,而 RP2350 的 PIO 运行在系统时钟(默认 150MHz)下,每个 PIO 指令周期约 6.67ns,足以实现每个 Z80 时钟周期多次采样,或精确对齐到特定边沿。

工程化参数与实施清单

基于上述分析,构建一个可用的 RP2350-Z80 逻辑分析仪需要关注以下工程参数:

硬件连接

  • 使用 47Ω 串联电阻保护 RP2350 GPIO,防止 Z80 总线冲突时过流
  • Z80 的 5V 逻辑电平需要通过电平转换器或分压电阻适配到 RP2350 的 3.3V 输入
  • CLK 信号建议通过缓冲器接入,避免负载效应影响 Z80 时序

PIO 配置

  • 状态机时钟分频:建议设置为 Z80 时钟的 4-8 倍,确保每个 T 状态有多个采样点
  • 采样触发条件:可选择 CLK 上升沿或 /MREQ、/IORQ 下降沿作为采样触发点
  • 数据打包格式:32 位字中,高 16 位存储地址总线,次 8 位存储数据总线,低 8 位存储控制信号状态

DMA 配置

  • 传输宽度:32 位字对齐
  • 传输模式:循环缓冲区模式,避免数据覆盖
  • 缓冲区大小:根据捕获时长和采样率计算,例如 10k 样本 × 4 字节 = 40KB

调试功能扩展

  • 时钟控制:RP2350 可输出可变频率时钟到 Z80,实现单步执行或降速调试
  • 指令感知单步:监控 /M1 信号,配合 /WAIT 实现指令级单步
  • 存储器映射外设:模拟 ROM/RAM,实现代码注入和断点设置

固件开发环境

  • 推荐使用 Earle Philhower 的 Arduino Pico Core,或直接使用 Pico C/C++ SDK
  • 注意 RP2350 的 GPIO base 处理差异,PIO 使用 pio_set_gpio_base() 设置 base

这种基于 RP2350 的方案不仅成本极低,而且具有极高的可编程性。从简单的总线监视器到复杂的指令级调试器,都可以通过软件迭代实现,为复古计算爱好者提供了一个功能强大且灵活的调试平台。


资料来源

  • Kevin's Blog: "Watching a Z80 from an RP2350" (2026-05-26)
  • GitHub: gamblor21/rp2040-logic-analyzer

embedded-systems

内容声明:本文无广告投放、无付费植入。

如有事实性问题,欢迎发送勘误至 i@hotdrydog.com