Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
内核
cortex-a9
cortex-A
Cortex-A9 内核中线程切换函数说明
1.00
发布于 2021-01-29 11:44:16 浏览:1249
订阅该版
[tocm] # Cortex-A9 内核中线程切换函数说明 ## rt_hw_context_switch_to() 只有目标,没有来源线程。 - rt_hw_context_switch_to ```asm /* * void rt_hw_context_switch_to(rt_uint32 to, struct rt_thread *to_thread); * r0 --> to (thread stack) * r1 --> to_thread */ .globl rt_hw_context_switch_to rt_hw_context_switch_to: ldr sp, [r0] # 将 r0 中新线程的栈地址放入 sp 寄存器 b rt_hw_context_switch_exit # 跳转准备执行上下文切换 ``` - rt_hw_context_switch ```asm /* * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to, struct rt_thread *to_thread); * r0 --> from (from_thread stack) * r1 --> to (to_thread stack) * r2 --> to_thread */ .globl rt_hw_context_switch rt_hw_context_switch: stmfd sp!, {lr} # 将 lr 中的值 push 到栈 stmfd sp!, {r0-r12, lr} # 将相关寄存器的值 push 到栈,这里的 lr 在主动切换时没有用处,只是起到占位的作用,这种写法下,寄存器 index 大在栈中处于栈 offset 较大的位置(栈中高地址的位置) mrs r4, cpsr # 将 cpsr 中的值放入到 r4 tst lr, #0x01 # 将 lr 中的值与 0x01 相与后查看状态 orrne r4, r4, #0x20 # 如果结果不为 0,则将 r4 寄存器中的值或上 0x20 stmfd sp!, {r4} # 将 r4 的值 push 到栈 /* fpu context */ vmrs r6, fpexc # 将 fpexc 中的值放入 r6 tst r6, #(1<<30) # 判断 r6 中的值第 30 位是否为 1 beq 1f # 如果不是 1 跳转到 1f vstmdb sp!, {d0-d15} # 将 d0-d15 寄存器 push 到栈 vstmdb sp!, {d16-d31} # 将 d16-d31 寄存器 push 到栈 vmrs r5, fpscr # 将 fpscr 中的值放入 r5 stmfd sp!, {r5} # 将 r5 中的值 push 到栈 1: stmfd sp!, {r6} # 将 r6 中的值 push 到栈 str sp, [r0] # 将 sp 中的值放入 [r0] 的地址中 ldr sp, [r1] # 将 [r1] 地址中的值(也就是 to 线程的栈地址)放入 sp b rt_hw_context_switch_exit # 跳转到 rt_hw_context_switch_exit 执行 ``` - rt_hw_context_switch_interrupt ```asm .globl rt_thread_switch_interrupt_flag .globl rt_interrupt_from_thread .globl rt_interrupt_to_thread .globl rt_hw_context_switch_interrupt rt_hw_context_switch_interrupt: ldr r2, =rt_thread_switch_interrupt_flag # 将 intr flag 的地址放入 r2 ldr r3, [r2] # 将 flag 中的值放入 r3 寄存器 cmp r3, #1 # 判断 r3 中的值是否为 1 beq _reswitch # 如果不为 1 则跳转到 _reswitch ldr ip, =rt_interrupt_from_thread # 将 from thread 的值放入 ip(R12) 寄存器 mov r3, #1 # 设置 r3 寄存器中的值为 1 str r0, [ip] # 将 r0 寄存器中的值放入到 rt_interrupt_from_thread str r3, [r2] # 将 r3 寄存器中的值(#1)放入到 rt_thread_switch_interrupt_flag _reswitch: ldr r2, =rt_interrupt_to_thread # 将 rt_interrupt_to_thread 的地址放入 r2 str r1, [r2] # 将 r1 中的值放入 rt_interrupt_to_thread bx lr # 返回到 lr 中存放的地址 ``` - rt_hw_context_switch_exit ```asm .global rt_hw_context_switch_exit rt_hw_context_switch_exit: /* fpu context */ ldmfd sp!, {r6} # 将栈顶的数据放入到 r6 寄存器中 vmsr fpexc, r6 # 将 r6 寄存器里的值放入到 fpexc 寄存器中 tst r6, #(1<<30) # 判断 r6 中第 30 位是否为 1 beq 1f # 如果不是 1,则跳转到 1f ldmfd sp!, {r5} # 将栈中的数据 pop 到 r5 寄存器中 vmsr fpscr, r5 # 将 r5 寄存器中的值放入 fpscr 寄存器中 vldmia sp!, {d16-d31} # 将栈中的数据 pop 到 d16-d31 vldmia sp!, {d0-d15} # 将栈中的数据 pop 到 d0-d15 1: ldmfd sp!, {r1} # 将栈中的数据放入 r1 寄存器 msr spsr_cxsf, r1 /* original mode */ # 将 r1 的内容放入 spsr_scsf ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ # 将栈中的数据 pop 回相应的寄存器 ``` ```asm .align 5 .globl vector_irq vector_irq: # 开始进行处理 irq 中断 stmfd sp!, {r0-r12,lr} # 将 r0-r12 lr push 到栈中 # 这里也需要将 fpu 相关的寄存器压入中断栈,然后再存放到当前线程的栈中 bl rt_interrupt_enter bl rt_hw_trap_irq # 开始处理 irq 中断,这和过程中可能会调用 rt_schedule,如果调用 # 了线程切换函数,就会将 intr flag 置为 1 bl rt_interrupt_leave @ if rt_thread_switch_interrupt_flag set, jump to @ rt_hw_context_switch_interrupt_do and don't return ldr r0, =rt_thread_switch_interrupt_flag # 将 intr flag 的地址放入到 r0 中 ldr r1, [r0] # 将 flag 的值放入 r1 寄存器中 cmp r1, #1 # 判断 r1 中的值是否为 1 beq rt_hw_context_switch_interrupt_do # 如果上述判断成立,则跳转到 interrupt_do ldmfd sp!, {r0-r12,lr} # 如果不成立,则 pop 栈到 r0-r12 subs pc, lr, #4 # 将 lr 的值减 4 放回 pc 中,相当于中断返回 rt_hw_context_switch_interrupt_do: mov r1, #0 # 清空 r1 寄存器中的值为 0 str r1, [r0] # 将 r1 中的值放入到 intr flag,也就是将 intr flag 置为 0 mov r1, sp @ r1 point to {r0-r3} in stack # 将 sp 中的值放入 r1 add sp, sp, #4*4 # sp = sp + 16 ldmfd sp!, {r4-r12,lr}@ reload saved registers # 从栈中恢复寄存器 mrs r0, spsr @ get cpsr of interrupt thread # 读取 spsr 寄存器到 r0 sub r2, lr, #4 @ save old task's pc to r2 # lr - 4 后存储到 r2 @ Switch to SVC mode with no interrupt. If the usr mode guest is @ interrupted, this will just switch to the stack of kernel space. @ save the registers in kernel space won't trigger data abort. msr cpsr_c, #I_Bit|F_Bit|Mode_SVC # 设置 cpsr_c 进入 SVC 模式 stmfd sp!, {r2} @ push old task's pc # 将 r2 中的值压栈 stmfd sp!, {r4-r12,lr}@ push old task's lr,r12-r4 # 将寄存器 push 入栈 ldmfd r1, {r1-r4} @ restore r0-r3 of the interrupt thread stmfd sp!, {r1-r4} @ push old task's r0-r3 # 将 r1-r4 压栈 stmfd sp!, {r0} @ push old task's cpsr # 将 r0 的值压栈 #ifdef RT_USING_FPU /* fpu context */ vmrs r6, fpexc # 取出 fpexc 寄存器中的值到 r6 tst r6, #(1<<30) # 判断是否开启了 fpu beq 1f # 如果没有开启则跳转到 1 vstmdb sp!, {d0-d15} # 将 d0-d15 压栈 vstmdb sp!, {d16-d31} # 将 d16-d31 压栈 vmrs r5, fpscr # 读取 fpscr 的值到 r5 stmfd sp!, {r5} # 将 r5 寄存器压栈 1: stmfd sp!, {r6} # 将 r6 压栈 #endif ldr r4, =rt_interrupt_from_thread # 将 rt_interrupt_from_thread 变量的地址存入 r4 ldr r5, [r4] # 将变量的值存入 r5 str sp, [r5] @ store sp in preempted tasks's TCB ldr r6, =rt_interrupt_to_thread ldr r6, [r6] ldr sp, [r6] @ get new task's stack pointer #ifdef RT_USING_FPU /* fpu context */ ldmfd sp!, {r6} vmsr fpexc, r6 tst r6, #(1<<30) beq 1f ldmfd sp!, {r5} vmsr fpscr, r5 vldmia sp!, {d16-d31} vldmia sp!, {d0-d15} 1: #endif ldmfd sp!, {r4} @ pop new task's cpsr to spsr msr spsr_cxsf, r4 ldmfd sp!, {r0-r12,lr,pc}^ @ pop new task's r0-r12,lr & pc, copy spsr to cpsr ``` ``` ldmfd sp!, {r0-r12,lr,pc}^ ``` 这句话最后`^`的作用: 1. 如果加载的目标地址有 pc,那么加载目标地址中有 lr 或 sp ,指的是当前状态下的 lr 或 sp。 2. 如果加载的目标地址没有 pc,此时加载目标的 lr 和 sp 指的是**用户态**的 lr 和 sp。 3. 如果加载的目标地址既没有 lr 也没有 sp, 则 ^ 没有作用。 加了`^`之后,在进行`lr`地址返回的同时,也会将`spsr`的状态恢复到系统中 cpsr 寄存器中,也就是同时更改了 CPU 目前所处的状态,例如从中断返回时,将从 IRQ 状态返回`SVC`状态,切换的同时也将打开系统中断。 重点是恢复了 pc 中的值后,程序将跳转到 pc 指向的代码执行,而没有机会在后面再插入代码恢复 CPSR 的状态。
0
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
我夏了夏天
Life isn't about finding yourself, life is about creating yourself.
文章
24
回答
1319
被采纳
19
关注TA
发私信
相关文章
1
rt-thread的学习疑惑
2
基于stm32的RTT在RTT Studio IDE环境中的启动顺序求解
3
关于 rt_object_detach 脱离内核对象函数的作用求解
4
RT-Thread内核什么时候考虑加入MPU功能?
5
rt_hw_board_init中开中断后,触发SysTick_Handler
6
Cortex-M0在bootloader环境下的上下文切换问题?
7
关于ART-PI的bootloader是怎么烧写进去的
8
为什么内核代码和bootloader的代码一样的
9
线程对象结构体为什么不直接选择继承内核对象?
10
使用rt_memset给线程栈初始化,为什么选择字符‘#’,而不是‘\0’?
推荐文章
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
篇文章
3
次点赞
qq1078249029
2
篇文章
2
次点赞
xnosky
2
篇文章
1
次点赞
Woshizhapuren
1
篇文章
5
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部