基于FPGA复刻Z80与8051双核系统:从硬件逻辑到完整计算机的实践

📅 2026/8/19 4:17:40
基于FPGA复刻Z80与8051双核系统:从硬件逻辑到完整计算机的实践
1. 项目概述在单颗FPGA上复刻经典微处理器系统最近在整理工作室的旧物翻出来几块上世纪八九十年代的“古董”电路板上面赫然印着Z80和8051的标识。这些老伙计曾是无数嵌入式系统和早期个人电脑的心脏如今却已蒙尘。一个念头突然冒出来能不能用一种现代的方式让这些经典的架构重新“活”起来并且让学习它们的过程变得更直观、更高效于是这个基于Intel MAX 10 FPGA在一块开发板上同时实现Z80和MCS-51即8051两款经典微处理器单板计算机SBC的项目就诞生了。这个项目的核心目标不是简单地用软件模拟器去运行古老的代码而是用硬件描述语言HDL在FPGA内部真实地“铸造”出这两颗CPU的硬件逻辑并围绕它们构建完整的微型计算机系统包括内存、外设和总线。对于嵌入式开发者、计算机体系结构爱好者甚至是那些想从软件跨入硬件世界的程序员来说这都是一次绝佳的实践。你不仅能深入理解Z80和8051的指令集、总线时序和中断机制更能亲手搭建一个从CPU核到应用层的完整数字系统体验“从沙子到系统”的全过程。MAX 10 FPGA以其低成本、高集成度内置闪存和RAM和友好的开发环境成为了实现这个想法的理想平台。2. 核心设计思路与架构选型2.1 为什么选择Z80和MCS-51选择Z80和Intel MCS-51常称8051作为复刻对象背后有技术和教育层面的双重考量。Z80是8位微处理器的黄金代表以其简洁而强大的指令集、清晰的存储器映射I/O和中断结构著称。许多经典的CP/M操作系统机器、游戏机如任天堂Game Boy的CPU都基于它。理解Z80就等于理解了早期计算机系统的基本范式。而MCS-51则是嵌入式单片机领域的绝对鼻祖和常青树。其哈佛架构程序与数据存储器分离、丰富的位操作指令、以及内置定时器、串口等外设的理念深刻影响了后来几乎所有的单片机设计。直到今天其变体仍被广泛使用。将两者放在同一个FPGA里可以让我们直观对比复杂指令集CISC下两种不同设计哲学的实现Z80更偏向通用微型计算机而8051从诞生起就为控制任务优化。在FPGA上实现它们意味着我们可以摆脱物理芯片的引脚限制和固定外设自由地配置内存大小、外设类型甚至动态切换两种系统这对于教学和原型验证具有无可比拟的灵活性。2.2 MAX 10 FPGA作为承载平台的优势Intel原Altera的MAX 10 FPGA系列是这个项目的硬件基石。它并非性能最强的FPGA但对此类项目而言其特性堪称“量身定制”。首先低成本与高集成度是最大亮点。MAX 10器件内部集成了闪存配置芯片和用户可用的嵌入式存储器如M9K块这意味着我们不需要外部的配置芯片和SRAM就能存储FPGA的配置文件和作为CPU的系统内存极大简化了板级设计。对于Z80和8051这样的8位系统其逻辑资源LE和存储资源也完全够用。其次友好的开发流程。通过Quartus Prime Lite免费版软件我们可以使用Verilog或VHDL进行设计并利用其内嵌的逻辑分析仪SignalTap实时抓取内部信号这对于调试CPU内核的时序问题至关重要。想象一下你可以像软件调试一样设置断点观察数据总线、地址总线和控制信号在每一个时钟周期的变化这是传统单片机开发难以企及的。最后灵活的外设扩展。MAX 10 FPGA的通用IO引脚可以映射为任何我们需要的接口。我们可以用硬件逻辑实现一个精确的UART串口用于与PC通信用PWM模块驱动LED或蜂鸣器甚至模拟PS/2键盘接口。所有的外设都通过自定义的总线或存储器映射与CPU核连接架构完全自主定义。2.3 整体系统架构设计在单颗FPGA内部我们需要构建两个相对独立的“片上系统”。整体架构可以看作是一种静态可重配置的模型虽然两个系统并非同时运行但通过顶层模块的选择可以快速在Z80 SBC和8051 SBC之间切换。共享的物理资源与隔离的逻辑空间时钟与复位外部晶振提供全局时钟通过FPGA内部的锁相环PLLIP核生成系统所需的主频例如Z80用2MHz8051用12MHz。复位电路也由FPGA逻辑实现。存储器系统这是设计的核心。我们利用MAX 10的M9K嵌入式内存块划分出多个逻辑区块Z80系统内存划分一块区域作为Z80的64KB统一寻址空间其中包含ROM存放监控程序或BASIC解释器和RAM。8051存储器严格遵循哈佛架构。划分两块区域一块作为程序存储器ROM另一块作为内部数据存储器RAM和特殊功能寄存器SFR空间。还需要用逻辑实现外部数据存储器XRAM的接口。内存仲裁逻辑虽然不共享但需要设计一个顶层选择器根据当前激活的CPU系统将FPGA的物理内存块映射到对应的CPU地址总线上。外设IP核设计一系列通用的外设模块如UART、定时器、GPIO控制器等。这些模块设计成具有标准接口如Wishbone或Avalon-MM总线并预留配置寄存器。在Z80或8051系统中它们被映射到各自的I/O空间或存储器空间。CPU内核这是项目的灵魂。我们需要用HDL编写或集成开源的Z80和8051兼容软核。例如T80是一个广泛使用的开源Z80兼容核而像mc8051这样的IP核则实现了8051指令集。将这些核实例化并连接到我们自定义的总线和存储器系统。注意直接使用未经深入理解的开源IP核可能会引入“黑盒”问题。建议至少对其顶层接口和基本时序有清晰认识最好能阅读关键部分的源码以便在出现总线冲突、中断响应异常等问题时能够进行调试。3. Z80软核系统实现详解3.1 Z80兼容软核的选择与集成实现Z80系统的第一步是获取一个可靠的CPU软核。T80是一个用VHDL编写的、高度兼容的Z80实现在开源社区历经考验。我们将其集成到Quartus工程中。集成并非简单例化。需要重点关注其接口信号与时序总线接口T80提供了类似原生Z80的MREQ_N存储器请求、IORQ_NI/O请求、RD_N/WR_N读/写等控制信号。我们需要编写一个总线控制器模块来解析这些信号并将其转换为对我们内部存储器M9K和外设IP核的访问。时钟与复位Z80对时钟边沿敏感。我们需要确保提供给T80核心的时钟是干净、稳定的。通常在PLL输出后使用全局时钟网络缓冲器如altclkctrlIP来驱动。中断处理Z80支持可屏蔽中断INT和非屏蔽中断NMI。我们需要设计一个中断控制器汇总UART接收完成、定时器溢出等中断源按照Z80的中断模式IM0/IM1/IM2产生正确的响应序列。一个常见的集成错误是忽略了T80的WAIT_N信号。当外设或存储器速度跟不上CPU时需要拉低此信号插入等待周期。在我们的FPGA系统中所有存储器和外设都是同步的理论上可以全速运行但为了兼容未来可能接入的慢速外设模拟建议实现简单的等待状态生成逻辑。3.2 Z80存储器与I/O映射设计Z80采用统一编址的64KB空间并通过MREQ_N和IORQ_N来区分内存访问和I/O访问。我们的设计需要清晰定义地址映射。存储器映射示例0x0000 - 0x1FFFROM区域。存放系统监控程序。在FPGA中这部分内容在编译时被初始化到对应的M9K内存块中。我们可以将编译好的二进制文件.hex格式通过Quartus的Memory Initialization File.mif功能载入。0x2000 - 0xFFFFRAM区域。作为用户程序和数据空间。实现时我们用一个Verilog模块作为“地址解码器”根据输入的地址高位产生对应存储器块或外设的片选信号。I/O端口映射示例0x00 - 0x07UART。分配数据端口和状态/控制端口。0x10 - 0x13定时器。分配计数器、控制寄存器等。0x20 - 0x23GPIO。控制LED和读取开关状态。I/O端口的读写时序与内存不同它使用IORQ_N信号。我们的外设IP核需要能正确响应IORQ_N和地址线。3.3 基础外设IP核的实现UART与定时器一个可用的SBC必须至少具备输入输出能力。串口UART是与PC通信的桥梁。UART IP核设计要点波特率生成使用一个自由运行的计数器根据系统时钟和所需波特率如115200计算分频值产生发送和接收采样时钟。发送器当CPU写入数据到发送数据寄存器时启动一个状态机依次送出起始位、8个数据位、停止位。接收器这是一个关键且容易出错的部分。为了避免在数据位中间采样通常采用过采样技术例如16倍波特率时钟。在检测到起始位下降沿后等待到比特位中间时刻再进行采样以提高抗噪性。状态寄存器需要提供“发送缓冲区空”和“接收数据就绪”两个状态位供CPU查询或用于产生中断。定时器IP核设计要点Z80系统需要一个周期性中断源用于任务调度或精确定时。我们可以实现一个简单的向下计数器。寄存器包括一个重载寄存器Timer Reload和一个当前计数寄存器Timer Current。CPU写入重载值计数器开始递减。工作模式当计数器减到0时产生中断请求信号并自动从重载寄存器重新加载计数值实现周期性中断。中断连接将定时器的中断输出连接到Z80的中断控制器输入端。实操心得调试UART时最容易出现的问题是波特率不准导致乱码。务必使用逻辑分析仪或SignalTap抓取实际的发送波形测量比特宽度与理论值对比。一个小技巧是将波特率生成器的分频系数计算用参数或宏定义方便修改和验证。另外接收器的起始位检测逻辑需要做去抖动处理通常连续采样几次确认是稳定的低电平后才认为是有效起始位。4. MCS-518051软核系统实现详解4.1 8051软核与独特的哈佛架构适配8051是哈佛架构程序存储器ROM和数据存储器RAM在物理上是分开的使用不同的地址总线和控制信号。这与Z80的冯·诺依曼架构截然不同。我们选用的mc8051等IP核其外部接口通常会明确区分ROM_ADDR/ROM_DATA和XRAM_ADDR/XRAM_DATA。关键适配工作程序存储器ROM接口mc8051核心会在取指周期输出程序地址。我们需要将这个地址连接到FPGA内部M9K块构成的ROM上并在一个时钟周期内将读出的指令数据返回给核心。ROM的初始化方式与Z80系统类似。内部数据存储器IRAM8051芯片内有128字节的RAM8052为256字节。这部分通常由软核内部实现我们无需额外管理。特殊功能寄存器SFR这也是软核内部实现的但软核会提供外部接口来访问那些需要硬件扩展的SFR功能例如串口、定时器的外部引脚。外部数据存储器XRAM接口这是连接我们自定义外设的关键。当8051执行MOVX指令时就会访问XRAM空间。我们需要设计一个总线桥接模块将XRAM_ADDR、XRAM_DATA和读写控制信号转换到我们为外设IP核设计的统一总线上如Wishbone总线。4.2 8051的存储器空间与SFR扩展8051的地址空间复杂但规整64KB 程序空间通过PC指针和MOVC指令访问。我们的FPGA ROM映射到此空间。256字节 内部数据空间低128字节为通用RAM高128字节与SFR地址重叠通过不同的寻址方式区分。这部分在软核内。64KB 外部数据空间通过MOVX指令和DPTR寄存器访问。我们的所有外设都应映射到这个空间。例如我们可以将UART的数据寄存器映射到XRAM的0xFFF0地址将状态寄存器映射到0xFFF1。当8051执行MOVX DPTR, ADPTR0xFFF0时数据就被发送出去。SFR的扩展标准的8051软核已经实现了定时器、串口等SFR的逻辑。但如果我们想增加新的功能如一个额外的PWM模块并希望像操作TH0、TL0一样通过SFR地址例如0xA0来访问它就需要修改软核的源码增加对应的寄存器逻辑和接口。这对于初学者来说挑战较大因此更推荐将新外设映射到XRAM空间虽然编程上稍麻烦需用MOVX指令但实现起来更独立、更安全。4.3 外设IP核的复用与总线桥接为了提高设计效率我们在Z80系统中实现的UART、定时器、GPIO等IP核可以经过一层简单的总线桥接后复用到8051系统中。总线桥接器设计思路我们为所有外设IP核定义一个内部轻量级总线标准例如一个简单的同步总线包含地址线、写数据线、读数据线、写使能和读使能信号。对于Z80系统Z80总线控制器模块将MREQ_N/IORQ_N等信号转换为此内部总线信号。对于8051系统设计一个“XRAM到内部总线”的桥接模块。当8051访问XRAM空间时此模块捕获XRAM_ADDR和XRAM_DATA并将其转换为内部总线信号。这样UART等外设IP核只需要适配这一套内部总线接口就能同时服务于两个不同的CPU架构。这体现了FPGA设计的模块化和可重用性优势。注意事项两个CPU系统不会同时运行因此不存在总线仲裁问题。但切换系统时通过拨码开关或软件命令必须确保当前CPU的所有操作已经完成并对共享的硬件资源特别是UART进行妥善的复位或状态清理避免数据错乱。一个稳妥的做法是在顶层设计一个全局复位分配网络切换系统时对即将运行的系统释放复位而对即将挂起的系统保持复位状态。5. 系统集成、调试与验证实战5.1 顶层模块设计与系统切换逻辑顶层模块Top-level Module是连接FPGA外部引脚、内部所有子系统的总调度中心。其主要职责包括时钟与复位生成实例化PLL IP核将外部晶振时钟如50MHz分频或倍频为各个子系统所需频率。生成全局复位信号并分发给两个CPU系统。系统选择器读取一个外部拨码开关或按钮的状态作为系统选择信号sys_sel。根据此信号决定将外部设备如UART的TX/RX引脚、LED引脚连接到哪个CPU子系统的对应接口上。物理引脚分配在Quartus的Pin Planner中将顶层模块的输入输出端口分配到MAX 10开发板的具体物理引脚上例如连接到板载USB-UART芯片的引脚、LED、按键等。一个简单的系统切换逻辑代码片段如下// 根据拨码开关选择活跃的系统 assign active_system dip_switch[0]; // 0: Z80, 1:8051 // 多路选择器将外部UART引脚连接到被选中的系统 assign uart_tx_pin (active_system 1‘b0) ? z80_uart_tx : mcs51_uart_tx; assign z80_uart_rx (active_system 1‘b0) ? uart_rx_pin : 1‘b1; assign mcs51_uart_rx (active_system 1‘b1) ? uart_rx_pin : 1‘b1; // 多路选择器LED显示当前系统 assign leds (active_system 1‘b0) ? z80_gpio_out : mcs51_gpio_out;5.2 使用SignalTap II进行在线逻辑调试当系统无法正常启动或者程序运行异常时传统的“点灯大法”效率低下。Quartus自带的SignalTap II Logic Analyzer是FPGA开发的“终极调试利器”。它允许你在FPGA运行时实时捕获内部任何信号的波形。调试Z80启动过程设置触发条件怀疑CPU没有正确从ROM读取第一条指令。可以设置触发条件为当Z80的地址总线A[15:0]等于16‘h0000且MREQ_N和RD_N同时为低电平时触发。添加观察信号将Z80核心的PC程序计数器、A[15:0]、D[7:0]数据总线、MREQ_N、RD_N、M1_N取指周期等关键信号添加到SignalTap观察列表。运行与分析重新编译并下载包含SignalTap探针的FPGA配置文件。触发后你将看到波形图。检查在第一个M1周期数据总线上是否出现了你预设在ROM0x0000地址的指令码例如Z80的复位向量通常是0x0000存放一条JP跳转指令。常见问题如果数据总线为高阻态或错误数据可能是ROM的片选信号或输出使能信号没有正确生成。通过波形图可以清晰地追踪到问题源头。调试UART通信添加UART模块内部的tx_state发送状态机、tx_data待发送数据、baud_clk波特率时钟等信号。观察当CPU写入数据寄存器后状态机是否启动波特率时钟是否正常以及最终tx_pin输出的波形是否符合UART协议。实操心得SignalTap会占用FPGA的存储资源和逻辑资源。务必在工程早期预留足够的调试通道。一个技巧是将不同模块的调试信号分组创建多个.stp文件需要调试哪个模块就使能哪个避免资源不足。另外采样深度和时钟的选择要权衡采样深度越大能观察的时间窗口越长采样时钟频率越高波形细节越清晰但都会消耗更多资源。5.3 编写与运行测试程序硬件平台就绪后需要软件来验证其功能。我们需要为两个系统编写简单的监控程序或测试程序。对于Z80系统开发环境可以使用经典的zasm汇编器或者更现代的z88dk交叉编译工具链。测试程序编写一个简单的循环程序通过GPIO让LED闪烁。再编写一个通过UART发送“Hello, Z80!”字符串的程序。将汇编代码编译成二进制.bin或英特尔十六进制.hex格式。程序载入在Quartus中将编译好的.hex文件指定为ROM内存块的初始化文件重新编译整个FPGA工程并下载。上电后程序即开始运行。对于8051系统开发环境使用Keil C51或SDCC开源Small Device C Compiler。测试程序用C语言或汇编编写LED闪烁和串口打印程序。注意访问我们映射在XRAM空间的外设时需要使用xdata关键字声明指针并通过MOVX指令操作编译器会自动处理。#include 8051.h // 包含SFR定义 #define UART_DATA_REGISTER XBYTE[0xFFF0] // 定义XRAM地址 #define UART_STATUS_REGISTER XBYTE[0xFFF1] void send_char(char c) { while (!(UART_STATUS_REGISTER 0x02)); // 等待发送就绪 UART_DATA_REGISTER c; }程序载入同样将编译器输出的.hex文件作为8051程序ROM的初始化文件。6. 进阶优化与功能扩展思路当基础系统运行稳定后可以尝试以下进阶改造这能极大提升项目的深度和学习价值。6.1 性能分析与优化技巧虽然Z80和8051是古老的架构但在FPGA上实现时我们仍然可以分析和优化其性能。关键路径优化在Quartus的Timing Analyzer报告中关注“最差负时序裕量”Worst Negative Slack。如果为负说明有路径无法在设定时钟周期内稳定需要优化。对于CPU软核关键路径往往在ALU或地址计算部分。可以尝试流水线化将复杂的组合逻辑拆分成多个时钟周期完成。例如将内存访问拆分为“地址输出-数据读取”两个周期。寄存器打拍在长组合逻辑路径中插入寄存器切断路径提高时钟频率。存储器访问优化FPGA的M9K内存块是同步RAM有固定的读取延迟通常1-2个时钟周期。如果CPU核设计为期望组合逻辑ROM零延迟就会出错。必须确保CPU核在发出地址后等待足够周期再读取数据。这通常通过插入等待状态WAIT或设计同步接口来实现。面积资源优化如果逻辑资源LE使用接近上限可以考虑资源共享如果两个系统绝对不同时运行一些大型模块如乘法器、除法器可以被两者分时复用。使用IP核Quartus提供的乘加器、RAM等IP核通常比直接用HDL描述更优化。6.2 扩展外设与交互功能基础通信和IO只是开始可以添加更多有趣的外设VGA显示控制器用FPGA逻辑实现一个VGA时序发生器和一个帧缓冲区使用FPGA RAM。CPU可以将字符或图形数据写入帧缓冲区从而在显示器上输出。这是一个挑战性较大但成就感极高的模块涉及精确的时序控制和内存带宽管理。PS/2键盘接口实现一个PS/2协议解码器将键盘扫描码转换为ASCII码并通过中断通知CPU。这样你就可以为你的SBC配备一个真正的键盘。SPI/I2C控制器连接外部EEPROM、传感器如温湿度传感器或实时时钟RTC芯片让系统具备数据存储和环境感知能力。音频发生器使用PWM或Delta-Sigma调制技术生成简单的方波、三角波甚至播放PCM音频样本重现老式游戏机的“哔哔”声。6.3 从仿真到上板的完整流程保障一个稳健的开发流程能节省大量调试时间。前仿真RTL仿真在Modelsim或Quartus自带的仿真工具中使用测试平台Testbench对每个子模块如UART、总线控制器进行仿真。用波形验证逻辑的正确性特别是边角情况如FIFO满/空、中断冲突。后仿真门级仿真在Quartus综合布局布线后导出包含实际延时信息的网表进行仿真。这更接近真实硬件行为能发现时序问题。静态时序分析STA每次编译后必须仔细阅读Timing Analyzer报告确保所有时序约束如时钟频率、输入输出延迟得到满足。板级调试电源与时钟首先用示波器测量FPGA核心电压和时钟输入引脚确保稳定无毛刺。引脚约束反复核对.qsf文件中的引脚分配确保与原理图一致。一个错误的引脚分配会导致无法预料的行为。渐进式测试不要一次性集成所有功能。先测试时钟和复位然后测试单个CPU核能否从ROM读取指令执行最简单的NOP循环再逐步添加内存、GPIO、UART等模块。完成这个项目后你收获的将不仅仅是两个能运行的复古计算机。更重要的是你打通了从硬件描述语言、数字逻辑设计、计算机体系结构、总线协议到软硬件协同调试的完整链条。下次当你再看到“CPU”、“内存”、“外设”这些词时脑海中浮现的将不再是抽象的概念而是清晰的信号波形和逻辑电路图。这种深度的理解是任何理论课程都无法替代的。