转载地址:https://www.cnblogs.com/MemoryOfStars/p/9681895.html
最近刚好用到了通过PCIE的与PC端相连的ARM板子,看了看代码,里面的地址转换确实把我也弄得有些晕,一边和组里的人讨论一边去问大神,终于算是把这块给弄的明白了,在博客里稍微记录一下,防止之后又忘记。
PCIE是一种高速串行计算机扩展总线标准,旨在替代老版的PCI总线,现在大多数主板都配有多个PCIE插槽,外设可以通过PCIE总线与主机HOST进行IO。
PCIE设备通过桥接到PCIE总线来访问HOST的资源。和其他设备一样,PCIE设备也被统一编址进了HOST,HOST可以访问PCIE外设。
于是,重点来了:
我所使用的外设是ARM处理器的板子,搭载了裁剪后的Linux操作系统,还拥有自己的DDR内存,所以,为了让ARM和HOST在体系结构上区分控制域,使用了非透明的桥接,对我一个小白,真是第一次接触到这个概念。
大概的意思就是指桥接到总线上的设备和普通的外设一样,有权利访问到HOST的资源,但是外设本身的资源,对于HOST来说是不可见的,也就是指外设相当于对主机来说是一个黑盒。
如下图的架构图所示:
所以,由这个问题就引出了很重要的一部分-----地址转换
HOST端是通过PCIE总线和外设进行信息交换的,HOST是信息的生产者,而外设是信息的消费者,从内存中取出信息进行处理。
在HOST端通过memmap在内存中申请一段预留的连续地址空间,这时需要让外设知道这个地址以便其进行数据的读取,但是外设中ARM芯片能够寻找的地址编码那又是Another story
所以需要进行地址转换!!!!!!!!!!!!!!!!!!!
先说明一下HOST操作PCIE设备的方式,在主机上电操作系统启动的时候,操作系统会扫描硬件接口,并且分配物理地址,我的PCIE外设当然也就在这个时候被赋予了物理地址,等设备接入时,驱动程序再向HOST告知这个外设需要占用的地址的Size,于是HOST的OS就会把这一段地址空间分配给external device--------这个物理地址就是HOST和PCIE交互的关键!
PCIE_BASE_Addr + BARX_OFFSET能够找到PCIE接口的一个叫做BAR的寄存器,操作系统能够写入BAR寄存器,而external device可以获取到BAR的值,于是,这个寄存器就充当了共享内存一样的角色。HOST和external device的信息交换渠道也就建立起来了。
理清一下大概的思路,HOST将memmap申请预留的地址空间的首地址写入PCIE接口的BAR寄存器,ARM外设内存也申请同样大小的一部分内存,在读写信息时,将BAR寄存器中的地址当做基址,通过偏移量的方式使得两部分地址空间一一对应。
① HOST端的数据生产者,memmap申请预留空间,将首地址通过memset设置到BAR寄存器
② external device端的数据消费者,通过mmap函数访问自身的内存指定位置(由驱动程序设置为专门用来与HOST进行内存映射的地址空间)
③ PCIE接口处的硬件实现两部分架构的内存地址的转换(基址寄存器 + 偏移量)
④ 其中,基址寄存器的值由PC端赋值的BAR寄存器来给出
流程图如下图所示:
基本上就是这么一个简单的流程。
基于TCP/IP协议的Socket通信中,也有大量的地址转换,而假如像这样的HOST与外设之间的通信也基于Socket来实现的话,其中必然夹杂着大量的冗余的地址转换等操作,一条socket语句甚至可能包含着几百上千条指令,这显然对于某些对速度效率要求苛刻的项目来说是扮演着披着羊皮的狼这样一个角色了,不如直接利用系统调用来实现地址的转化,让整个流程更加的迅速。
Ps:Basically,第一次正经博客差不多就这样一个draft一样的感觉吧,亲身写了一下才发现没有想象的那么轻松,感谢一下那些个被我白嫖至今的博客博主们,看了快四年的博客,每一篇博客都不好写啊,虽说过程没有想象中的那么开心,但记录总归是很有意义的事情,以后还会继续下去的,也算是对自己的一点激励吧。。