8.1 简介
8.1.1 关于本章
本章主要聚焦于ARM Cortex - M3与Cortex - M4处理器中异常处理的深入知识。旨在让读者全面理解异常处理机制,包括异常处理的实现方式、栈帧的管理以及异常流程中的各种细节,这些知识对于开发高效、可靠的基于这两款处理器的嵌入式系统至关重要。
8.1.2 C实现的异常处理
在C语言中实现异常处理,通常需要借助编译器提供的特定机制。由于ARM Cortex - M3与M4处理器的硬件架构特性,C语言实现异常处理需要考虑如何与硬件的异常机制进行交互。
- 函数定义:异常处理函数通常没有显式的形式参数,因为异常的相关信息一般通过硬件寄存器或特定的全局变量来传递。例如,异常原因可能存储在特定的硬件寄存器中,C函数通过读取该寄存器来确定如何处理异常。
- 代码示例:
// 假设硬件提供了一个函数来获取异常原因寄存器的值
extern int get_exception_cause();
// 异常处理函数
void exception_handler(void) {
int cause = get_exception_cause(); // 获取异常原因
if (cause == 0x01) {
// 处理特定类型的异常,例如内存访问错误
// 这里可以添加修复错误或记录日志等操作
} else if (cause == 0x02) {
// 处理另一种类型的异常,如总线错误
}
}
- 设计模式:这里采用了一种简单的条件判断模式。根据获取到的异常原因,通过
if - else
语句分支来执行不同的异常处理逻辑。这种模式简单直接,适用于异常类型相对较少且处理逻辑较为明确的情况。
8.1.3 栈帧
栈帧是在函数调用过程中,在栈上为该函数分配的一块内存区域,用于存储函数的局部变量、参数以及返回地址等信息。在异常处理中,栈帧同样起着关键作用。
- 栈帧结构:当异常发生时,硬件会自动将部分寄存器的值压入栈中,形成异常处理的栈帧。这些寄存器通常包括程序计数器(PC)、链接寄存器(LR)以及部分通用寄存器(如R0 - R3)等。压入栈中的这些值记录了异常发生时处理器的运行状态,以便在异常处理结束后能够恢复到原来的执行状态。
- 栈帧管理:异常处理函数在执行过程中,可能需要进一步在栈上分配空间来存储临时变量等。例如,如果异常处理函数需要进行复杂的计算或调用其他函数,就需要在栈帧中为这些操作预留空间。异常处理结束后,栈帧需要正确地恢复,确保处理器能够准确地回到异常发生前的状态继续执行。
- 设计模式:栈帧管理遵循一种后进先出(LIFO)的模式。异常发生时压入栈的信息,在异常处理结束时按照相反的顺序弹出。这种模式与栈的特性相匹配,保证了状态的正确恢复和函数调用的正确性。
8.1.4 exc_return
exc_return
是一个特殊的值,用于在异常返回时告诉处理器如何恢复执行。它存储在链接寄存器(LR)中。
- 值的含义:
exc_return
的值的不同位编码了不同的信息,例如返回的模式(线程模式还是处理模式)、使用的栈指针(主栈指针还是进程栈指针)等。例如,当exc_return
的最高位为1时,表示返回线程模式;为0时,表示返回处理模式。 - 作用:在异常返回时,处理器根据
exc_return
的值来正确地恢复寄存器的值,并切换到合适的执行模式和栈指针。这确保了异常处理结束后,处理器能够准确地回到异常发生前的上下文继续执行。 - 设计模式:
exc_return
采用了一种编码设计模式,通过特定的位组合来传递丰富的控制信息。这种设计模式使得在有限的寄存器空间内能够传递多种关键的恢复信息,提高了系统的灵活性和效率。
8.2 异常流程
8.2.1 异常进入和压栈
- 异常检测:当处理器检测到一个异常事件,如中断、错误等,异常处理流程开始。处理器会暂停当前正在执行的指令,准备进入异常处理程序。
- 硬件压栈:处理器自动将部分寄存器的值压入栈中,这些寄存器包括程序计数器(PC)、链接寄存器(LR)、R0 - R3以及xPSR(扩展程序状态寄存器)。压栈的顺序和方式由硬件规定,确保了异常发生时的现场能够被完整保存。例如,PC寄存器的值被压入栈,记录了异常发生时正在执行的指令地址,以便在异常处理结束后能够继续执行。
- 寻找异常向量:处理器根据异常类型,在异常向量表中查找对应的异常处理程序入口地址。异常向量表是一段存储异常处理程序入口地址的内存区域,每个异常类型对应一个特定的地址。
- 设计模式:异常进入和压栈过程采用了一种状态保存与转移的模式。首先保存当前处理器的运行状态(通过压栈),然后根据异常类型转移到相应的处理程序。这种模式保证了系统在遇到异常时能够安全地暂停当前任务,并切换到合适的处理流程。
8.2.2 异常返回和出栈
- 恢复寄存器:异常处理程序执行完毕后,通过读取链接寄存器(LR)中的
exc_return
值,处理器按照特定规则从栈中弹出之前压入的寄存器值,恢复到异常发生前的状态。例如,将栈中的PC值弹出,恢复到程序计数器,使得处理器能够继续从异常发生处的下一条指令开始执行。 - 栈指针调整:在出栈过程中,栈指针会相应地调整,恢复到异常发生前的位置。这确保了栈的状态与异常发生前一致,为后续的函数调用等操作提供正确的栈环境。
- 返回执行:处理器根据
exc_return
的值确定返回的模式(线程模式还是处理模式),然后返回到异常发生前的上下文继续执行。 - 设计模式:异常返回和出栈过程是异常进入和压栈的逆过程,遵循后进先出的原则。这种模式保证了系统能够准确地恢复到异常发生前的状态,继续正常的执行流程。
8.3 中断等待和异常处理优化
8.3.1 什么是中断等待
中断等待是指当一个中断请求到达时,由于当前正在执行的指令处于一个不适合被中断的阶段(例如一些多周期指令正在执行中),处理器不能立即响应中断,而是需要等待当前指令执行完毕后才进入中断处理流程。这种等待机制确保了指令执行的完整性,避免因中断而导致数据或状态的不一致。
8.3.2 多周期指令执行时的中断
在ARM Cortex - M3与M4处理器中,某些指令需要多个时钟周期才能完成执行。当这些多周期指令正在执行时,如果有中断请求到来,处理器不会立即响应中断。例如,一个乘法指令可能需要多个时钟周期来完成运算和结果存储。在这个过程中,处理器会继续执行该指令,直到其完成,然后才检查是否有中断请求并进入中断处理流程。这样可以保证指令操作的原子性,防止中断干扰指令的正常执行。
8.3.3 末尾连锁
末尾连锁是一种优化机制,用于提高异常处理的效率。当一个异常处理程序即将结束,并且此时又有一个新的异常请求到达时,处理器可以直接从当前异常处理程序跳转到新的异常处理程序,而不需要先完全返回主程序再进入新的异常处理。这样减少了异常处理的额外开销,例如不需要重新进行栈的切换和寄存器的保存/恢复等操作。例如,在一个实时系统中,可能会频繁地出现多个中断请求,末尾连锁机制可以使得系统能够更快地响应新的中断,提高系统的实时性。
8.3.4 延迟到达
延迟到达是指当一个异常请求到达时,由于当前正在处理一个更高优先级的异常,该异常请求会被暂时搁置,直到高优先级异常处理完成后才被处理。这种机制确保了高优先级异常能够得到及时处理,而低优先级异常不会干扰高优先级异常的处理流程。例如,在一个系统中,硬件故障相关的异常可能具有较高优先级,而一些普通的外设中断具有较低优先级。当硬件故障异常正在处理时,普通外设中断请求会被延迟到达,等待硬件故障异常处理完毕。
8.3.5 出栈抢占
出栈抢占是指在异常处理过程中,当一个更高优先级的异常请求到达时,处理器可以在当前异常处理的出栈过程中暂停,转而处理新的更高优先级异常。这样可以确保高优先级异常能够得到更及时的响应,而不需要等待当前异常处理完全结束。例如,在一个同时处理多个任务的嵌入式系统中,可能会出现一些紧急任务的中断请求,出栈抢占机制可以让系统快速响应这些紧急任务。
8.3.6 惰性压栈
惰性压栈是一种优化策略,它不是在异常发生时立即将所有需要保存的寄存器都压入栈中,而是在实际需要时才进行压栈操作。例如,如果一个异常处理程序不需要使用某些寄存器的值,那么这些寄存器的值就不需要在异常发生时立即压栈。这样可以减少异常处理的时间开销,提高系统的性能。例如,在一些简单的中断处理程序中,可能只需要处理特定的标志位,而不需要涉及到所有寄存器,惰性压栈就可以避免不必要的压栈操作。
标签:处理,压栈,M4,指令,Cortex,M3,寄存器,异常,处理器 From: https://blog.csdn.net/qq_40844444/article/details/145072919