连贯地了解有关DMA和总线的软硬件交互

时间:2015-02-17 21:40:18

标签: arm embedded embedded-linux dma amba

我已经在基于ARM的主板上的一般DMA事务中涉及的几个组件(包括软件和硬件)上积累了一些知识,但我不明白它是如何完美集成的,我没有找到关于此的完整连贯的描述。

我会写下我已经拥有的高水平知识,我希望有人可以解决我错误的地方,并完成缺失的部分,这样整个画面就会清晰。我的描述从用户空间软件开始,并深入到硬件组件。被误解的部分采用 斜体粗体 格式。

  • 用户模式应用程序请求从某个设备读/写,即进行I / O操作。
  • 操作系统收到请求并将其交给相应的驱动程序(每个操作系统都有自己的机制来执行此操作,我不需要在此处进一步深入了解,但如果您想在此处分享见解,欢迎)
  • 负责处理I / O请求的驱动程序必须知道设备映射到的地址(因为我对基于ARM的板感兴趣,afaik只有内存映射我/ O,没有端口I / O)。在大多数情况下(如果我们考虑类似智能手机的主板),有一个Linux内核可以解析设备树中的设备地址,它是在启动时从引导加载程序(现代方法)提供的,或者是linux预编译的对于具有其中的设备地址的特定型号系列和电路板(在其源代码中硬编码)(在旧的和过时的方法中)。在某些情况下(在智能手机中发生很多),部分驱动程序是预编译的,并且只是打包到内核中,即它们的源是关闭的,因此,对应于设备的地址是未知的。 是否正确?
  • 鉴于驱动程序知道要与之通信的设备的相关寄存器的地址,它会分配一个缓冲区(通常在内核空间中),设备将在该缓冲区中写入其数据(在DMA的帮助下) 。驱动程序需要通知设备该缓冲区的位置,但设备使用的地址(操作内存)与驱动程序(cpu)使用的地址不同,因此驱动程序需要通知设备关于公交车地址'它刚刚分配的缓冲区。 驱动程序如何通知设备该地址?使用IOMMU有多受欢迎?使用IOMMU时是否有一个管理寻址的硬件组件或每个设备一个?
  • 然后驱动程序命令设备完成其工作(通过操作其寄存器),并且设备将输出数据直接传输到存储器中的已分配缓冲区。 在这里,我对设备驱动程序的关系感到困惑:bus:bus-controller:actual-device。以一些知道在I2C协议中通信的想象设备为例; SoC指定一个I2C总线接口 - 实际上是什么? I2C总线是否有某种总线控制器? cpu是与I2C总线接口通信还是直接与设备通信? (即I2C总线接口是无缝的)。我想那些有设备驱动程序经验的人可以轻松回答这个问题。
  • 设备填充DMA通道。由于器件没有直接连接到存储器,而是通过某个总线连接到DMA控制器(它控制总线),它与DMA交互以将所需数据传输到存储器中分配的缓冲区。当电路板供应商使用ARM IP内核和总线规范时,此步骤涉及来自AMBA规范(即AHB /多AHB / AXI)的总线上的事务,以及设备和其上的DMAC之间的某些协议。 我想更多地了解这一步,实际发生了什么? ARM的DMA控制器有很多规格,哪一种很受欢迎?哪个已经过时了?
  • 当设备完成时,它会发送一个中断,通过中断控制器传送到操作系统,操作系统的中断处理程序将它指向适当的驱动程序,该驱动程序现在知道DMA传输已完成。 / LI>

1 个答案:

答案 0 :(得分:4)

