HPI中断丢失

来源:互联网 发布:微信发淘宝优惠券链接 编辑:程序博客网 时间:2024/05/17 23:30

背景

XX板上DM8168的GPMC接口与C6455的HPI接口连接,既用作C6455的启动接口,又用作启动完成后两芯片的数据传输通道。
HPI连接图

简化后两者的通信机制如下:
两者间采用中断方式。在C6455上预留读写两段缓冲空间,读缓冲用于存放C6455写给DM8168的数据,写缓冲用作存放DM8168写给C6455的数据。

A方向:C6455把数据写入读缓冲区后,置位HPIC寄存器的HINT位,则HINT引脚会发出一个边沿触发DM8168的IO中断。B方向:DM8168把数据写入写缓冲区后,置位HPIC寄存器的DSPINT位,则直接触发C6455的HPI中断。

发送方会将缓冲区头部置一个标志,来标记数据有效性。即发送方置位,接收方读走数据后清标志位。当发送方发现标志位置位时,不会再次发送,避免上一包数据被覆盖。

接收方DM8168采用IO复用的方式接收数据,即实现了read/poll函数。DM8168的中断处理区分顶半部和底半部。中断处理函数中清中断,然后用全局变量flag记录该事件后退出。在poll函数中查询flag变量,若为真则返回POLLIN标志。最后在read函数中读缓冲区数据,清缓冲区头标记,清flag变量,最后把数据返回给应用。
应用层调用select和read函数来取数据。示意代码为:

while(1){    nReady = select(&fdset);    if(FD_ISSET(fdHpi, &fdset))    {        read(fdHpi, pData, len);    }}

问题描述

上架使用一段时间后,发现几天内HPI通道会死一次,且多个板子都出现了这样的现象。于是先怀疑是软件的问题。

在某个问题板上抓现场分析。给C6455架上仿真器,然后用Load Symbol的方式访问其内存和变量,发现读缓冲区的数据已经满,但没有被DM8168读走,导致后续的数据发送不出去。手动给DM8168触发一个中断后数据立即被读走,后续通信恢复正常。

问题分析

这时怀疑是丢失了一次中断,做了如下的分析和测试:
1.先测试是否是硬件原因丢失中断,针对该接口做了高强度长时间的硬件稳定性测试,然而最终都没有出现错误。
2.分析各自软件逻辑,经过一段时间代码走查,证实发送方并没有少发中断。而接收方也没有明显的逻辑问题。
3.分析通信机制,怀疑是时序问题,再次走查代码,终于找到了原因。

如下图,在DM8168读函数执行完清头标记后发生进程切换,这时遇到C6455要写一包数据。由于头标记已清,所以C6455正常执行且发出中断, 然后DM8168进入中断服务函数,置flag变量。一段时间后进程切回,继续执行read函数,马上把flag变量清除后退出。
HPI时序图

这样flag变量为零,曾经发生的中断失效了!DM8168阻塞在select中无法返回,而C6455看到的头标记永远置位无法再次发送。系统进入了死锁的状态。

验证

为了验证是否真的是这个原因,做了如下实验,人为在read函数的清头标记和清flag变量两步中间加入睡眠,即把问题放大,发送方不停发送数据。这时问题立即复现。这就证实确实是该时序缺陷导致的问题。

解决方案

出现这种时序问题的原因是对各个变量的使用不严谨。头标记是用来防止发送方再次发送数据的,清完标记后就有可能立即再发生中断,因此一定要最后再清。解决办法是把read函数中清flag变量和清头标记两个动作的次序互换。就可以保证不会出现中断“丢失”的现象了。

测试

修改驱动后回归测试,很过几天几夜运行,没有再出现HPI通道的死锁,问题解决。

0 0