中止表示当前存储器访问不能被完成,有两种类型的中止:
预取指中止: 发生在指令预取指过程中
数据中止: 发生在对数据访问时
1.预取指中止
当发生预取指中止时ARM7TDMI-S
内核将预取的指令标记为无效但在指令到达流水线的执行阶段时才进入异常,如果指令在流水线中因为发生分支而没有被执行,中止将不会发生。
在处理中止的原因之后不管处于哪种处理器操作状态处理程序都会执行下面的指令
SUBS
PC,R14_abt,#4
这个动作恢复了PC
和CPSR
并重试被中止的指令
2.数据中止
当发生数据中止时根据指令的类型产生不同的动作
●数据转移指令LDR,STR
回写到被修改的基址寄存器中止处理程序必须注意这一点
●交还指令SWP
中止好像没有被执行过一样中止必须发生在SWP
指令进行读访问时
●块数据转移指令LDM,STM
完成 当回写被设置时基址寄存器被更新在指示出现中止后。ARM7TDMI-S
内核防止所有寄存器被覆盖这意味着ARM7TDMI-S
内核总是会保护被中止的LDM指令中的r15(总是最后一个被转移的寄存器)
中止的机制使指令分页的虚拟存储器系统能够被实现。在这样一个系统中,处理器允许产生仲裁地址。当某一地址的数据无法访问时,存储器管理单元MMU
通知产生了中止,中止处理程序必须找出中止的原因,使请求的数据可以被访问并重新执行被中止的指令。应用程序不必知道可用存储器的数量,也不必知道它的被中止时所处的状态。在修复产生中止的原因后,不管处于哪种处理器操作状态,处理程序都必须执行下面的返回指令
SUBS
PC,R14_abt,#8
这个动作恢复了PC
和CPSR
并重试被中止的指令
因LPC2000中没有MMU的功能,发生DataAbort中止的原因只能是程序书写的错误。后来反复检查,发现问题出在void
OSSemPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)和void
*OSMboxPend (OS_EVENT *pevent, INT16U timeout, INT8U *err)的调用上。
以下为OSSemPend的代码:
void OSSemPend (OS_EVENT *pevent, INT16U timeout,
INT8U *err)
{
#if OS_CRITICAL_METHOD ==
3 /* Allocate storage for
CPU status register */
OS_CPU_SR cpu_sr;
#endif
if (OSIntNesting > 0) { /* See if called from
ISR ... */
*err = OS_ERR_PEND_ISR; /* ... can't PEND from an
ISR */
return;
}
#if OS_ARG_CHK_EN > 0
if (pevent == (OS_EVENT *)0) {
/* Validate 'pevent' */
*err = OS_ERR_PEVENT_NULL;
return;
}
if (pevent->OSEventType !=
OS_EVENT_TYPE_SEM) { /* Validate event
block type */
*err = OS_ERR_EVENT_TYPE;
return;
}
#endif
OS_ENTER_CRITICAL();
if (pevent->OSEventCnt > 0) { /* If sem. is positive,
resource available ... */
pevent->OSEventCnt--; /* ... decrement semaphore
only if positive. */
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
return;
}
/* Otherwise, must wait until event
occurs */
OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on
semaphore */
OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */
OS_EventTaskWait(pevent); /* Suspend task until event
or timeout occurs */
OS_EXIT_CRITICAL();
OS_Sched(); /* Find next
highest priority task ready */
OS_ENTER_CRITICAL();
if (OSTCBCur->OSTCBStat & OS_STAT_SEM)
{ /* Must have timed out if still
waiting for event */
OS_EventTO(pevent);
OS_EXIT_CRITICAL();
*err = OS_TIMEOUT; /* Indicate that didn't get
event within TO */
return;
}
OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0;
OS_EXIT_CRITICAL();
*err = OS_NO_ERR;
}
它通过对传入的指针err赋值来实现异常的返回,没有错误则返回OS_NO_ERR。
为了简单,我对于通过指针返回的err没有处理,直接用OSSemPend(OSSem1,
0, (uint8*)0)来等待信号量。
此时问题发生了:如果传入的err指针为(uint8*)0,则*err
= OS_TIMEOUT相当于对地址在0x00000000的变量赋值,而0x00000000是位于Flash中的0地址,是只读的Flash存贮器,不能赋值,于是就发生了DataAbort中断。
而DebugInRAM中时,LPC2000有Remap的功能,将(uint8*)0复位位到0x40000000,
此地址是位于LPC2000内部RAM的首地址,是可擦写的(此地址在硬件层面上是可写的,但是这里是程序的ResetVector,对其赋值在软件上来说也是不允许的,但不会发生Abort中断)。
要防止这个问题的发生,只要在程序中声明一变量uint8
Err, 用OSSemPend(OSSem1, 0, &Err)调用即可。
为这个问题折腾了好几天,现在终于搞清楚了,总结一下,同时为碰到此问题的朋友参考。
文章评论(0条评论)
登录后参与讨论