用Windriver调试开发PLX9052的几点经验版权声明:转载时请以超链接形式标明文章原始出处和作者信息及本声明
https://www.wendangku.net/doc/594396839.html,/logs/49676398.html
用WinDriver的优点就是开发简单,不必了解底层的很多东西,而且
编出的程序与操作系统无关,不像Vxd那样,只能在98下用,否则就要
写WDM.
任何东西都有缺点,WinDriver也不例外,主要wdpnp.sys来维护底层,
这样就会造成相应时间的增加,但是对于一般的应用已经足够了(本人
曾经做过一块数据传输率为12Mbps的PCI卡,驱动就是用Windriver写的).
而且,如果是对性能要求比较苛刻,也可以自己编写Kernal程序. 本人
的观点就是如果作研究,PCI只是作为数据传输的通道,主要的为了验证板上的电路功能,
那么PCI驱动开发越简单越好,可以把主要精力放在真正的关键设计上.
如果是作产品开发,好的驱动还是必不可少的,至少要考虑对主板的兼容性和稳定性.
以下是自己开发过程的一点经验总结,供入门者参考,也请高手们批评指正.
Pci的接口芯片本人只用过AMCC的S5933和PLX的9052,现在正在用9054,觉得
还是PLX的芯片价格便宜,量又足(好买),开发简单,所以我们一直都用它.
关于Plx芯片的硬件电路设计,可以参考公司提供的RDK.(ThreeWater的FTP上就
有),布线时只要注意时钟线,其他线只要别太长(一般接口芯片离插槽都很近,想
画长也不容易,像时钟线,还得走蛇行才行).还有就是93cs46不好买,可以用93LC46
来替代.
这里主要介绍用WinDriver的开发流程:
1. 运行WinDriver Wizard,找到PCI卡.
先将卡插好,确认E2PROM已经插上(可以是空的),启动计算机.发现新硬件提示,忽略.
运行WinDriver Wizard 之后,选"Creat a new driver"选项,在列表中查找PCI : PLX 9050,如果找到,
说明Windriver已经识别出你的卡了.双击之后,进入调试程序,可以查看计算机给
卡分配的各种资源,如IO地址,Memery空间,Int号等等.
2. 生成.inf文件.
在右边的按钮中选择"Generate .INF file",下次启动提示发现新硬件时,就可以
用这个.inf文件来加载wdpnp.sys. 这个文件在X:\windriver\redist目录里.这样
以后启动就不会在提示有新硬件了.
3. 填写E2PROM.
从开始菜单里选择"程序->Windriver->Simples->PLX 9050 Diagnostic",出现
一个调试菜单.选择对E2prom操作即可.
注意: 93LC46的6脚要结VCC,这样才能读E2prom写,否则只能读了.
4. 用上面的程序就可以对卡进行调试了,功能和PLXMon差不多.如果对板上的各个
空间(Bar)都能正常读写,中断可以相应(如果有的话),那么你就已经成功了一半了,
接下来,就可以开发应用程序了.
关于应用程序的开发,
强烈建议:使用WinDriver提供的Sample的源代码作为框架,不要用Wizard生成的代码
或者自己写代码.这不仅是WinDriver公司的建议,也是实际经验教训.本人在一块有中断
的pci调试时,就是因为用生成的代码,始终接收不到中断,而用它的sample就可以.
最后,几点个人经验,仅供参考:
1. 查询方式. 如果数据量不大,对效率要求不高,可以用查询方式. PLX9052提供了4个
用户定义的IO脚,通过Local Registor CNTRL(50H) 来控制读写,可以直接与FGPA相连, 用起来很方便的.
2. 中断方式. 用中断时,要注意对P9050_IntEnable()函数做适当修改.首先,要关闭中断
源,然后才开启中断线程, 然后在打开. PCI一般都使用电平触发的中断,这样中断可以实
现
共享. 所以,Local端的LINT1和LINT2都可以产生中断,但一般都需要一个ACK信号来清除
中断. 如果这两个管脚连到FPGA时要注意: 不下载时,输出为高阻,plx9052会读成高,这
时
如果中断高有效,就会产生连续不断地中断;下载后,如果没指定管脚,会输出低,如果此时
中断
是低有效,这时也会产生连续不断地中断,往往会造成计算机Hang,就是死掉了.
我的做法一般是中断由FPGA产生,是高有效的电平信号;PLX9052的USER0作为INT_ACK信号,
高有效.以下是P9050_IntEnable()函数:
BOOL P9050_IntEnable (P9050_HANDLE hPlx, P9050_INT_HANDLER funcIntHandler)
{
DWORD dwIntStatus,dwCntrlStatus;
DWORD dwAddr;
if (!hPlx->fUseInt) return FALSE;
// check if interrupt is already enabled
if (hPlx->Int.hThread) return FALSE;
dwIntStatus = P9050_ReadReg (hPlx, P9050_INTCSR);
BZERO(hPlx->Int.Trans);
/**************************************************************
printf("置INT_ACK,清除中断源,准备打开中断线程 \n");
dwCntrlStatus = P9050_ReadReg(hPlx,P9050_CNTRL);
dwCntrlStatus = 0x10064006 ;
P9050_WriteReg(hPlx,P9050_CNTRL,dwCntrlStatus);
/**************************************************************
// This is a samlpe of handling interrupts:
// Two transfer commands are issued. First the value of the interrrupt c ontrol/status
// register is read. Then, a value of ZERO is written.
// This will cancel interrupts after the first interrupt occurs.
// When using interrupts, this section will have to change:
// you must put transfer commands to CANCEL the source of the interrupt,
otherwise, the
// PC will hang when an interrupt occurs!
dwAddr = hPlx->addrDesc[P9050_ADDR_REG].dwAddr + P9050_INTCSR;
hPlx->Int.Trans[0].cmdTrans = hPlx->addrDesc[P9050_ADDR_REG].fIsMemory ? RM_DWORD : RP_DWORD;
hPlx->Int.Trans[0].dwPort = dwAddr;
hPlx->Int.Trans[1].cmdTrans = hPlx->addrDesc[P9050_ADDR_REG].fIsMemory ? WM_DWORD : WP_DWORD;
hPlx->Int.Trans[1].dwPort = dwAddr;
printf("清除PCI中断允许,准备打开中断线程 \n");
hPlx->Int.Trans[1].Data.Dword = dwIntStatus & ~BIT6 ;
// put here the data to write to the control register
hPlx->Int.Int.dwCmds = 2;
hPlx->Int.Int.Cmd = hPlx->Int.Trans;
hPlx->Int.Int.dwOptions |= INTERRUPT_CMD_COPY;
// this calls WD_IntEnable() and creates an interrupt handler thread
hPlx->Int.funcIntHandler = funcIntHandler;
if (!InterruptThreadEnable(&hPlx->Int.hThread, hPlx->hWD, &hPlx->Int.Int , P9050_IntHandler, (PVOID) hPlx))
{
printf("now return false\n");
return FALSE;
}
printf("中断线程开启,重新设置PCI中断允许 \n");
P9050_WriteReg (hPlx, P9050_INTCSR, dwIntStatus | BIT6 |BIT0 |BIT1);
/***********************************************************************
printf("中断线程开启,清除INT_ACK,准备接收中断 \n");
dwCntrlStatus = P9050_ReadReg(hPlx,P9050_CNTRL);
//dwCntrlStatus |= BIT0;
dwCntrlStatus = 0x10064002;
//dwCntrlStatus &= ~BIT2 ;
P9050_WriteReg(hPlx,P9050_CNTRL,dwCntrlStatus);
/************************************************************************
printf("开始接收中断... ... \n");
return TRUE;
}
中断处理程序中也要先清除中断,处理完再打开中断允许.
void WINAPI PLX_IntHandlerRoutine(P9050_HANDLE hPlx, P9050_INT_RESULT *intRe sult)
{
DWORD dwCntrlStatus;
DWORD dwIntStatus;
//置INT_ACK,清除中断源
dwCntrlStatus = P9050_ReadReg(hPlx,P9050_CNTRL);
dwCntrlStatus |= BIT2 ;
P9050_WriteReg(hPlx,P9050_CNTRL,dwCntrlStatus);
//中断处理程序
printf("\n 这是第 %d 次中断 \n",intResult->dwCounter);
dwIntStatus = P9050_ReadReg(hPlx,P9050_INTCSR);
P9050_WriteReg(hPlx,P9050_INTCSR,dwIntStatus |BIT6 |BIT1 |BIT0); //清INT_ACK,允许中断
dwCntrlStatus = P9050_ReadReg(hPlx,P9050_CNTRL);
dwCntrlStatus &= ~BIT2 ;
P9050_WriteReg(hPlx,P9050_CNTRL,dwCntrlStatus);
}