最近几天,我在为公司自研的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给中断信号的时候,使用电平触发比较合理,沿触发,会丢中断。