0 引言 随着嵌入式系统的广泛应用,新的嵌入式操作系统也在不断地出现,各种设备驱动程序的开发要求也就越来越多。本文阐述了在VxWorks 嵌入式系统下,使用东芝TX3927嵌入式处理器(基于MIPS R3000 技术的一种RISC 控制器)在开发数字机顶盒的项目中,针对PCI总线驱动程序的设计并实现了对PCI总线上的所有设备进行存储器空间和I/O空间的地址分配及中断分配的设计,来满足系统的需要.同时给出了操作系统与硬件的接口以及相应的图示. 1 BSP与VxWorks 的关系 BSP提供给VxWorks 对硬件环境的主要接口,它与VxWorks 之间的主要关系可用下面的图1 进行表示:
从图1可以看出,驱动系统硬件的工作是由BSP来完成的.BSP 中的驱动程序管理特定目标环境中的设备,对其进行控制和初始化. 本文主要阐述其中的一个重要的驱动程序,即PCI设备的配置驱动程序的设计及实现. 2 PCI 配置空间 每个PCI 设备有3种物理空间:配置空间、存储器空间和I/O空间. 配置空间是长度256 字节的一段连续空间(16 个32bit寄存器),其中前64字节为Header(头标),其余192字节为设备相关信息. 在64字节的Header中,前16 字节的定义是确定的,后48字节的具体含义因设备而异,图2 为配置空间头标区. 配置空间中的一个重要部分是基地址寄存器(Base address Register),它的内容是PCI设备的地址空间映射到系统地址空间的起始物理地址.其中,bit0=1 表示I/O 空间映射,bit0=0 表示存储器空间映射. 所有PCI 设备必须实现存储器空间映射. 通过向BAR 写全1 即可确定所需地址空间的大小. 3 访问PCI 设备的配置空间 访问某一PCI 设备的配置空间需要给出总线号、设备号、功能号和寄存器号,然后通过配置机制(即由PCI 桥产生配置访问周期来访问该设备的配置空间中的某个寄存器)进行访问. 配置机制可由软件控制,分为机制1和机制2,所有的PCI 桥必须实现机制1,我们主要研究配置机制1. 配置机制1. 通过PCIConfigaddrReg(PCI 配置地址寄存器)和PCIConfigDataReg(PCI配置数据寄存器)实现,这两个32bit寄存器属于PCI 桥的内部寄存器,具体地址参考PCI桥的手册,对于具有I/O空间的系统(如Intel X86),这两个寄存器分别位于CF8H 和CFCH 两个I/O端口,PCIConfigAddrReg 的格式如下:
使能位 保留 总线号 设备号 功能号 寄存器号 0 0
从PCIConfigDataReg 中读出32bit值或向PCIConfigDataReg 中写入32bit 新值,就能够访问PCI总线上设备的配置信息,以便对其进行初始化. 4 驱动程序的设计与实现 4.1 设计部分 为了正确设计程序的流程,首先必须明确PCI 总线与CPU 以及各PCI 设备之间的关系,如图3. PCI 总线是一个无终端匹配的、高阻抗的CMOS总线. PCI总线在长度和负载数量上都有一定限制,总线上的每个设备代表一个负载,其中包括适配器和桥. 为了适应附加的设备,PCI标准允许多条PCI 总线通过PCI桥设备相连,形成一个“ 大总线”,每条PCI总线作为这条“大 总线”的一部分,并且受它的限制. 与系统总线—PCI 桥最接近的PCI 总线设定为总线零,然后是总线1和总线2. 由于时差和传播延迟,实际上PCI总线结构的实现不能超过总线2. 本文的驱动程序是基于此图进行设计的. 在PCI总线上进行搜索,把连接在不同总线上的设备全部找出来,标记出此设备属于哪一个桥上,并把各设备具有的不同功能全部找出,最后形成一个设备表. 通过具体的函数对所有设备进行配置和初始化.
4.2 实现部分 通过本系统的一个实例,即tc35815 PCI快速以太网适配器来说明它的实现过程.首先,要通过VxWorks的启动顺序,来确定何时调用驱动程序.这里以MIPS CPU启动的顺序为例:先对ROM初始化,将其中的程序载入RAM;接着对系统硬件初始化,其中调用第一个用C 编写的函数sysHwInit()来实现对TX3927 板进行初始化. 在执行此函数的过程中,该函数又调用了sysPciautoConfig()函数,它是本驱动程序的入口,从此将开始对PCI 总线上的设备进行配置,创建各设备. 这个驱动程序的实现可分为两个阶段来完成的. 第一个阶段是寻找PCI总线上的所有设备并返回一个设备表值. 其具体步骤如下: (1)由函数PciAutoListCreat()建立一个设备表pPciList,具体地扫描PCI总线上的设备是通过函数PciAutoDevProbe()来实现的. (2)在执行过程中,若发现PCI总线上存在设备时,则开始判断该设备属于哪种类型的设备. 因为在其上存在两种类型的设备,一种是PCI总线上的设备,另一种是PCI桥设备.例如在本系统的PCI插槽上插入tc35815以太网适配器,它属于PCI总线上的设备,这时就在设备表pPciList长度上直接加1. (3)若发现是桥设备,则通过函数PciAutoBusProbe()对连接桥上总线的设备进行搜索,执行这个函数时又回调了PciAutoDevProbe()函数,只要发现新设备就在表的长度上增加1 . (4)最后返回一个设备表.
第二阶段是利用上述返回的设备表值,对现有的设备进行分配和初始化. 其具体步骤如下: (1)函数pciAutoFuncDisable()首先使所有设备在初始化之前不能使用. (2)调用函数PciAutoDevConfig()分配某一个总线上的所有设备的I/O空间和Memory空间,对某一个确定的设备分配是由PciAutoFuncConfig()函数完成. (3)在分配过程中,利用函数PciConfigOutLong()向基地址寄存器写全1,然后通过读函数PciConfigInLong()来读出基地址寄存器里的值,就可知道所需的配置空间的大小. (4)通过PciAutoRegConfig()函数进行对某一设备的基地址寄存器进行两种空间分配,实现的过程是通过函数PciAutoIoAll()和PciAutoMemAll()完成的. (5)对某一确定的设备,我们可以通过函数sysPciAutoConfigIntrAssign()来分配它的中断向量. (S)在所有的设备进行了资源分配后,在使用之前必须对它们进行使能操作,系统才能使用.本程序中使用函数pciAutoFuncAble()来完成这一功能. 通过以上两个阶段,完成了对PCI 总线上所有设备的分配,以满足系统所需要的资源. 5 结论 本文中的驱动程序是在VxWorks操作系统下实现的. 只需修改少量代码,即可将其移植到其它操作系统中.
|