只读存储器 (ROM)
9.1 概览
正如我们之前所见,在 CPU 启动或复位后,它将从第一个虚拟地址 0x0000 开始执行代码,此时该地址也等于物理地址 0x000000。
因此,此地址必须存在一些 CPU 可以理解并执行的有效二进制代码,所以我们需要一个设备来存储这些数据。这就是只读存储器(ROM)的作用:它将存储 CPU 将要执行的代码。
9.2 硬件实现
尽管名为只读存储器(ROM),但它不一定是不可擦除的。存在多种可以擦除和重写的 ROM 类型:EPROM、EEPROM、NOR Flash、NAND Flash 等。
在 Zeal 8 位计算机上,充当 ROM 的组件是 SST39SF020 NOR Flash,容量为 256KB,组织为 256K 字 × 8 位,在 PCB 上标记为 U4。它采用带插座的 PLCC32 封装,使其紧凑且仍然可以轻松移除和重新插入,无需焊接或拆焊。
这款 SST39SF020 NOR Flash 的主要优点有:
- 可在电路内编程,无需取出并使用专用编程器进行擦除或重写,可以直接从 Z80 完成!
- 不需要任何特殊的硬件协议来擦除或编程,从 CPU 的角度看,它仍然像一个常规的(EEP)ROM。
- 对于 8 位计算机来说容量大:最高可达 512KB。
- 引脚与传统的 27C020 ROM 兼容。
- 可用性强,由于仍在大批量生产,很容易找到。
在引脚排列方面,其组织方式如下:
让我们看看连接到它的常规信号:
- $ADDR\ 0$ 到 $ADDR\ 17$:表示要读取的字节的地址。ROM 为 256KB,因此最后一个字节地址为
0x3ffff。 - $DATA\ 0$ 到 $DATA\ 7$:表示刚从 ROM 读取的字节。
- $\overline{RD}$:来自 CPU 的信号,当 CPU 正在执行读取时有效。
- $\overline{WR}$:来自 CPU 的信号,当 CPU 正在执行写入时有效。更多信息请参见编程 ROM 部分。
- $\overline{CE}$:来自逻辑胶合的信号,当 CPU 尝试访问 ROM 映射的地址范围时有效,该范围是物理地址空间的前 512KB。更多信息请参见逻辑胶合章节。
两个带有星号标记的信号具有特殊功能或状态,以下各节将解释这些细节。
9.3 硬件依赖与时序
在时序方面,ROM 依赖于 MMU,因为它需要 18 或 19 条地址线(取决于 ROM 的大小),同时也依赖于逻辑胶合。实际上,后者输出的 $\overline{ROM\_ENABLE}$ 连接到 ROM 的 $\overline{CE}$ 信号。两者都应考虑在内,以确定从 CPU 执行读取到 ROM 输出数据之间的总传播延迟。
根据数据手册,SST39SF020A-70 本身在最坏情况下标称传播延迟为 70ns。
关于延迟和时序的所有详细信息在官方数据手册中有详细说明。
9.4 安装更大的 NOR Flash
ROM 的第一个引脚 $NC / ADDR\ 18$ 有两种可能的状态:如果在插座 U4 中安装了 256KB ROM(默认情况),此引脚必须保持不连接。
但是,如果安装的是 512KB ROM(例如 SST39SF040),则必须将此引脚连接到来自 MMU 的 $ADDR\ 18$。为此,只需在 PCB 上标记为 JP1 的焊桥上连接一个跳线即可。
通过连接此跳线,MMU 的 $ADDR\ 18$ 将连接到该引脚,使 Z80 CPU 能够访问整个 512KB ROM。
这个跳线 JP1 解释了为什么我们需要在逻辑胶合中为 ROM 分配 512KB 的连续内存。如果只分配了 256KB(从 0x00000 到 0x3ffff),用 512KB ROM 替换将更加困难,因为需要额外的逻辑来映射 ROM 的第二个 256KB 部分。
其原理图如下所示:
9.5 禁用 ROM
如逻辑胶合章节所述,无论安装的 ROM 是 256KB 还是 512KB,当 CPU 尝试读取(或写入)任何物理地址在 0x00000 到 0x3ffff 之间时,ROM 的 $\overline{CE}$ 信号都将有效。
这样做的结果之一是,ROM 始终是 CPU 获取指令的第一个位置。然而,在某些情况下,拥有一个外部可引导卡带会很有用,它可以完全绕过 Zeal 8 位计算机的内部 ROM,只让 CPU 从该新外部板中嵌入的 ROM 读取和执行数据。
一种解决方案是暂时移除内部 ROM,然后插入扩展板,这样 ROM 之间就不会发生冲突。另一种不需要修改主板的解决方案是将 $\overline{CE}$ 线拉高,使其失效。
为此,$\overline{CE}$ 信号通过一个 1kΩ 电阻(标记为 R5)连接到逻辑胶合的 $\overline{ROM\_ENABLE}$ 信号,并且也连接到扩展端口(标记为 J2)的第 49 引脚,不经过任何电阻。
因此,每当外部扩展板需要禁用内部 ROM 时,此 ROM_DISABLE 引脚必须设置为高电平(5V)。请查看扩展端口部分以了解其确切位置。
9.6 编程 ROM
NOR Flash 与 EPROM 和 EEPROM 一样,可以通过一些通用编程器进行编程。
然而,如前所述,NOR Flash 相较于前面提到的 ROM 的主要优点是它可以在电路内编程,无需从插座中取出,因此不需要这样的通用编程器。
擦除和编程 NOR Flash 的整个过程在官方数据手册中有详细说明。
编程原理
简而言之,NOR Flash 通过软件命令实现这一点,这些命令由在预定义地址上连续写入预定义值组成,如下表所示(取自上述文档):
值得注意的是,这些地址是相对于 NOR Flash 的地址,但由于 NOR Flash 在 Zeal 8 位计算机上映射到物理地址 0x00000,我们可以将它们视为物理地址,而不是虚拟地址!
换句话说,要执行这些命令中的任何一个,必须将 ROM 的至少两个 16KB 页面映射到 64KB 虚拟地址空间中。这两个页面分别是包含地址 0x2AAA(页面 0)和 0x5555(页面 1)处字节的页面。它们不一定需要同时被映射。
另一个重要注意事项是,在尝试编程一个字节之前,必须先执行擦除操作。因此,即使只需要(重新)编程某个扇区中的一个字节,也必须先擦除整个扇区!
实际示例
如前表所述,擦除芯片的步骤为:
- 将
0xAA写入 ROM 地址0x5555 - 将
0x55写入 ROM 地址0x2AAA - 将
0x80写入 ROM 地址0x5555 - 将
0xAA写入 ROM 地址0x5555 - 将
0x55写入 ROM 地址0x2AAA - 将
0x10写入 ROM 地址0x5555
然后 NOR Flash 将开始擦除自身,这最多需要 100ms。这意味着擦除 Flash 的程序不能位于 Flash 本身中,否则它会被擦除,它应该从其他地方(如 RAM)执行。
以下是一个实际的 Z80 汇编示例,展示如何通过软件命令擦除芯片:
; 以下代码从 RAM 中运行,位于虚拟地址 0x8000
; 前两个 16KB 虚拟页面是空闲的,我们可以用它们来映射 ROM 的前几个页面
erase_chip:
; 将 A 初始化为 0
xor a
; 将虚拟页面 0 映射到物理地址 0x0000 - 0x3FFF
out (0xF0), a
inc a
; 将虚拟页面 1 映射到物理地址 0x4000 - 0x7FFF
out (0xF1), a
; 我们现在可以访问 NOR Flash 地址 0x2AAA 和 0x5555,开始擦除过程
ld hl, 0x5555
ld a, 0x55
; 1. 将 `0xAA` 写入 ROM 地址 `0x5555`
ld (hl), 0xAA
; 2. 将 `0x55` 写入 ROM 地址 `0x2AAA`
ld (0x2AAA), a
; 3. 将 `0x80` 写入 ROM 地址 `0x5555`
ld (hl), 0x80
; 4. 将 `0xAA` 写入 ROM 地址 `0x5555`
ld (hl), 0xAA
; 5. 将 `0x55` 写入 ROM 地址 `0x2AAA`
ld (0x2AAA), a
; 6. 将 `0x10` 写入 ROM 地址 `0x5555`
ld (hl), 0x10
; 等待至少 100ms 以完成擦除 = 在 10MHz 下为 1,000,000 个 T 状态
; 下面是 24 个 T 状态
ld bc, 1000000 / 24
wait:
; 以下循环(直到 'jp nz, wait')共 24 个 T 状态
dec bc
ld a, b
or c
jp nz, wait
; 100ms 已过,芯片已擦除。