Cortex-M FPU的Lazy Stacking机制
<p class="artical_littlestyle1">1.浮点运算指令</p><p style="text-indent: 2em;">浮点运算指令用于FPU单元的单精度浮点运算。浮点运算指令都是用V开头的汇编指令。</p><p style="text-indent: 2em;">只有在FPU开启的状态下,才能运行这些指令。</p><p style="text-indent: 2em;">如果在FPU没有开启的状态下,执行了浮点运算指令,系统会产生一个硬fault异常。<br/></p><p class="artical_littlestyle2">2.为什么FPU需要Lazy Stacking</p><p style="text-indent: 2em;">当Cortex-M系列的芯片多了对浮点运算的支持之后,在中断响应和退出时会增加对FPU扩展寄存器的保护。</p><p style="text-indent: 2em;">入栈浮点寄存器组所带来的影响有如下几方面:</p><p style="text-indent: 2em;">a.增加stack frame所占的存储区域;</p><p style="text-indent: 2em;">b.增加中断响应延迟;</p><p style="text-indent: 2em;">c.在OS环境下,增加上下文切换时间;</p><p style="text-indent: 2em;">为了减少中断响应延迟和OS环境下的上下文切换时间,引入了FPU的Lazy Stacking机制。<br/></p><p class="artical_littlestyle3">3.FPU Lazy Stacking简述</p><p style="text-indent: 2em;">Lazy Stacking机制在下面的情况下,会跳过对浮点寄存器组的入栈操作(仅预留浮点寄存器组S0~S15和FPSCR的存储空间),以避免中断延迟的增加:</p><p style="text-indent: 2em;">a.中断处理函数不使用FPU;</p><p style="text-indent: 2em;">b.被中断的程序未曾用到FPU;</p><p style="text-indent: 2em;"><span style="color: rgb(255, 0, 0);">被中断函数使用了FPU,如果在执行中断处理函数时也用到了FPU,在执行到中断函数的第一条浮点指令时,内核会暂停,然后硬件自动将先前的浮点寄存器内容(S0~S15,FPSCR)压入预留的存储空间中。</span></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">Lazy Stacking是可以通过软件使能和关闭的,如下述操作:</span></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">a.使能Lazy Stacking必须同时置位FPCCR寄存器的LSPEN位和ASPEN位;</span></p><p style="text-indent: 2em;"><span style="color: rgb(0, 112, 192);">b.清除FPCCR寄存器的LSPEN位可以关闭Lazy Stacking;</span></p><p style="text-indent: 0em;">关于FPCCR寄存器的LSPEN位和ASPEN位的组合情况有如下说明:</p><table><tbody><tr class="firstRow"><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="197" valign="bottom" align="center">FPCCR.ASPEN</td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="215" valign="bottom" align="center">FPCCR.LSPEN</td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="632" valign="bottom" align="center">说明</td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="197" valign="middle" align="center">0<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="215" valign="middle" align="center">0<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="632" valign="top">取消自动状态保存。中断响应时不入栈FPU寄存器。<br/>应用场景:<br/>1. 应用中没有用到OS或者多任务调度,如果没有任何中断异常用到FPU。<br/>2. 在应用程序代码中只有一个中断用到FPU。如果有多个中断用到FPU,那么中断嵌套必须被禁止。可以通过把所有的中断优先级设置位相同优先级实现。<br/></td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="197" valign="middle" align="center">1<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="215" valign="middle" align="center">0<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="632" valign="top">关闭Lazy Stacking,仅打开自动状态保存。<br/>如果用到FPU,CONTROL.FPCA位自动置1。中断响应时,硬件自动入栈S0~S15和FPSCR寄存器</td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="197" valign="middle" align="center">1<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="215" valign="middle" align="center">1<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="632" valign="top"><span style="color: rgb(255, 0, 0);">打开Lazy Stacking,打开自动状态保存。<br/>如果用到FPU,CONTROL.FPCA位自动置1。如果响应中断时,CONTROL.FPCA为1,处理器在堆栈中预留FPU寄存器的空间,同时将FPCCR.LSPACT位置1。但是PFU寄存器并没有马上入栈,直到在中断处理函数中用到FPU时再入栈。</span></td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="197" valign="middle" align="center">0<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="215" valign="middle" align="center">1<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="632" valign="top">非法配置</td></tr></tbody></table><p class="artical_littlestyle4">4.FPU Lazy Stacking用到的几个重要标志</p><p style="text-indent: 2em;">浮点寄存器入栈和出栈过程中用到的标志位:<br/></p><table><tbody><tr class="firstRow"><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="324" valign="bottom" align="center">标志位</td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="742" valign="bottom" align="center">说明<br/></td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="324" valign="middle" align="center">CONTROL.FPCA</td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="742" valign="top">0 = 当前上下文中没有用到FPU<br/>1 = 当前上下文中用到FPU<br/>这里的上下文,即表示普通的任务上下文,也表示中断上下文。<br/></td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="324" valign="middle" align="center">FPCCR.LSPACT</td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="742" valign="top">0 = 退出Lazy状态(实际入栈后被硬件清零或者中断返回时硬件清零)<br/>1 = 进入Lazy状态(栈帧中预留了FPU寄存器的空间,但没有实际入栈)</td></tr><tr><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="324" valign="middle" align="center">EXC_RETURN[4]<br/></td><td style="word-break: break-all; border-width: 1px; border-style: solid;" width="742" valign="top">0 = 栈帧中包括了FPU寄存器空间<br/>1 = 栈帧中不包括FPU寄存器空间<br/></td></tr></tbody></table><p class="artical_littlestyle1">5.FPU Lazy Stacking使用实例</p><p style="text-indent: 2em;">下面的例子都是在使能了FPU的Lazy Stacking机制下进行说明的。</p><p style="text-indent: 2em;">(1).被中断的程序和中断程序中都没有使用到FPU,其示意图如下所示:<br/></p><p style="text-align:center"><img src="/uploads/AilsonJack/2020.07.15/1594814638140123.png" onclick="preview_image('/uploads/AilsonJack/2020.07.15/1594814638140123.png')"/></p><p style="text-indent: 2em;">(2).被中断的程序用到了FPU,中断程序中没有使用到FPU,其示意图如下所示:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2020.07.15/1594814638862067.png" onclick="preview_image('/uploads/AilsonJack/2020.07.15/1594814638862067.png')"/></p><p style="text-indent: 2em;">(3).被中断的程序和中断程序中都用到了FPU,其示意图如下所示:</p><p style="text-align:center"><img src="/uploads/AilsonJack/2020.07.15/1594814640560990.png" onclick="preview_image('/uploads/AilsonJack/2020.07.15/1594814640560990.png')"/></p><p class="artical_littlestyle2">6.RTOS使用Lazy Stacking</p><p style="text-indent: 2em;">首先肯定是要使能FPU的Lazy Stacking,然后在PendSV切换任务的时候做下面的处理:</p><p style="text-indent: 2em;">(1).上文保存阶段:检测 EXC_RETURN 的 bit4(通过LR访问),如果该位为零,就入栈剩下的S16-S31 即可;如果该位为 1,则无需保存 FPU 寄存器,因为该任务未曾使用过 FPU。当然了这里有一个标志数据,表示当前任务是否使用了FPU,便于下次任务恢复时,确定是否需要从堆栈弹出内容到S16-S31。标志数据也压入当前任务的堆栈进行保存。</p><p style="text-indent: 2em;">(2).下文恢复阶段:从堆栈中弹出标志数据,判断待恢复的任务是否使用了FPU,如果使用了FPU,那么从堆栈中弹出内容到S16-S31;如果没有使用FPU,则不需要恢复FPU寄存器。接下来需要修改当前LR寄存器的内容,如果使用了FPU,LR(EXC_RETURN)的bit4需要清零,来保证在退出PendSV时将先前压入该任务堆栈的S0-S15和FPSCR寄存器出栈;如果没有使用FPU,LR(EXC_RETURN)的bit4需要置1,来告诉CPU该任务栈帧中不包括FPU寄存器内容,不需要进行出栈操作。</p><p style="text-indent: 2em;">对于S0-S15和FPSCR寄存器,如果任务使用了FPU,在进入到PendSV之前,就在任务堆栈中预留了存储空间,当执行上文保存阶段的S16-S31的入栈操作时,会首先将S0-S15和FPSCR寄存器压入先前堆栈预留的存储空间中,接着再将S16-S31压入堆栈。</p><p style="text-indent: 2em;">如果这篇文章对你有帮助,记得点赞和关注博主就行了^_^。</p>
你可能也喜欢:
暂无评论,要不要来个沙发
发表评论
JLink V9掉固件修复(灯不亮) 3Zephyr笔记2 - 在STM32F429上运行HelloWorld 2计算NandFlash要传入的行地址和列地址 1Linux MMC子系统 - 6.eMMC 5.1工作模式-设备识别模式 0Linux MMC子系统 - 5.eMMC 5.1工作模式-引导模式 0Linux MMC子系统 - 4.eMMC 5.1常用命令说明(2) 0
标签云
Linux嵌入式实用技巧ARM内核学习问题集合CC++编程语言阅读笔记汇编Linux内核完全注释Windows驱动开发计算机基础ARM11ARMv7-ASTM32IDESublimeLinux内核学习eMMCMMC子系统Ubuntu操作系统OfficeVMWareAPUEgccRTOS中断漫游世界随笔感悟开发工具软件应用编程VsCodearmccarmclang编译器ZephyrSPIJLink网卡驱动安装各种芯片库函数NFSμCOS内核sambaFlashUnix命令与脚本输入法Linux内核设计与实现gitRIFFWAVJATGFTPar8161安装centos有线上网μCGUI字库工程建立右键菜单网络文件系统Firefox百度NTFS文件系统CodeBlocksCentOS数据结构算法PhotoShop51KeilQTUltraEditscanfglibc宏定义UIDGID优先级娱乐天地SourceInsight磁盘扇区总线I2CPDFBComparePythonI2SFPUMakefileSWDCPUARP软件推荐FileZilla