最近在PowerPC的板子上加一个FPGA,需要用PCI-E连接,就调试了PCI-E。由于vxWorks本身已经将驱动写好了,使用时直接调用即可,但是很快就出现了问题:一开始直接使用mmap的方式映射FPGA的总线上到应用程序的内存空间中尝试读写,这种方法比较简单,不需要内核与应用程序之间的拷贝,效率也应该不错。然而实际测试中速度却相当的慢,上网搜索原因,才发现PCI-E默认传输每次只能传四个字节的包!!要想传大包提高效率,就必须要使用DMA传输,否则只能每次发送四个字节。
因此换用DMA方式,也比较好理解,PCI-E上有一个DMA描述符,内容和传统DMA类似,只是多配置了一个DMA传输链,换用之后速度有了不小的改观。
下面是在调试过程中看到的一些比较让人纠结的概念,列举如下,加深自己的理解。本人菜鸟,所以大多数内容都是根据网上的资料整改的,还望海涵。
MSI 中断与传统中断
PCI-E支持两种中断:传统INTx中断和MSI中断。 比较两种中断能让我们了解PCI规范发展的来龙去脉,也能使我们把握PCI发展的技术方向。
MSI 是 Message Signaled Interrupt (MSI) 的缩写,PCI-E设备写一个特定消息到特定地址,从而触发一个CPU中断。MSI 相对于传统中断有三个主要优势:
1、共享中断带来的性能损失:
传统中断的中断引脚常常被多个设备共享. 当中断触发时, 内核必须依次触发每个设备相应的中断处理,这必将损失系统的整体性能. 每个MSI中断属于设备所独有,因此不会产生共享中断带来的性能损失。
2、传统中断超前产生,实际数据并未真正达到:
众所周之,中断通常是设备发送完数据后,给CPU一个中断通知CPU进行处理。但是这种看似简单的应用如果是传统中断也会产生问题,那就是,中断已经产生,并且数据也已经从设备发出,但是实际上并没有到达主存。这时候CPU是读不到它想要的数据的。在这种情况下,CPU必须从设备端读取寄存器来知道数据是否真的已经到达目的地,PCI事物排序规则确保该寄存器只有在数据真实到达之前才会更新。这是传统中断的另一弊端,而MSI中断因为与数据包共享同一通路并且有严格的先后顺序,所以不会出现这种问题,更不需要更新和查询设备端寄存器,从而节省了很多开销。
3、每个设备最多只有四个传统中断引脚:
对于多功能PCI设备而言,每一个功能最多只有一个中断引脚。设备驱动程序必须查询设备产生的具体事件,势必降低中断处理速度。而一个设备可以支持最多32个MSI中断,每个中断有其特定功能,譬如,一些一场情况和错误处理有其单独的中断能让驱动程序处理如数据收发中断更有效。
物理地址和总线地址
这两个概念主要出现在DMA传输中,二者的差别主要是在引入了IOMMU后。在DMA操作的时候,CPU访问的物理地址会被IOMMU再做一次转换,转换后的地址称为总线地址。在没有IOMMU的情况下,物理地址和总线地址是相等的。
总线地址在外围总线和内存之间使用,通常它们与处理器使用的物理地址相同(如X86平台),但这么做不是必需的.一些计算机体系架构提供了I/O内存管理单元(IOMMU),它实现总线和主内存之间的重新映射。
在PowerPC平台上,使用linux内核函数dma_alloc_coherent分配DMA内存时,函数会同时返回总线地址供DMA使用和内核虚拟地址供CPU使用。可以说该函数封装了总线地址和内核虚拟地址的映射,从而在驱动开发时能方便的对DMA编程。
内核逻辑地址和内核虚拟地址
虚拟地址是CPU核心通过MMU页表访问的地址,其地址范围是3G-4G(无特殊设置),跟用户态的0-3G的用户虚拟地址相对应。在3G-4G这段范围内,有段子集3G -3G+main_memory_size,这段主存大小的虚拟地址空间,由于在MMU页表映射时是采用的是平坦的线性映射,在LDD里有专门的称呼:内核逻辑地址。
在内核代码里,对于内核逻辑地址,可以通过简单的偏移(3G)获取对应的物理地址,在内核中是__ph()宏完成这部分功能。
对于内核虚拟地址,是通过virt_to_phy()来获取物理地址的。
PCI-E的outbound和inbound
这个见于pcie设备和系统内存互访问时,outbound是指CPU到设备方向;inbound指Device --> RC(CPU端)方向。从这个概念上说,设备(device)都是外部的,没有内部设备之说。CPU读写RC 端的寄存器时,还是属于片上系统的范围,所以既不是inbound 也不是outbound。
简单的说,如果CPU读写PCI BAR 的总线地址,就是outbound,如果设备读写CPU端的主存,就是inbound。
Local Memory Map