对于驱动的开发,调试是一个很痛苦的过程,尤其对于不熟悉的领域,很小的一个问题可能就需要花费大量的时间去调试,甚至以月时间计,甚至可能就解决不了。所以经验变得很重要,经验即对相应的IP的熟悉程度,这里的熟悉不是做过,而是分析过调试过。包括遇到过的各种坑,对各种细节的了解,对各种行为的了解,只有遇到足够多的坑且是实在的去调试分析过的,才可能不断积累经验,否则就算做一万遍也算不上经验(这也是在嵌入式开发尤其是驱动开发中做过十年的不一定比做过三年的有经验,做过十年只是改个参数,构建下,跑一下,真算不上任何经验,而后者如果能从零开始或者真的进行了底层的调试分析那么就是实在的经验)。DWC2的USB控制器也是一个比较复杂的IP,我们从零开始编写驱动必然会遇到各种问题,所以这里就会对遇到的各种问题及其分析过程进行记录,以作为经验积累,为后续提供checklist。
本篇分析一个IN时数据无法发出的问题。
问题背景是代码原本是使用高速传输,现在需要支持全速传输,所以将DCFG寄存器中速度配置为了全速,但是枚举失败。
先确认问题点大致对应的代码位置,
通过打印确认复位后,全速速度枚举正常。
所以我们继续打印中断信息,以确认更详细的位置,
可以看出软件收到了设备描述符请求的8字节内容并进行了解析,
也准备了DMA的描述符以发送18字节的内容。
但是没有预期的产生IN的XferComplete中断
于是我们使用分析仪确认总线上的情况
可以看到SETUP之后。主机IN时设备一直是NAK,确实是没有返回数据
此时我们已经定位到了问题出现在了设备准备了DMA的描述符以返回18字节数据,但是DMA没有执行,因为没有产生XferComplete中断。
以上自然而然我们就要去排查DMA的状态了,这个时候就需要我们熟悉相关的寄存器了(所以前面专门讲寄存器的的内容就派上用场了)。
我们从手册中看到DMA描述符的第一个WORD以下字段表示DMA状态
于是我们查看该字段为01即DMA busy
既然DMA的作用是根据DMA描述符从用户存储搬运数据到TxFIFO中去,DMA描述符已经准备好了,但是还是DMA busy那么就只能和TxFIFO有关,
猜测就是TxFIFO里没有足够 的空间可以满足本次DMA的搬运。
那么为什么会导致TxFIFO中空间不够呢,
要不是分配的不够,要不就是之前里面有数据没有发送出去,
前者和初始化配置有关,后者和端点状态是否工作状态有关。于是分别确认。
先查看端点寄存器状态
可以看到EPEna为1,USBACtEp为1,
EPEna为1是软件设置的,硬件的DMA搬运完后自动清零(见前面专门的寄存器讲解的文章),这里没有搬运完所以一直是1,
USBACtEp为1说明该端点是使能的。
那么排除了端点状态不对的问题,端点确实是工作的。
那么继续排查是否TxFIFO缓冲区不够的问题,
看到TxFIFO的大小配置为了4个WORD即16字节,不足18字节,所以DMA无法一次搬进去。
那么我们是否可以验证下呢,可以手动改下只发16个字节试一下
此时可以看到,发送了出去,那么确认就是这个原因了。
此时问题就好查了,那么对照下代码确认应该就是初始化配置时配置的了
代码如下全速时配置为了包大小为8,且计算TxFIFO时根据双缓冲,2x8/2=4个WORD和寄存器值一样。
pep0_in->maxpacket = ((usb_handle -> setspeed & 0x0F) == USB_SPEED_HIGH) ? 64 : 8;
那么问题就定位了,但是到此就完了吗?我们再来回顾下,全速的控制端点也是可以配置最大包大小为8个字节大小的,理论上也应该可以呀?
我们再来看上面的端点0的IN控制寄存器里配置的值
Bit[1:0]为00确是64字节,
再来看代码
原来是写死了
所以这个驱动写的就有问题,应该根据配置参数自动调整而不是写死。
所以根本原因其实是在这里。
MPS改为8,也是可行的,发送18字节就会拆分为多包发送,这里不再记录了,可以自行验证。
以上问题根本点即端点的MPS配置的不对,当然更上一层的根本原因是,TxFIFO的大小不能太小,至于是小于多少呢,小于MPS和发送数据的最小值,因为发送数据可以小于MPS的。于是这里我们又总结出了一个checklist,即FIFO大小不能小于MPS和最小一次发送数据大小的最小值。
以上以一个实例进行分析,虽然问题不是复杂的问题,总结一下就是TxFIFO缓冲区不足,使得DMA无法从用户指定的描述符中指定的存储区域搬运指定大小数据到TxFIFO,使得DMA一直处于Busy状态。
但是如果对此不熟则很可能也会耽误很多时间去分析,以上问题可以总结出checklist以后遇到类似问题可以直接查看,确认排查,这也是经验积累的过程。对于公司技术的积累也是依赖于此,而不是依赖于人。
主要的是要熟悉整个分析过程,分析思路,对驱动开发这是常态,你将会发现经常改了一点内容可能就有问题,我们一定要找到根本原因,而不是通过修改代码去回避,这在驱动开发中也是很重要的思想。
以下做一个简单的方法总结:
1.缩小范围,定位代码对应位置,确认问题点,比如上面的定位到问题出现在,准备了IN描述符,但是没有产生IN的XferComplete中断之间。一般使用打印,断点,逐步增删代码,二分法增删代码逻辑等技术手段进行。
2.定位数据交互在哪里出现问题,比如上面定位到发了请求设备没有返回数据。一般使用逻辑分析仪,USB分析仪,协议分析软件等技术手段,配合上述软件的定位。
3.确认位置后通过状态寄存器等确认具体的问题,比如上面通过描述符DMA的状态去判断。一般需要熟悉IP对应的寄存器,尤其是一些状态寄存器。
审核编辑 黄宇
全部0条评论
快来发表一下你的评论吧 !