你在这里略微混淆了两件事 - 有些设备(例如UART,MMC控制器,音频控制器,通常是低带宽设备)依赖于外部DMA控制器(" DMA引擎&#34 ;在Linux术语中),但许多设备本身就是简单的总线主控器并直接执行自己的DMA(例如GPU,USB主机控制器,当然还有DMA控制器本身)。前者涉及CPU编程DMA控制器的一系列额外复杂性,因此我将忽略它并且只考虑直接的总线主DMA。

在典型的ARM SoC中,CPU集群和其他主外设,以及内存控制器和其他从外设都通过各种AMBA互连连接在一起,形成单个总线" (通常都映射到"平台总线"在Linux中),主设备根据互连的地址映射来寻址从设备。您可以放心地假设设备驱动程序知道(无论是通过设备树还是硬编码)设备出现在CPU的物理地址映射中,因为否则它们将无用。

在更简单的系统上,只有一个地址映射,因此CPU用于寻址RAM和外设的物理地址可以作为DMA地址与其他主站自由共享。其他系统更复杂 - 其中一个比较着名的是the Raspberry Pi's BCM2835,其中CPU和GPU具有不同的地址映射;例如互连是硬连线的,这样GPU就可以看到外围设备的总线地址" 0x7e000000,CPU在"物理地址" 0x20000000。此外,在具有40位物理地址的LPAE系统中,互连可能需要以向不同的主设备提供不同的视图 - 例如,在TI Keystone 2 SoC中,所有DRAM都高于CPU的32位边界。从某种角度来看,如果互连没有向他们展示不同的地址映射,那么32位DMA主设备将毫无用处。对于Linux,请查看dma-ranges device tree property以了解如何描述此类CPU→总线转换。在告诉主设备访问特定RAM或外围设备地址时,CPU必须考虑这些转换; Linux驱动程序应使用提供适当转换的DMA地址的DMA mapping API

IOMMU提供比固定互连偏移更多的灵活性 - 通常,地址可以动态地重新映射,并且对于系统完整性,可以防止主设备访问除了在任何给定时间映射的DMA之外的任何地址。此外,在具有4GB以上RAM的LPAE或AArch64系统中,如果32位外设需要能够访问RAM中的任何位置,则需要IOMMU。您可以在许多当前的64位系统上看到IOMMU,以便集成传统的32位设备,但它们也越来越受欢迎,用于设备虚拟化。

IOMMU拓扑结构取决于系统和正在使用的IOMMU - the system I'm currently working with在各个总线主控外设之前有7个独立的ARM MMU-401/400设备;另一方面,ARM MMU-500可以作为单个系统范围的设备实现,每个主设备都有一个单独的TLB;其他供应商有自己的设计。无论哪种方式,从Linux的角度来看,大多数设备驱动程序应该使用上述DMA映射API为DMA分配和准备物理缓冲区,如果设备连接到一个,也会自动设置适当的IOMMU映射。这样,各个设备驱动程序无需关心IOMMU的存在。但是,其他驱动程序(通常是GPU驱动程序)依赖于IOMMU并希望完全控制,因此可以通过IOMMU API直接管理映射。本质上,IOMMU的页表设置为将某些范围的物理地址 * 映射到I / O虚拟地址范围,那些IOVA作为DMA(即总线)提供给设备地址,IOMMU在设备访问时将IOVA转换回物理地址。 DMA操作完成后,驱动程序通常会删除IOMMU映射,以释放IOVA空间,从而使设备不再能够访问RAM。

请注意,在某些情况下,DMA传输是循环的,永远不会完成"。使用像显示控制器这样的东西,CPU可能只是映射DMA的缓冲区,将该地址传递给控制器​​并触发它启动,然后它将继续执行DMA读取以扫描CPU写入该缓冲区的任何内容,直到它为止。告诉停止。

SoC互连之外的其他外围总线,如I 2 C / SPI / USB /等。你怀疑的工作 - 有一个总线控制器(它本身就是AMBA总线上的一个设备,所以上面的任何一个可能都适用于它)和它自己的设备驱动程序。粗略概括,CPU不直接与外部总线上的设备通信 - 其中AMBA设备的驱动程序说"将X写入寄存器Y",这恰好发生在CPU执行存储到内存映射地址;其中I 2 C设备驱动程序说"将X写入寄存器Y",OS通常有一些bus abstraction layer总线控制器驱动程序实现,由此CPU编程控制器用命令说"将X写入设备Z"上的寄存器Y,总线控制器硬件将关闭并执行该操作,然后通过中断或某些操作通知操作系统外围设备的响应其他方式。

*从技术上讲,IOMMU本身,或多或少只是另一个设备",可以在互连中具有不同的地址映射,如前所述,但我会怀疑任何人真正建立这样一个系统的理智。