DMA 控制器
16.1 概述
DMA(直接存储器访问)控制器使主机 CPU 能够在 22 位(4MB)物理地址空间内的任何位置发起高效的数据传输。
传输由一个 22 位地址描述,该地址指向一个描述符数组。每个描述符定义了单个传输的源地址、目标地址和大小,从而允许在没有进一步 CPU 干预的情况下执行多个顺序操作。
DMA 在传输期间使用 Z80 风格的总线仲裁信号(BUS_REQ 和 BUS_ACK)来控制系统总线。这意味着在 DMA 执行数据移动时,CPU 被暂时挂起,从而确保安全且原子化的块传输。
这种简单而高效的设计将数据移动任务从 CPU 中卸载出来,在保持配置简洁的同时提高了整体系统性能。
与其他控制器一样,DMA 控制器可以映射到设备组中,I/O 端口起始地址为 0xA0,其设备索引为 4。
16.2 描述符
组织方式
DMA 控制器依赖于位于 4MB 内存中任意位置的描述符数组来定义每个传输操作。
每个描述符指定源地址和目标地址、传输长度以及额外的控制标志。其大小为 12 字节。
在 C 语言中,单个描述符可以表示为:
struct dma_descriptor_t {
uint16_t rd_addr_lo; // 源地址低 16 位
uint8_t rd_addr_hi; // 源地址高 6 位(在 22 位地址空间内)
uint16_t wr_addr_lo; // 目标地址低 16 位
uint8_t wr_addr_hi; // 目标地址高 6 位(在 22 位地址空间内)
uint16_t length; // 要传输的字节数
uint8_t flags; // 传输的控制标志
uint8_t reserved[3]; // 保留用于对齐或将来使用
};
其中 uint16_t 表示无符号 16 位值,uint8_t 表示无符号 8 位值。
这些描述符可以在内存中连续排列成一个数组。DMA 控制器按顺序处理每个描述符,逐个执行指定的传输,直到遇到设置了 last 标志的描述符。此时,DMA 传输序列完成。因此,只需将第一个描述符的物理地址(22 位)通过 DESC_ADDR0(低字节)、DESC_ADDR1 和 DESC_ADDR2(高字节)寄存器提供给 DMA 控制器。
这种设计允许高效地批量处理多个传输,无需进一步的 CPU 干预。
标志
每个 DMA 描述符中的 flags 字段宽度为 5 位,用于控制传输行为:
-
位 0(
last): 如果此描述符是数组中的最后一个,则设置为1,表示 DMA 控制器在完成此传输后停止。 -
位 1-2(
rd_op): 指定在传输每个字节后应用于读取/源地址的算术运算。 -
位 3-4(
wr_op): 指定在传输每个字节后应用于写入/目标地址的算术运算。
运算编码如下:
| 操作码 | 行为 |
|---|---|
| 0 | 递增 |
| 1 | 递减 |
| 2 | 无操作(地址保持不变) |
| 3 | 保留(不应使用) |
这些操作支持灵活的数据传输模式,例如向前复制内存、向后复制内存,或重复写入/读取固定位置。
16.3 传输频率
DMA 控制器由全局 50 MHz 系统时钟驱动。因此,DMA 传输期间的读取和写入操作都与该时钟同步,对应 20ns 的周期。
由于这种时序对于 8 位系统中的许多组件来说通常过快,控制器提供了可配置的等待状态来降低存储器事务的速度。每个等待状态增加 20ns 的额外延迟,为外部组件提供更多时间来输出或接收正在传输的数据。
这些等待状态通过 CLK_DIV 寄存器指定:
- 低四位(4 位)
READ_WAIT,设置 DMA 控制器在内存读取期间插入的等待状态数量。 - 高四位(4 位)
WRITE_WAIT,设置 DMA 控制器在内存写入期间插入的等待状态数量。
调整这些等待状态可以通过适应较慢的内存或外设时序来确保可靠的数据传输。
READ_WAIT 值还决定了 DMA 控制器从内存读取描述符的速度。无法为读取描述符和执行实际数据传输设置不同的等待状态——两者使用相同的 READ_WAIT 设置。
⚠️ 尽管单个时钟周期(20 ns)就足以写入视频板内存的任何部分,但建议至少配置 1 个等待状态,以避免潜在的内部写入冲突。
16.4 传输过程
执行 DMA 传输的步骤如下:
-
设置 DMA 等待状态 (可选)
- 通过设置
CLK_DIV寄存器选择所需的读取和写入等待状态。
- 通过设置
-
准备描述符
- 按上述方式写入一个 12 字节的结构体,包含源地址、目标地址和需要传输的字节数。
- 根据需要重复此操作,并将最后一个描述符的
flags中的last位标记为 1。 - 将第一个描述符的物理地址(22 位)写入
DESC_ADDR0(低字节)、DESC_ADDR1和DESC_ADDR2(高字节)寄存器。
-
启动传输
- 在
CTRL寄存器中设置START位。 - 将
BUS_ACK线拉低后,CPU 应自动暂停。
- 在
-
完成
- 一旦 CPU 恢复执行代码,即表示传输成功,无需检查任何 BUSY 或 IDLE 标志。
16.5 时序图
下图解释了 DMA 传输所涉及的整个过程,当然是简化版本。
16.6 寄存器总览
| Name | Description | Base-relative address | Access |
| CTRL | DMA控制寄存器 | 0x00 | WO |
| DESC_ADDR0 | 描述符地址字节0(最低字节) | 0x01 | WO |
| DESC_ADDR1 | 描述符地址字节1 | 0x02 | WO |
| DESC_ADDR2 | 描述符地址字节2(最高字节) | 0x03 | WO |
| CLK_DIV | DMA内存总线访问等待状态配置。控制完成读或写操作前等待的时钟周期数。 | 0x09 | RW |
16.7 寄存器
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
| 0 | 1 | 0 | 1 | 0 | 1 | 1 | 0 |