查看“DMA”的源代码
←
DMA
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
用户
您可以查看和复制此页面的源代码。
;有关ISA DMA的要点有: * ISA DMA与PCI总线主控DMA不是一回事; * ISA DMA通道1、2和3可用于8位传输到ISA外围设备; * ISA DMA通道5、6和7可用于向ISA外围设备进行16位传输; * 传输不得跨越物理64KB边界,且不得大于64KB; * 传输必须是物理上连续的,并且只能针对最低的16 MB物理内存; * ISA DMA速度很慢——理论上是4.77 MB/秒,但由于ISA总线协议,速度更接近400 KB/秒; * ISA DMA释放了CPU资源,但给内存总线增加了极重的负载; * 目前还在使用ISA DMA的设备很少 -- 只有内部软盘、一些嵌入式声音芯片、一些并行端口和一些串行端口。 注: * Sound Blaster和Sound Blaster PRO仅支持8位DMA; * Sound Blaster16+两者都支持; * 软盘控制器仅支持8位DMA,并通过硬连线使用DMA通道2。 == PC上有多种DMA == 现代PCI控制器总是有自己的 “总线控制DMA(Busmastering DMA)”,这远远优于ISA DMA。 甚至USB软驱也通过PCI USB控制器使用PCI Busmastering发送DMA数据。 PCI Busmasters可以使用32位寻址访问内存。 较新的PCI卡开始支持64位寻址 (尽管目前大多数还不支持)。 通常PCI卡使用“scatter-gather”总线管理,其中单页用作数据页的目录。 这几乎完全克服了所有形式的DMA“仅限物理内存”的限制。 == ISA DMA背景 == 与ISA本身一样,'''ISA DMA'''(“Industry Standard Architecture Direct Memory Access”)也是现代PC架构的早期产物。 它由内部软盘控制器、ISA声卡、ISA网卡和并行端口(如果它们支持ECP模式)使用。 尽管中断,键盘和计时器接口电路具有相关明显的用途,但ISA DMA控制器及其编程接口仍然基本停留在最初设计的20世纪70年代中。 DMA背后的想法是,你可以设置一个“通道”,地址指向内存,数据传输的长度。 一旦设置好,CPU就可以告诉拥有通道的外设独立去做它应该做的事情(例如,读取扇区)。 然后中央处理器可以去做别的事情。 当CPU不使用内存总线时,DMA芯片在外围设备和内存之间接管并传输数据,而不涉及CPU。 当传输完成时(例如,整个扇区已被发送到软盘驱动器),DMA芯片随后发出完成的信号。 如果数据用完,DMA芯片甚至可以发出信号,从而使系统能够在同一DMA事务上定位要传输的下一个数据块。 DMA可以大大提高系统的速度,它是由Intel(设计DMA控制器芯片的人)从上世纪60年代的旧大型机上借来的,当时哪些大型机的所有设备都有DMA通道(CPU当时非常慢,且不全在一个芯片上)。 当然,所有好的想法都会有不利之处,虽然Intel不能真的为即将被描述的事情而受到指责,但IBM肯定要背这个锅。 一开始有PC,但是PC很慢。 IBM从天堂上往下看,说“装上DMA控制器——这应该会加快速度。” IBM的用心是正确的; 但是DMA的控制大脑总是在状况外,导致DMA控制器一直无法匹配系统需求。 PC/AT标准包含2块Intel 8237A DMA芯片,作为主/从芯片连接。 第二个芯片是Master,它的第一条线路(通道4)由第一个芯片Slave使用。 (这与中断控制器不同,中断控制中第一个芯片是Master。) 8237A是为旧的8080 8位处理器设计的,这可能是造成如此多DMA问题的主要原因。 IBM为其PC选择的8088和8086处理器相对于DMA控制器来说太先进了。 前面提到DMA控制器应该能够发出完成信号,或者甚至能发出请求需要更多信息。 不幸的是,这会使扩展槽太大,所以IBM切断了与DMA芯片的所有连接。 知道传输何时完成的唯一时间是外设发出中断信号。 这意味着所有使用ISA DMA通道的外围设备都被限制为不超过64 KB的传输,因为担心会扰乱DMA控制器。 即使在PC/AT中,IBM也开始绕过PC/XT中使用的ISA DMA,并对硬盘使用[[ATA PIO Mode]]。 正是因为上面概述的64KB限制,以及286处理器可以在6 MHz下执行16位事务的事实。 甚至ISA总线也可以以高达12 MHz的速度运行,远远快于DMA控制器运行的4.77 MHz。 扩展卡设计人员也对DMA的功能不足感到不满,尤其是依赖数据传输速度的“硬盘”扩展卡制造商。 为了绕过“板载”DMA控制器的限制,扩展卡制造商开始在他们的扩展卡上安装他们自己的DMA控制器。 它们的功能与 “机上” DMA完全相同,在处理器不看时 “窃取内存总线周期”,从而提高了整个系统的性能。 这些“ISA总线主控器”传输通常仍限于较低的16 MiB内存,但没有4.77 MHz的问题。 这一趋势通过PCI总线的创建继续发展,PCI总线也最终完全取代了PC中的ISA总线。 == 技术细节 == 每个8237 DMA芯片有4个DMA通道。 第一个芯片上有DMA0-DMA3,第二个芯片上有DMA4-DMA7。 第一个DMA控制器连接起来进行8位传输,而第二个连接起来进行16位传输。 在一些教程或其他wiki文章中,你有时会看到“第二个”DMA芯片(通道4到7)标记为“主”控制器,第一个(通道0到3)标记为“从”。 这非常令人困惑,这里不会再使用这些术语。 DMA通道0不可用,因为它在短时间内用于DRAM内存刷新,因此保留 (即使现代计算机不使用它)。 DMA通道4不能用于外围设备,因为它用于级联其他DMA控制器。 DMA控制器的内部地址寄存器只有16位。 为了扩展这一点,IBM在每个通道增加了一个 “外部(external)” “页面寄存器(page register)” 字节,允许访问16 MB的内存 (总共24位)。 如果DMA传输跨越64 KB的边界,内部地址寄存器将变为零,外部页面寄存器将“不”递增。 这时DMA控制器将用它在新地址找到的任何数据继续传输。 基于ISA的DMA控制器被指定为以4.77 MHz运行。'''没有例外'''。 如果系统的“前端”(内存)总线以133 MHz的频率运行,在传输每个ISA DMA字节/字时,它将被人为地降低到4.77 MHz。 这包括EISA和PS/2 32位控制器,即使这些控制器具有额外的页面寄存器(允许4GiB寻址空间)和执行32位传输的能力。 (这些DMA控制器仅存在于EISA和MCA系统上,这两个系统现已过时,此处不再进一步介绍。)== 编程细节 == === 16位问题=== 16位通道(5、6和7)具有特殊的寻址方案来处理它们递增地址的方式。 内部寄存器增加1,但每次访问之间需要将内存地址增加2。 解决方案是,CPU存储在DMA控制器中的“起始地址”需要右移1位。 在每次存储器访问时,该内部16位地址值递增1,然后取该值并将其左移一位(清除低位),然后将其用作地址。 然后以正常方式附加外部 “页寄存器” 寻址字节。 重要的是要注意,内部地址的高位在向左移位时是“丢失的”——它不会被或运算到页面寄存器字节中。 因为地址寄存器将有效位绕回到零,而不会递增页面寄存器字节,这会阻止大于64 KB的DMA传输工作,即使它们在技术上对于16位通道是可行的。 === 物理内存与分页 === 分页内存映射完全由CPU控制。 DMA的全部意义在于绕过CPU。(译者注:也就不知道MMU分页表的存在) 因此,DMA不可以访问任何虚拟内存地址。 所有DMA始终仅在物理内存地址上完成。 ISA DMA的物理地址限制为16 MB。 由于DMA独立于CPU运行,因此重要的是,OS要为为DMA传输分配一个连续的物理内存块,以防止该内存用于任何其他目的或交换出去,直到DMA传输完成。 注:VM86模式不使用“物理”寻址。 内存地址是“假的”。 在VM86模式下,操作系统必须代表应用程序模拟任何DMA事务。 ===缓冲区大小=== 一张典型的1.44MB软盘可以在一次传输中轻松地传输36个扇区的数据。 这只有18 KB。 最大的内部软盘使用最差格式,一次可以传输84个扇区。 这仍然只有42KB。 Soundblaster卡使用64 KB缓冲区可以最佳运行。 从来没有必要尝试将缓冲区ISA DMA传输加倍,因为它们无论如何都非常慢。 最多有6个可用DMA通道,不需要为每个通道分配完整的64KB。 将所有这些放在一起,任何操作系统都应该能够轻松地从256 KB的池中分配所需的所有ISA DMA物理内存,甚至有时只需一半。 === Flip-Flop === PC上的许多设备(例如ATA磁盘驱动器)使用8位IO端口来接收16位值。 这是使用flip-flop完成的。 设备首先需要低字节。 一旦接收到一个字节,flip-flop就会改变状态,然后器件就会等待高位字节。 当接收到高字节时,flip-flop再次改变状态,并且设备期望一个新的低字节。 通常,每个16位“寄存器”都有自己的Flip-Flop,但ISA DMA控制器在这方面存在问题。 在8237芯片上,'''只有一个'''Flip-Flop。 有8个16位寄存器。 最多可以有三个设备驱动程序同时竞争使用一个触发器。 这就产生了两个严重的问题。 一是 “争用问题”。 另一个问题是,很难确定触发器当前处于什么状态。 处理触发器状态问题的标准解决方案是在你每次想要使用Flip-Flop时将其重置为“低字节”状态,这样你就可以在发送字节之前确定它处于正确的状态。 “争用” 只有两种解决方案: 要么使用 [[lock|锁]],要么只允许存在一个ISA DMA驱动程序,则无法进行争用。 === 屏蔽DRQ(DMA Request) === 设置DMA传输总是需要设置传输的两端。 也就是说,无论哪个外围设备拥有DMA通道,都需要被告知如何通过DMA传输数据块。 DMA控制器需要被告知内存地址、传输长度、传输“模式”和传输方向(读或写)。 因此,外围设备和内存两者中总有一个需要“先”完成--通常是外围设备在准备好传输第一个字节之前有很长的延迟时间。 如果首先设置外围设备,则在设置DMA控制器的过程中,它的第一个DMA请求信号 (通常称为DMA request signal-DRQ) 就可能会到达。 答案是在初始化DMA控制器时屏蔽特定信道的DRQ。 下面会介绍三种临时禁用通道的方式。 === 传输长度 === 存储在每个计数寄存器中的值始终是传输长度(字节或字)“-1”。 如果你忘了减去1,你在传输时会出错。 === 完成时中断 === 在PC机上实现时,DMA控制器不能发送中断。 希望在传输完成时,无论哪个外设“拥有”的每个DMA通道,都会向CPU发送中断。 但是,如果传输失败并出现错误,某些外围设备可能会 “不” 发送中断。 和其它类似场景一样,有超时策略很重要。 ===寄存器=== 主从DMA控制器非常相似,因此 (为了节省空间) 它们都已合并到下表中。 希望你不要为这种排版困惑。 注:有关通道5至7上的地址和计数寄存器,请参见上面的[[#16位问题|16位问题]]。 每个8237A都有18个寄存器,通过I/O端口总线寻址: {| {{wikitable}} |- !Channels 0-3 !Channels 4-7 | | | |- !IO Port !IO Port !Size !Read or Write !Function |- |0x00 |0xC0 |Word |W | 起始地址寄存器通道0/4(不可用) |- |0x01 |0xC2 |Word |W |计数寄存器通道0/4(不可用) |- |0x02 |0xC4 |Word |W | 开始地址寄存器通道1/5 |- |0x03 |0xC6 |Word |W | 计数寄存器通道1/5 |- |0x04 |0xC8 |Word |W |起始地址寄存器通道2/6 |- |0x05 |0xCA |Word |W | 计数寄存器通道2/6 |- |0x06 |0xCC |Word |W |起始地址寄存器通道3/7 |- |0x07 |0xCE |Word |W |计数寄存器通道3/7 |- |0x08 |0xD0 |Byte |R | 状态寄存器 |- |0x08 |0xD0 |Byte |W |命令寄存器 |- |0x09 |0xD2 |Byte |W |请求寄存器 |- |0x0A |0xD4 |Byte |W | 单通道屏蔽寄存器 |- |0x0B |0xD6 |Byte |W |模式寄存器 |- |0x0C |0xD8 |Byte |W |Flip-Flop复位寄存器 |- |0x0D |0xDA |Byte |R | 中间(Intermediate)寄存器 |- |0x0D |0xDA |Byte |W |主复位寄存器 |- |0x0E |0xDC |Byte |W |屏蔽复位寄存器 |- |0x0F |0xDE |Byte |RW | 多通道屏蔽寄存器 (文章中没有说明有读取操作,但可以读!) |- |} 每个通道还有一个外部R/W页地址寄存器,还要和24位传输存储器地址的上8位组成数据: {| {{wikitable}} |- |0x87 |通道0页地址寄存器(不可用) |- |0x83 | 通道1页地址寄存器 |- |0x81 |通道2页面地址寄存器 |- |0x82 |通道3页地址寄存器 |- |0x8F | 通道 4页面地址寄存器 (不可用) |- |0x8B |通道5页面地址寄存器 |- |0x89 |通道6页地址寄存器 |- |0x8A | 通道7页面地址寄存器 |- |} ==== 有用的寄存器说明==== ; 单通道掩码寄存器0x0A和0xD4(写入) {| {{wikitable}} |- |Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |- | | | | | |MASK_ON |SEL 1 |SEL 0 |} 这些寄存器仅用于在主DMA芯片或从DMA芯片上屏蔽(或取消屏蔽)单个通道的DRQ。 也就是说,如果你不想计算出所有其他通道的屏蔽状态,你可以一次屏蔽/取消屏蔽一个通道的DRQ。 使用SEL 0和1位选择通道,并使用MASK_ON位为其设置或清除屏蔽。 注意,由于级联关系,屏蔽DMA信道4将屏蔽7、6、5和4。 ;多通道掩码寄存器0x0F和0xDE(读和写) {| {{wikitable}} |- |Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |- | | | | |MASK3 |MASK2 |MASK1 |MASK0 |} 将适当的位设置为0或1允许你取消屏蔽或屏蔽 (分别) 这些通道的DRQ。 使用此寄存器意味着你的驱动程序需要知道此时“所有”通道的所需掩码状态。 有几种方法可以做到这一点,这里可以考虑简单地读取该寄存器。 注意,由于级联,屏蔽DMA通道4将屏蔽7、6、5和4。 ;DMA模式寄存器0x0B和0xD6(写入) {| {{wikitable}} |- |Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |- |MOD1 |MOD0 |DOWN |AUTO |TRA1 |TRA0 |SEL1 |SEL0 |} 设置该寄存器有点棘手,因为它在很大程度上取决于要为其编程DMA控制器的外设。 但是,外围设备的驱动程序是需要设置此寄存器的主体,它应该知道外围设备需要什么模式。 * '''SEL0'''和'''SEL1'''选择要更改的通道; * '''TRA0'''和'''TRA1'''选择传输类型; ** 0b00 运行控制器的自检; ** 0b01 外围设备正在写入内存; ** 0b10 外设正在从存储器中读取; ** 0b11无效。 * '''AUTO''':设置此位时,在传输完成后,通道将自身重置为编程到其中的地址和计数值。 这对于软盘传输非常有用。 在磁道中读的时候-这些值可以设置好它们,以便立即再次读数据。 对于写操作,你只需要更改传输模式,而不需要更改地址。 某些扩展卡不支持自动初始化DMA,如Sound Blaster 1.x。 如果与auto-init DMA一起使用,这些设备将崩溃。 Sound Blaster 2.0及更高版本确实支持自动初始化DMA。 * '''DOWN''':设置时颠倒数据的内存顺序。 从高地址到低地址访问内存 (每次传输之间地址递减)。 * '''MOD0'''和'''MOD1''':基于DMA控制器所连接的外围设备,这可能会出现一些问题。 DMA控制器有几种模式: ** 0b00 = 按需传输; **0b01 = 单DMA传输(Single DMA Transfer); **0b10 = 块DMA传输(Block DMA Transfer); ** 0b11 = 级联模式 (用于级联另一个DMA控制器)。 单传输模式对外围设备来说是一种好方法,因为它不能同时缓存大量数据。 非82077AA软盘控制器、Sound Blaster和Sound Blaster Pro应使用单传输DMA模式。 块传输模式对于可以缓冲整个信息块的外围设备是很好的。 硬盘控制器板就是一个例子。 按需传输模式适用于间歇性启动和停止的外围设备,如磁带机。 驱动器可以读取整个信息负载,只要它可以,并暂停传输以移动到磁带的另一部分。 较新的软盘控制器也适用于按需传输,因为它们有FIFO缓冲区来存储读取和写入的信息(但需要正确设置FIFO)。 外围设备控制流量,由于信息流不中断,因此可以获得好的性能。 一些后来的计算机中的cpu通常具有缓存,并且可以在需求DMA传输期间继续不间断地工作。 较旧的计算机会在CPU等待内存总线可用时可能会减慢速度。 ;Flip-Flop复位寄存器0x0C和0xD8(写入) ;主复位寄存器0x0D和0xDA (写) ;屏蔽重置寄存器0x0E和0xDC(写入) 将需要的值发送到复位寄存器以激活它们。 主复位将Flip-Flop设置为低电平,清除状态,并将所有掩码位设置为ON。 屏蔽重置将关闭所有掩码位。 “必须在任何“16位事务之前发送复位触发器命令。(以前的维基文章中的会说明需要在真实硬件上进行验证,因为它很可能在模拟器上是错误的) 在DMA控制器收到第二个字节后,Flip-Flop “不重置”。 ==== 其他寄存器==== ;状态寄存器0x08和0xD0(读取) {| {{wikitable}} |- |Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |- |REQ3 |REQ2 |REQ1 |REQ0 |TC3 |TC2 |TC1 |TC0 |} * REQ3-0: 设置1时: DMA请求待处理。 *TC3-0:设置1时:传输完成。 * 读取该寄存器将清除TC位。 鉴于8237不能发送IRQ来告诉你它已经完成,这个寄存器不是很重要。 通常不需要轮询该寄存器,因为当事务完成时,外围设备(在DMA的另一端)将发送一个中断。 ;命令寄存器0x08和0xD0(写入) {| {{wikitable}} |- |Bit 7 |Bit 6 |Bit 5 |Bit 4 |Bit 3 |Bit 2 |Bit 1 |Bit 0 |- |DACKP |DRQP |EXTW |PRIO |COMP |COND |ADHE |MMT |} 此寄存器也确实表现出了8237与PC硬件的不兼容程度。 * 让我们从EXTW和COMP开始。 这些使DMA传输速度提高了25%,消除了一个时钟周期。 它能用吗? 不能。 * PRIO。 置零时,这允许DMA优先级轮换,从而允许共享数据总线的所有外设的自由。它能用吗?不能。 * MMT和ADHE。 你是否知道IBM PC可以1981年进行内存到内存(memory to memory)的传输? 没错,一种硬件精灵(hardware sprites)技术,从一个位置到另一个位置的硬件帧缓冲。它能用吗?不能。 * COND。 Hooray是控制寄存器中唯一有用的部分。 设置此位将禁用DMA控制器。 这是在不屏蔽每个通道的情况下设置多个DMA通道的一种方式。 ; 请求寄存器0x09和0xD2 (写) 用于内存到内存的传输和设置优先级轮换——绝对没有用。 ;“Intermediate”寄存器0x0D和0xDA(读取) 从未在个人电脑上实现。没用。 ==示例== === 软盘DMA初始化 === 你只需要实现1到3个小例程即可执行DMA传输。 这个例子是软盘驱动器控制器(可能是最常见的,其次是SoundBlaster)。 注意:以下代码不是最佳代码,因为存在两次到同一IO端口的输出(在两个位置)。 这会导致IO端口总线上的额外延迟。 真正的代码应该将两个“out 0x4”和“out 0x5”调用与另一个端口的“out”分开。 <source lang="asm"> initialize_floppy_DMA: ; 将DMA通道2设置为在内存中传输来自0x1000-0x33ff的数据 ; 分页必须将此 _物理_ 内存映射到其他地方,并且进行从分页到磁盘 _pin_ ! ; 将计数器设置为0x23ff,即1.44 MiB软盘上的磁道长度-1(假设是512字节扇区) ; transfer length = counter + 1 out 0x0a, 0x06 ; 屏蔽DMA通道2和0(假设0已屏蔽) out 0x0c, 0xFF ; 重置主Flip-Flop out 0x04, 0 ; 地址为0(低字节) out 0x04, 0x10 ; 地址设为0x10(高字节) out 0x0c, 0xFF ; 重置主Flip-Flop (再次!) out 0x05, 0xFF ; 计数为0x23ff(低字节) out 0x05, 0x23 ; 计数为0x23ff(高字节), out 0x81, 0 ; 外部页寄存器为0,总地址为00 10 00 out 0x0a, 0x02 ; 解除DMA信道2的屏蔽 ret </source> 一旦你设置了起始地址和传输长度,如果你使用的是autoinit,则不需要再次管它。 一旦选择了读或写,你也不需要改变它。 要“更改”选择读取或写入,请使用模式寄存器。 <source lang="asm"> prepare_for_floppy_DMA_write: out 0x0a, 0x06 ; 屏蔽DMA通道2和0(假设0已屏蔽) out 0x0b, 0x5A ; 01011010 ; single transfer, address increment, autoinit, write, channel2) out 0x0a, 0x02 ; 解除DMA通道2的掩码 ret prepare_for_floppy_DMA_read: out 0x0a, 0x06 ; 屏蔽DMA通道2和0(假设0已屏蔽) out 0x0b, 0x56 ; 01010110 ; single transfer, address increment, autoinit, read, channel2) out 0x0a, 0x02 ; 取消屏蔽DMA通道2 ret </source> 有些硬件以及VirtualPC不支持autoinit。 在上述例程中,你可能要将模式寄存器设置为0x4A和0x46。 上述例程使用single transfer模式来实现兼容性,但是在你的软盘驱动程序初始化期间,如果你检测到 “高级” 软盘控制器 (使用Version命令),则应使用 “demand transfer” 来减少开销。 ==参考文献== === 相关文章 === * [[Floppy Disk Controller|软盘控制器]] ===外部链接=== * [http://www.intel-assembler.it/PORTALE/4/231466_8237A_DMA.pdf Intel 8237A datasheet] *[http://bos.asmhackers.net/docs/dma/docs/ http://bos.asmhackers.net/docs/dma/docs/] [[Category:Storage]] [[de:DMA]]
本页使用的模板:
模板:Wikitable
(
查看源代码
)
返回至“
DMA
”。
导航菜单
个人工具
登录
命名空间
页面
讨论
变体
已展开
已折叠
查看
阅读
查看源代码
查看历史
更多
已展开
已折叠
搜索
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息