中断底层特性与优化机制

前段时间我们深入研究了 Cortex-M3、M4 处理器的中断与异常,从异常和中断的基本概念,到其内部实现的原理以及处理器进出异常时的内部操作,可以说对于异常和中断,我们已经理解得相对比较全面和深入了。相信认真钻研的朋友在日常开发工程中一旦遇到与其相关的问题时都能快速定位问题点,迅速找到解决方案。

感兴趣的朋友也可以翻阅之前的文章,或是直接进入下方合集链接查看:

嵌入式工程师的自我修养

在异常与中断章节的最后,我们再来一起看一下其内部实现的几个特性与处理器的一些优化机制。这些概念可能你从未了解过,但你实现的每一个中断都会真实经历这其中的一个或多个流程。理解并运用它们,可以帮助你写出更高效,更快速的中断处理逻辑!

中断延迟

中断等待表示从中断请求开始到中断处理开始执行的时间。对于 Cortex-M3 和 Cortex-M4 处理器,若内存系统为零等待的,而且假定系统设计允许取向量和压栈同时进行,那么中断延迟为 12 个周期,其中包括寄存器压栈、取向量以及取中断处理的指令。然而,大多数情况下,由于处理器系统中会存在等待状态,就会导致中断的延迟时间更长。若处理器在执行一次包括缓冲写操作在内的存储器传输,则该传输必须在异常流程开始前完成。此外,中断执行流程的整体耗时实际还会受到存储器访问速度的影响。

除了存储器设备或外设产生的等待状态外,以下情况同样会加大中断等待时间:

  • 处理器正在处理另外一个相同或更高优先级的异常。

  • 调试器正在访问存储器系统。

  • 处理器正在执行非对齐传输。从处理器角度来看,它可能是单次传输,不过由于总线接口需要将非对齐传输转换为多个对齐传输,所以从总线角度来看,这可能会占用多个周期。

  • 处理器正在执行对位段别名的写操作。内部总线系统会将其转换为 读——修改——写 流程,这会花费至少两个周期。

Cortex-M3 和 Cortex-M4 处理器使用多种方式来降低中断处理的等待时间。例如,嵌套中断等多数操作会由处理器硬件自处理。你无需额外编写软件来确定中断响应时应该使用哪个中断服务函数,或定位中断服务函数的起始地址。

多周期指令执行的中断

部分指令需要多个时钟周期来执行。若在处理器正在执行多周期指令(如整数除法)时来了一个中断请求,这个指令就可能被丢弃并在中断处理结束后执行。这种行为同样受用于双字加载(LDRD)和双字存储(STRD)指令。

另外,Cortex-M3 和 Cortex-M4 处理器允许中断在多重加载和存储(LDM/STM)以及压栈和出栈指令执行过程中产生。若在中断产生时正在执行 LDM/STM/PUSH/POP 指令中的某条指令,则当前的存储器访问会结束,且下一个寄存器编号会被存放在压栈的 xPSR 中(中断继续指令 [ICI] 位)。在异常处理结束后,多重加载/存储/压栈/出栈指令会从传输停止的位置继续执行。这种方式还适用于具有浮点单元的 Cortex-M4 处理器的浮点存储器访问指令(如VLDM、VSTM、VPUSH 和 VPOP)。另外还有一个边界情况:若被打断的多重加载/存储/压栈/出栈指令为 IF-THEN(IT)指令块的一部分,则该指令会被取消且等中断结束后重新执行,这是因为 ICI 位和 IT 执行状态位共用执行程序状态寄存器 EPSR 中的相同空间。

对于具有浮点单元的 Cortex-M4 处理器,若在处理器执行 VSQRT(浮点平方根)或 VDIV(浮点除法)时产生了中断请求,浮点单元的执行和压栈操作会同步执行。

末尾连锁

若某个异常产生时处理器正在处理另一个具有相同或更高优先级的异常,该异常就会进入挂起状态。在处理器执行完当前的异常处理后,它可以继续执行挂起的异常/中断请求。此时处理器不会从栈中恢复寄存器(出栈)再将它们存入栈中(压栈),而是跳过出栈和压栈过程并会尽快进入挂起异常的处理中,如下图所示:

图片

这样,两个异常处理的间隔时间就会降低很多。对于无等待状态的内存系统,末尾连锁的中断等待时间仅为 6 个时钟周期。

延迟到达

当异常产生时,处理器会接收异常请求并开始压栈操作。若在压栈操作期间产生了另外一个更高优先级的异常,则更高优先级的异常会首先得到服务。

例如异常#1(低优先级)在异常#2(高优先级)的前几个周期产生,处理器的运行情况就如下图所示,处理 #2 会在压栈结束时尽快执行。

图片

出栈抢占

若某个异常请求在另一个刚完成的异常处理出栈期间产生,处理器会舍弃出栈操作且开始取向量以及下一个异常服务的指令。该优化被称作出栈抢占,如下图所示:

图片

惰性压栈

惰性压栈是和浮点单元寄存器压栈相关的一种特性,因此它只同具有浮点单元的 Cortex-M4 设备有关。Cortex-M3 和不具备浮点单元的 Cortex-M4 则不需要该特性。

若浮点单元存在且已使能,在其被使用时,浮点单元的寄存器组中的寄存器可能会包含需要保存的数据:

图片

若要将每个异常所需的浮点单元寄存器压栈,则每次都需要额外执行 17 次的压栈操作,这会将中断等待时间增加 12 ~ 29 个周期。

为了减少中断等待时间,Cortex-M4 处理器实现了一种名为惰性压栈的特性,这个特性默认是使能的。若在浮点单元使能(CONTROL 寄存器的 bit2 ,名为 FPCA,在 Cortex-M3 中不存在)且使用的情况下产生了异常,则栈帧的长度会增加。不过,这些浮点寄存器的数值实际上是不会写入栈帧中的。惰性压栈机制只会为这些寄存器保留一定的栈空间,但只有 R0~R3、R12、LR、返回地址和 xPSR 被压栈。这样,中断等待时间还是会保持在 12 个时钟周期。当出现惰性压栈时,一个名为 LSPACT(惰性压栈保持活跃)的寄存器会被置位且浮点单元上下文地址寄存器(FPCAR)则保存了浮点寄存器预留栈空间的地址。

若异常处理不需要任何浮点运算,浮点单元寄存器在异常处理期间会保持不变,而且不会在异常退出时恢复。若异常处理需要浮点运算,处理器检测到冲突后会暂停当前处理并将浮点寄存器存储到之前保留的栈空间并清除 LSPACT,接下来异常处理会继续执行。这样,浮点单元寄存器只会在必要时压栈。

惰性压栈操作可能会被打断,当在惰性压栈期间产生了中断请求,则惰性压栈操作会停止,取而代之的是普通压栈开始执行。由于触发了惰性压栈的浮点指令还没有执行,压入栈中的 PC 值会指向那条浮点指令。当中断服务结束时,异常会返回到浮点指令,而且重新执行这条指令会再次触发惰性压栈操作。

若当前执行上下文(线程或处理)未使用浮点单元,则 FPCA 为 0(CONTROL 寄存器的 bit2),且栈帧会使用较短的形式。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

WKJay_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值