13.2 DMA 与 8237 控制器
程序控制输入输出循环让每个字节都经过 CPU 寄存器。这适合短命令,但大型数据流会让处理器不断重复读取、写入和更新地址。直接存储器访问(direct memory access, DMA)允许硬件控制器在输入输出设备与存储器之间传送数据。CPU 负责设置任务、把系统总线暂时借给控制器,并处理完成事件。
在没有电气冲突的前提下移交总线所有权
任何时刻只能有一个总线主设备驱动地址线与控制线。与 8237 兼容的控制器用 HOLD 请求所有权。CPU 完成当前总线周期,停止驱动共享总线,再用保持响应(hold acknowledge, HLDA)作答。随后 DMA 控制器可以把地址放上总线,并产生存储器与输入输出读写选通信号。
设备使用 DMA 请求(DMA request, DREQ)向所属通道申请传送。控制器用 DMA 响应(DMA acknowledge, DACK)回答被选设备。安全传送遵循这组可观察握手:
1. 设备置位 DREQ。
2. 控制器置位 HOLD。
3. CPU 完成当前总线周期,释放总线并置位 HLDA。
4. 控制器置位匹配的 DACK,驱动地址并执行传送周期。
5. 所有权区间结束后,控制器释放 HOLD;CPU 撤销 HLDA 并继续运行。
在单字节传送(single-transfer)或周期窃取模式中,控制器每传一个单位就交回总线,下一个字节要再次仲裁。CPU 进展与 DMA 交错。在块模式(block mode)中,控制器在整个编程块期间保留总线,传送吞吐量最高,但 CPU 的总线访问会被推迟。请求模式在 DREQ 保持有效期间继续。这些模式在前台响应速度与传送完成时间之间作取舍。
在收到 HLDA 前驱动总线会形成总线争用(contention):CPU 与控制器可能向同一组导线输出不兼容的值。这不只是调度错误,而是无效的电气状态。所有权实验会明确显示这种错误状态。
配置通道并核对每一个传送地址
8237 提供四个 DMA 通道。每个通道都有当前地址寄存器、当前字计数寄存器、请求与屏蔽状态以及模式设置。虽然历史名称叫“字计数”,标准 8237 的一个传送单位实际上是一个字节。它的字计数保存的是传送单位数减一:
因此,传送 16 字节需要写入计数值 15。每次传送后,地址递增或递减,计数减少。当计数从 0000H 回绕到 FFFFH 时,控制器产生终值计数(terminal count, TC)并结束数据块。若直接写入所需字节数,就会多传一个单位。
方向是从控制器面对存储器的角度命名的:输入输出到存储器的任务要写存储器,存储器到输入输出则读取存储器。通道屏蔽会阻止选择,但不会销毁其配置。自动初始化(auto-initialization)会在终值计数后恢复基础地址与基础计数,适合重复缓冲区,但若软件期望单次任务就很危险。
经典 PC 风格系统把 8237 的 16 位地址与外部分页寄存器组合,以访问 64 KiB 之外的位置。这会暴露边界风险:控制器递增低 16 位地址时,不会自动向外部分页寄存器进位。稳健设计应选择不跨越控制器地址窗口的缓冲区,并核对首地址、末地址、方向、精确传送次数与终值计数行为。
第 13.2 节在保持总线独占与精确通道状态的同时搬运了数据块。第 13.3 节把同样的“设置—状态—服务”纪律应用到每次只传一位的链路。