Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
cortex-M
线程上下文调度切换_context
关于STM32 PendSV非精确异常触发第一个任务的问题
5.00
发布于 2022-03-30 16:09:34 浏览:2757
订阅该版
[tocm] ## 1 问题: 最近调试ST-link 单步调试时发现:rt_system_scheduler_start->rt_hw_context_switch_to ![image-20220330114558907.jpg](https://oss-club.rt-thread.org/uploads/20220330/23a829e9a23d846e987d44b335d4e12c.jpg) rt_hw_context_switch_to 执行完后,会继续往下执行, `;never reach here!` 被笑话了😄 ## 2 初步分析: 在《ARM Cortex-M3与Cortex-M4权威指南》chapter10 讲了 SVC和 PenSV异常: | ![image-20220330115258625.jpg](https://oss-club.rt-thread.org/uploads/20220330/0d20fcc65ae88c2301357172a851116f.jpg.webp) | ![image-20220330115409382.jpg](https://oss-club.rt-thread.org/uploads/20220330/c87a50bcbac84d8e77b4d495203d6399.jpg.webp) | | :----------------------------------------------------------: | :----------------------------------------------------------: | | SVC exception | PendSV exception | > 合起来就是: > > SVC 是精确异常,只要挂起并开了中断,立即执行,除非当前有更高的异常存在。 > > PendSV 是非精确异常,尽管你挂起了一个PendSV异常并开启了中断,但是实际执行异常之前,可能还有几个指令会被继续执行 > > 这个很符合我们观察到的现象。 ## 3 测试一下 ### 3.1 直接追加LDR指令 ``` ;/* ; * void rt_hw_context_switch_to(rt_uint32 to); ; * r0 --> to ; * this fucntion is used to perform the first thread switch ; */ rt_hw_context_switch_to PROC ; 省略部分代码 ; enable interrupts at processor level CPSIE F CPSIE I ; never reach here! ENDP LDR r0, =0x1234567 SVC 0 ; compatible with old version rt_hw_interrupt_thread_switch PROC EXPORT rt_hw_interrupt_thread_switch BX lr ENDP ``` 然后在PenSV入口处设置断点,可以看到R0 确实被我们改变了,`;never reacher here `注释确实不准确。 ![image-20220330125407701.jpg](https://oss-club.rt-thread.org/uploads/20220330/87e48797b9de00c63d477904e3377e16.jpg) ### 3.2 延迟了几条指令 准确说应该是时钟周期,不同指令,占用时钟周期不同。为了测试直观: > 1. 直接使用SVC 指令触发 SVC异常(里面死循环,OS 就运行不了),作为结束指令, > 2. 在SVC前加简单的NOP 指令,看看几条后,PendSV才运行, SVC指令未执行 #### 3.2.1 1个NOP ``` ;/* ; * void rt_hw_context_switch_to(rt_uint32 to); ; * r0 --> to ; * this fucntion is used to perform the first thread switch ; */ rt_hw_context_switch_to PROC ; 省略部分代码 ; enable interrupts at processor level CPSIE F CPSIE I ; never reach here! ENDP NOP SVC 0 ; compatible with old version rt_hw_interrupt_thread_switch PROC EXPORT rt_hw_interrupt_thread_switch BX lr ENDP ``` 断点调试发现, 程序先运行到 PendSV,然后运行SVC 死循环(发生了中断咬合,PendSV和SVC优先级在rt_hw_context_switch_to被配置成了最低优先级,均为15) ``` void SVC_Handler(void) { /* USER CODE BEGIN SVCall_IRQn 0 */ while (1) { /* USER CODE BEGIN W1_BusFault_IRQn 0 */ /* USER CODE END W1_BusFault_IRQn 0 */ } /* USER CODE END SVCall_IRQn 0 */ /* USER CODE BEGIN SVCall_IRQn 1 */ /* USER CODE END SVCall_IRQn 1 */ } ``` #### 3.2.2 2个NOP 未运行SVC 死循环,OS 正常运行 #### 3.3 LDR 指令 ``` ;/* ; * void rt_hw_context_switch_to(rt_uint32 to); ; * r0 --> to ; * this fucntion is used to perform the first thread switch ; */ rt_hw_context_switch_to PROC ; 省略部分代码 ; enable interrupts at processor level CPSIE F CPSIE I ; never reach here! ENDP LDR r0, =0x1234567 SVC 0 ; compatible with old version rt_hw_interrupt_thread_switch PROC EXPORT rt_hw_interrupt_thread_switch BX lr ENDP ``` 未运行SVC 死循环,OS 正常运行 > 来个图总结一下 > > | SVC 前插入指令 | 结果 | 备注 | > | -------------- | -------------------------- | ----------------------------- | > | 1个NOP | SVC指令运行,OS 未正常运行 | 发生中断咬合, PendSV先运行的 | > | 2个NOP | SVC指令未运行,OS 正常运行 | | > | LDR指令 | SVC指令未运行,OS 正常运行 | | ## 4 处理建议: 目前看来, 有几种处理方法 ### 4.1 基于测试结果,直接在rt_hw_context_switch_to后面追加两个NOP ``` ; enable interrupts at processor level CPSIE F CPSIE I NOP NOP ; never reach here! ENDP ``` ### 4.2 使用BL 返回 其实在正常调度使用的rt_hw_context_switch_interrupt, 也是这样做的 ``` _reswitch LDR r2, =rt_interrupt_to_thread ; set rt_interrupt_to_thread STR r1, [r2] LDR r0, =NVIC_INT_CTRL ; trigger the PendSV exception (causes context switch) LDR r1, =NVIC_PENDSVSET STR r1, [r0] BX LR ENDP ``` 碰巧现在的源码后面有个rt_hw_interrupt_thread_switch也算是实现了这个方法, 但这不是我们的本意, 还是在 never reach here!前加合适 ``` ; enable interrupts at processor level CPSIE F CPSIE I BX lr ; never reach here! ENDP ; compatible with old version rt_hw_interrupt_thread_switch PROC EXPORT rt_hw_interrupt_thread_switch BX lr ENDP ``` 这又要担心返回到rtthread_startup后,是否还会执行其他指令 ``` int rtthread_startup(void) { rt_hw_interrupt_disable(); ... /* start scheduler */ rt_system_scheduler_start(); __asm("SVC 0\n"); /* never reach here */ return 0; } ``` > 加了一个内联SVC 0指令,发现没有被执行,这么看BX lr 也挺占机器周期 ### 4.3 dsb isb指令 参考FreeRTOS常用的同步指令dsb isb,搜索《ARM Cortex-M3与Cortex-M4权威指南》发现这个表格 ![image-20220330145412079.jpg](https://oss-club.rt-thread.org/uploads/20220330/9bc62bdb73732958dc897f8462854e2a.jpg.webp) 完美的解决方案! ``` ;/* ; * void rt_hw_context_switch_to(rt_uint32 to); ; * r0 --> to ; * this fucntion is used to perform the first thread switch ; */ rt_hw_context_switch_to PROC ... ; enable interrupts at processor level CPSIE F CPSIE I ;ensure PendSV exception taken place before subsequent operation dsb isb ; never reach here! ENDP ; compatible with old version rt_hw_interrupt_thread_switch PROC EXPORT rt_hw_interrupt_thread_switch BX lr ENDP ``` > 至于正常的rt_hw_context_switch里需不需要加这两条指令,线程任务中,BL lr后还是在线程任务里,最多多执行一两条下面的指令,问题不大。 > > FreeRTOS 倒是比较谨慎,在portYIELD里加了 > > ``` > /* Scheduler utilities. */ > #define portYIELD() \ > { \ > /* Set a PendSV to request a context switch. */ \ > portNVIC_INT_CTRL_REG = portNVIC_PENDSVSET_BIT; \ > \ > /* Barriers are normally not required but do ensure the code is completely \ > within the specified behaviour for the architecture. */ \ > __dsb( portSY_FULL_READ_WRITE ); \ > __isb( portSY_FULL_READ_WRITE ); \ > } > ``` > > **现在 `; never reach here!` 不心虚了**🙂
22
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
blta
这家伙很懒,什么也没写!
文章
12
回答
9
被采纳
2
关注TA
发私信
相关文章
1
modbusRTU如何避免因为被高优先级任务切走而导致本次通讯失败
2
RT-Thread 4.0.2初次上下文切换失败
3
pendSV中bx lr指令,lr指向哪里?psp中剩余的寄存器啥时候弹出的?
4
Cortex-M0在bootloader环境下的上下文切换问题?
5
arc内核移植线程切换
6
rt_thread_yield 无法在同级别中释放cpu
7
线程执行完后退出 是如何通知cpu切换任务的
8
RTT-NANO 3.1.3 线程切换问题
9
线程切换,打印出来的时间不对
10
软件定时器的回调函数里可以挂起或解挂另一个线程吗?
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
UART
WIZnet_W5500
ota在线升级
freemodbus
PWM
flash
cubemx
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
flashDB
GD32
socket
中断
编译报错
Debug
SFUD
rt_mq_消息队列_msg_queue
msh
keil_MDK
ulog
C++_cpp
MicroPython
本月问答贡献
a1012112796
10
个答案
1
次被采纳
踩姑娘的小蘑菇
4
个答案
1
次被采纳
红枫
4
个答案
1
次被采纳
张世争
4
个答案
1
次被采纳
Ryan_CW
4
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
YZRD
2
篇文章
5
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部