前言
很早以前,受 The 8-Bit Guy 的影响,我对复古计算机产生了浓厚的兴趣。它们简单、纯粹,有着一种难以言说的魅力。相比之下,现代计算机虽然拥有远超当年的性能,却往往被层层封装在复杂的软硬件体系之中,很难再让人直观地感受到「一台计算机究竟是如何工作的」。
于是,我也产生了自己搭建一台 8-bit 计算机的想法。不过,这个想法断断续续地存在了很长时间,直到最近才终于开始真正着手实现这个项目。
项目的最终目标如下:
- 使用真正的 8-bit CPU,而非 MCU 模拟;
- 保持电路结构简单、容易理解、调试和维修;
- 使用仍在生产中的元件;
- 可编程:
- 即插即用;
- 可运行游戏和图形软件;
- 支持直接在电脑上编写汇编和更高级的编程语言(BASIC,C 等);
- 能够输出图像(LCD1602,LCD12864,VGA 等);
- 能够输出音频(三角波、方波、锯齿波、噪声、DPCM 等);
- 元件:
- 使用易于手工焊接的直插式元件,或配合直插式插座;
- 易于拓展(RAM,ROM,其他 IO 设备等等);
- 其他:
- 支持 PS/2 的键盘和鼠标接口;
- 支持 SD 卡,能够读写文件;
- 支持 UART, I2C 等与外部设备进行串口通信;
不过,现在还不会直接实现上述所有功能。对于这样一个项目而言,一开始就把所有模块放在一起,既难以设计,也很难在出现问题时确定究竟是哪一部分出了故障。因此,我决定先从一个最小系统开始。
这个最小系统并不需要键盘、显示器,也不需要复杂的外围设备。它只需要完成一件最基本的事情:让 CPU 从 ROM 中读取程序、执行指令,并通过地址总线和数据总线与外部电路进行交互。只要这一步能够稳定工作,后续的 RAM、I/O、视频、音频以及其他外围设备,都可以在此基础上逐步加入。这就是整个项目的起点,也是第一版原型机 v1.0.0 所要完成的目标。
元件选型
要说 8-bit 计算机时代使用最广为人知的 CPU 之一,非 MOS 6502 莫属了。凭借低廉的价格、简洁的架构和出色的性能,它在推出后迅速获得了广泛应用,与 Zilog Z80 促成了 80 年代的家用计算机革命,从 Apple II、Commodore 64 到 Atari 2600,都能看到 6502 家族的身影。[1]
如今,最初的 NMOS 版 6502 早已停止生产,不过 WDC (Western Design Center) 在 1981 年推出了 CMOS 版的 W65C02S,在保留原有指令集和架构的基础上,对部分设计缺陷进行了修正,同时降低了功耗,并增加了一些新的指令。更重要的是,它至今仍在生产,使得我们今天依然可以使用一颗全新的 6502 系列 CPU 来搭建一台复古风格的计算机。
因此,本项目最终选择了 WDC 的 W65C02S 作为 CPU。它基本保留了经典 6502 的总线结构,同时采用现代 CMOS 工艺,非常适合作为这台 8 位计算机的核心。
有了 CPU 以后,它该如何运行程序呢?W65C02S 复位后,会从地址 $FFFC 和 $FFFD 读取 Reset Vector,从中获得程序的起始地址,然后跳转到该地址开始执行(详见 W65C02S 手册描述 RESB 引脚功能的部分)。因此,系统中必须存在一块能够在上电后立即提供数据的 ROM (Read-Only Memory, 只读存储器)。
这里选择的是 AT28C256。这是一颗容量为 32 KB 的并行 EEPROM (Electrically Erasable Programmable Read-Only Memory, 带电可擦可编程只读存储器),拥有 15 根地址线 A0-A15 和 8 根数据线 D0-D7,可以直接连接到 W65C02S 的地址总线和数据总线。与需要专用控制器才能访问的现代 SPI Flash 相比,并行 EEPROM 的接口非常直观,CPU 可以直接通过地址总线访问其中的数据。这也更符合本项目希望保持简单、透明的设计理念。
另一个选择的原因是它仍然比较容易获得,而且支持电擦除和重新编程。在开发阶段,可以很方便地修改程序、重新烧录,然后立即放回计算机进行测试。相比之下,传统的 EPROM (Erasable Programmable Read-Only Memory, 可擦除可编程只读存储器) 通常需要使用紫外线擦除,擦除过程不仅耗时,还需要专用的紫外线擦除设备,因此在反复修改程序的开发阶段远不如 EEPROM 方便。对于这台以学习和实验为目的的原型机来说,这种方式比追求更高的存储密度更加合适。
有了 ROM,CPU 已经可以获得并执行程序,但这还不足以构成一个实用的计算机系统。程序在运行过程中还需要保存变量、临时数据以及运行时状态,因此还需要一块可以随时读写的存储器,也就是 RAM (Random Access Memory, 随机存取存储器)。
本项目选择的是 62256 SRAM。它同样拥有 15 根地址线和 8 根数据线,容量为 32 KB,与前面的 AT28C256 类似,可以非常直接地连接到 W65C02S。SRAM 不需要擦除或编程过程,可以直接进行读写,非常适合作为程序运行时的工作内存。
如果只有 CPU、ROM 和 RAM,即使程序已经正常运行,我们也很难从外部直接观察到计算机正在做什么。因此,需要一个最简单的输出设备,让 CPU 可以把数据「展示」出来。这里没有使用复杂的显示控制器,而是直接使用 8 个 LED 作为最原始的输出设备。
不过,W65C02S 的数据总线并不能直接很好地驱动 LED,因此在 CPU 和 LED 之间增加了一颗 74HC574。它是一个 8 位 D 触发器,可以在时钟信号的控制下锁存数据总线上的 8 个比特,并持续保持输出状态。这样,CPU 只需要向特定的地址执行一次写操作,74HC574 就可以把数据锁存下来,并通过 8 个 LED 显示出来。
除此之外还有一些 74 系列的芯片充当逻辑胶水,用于进行地址解码和反相、消抖等。稍后将详细介绍。
地址空间
W65C02S 拥有 A0-A15 共 16 根地址线,因此它可以访问从 $0000 到 $FFFF 的地址,总共 的空间。对于 CPU 来说,RAM、ROM 以及 I/O 设备本质上都是可以通过地址访问的资源。CPU 并不关心某个地址背后究竟连接的是哪一种芯片,具体由什么设备响应某个地址,是由外围电路决定的。
v1.0.0 的地址空间采用了如下划分方式:
| 地址范围 | 大小 | 设备 | 用途 |
|---|---|---|---|
$0000-$7FFF |
32 KB | 62256 SRAM | 系统 RAM |
$8000-$BFFF |
16 KB | I/O | 外围设备 |
$C000-$FFFF |
16 KB | AT28C256 EEPROM | 程序代码及系统 ROM |
在 6502 系统中,有两个比较特殊的区域。首先是零页(Zero Page),位于地址 $0000–$00FF。它并不是一个独立的存储器,而是 RAM 的前 256 个字节。它之所以特殊,是因为 6502 为零页提供了专门的寻址方式。访问普通地址时,指令通常需要在操作码之后提供完整的 16 位地址,例如:
LDA $1234 ; Load Accumulator with value from $1234
LDA 即 LoaD Accumulator with memory, 将内存载入累加器。这条指令需要同时编码地址的低字节 $34 和高字节 $12。而当访问零页中的地址时,只需要提供一个 8 位地址:
LDA $34 ; Load Accumulator with value from $0034
CPU 会自动将其解释为 $0034。因此,零页寻址不仅可以减少指令长度,也通常可以减少执行所需的时钟周期。对于 6502 来说,零页相当于一组访问速度较快、指令编码更紧凑的工作寄存器,常用于保存临时变量、指针和中间结果。
其次是堆栈(Stack),位于 $0100–$01FF,大小为 256 字节。6502 使用一个 8 位的堆栈指针寄存器 S 来记录堆栈位置,但堆栈的高字节固定为 $01。因此,当堆栈指针为 $FF 时,栈顶对应 $01FF;执行一次入栈操作后,堆栈指针递减,下一项数据会写入更低的地址。相反,出栈操作会先递增堆栈指针,再从对应地址读取数据。用于保存返回地址、寄存器状态以及其他临时数据。
例如,执行 PHA (PusH Accumulator on stack, 将累加器压入栈) 指令时,CPU 会将累加器 A 的内容写入当前栈顶,然后调整堆栈指针;执行 PLA (PuLl Accumulator from stack, 从栈拉出至累加器) 时,则会从堆栈中取回之前保存的内容。调用子程序时,JSR (Jump to new location Saving Return/Jump to SubRoutine, 跳转至新位置并保存返回地址/跳转到子程序) 会将返回地址压入堆栈,之后 RTS 再从堆栈中取出这个地址并返回。发生中断时,CPU 也会利用堆栈保存返回地址和处理器状态。
因此,虽然从容量上看,零页和堆栈都只是 RAM 中很小的一部分,但它们是 6502 程序正常运行所依赖的基础区域。将完整的 32 KB RAM 放在 $0000–$7FFF,可以让零页和堆栈自然地落在正确的位置,同时为程序提供足够的普通工作内存。
接下来是位于地址空间最高端的 16 KB ROM。虽然使用的 AT28C256 实际容量为 32 KB,但在这个版本中,为了简化地址译码难度,只需要将其中一部分映射到 CPU 的地址空间。更重要的是,W65C02S 的复位、中断向量等位于 $FFF0–$FFFF,因此 ROM 必须覆盖这一地址区域。将 ROM 放在 $C000–$FFFF,正好可以满足这一要求。
剩下的中间部分则被预留给 I/O。与 RAM 和 ROM 不同,I/O 地址并不对应实际的存储单元,而是由外围设备对特定地址进行响应。例如,向 LED 所对应的地址执行一次写操作时,译码电路会识别这次访问,并让 74HC574 锁存数据总线上的内容。
之所以预留整整 16 KB 给 I/O,是因为这台计算机并不打算只拥有一个简单的 LED 输出。后续还需要加入 VIA、UART、键盘、存储设备以及其他外围电路,因此提前划出一块独立的 I/O 地址空间,可以让这些设备在后续版本中逐步加入,而不需要频繁修改 RAM 和 ROM 的基本布局。
地址译码
确定了地址空间之后,还需要让实际的硬件能够按照这个划分工作。
W65C02S 本身只负责把地址放到地址总线上。对于 CPU 来说,$1234、$8000 和 $C000 都只是一个 16 位地址,它并不知道这个地址对应的是 RAM、I/O 还是 ROM。因此,需要额外的逻辑电路对地址总线进行判断,并根据地址范围产生相应的片选信号(Chip Enable,CE)。
由于地址空间的划分非常规整,因此并不需要复杂的译码逻辑。三个区域的边界分别位于 $8000 和 $C000。将这两个转换为二进制:
只有最高两位发生了变化,这意味着只需要观察地址总线最高的两位 A15 和 A14,就可以确定 CPU 当前访问的是哪一个区域。
A15、A14 的真值表如下:
| A15 | A14 | 地址范围 | 区域 |
|---|---|---|---|
0 |
0 |
$0000-$3FFF |
RAM |
0 |
1 |
$4000-$7FFF |
RAM |
1 |
0 |
$8000-$BFFF |
IO |
1 |
1 |
$C000-$FFFF |
ROM |
因此,三个区域的逻辑关系非常简单:
这里还需要注意一个实际电路中的细节:W65C02S 的片选信号以及 AT28C256、62256 等存储器的 CE 输入通常是低电平有效,因此实际电路中可能会使用与上面逻辑表达式相反的有效电平信号。
为了实现这些逻辑,我没有使用专门的地址译码器,而是使用了两块 74HC00。芯片内部包含四个独立的 2 输入 NAND 门。NAND 是一种非常基础的逻辑门,但通过适当的组合,它实际上可以构成 NOT、AND、OR 等其他逻辑功能。再将 A15、A14 以及必要的控制信号组合起来,就可以产生 RAM、I/O 和 ROM 所需要的片选信号。
这里还有一个重要的问题:地址译码本身还不足以判断一次访问究竟是「读」还是「写」。例如,当 CPU 访问 I/O 区域时,LED 只应该在写操作发生时锁存数据,而不能在 CPU 从这个区域读取数据时也产生动作。因此,译码逻辑还需要结合 W65C02S 的 RWB 信号以及时钟信号 PHI2,进一步产生实际的设备控制信号。
读写时序
对于 W65C02S 来说,一次内存或 I/O 访问并不是简单地「给出一个地址,然后立即读写数据」。CPU 会按照时钟信号 PHI2 的节拍,在一个个时钟周期内依次完成地址输出、读写控制以及数据传输。外围设备必须在正确的时间响应,否则 CPU 可能读到错误的数据,或者无法正确地将数据写入设备。
在一个典型的存储器访问周期中,CPU 首先在地址总线上输出目标地址,同时通过 RWB 信号表明当前操作是读取还是写入。地址译码电路根据地址产生对应存储器的片选信号,而存储器则根据自身的控制信号决定是否参与这次总线访问。
当 CPU 执行读取操作时,存储器需要在规定的时间内将对应地址的数据放到数据总线上。CPU 随后在这个总线周期的适当时刻读取数据;而在写入操作中,情况正好相反。CPU 将要写入的数据放到数据总线上,并保持地址、数据以及写控制信号满足存储器的时序要求。存储器在规定的时间窗口内把数据锁存到对应地址。
因此,一次完整的存储器访问实际上同时涉及三类信号:
- 地址总线:决定访问哪个存储地址;
- 数据总线:在读取时由存储器提供数据,在写入时由 CPU 提供数据;
- 控制信号:决定当前是否应该访问该设备,以及当前是读取还是写入。
译码产生的片选信号也属于控制信号的一部分。只有当某个设备的片选有效时,它才应该对这次总线访问作出响应。
62256 的读取时序
时序图上的时间从左向右推进。当 W65C02S 执行一条需要从 RAM 中读取数据的指令时,首先会在地址总线上建立目标地址,对于 SRAM 来说,这个时刻可以作为一次读取周期的起点。这里需要特别区分几个容易混淆的时间参数。62256 的 是 Read Cycle Time,即读取周期时间,它规定了连续两次读取周期之间允许的最小时间。真正表示地址发生变化以后,最迟需要经过多久才能得到有效数据的参数是 ,即 Address Access Time。
在地址建立之后,CE# 和 OE# 也会变为低电平,使 SRAM 被选中并允许输出数据。而数据有效的时间分别受到 、 和 的限制。芯片并不会马上就输出有效数据。它们分别表示从地址变化、CE# 有效以及 OE# 有效开始计算,到数据输出有效所需要的最大时间。因此,这三个参数分别从不同的信号变化时刻开始计时。对于一次正常的读取操作,如果地址、CE# 和 OE# 在相近的时间建立,那么最终能够保证数据有效的时间取决于其中最慢的一条。由手册给出的 AC 电气特性可以得到:
对于这里使用的 AS6C62256-55PCN:
因此,在最坏情况下,从地址稳定开始计算,至少需要经过 55 ns,SRAM 才能保证输出数据已经有效。这里的 55 ns 是器件数据手册给出的最大访问时间,而实际系统设计还需要额外留出一定的时序裕量。
接下来就要看看,W65C02S 能不能给 SRAM 留出足够的时间。
那么,CPU 究竟会在何时建立好有效地址,又会在何时开始读取数据呢?W65C02S 的时序图初看上去很唬人,因为它将读取、写入以及其他控制信号的时序都综合在了一张图中,所以容易让人感觉无从下手。我们先看 CPU 的读取操作,可以暂时只关注图中与 PHI2、地址总线以及读取数据有关的几行时序。
首先来看 PHI2,通过电气特性可以得知, 和 分别是时钟高电平和低电平的最小脉冲宽度。在 +5V 供电条件下,两者均为 35 ns,因此对应的最小时钟周期为:
不过我们暂时不需要这么快。由于 W65C02S 内部采用的是静态设计,由于 W65C02S 内部采用静态设计,因此时钟频率可以非常低,理论上甚至可以手动发送时钟信号进行单步调试。在这里我们将使用 2 MHz 的晶振,一个完整的时钟周期则为 .
观察 W65C02S 的 AC 电气特性,其中有几个时间参数需要重点关注: 地址建立时间, 地址保持时间, 读取数据建立时间和 读取数据保持时间。
这些参数描述的并不是 CPU 内部某个明确可见的操作,而是 CPU 对外部总线信号提出的时序要求。对于地址信号,先找到当前总线周期的参考边沿。以该边沿为界,向前查看 ,向后查看 ,这段时间就是 CPU 要求地址保持有效的区间。也就是说,外部设备应当在这段时间内完成地址译码,并将地址视为稳定输入;对于读取数据,则需要以 CPU 的数据采样时刻为中心进行判断。向前查看 ,向后查看 ,这段时间就是 CPU 要求外部设备提供有效数据的区间。外部设备必须在这段区间内将数据稳定地驱动到数据总线上,CPU 才能可靠地完成读取。
因此,阅读这类时序图时,可以先确定当前总线周期的参考边沿,再分别根据建立和保持确定有效区间。前者告诉我们地址最迟何时必须建立、最早何时可以撤销;后者则告诉我们外部设备最迟何时必须提供有效数据,以及数据最早何时可以停止保持。
读者可能注意到地址轴上出现了两次 ,读取数据轴上也有类似情况,出现了两次 . 这是因为前面的其实属于上一个周期。而本周期真正的保持时间是最后面的那一个。由此我们可以得出地址和数据有效的时刻:
读取数据需要在采样点之前满足 的建立时间,因此 CPU 的数据采样时刻可以看作 .
另一方面,CPU 至多仍需 建立地址。因此,即使按照最不利的情况计算,外部存储器从地址建立到 CPU 采样数据之间仍然有 ,远大于 RAM 所需的 55 ns 的时间。
在完成读取数据操作以后,CPU 还要求数据继续保持 的时间。与此同时,在完成数据读取之后,CPU 并不会立即改变地址总线上的内容,还将继续保持 ;随后,RAM 在地址发生变化以后,数据并不会马上消失,也将保持一小段时间的输出 . 也就是说,从 CPU 完成数据采样开始算起,这两个时间段恰好可以首尾相接,RAM 输出的原数据理论上至少还能保持 20 ns,仍然有约 10 ns 时间裕量。
62256 的写入时序
接下来再看写入时序。除了需要提前建立好地址和数据之外,根据时序图的注释,RAM 的实际写入发生在 CE# 和 WE# 均处于有效电平的重叠期间。
这里存在一个值得注意的时序问题。当前设计中,RAM 的片选信号 CE# 直接由 CPU 的最高位地址线 A15 产生。对于 W65C02S 而言,地址总线、读写控制等信号都在相近的时刻发生变化,CPU 并没有保证外部组合逻辑经过传播延迟之后,最终生成的 CE#、WE# 一定能够以我们希望的先后顺序到达 SRAM。
AS6C62256 的电气特性虽然给出 和 的最小值虽然可以达到 0 ns,但这并不意味着实际电路中可以忽略它们。0 ns 只是器件在满足其他时序条件时允许的最小裕量,器件本身没有额外规定的最小地址建立时间和写恢复时间。CPU 的地址输出、RWB、PHI2 以及由这些信号产生的 CE#、WE# 都存在不同的传播延迟,因此在地址正在切换的短暂窗口内,RAM 可能会看到旧地址、新地址以及尚未完全稳定的地址组合。尤其是在写周期中,如果 WE# 在地址尚未稳定时就已经进入有效状态,就可能使 RAM 在错误的地址下产生一个有效写入窗口;如果地址或数据在写入窗口内发生变化,导致不可预测的数据被写入。
那么,如何保证在整个有效写入窗口内,RAM 所看到的地址、数据和控制信号都满足数据手册规定的时序关系?
好,分析了这么一大堆,虽然要意识到问题的存在这一点确实很困难,但是解决方案却非常简单。
对于当前这个设计,最直接的思路就是让写入窗口避开地址总线的过渡过程。观察 W65C02S 的时序图可以发现,地址尚未稳定的阶段基本处于 PHI2 的低电平期间,而建立并保持稳定的阶段基本处于高电平期间。
W65C02S 的 RWB 信号用于表示当前总线周期是读取还是写入。当 RWB=1 时表示读取,当 RWB=0 时表示写入。因此,如果只考虑读写方向,可以用 NOT RWB 表示「当前正在写入」。另一方面,PHI2 可以用来限制写入发生的时间窗口。只有当 PHI2=1,并且当前又是写周期时,RAM 才允许进入写入状态。因此,理想的逻辑表达式就是:
反相 RWB 只需把与非门的两个输入接在一起即可。这样一来,最终只用了两个与非门就解决了问题。而从 SN74HC00N 手册上的电气特性可以得出,在 4.5V、室温的情况下,信号传播时间的延迟在 9~18ns 以内,和之前计算得到的 相比仍在允许范围内。
AT28C256 读取时序
AT28C256 的读取时序与前面分析的 62256 基本相同。AT28C256 同样属于异步存储器,CPU 通过地址总线提供地址,ROM 根据地址输出对应的数据,而不需要额外的时钟信号。
因此,前面针对 RAM 读取时序所采用的分析方法同样适用于 ROM:只需要确认地址建立之后,ROM 能否在 W65C02S 采样数据之前输出有效数据,并确认数据在采样之后能够保持足够长的时间即可。
AT28C256 的具体访问时间取决于所使用的速度等级,但即使按照较慢的器件规格计算,相对于本项目 2 MHz 时钟所提供的 500 ns 周期,这个时间窗口仍然非常充裕。因此,在当前的工作频率下,ROM 的读取速度不会成为系统的时序瓶颈。
在控制信号方面,ROM 与 RAM 略有不同。RAM 既需要读取控制,也需要通过 WE# 控制写入;而 AT28C256 在正常运行时只需要响应相应的片选和输出使能信号即可,暂不考虑对 ROM 进行写入操作。
因此,在完成前面的 RAM 读取时序分析之后,这里不再重复绘制完整的 ROM 读取时序图。只要将 AT28C256 的地址访问时间、CE# 和 OE# 的响应时间与 W65C02S 的数据建立、保持要求进行比较,即可确认当前 2 MHz 的设计具有足够的时序裕量。
74HC574 写入时序
除了 RAM 和 ROM 以外,还有一个需要特别处理的元件即 74HC574.
74HC574 是一个 8-bit D 触发器。与前面介绍的 RAM 不同,它不是在一段电平有效期间持续进行写入,而是在时钟的有效边沿到来时,将数据总线上的内容锁存下来。因此,CPU 若想往其中写入数据,不能简单地给 74HC574 一个持续有效的片选信号,而需要产生一个 CLK 脉冲信号。
xD 为数据输入,xQ 为数据输出。使能引脚 OE# 低电平有效。CLK 引脚表明该器件在时钟有效边沿上触发。
将 74HC574 的片选信号和写入条件合并,可以得到最终的逻辑表达式。它只应当在被选中且 CPU 正在执行写操作时锁存数据。由于它是在时钟信号的上升沿触发,因此,时钟信号可以表示为:
其中, 表示当前地址属于 LED 所对应的地址范围。由于当时我手头上正好有两片 74HC00,因此需要将上式转换成通过与非门表达。在当前地址划分方案中, I/O 位于 $8000-$BFFF,而 LED 只需响应其中任意一个地址。因此通过总线中的 A15 和 A14 便可生成片选信号:
在选中时与非门输出的是低电平,因此需要将其反相:
而 是之前生成的写使能经反相后的结果:
复位与 NMI
最后还有一个不能省略的部分,就是复位和 NMI (Non-Maskable Interrupt, 不可屏蔽中断) 电路。
由于机械按钮不是一个理想的开关。当按键按下或释放时,触点通常会在很短的时间内发生多次快速导通和断开。如果直接把按键连接到 CPU 的控制输入,那么 CPU 可能会把这些瞬间的电平变化误认为多次触发。
因此,这里首先使用 RC 电路对按键产生的快速变化进行滤波。电容会使电压变化变得相对缓慢,从而抑制机械触点产生的高频抖动。随后,再通过施密特触发器将缓慢变化的模拟电压重新转换成干净的数字电平。而施密特反相器与普通的相比,区别在于它具有迟滞特性。输入电压上升和下降时分别使用不同的阈值,因此即使 RC 电路的输出在阈值附近缓慢变化,也不会因为微小的电压波动左右横跳。
最终成品
经过前面的地址空间规划、地址译码、读写时序以及复位和 NMI 电路设计,v1.0.0 的整个硬件设计基本确定。这里不再逐一分析每个模块的具体工作原理,而是直接给出完整原理图。它可以作为前面各章节的一个总览,也方便后续调试时根据实际连接关系检查信号。
此后就是 PCB 设计和焊接。相比前面的电路设计,这一部分没有太多值得展开分析的内容,就借这个机会聊聊完成这个项目过程中的一些感受。
这其实是我第一次独自完成一个相对完整的硬件项目,从元件选型、原理图设计、PCB 绘制,到后续的元件采购和实际焊接,基本把整个流程都走了一遍。前前后后花了差不多一个月的时间才最终完成。设计 PCB 和等待打样的那段时间里,几乎每天都在期待 PCB 和各种元件能够尽快送到。毕竟在电脑里反复检查了那么多次的电路,最终能不能真正工作,还得等它变成一块实实在在的电路板才能知道。
PCB 到货以后,第一眼看上去还是比较满意的。实际拿到手里之后,又是另一种感觉:原本只是电脑屏幕上的原理图和 PCB 布局,现在变成了一块可以拿在手里的实体电路板,整体看上去也比预想中更有分量。唯一一点就是丝印的字体太小了,在软件里绘制的时候感觉 10*10 cm 似乎已经挺大了,拿在手里才发现不过才比巴掌宽一圈而已。
接下来就是元件焊接。虽然之前在学校的实习课中学习过基本的焊接方法,但毕竟已经很久没有实际操作,刚开始焊的时候还是有些生疏。因此,最终留下的部分焊点并不算特别美观,有些元件还焊的歪歪斜斜的。不过检查之后,好在没有发现虚焊、短路或者焊点粘连等影响电路正常工作的明显问题。
至此,从最初的设计,到最终变成一块真正可以运行的电路板,v1.0.0 的硬件部分终于完成。前面讨论的地址总线、数据总线、存储器、I/O 和各种控制信号,也将在这里第一次真正同时工作。
不过,焊接完成并不意味着这个项目已经成功。对于一台自己设计的计算机来说,真正的验证还要从第一次上电开始。下一篇文章将进入实际测试阶段,介绍系统的第一次上电、程序烧录,以及程序运行过程中遇到的问题和后续的调试过程。
MOS Technology 6502. Wikipedia. https://en.wikipedia.org/wiki/MOS_Technology_6502 ↩︎