基于vxworks的PCI设备驱动编写

时间:2022-10-07 17:58:48

     最近几天,我在为公司自研的PCI板卡写板卡驱动,板卡类型主要为串口卡,AD采集,DA输出,DIO等类型的常用板卡。硬件实现很简单,桥片PXI9054+FPGA结构。因为板卡功能主要为定制,不是商用,所以功能很简单,寄存器定义的很简单,驱动编写相对的很简单。

     我使用的vxworks版本号为vxworks6.X, 考虑到兼容性(vxworks5.5),没有使用vxbus总线,另一方面,也没有使用vxbus的必要。将驱动文件和映像文件一起编译,就可以满足应用的使用。因为板卡驱动需要支持X86平台和PPC平台,所以需要综合考虑。

 

    1.   PCI设备初始化。

          在vxworks中,使用接口pciFindDevice()根据VENDERID和DEVID 查找设备的BusNo, DeviceNo,FuncNo;  自研的设备号和版本号 是0x10b5和0x9054;

          例:查找设备

    if(pciFindDevice(DIO_VENDERID, 
                     DIO_DEVID,
                     uchBoardNo,  //板卡号()
                     &BusNo,
                     &DeviceNo,
                     &FuncNo) != OK)
    {
        printf("%s(Line:%d):Can not find the specified board! \n", __FUNCTION__, __LINE__);

        return ERROR;
    }

    根据获取到的BusNo, DeviceNo,FuncNo, 调用接口 pciConfigInLong(BusNo, DeviceNo, FuncNo, BarOffset, pulBaseAddr); 获取bar的空间值(偏移地址)。 一般BAR0的值为操作PXI9054寄存器的基地址,BAR2的值为操作板卡寄存器的偏移地址;

   注意:1.  获取到的偏移地址需要根据PCI协议标准,将低4bit去掉:  *pulBaseAddr = (*pulBaseAddr) & (~0xf);

                2. 根据获取到的偏移地址的最低位,判断访问板卡寄存的方式是IO访问,还是内存访问。1为IO访问,0:内存访问。在X86上,IO访问和内存访问是不一样的。IO访问使用接口sysInlong()。 内存访问使用#define  SYSOUTLONG_MEM(addr, data) (*((volatile unsigned long *)(addr)) = (data))  的方式,在PPC上,一般使用内存访问。

                3.  往bar的地址写-1,然后再重新读取值,可以获取bar空间的大小。(我很少用。)

2.  使用第一步获取到的bar2地址,配置板卡的寄存器,这个比较简单。

     有一点需要说明,在配置寄存器的时候,使用的寄存器操作接口,要根据IO访问和内存访问,不同的CPU平台确定接口。

      我们自研的板卡都是内存访问,因为使用X86平台和PPC平台,有大小端的格式,分别定义了不通的接口。

/*寄存器空间映射在内存空间*/

#ifdef CPU_FAMILY_X86
#define SYSINBYTE_MEM(addr)         *((volatile unsigned char *)(addr))
#define SYSOUTBYTE_MEM(addr, data) (*((volatile unsigned char *)(addr)) = (data))
#define SYSINWORD_MEM(addr)         *((volatile unsigned short *)(addr))
#define SYSOUTWORD_MEM(addr, data) (*((volatile unsigned short *)(addr)) = (data))
#define SYSINLONG_MEM(addr)         *((volatile unsigned long *)(addr))
#define SYSOUTLONG_MEM(addr, data) (*((volatile unsigned long *)(addr)) = (data))
#endif

PPC平台部分接口:

unsigned short SYSINWORD_PPC(addr)
{
    unsigned short usData;
    usData = sysIn16(addr);
    usData = SWAPWORD(usData);
    return usData;
}


unsigned long SYSINLONG_PPC(addr)
{
    unsigned long ulData;
    ulData = sysIn32(addr);
    ulData = SWAPLONG(ulData);
   
    return ulData;
}

 

3. 中断的挂接

      X86和PPC平台有些区别;

      X86主要接口为:


void IntDisable(int level)
{
    sysIntDisablePIC(level);
}

void IntEnable(int level)
{
    sysIntEnablePIC(level);
}

STATUS IntConnect(int level, VOIDFUNCPTR routine, int parameter)
{
    return (pciIntConnect(INUM_TO_IVEC(INT_NUM_GET(level)), routine, parameter));//注意 中断号的转换
}

 

PPC主要接口:

STATUS  intConnect (VOIDFUNCPTR * vector, VOIDFUNCPTR routine,
                  int parameter);

extern int intDisable (int);
extern int intEnable (int);

 

中断号获取:  根据总线号,设备号,功能号,查询PCI配置空间0x3c, 找到中断号;

       pciConfigInByte(BusNo, DeviceNo, FuncNo, PLX9054_PCIILR, &ucIntNum);

 

注意: 在自研的PCI板卡中,桥片 PXI9054 有一个bit需要配置, 0x68的11bit,需要置为1, 支持local bus总线,否则不能产生中断。

               在FPGA给中断信号的时候,使用电平触发比较合理,沿触发,会丢中断。