rtthread在cortex-m4架构芯片上的启动流程
startup_pisces.S:
- 栈的配置(stack段)
- 堆的配置(heap段)
- 异常向量的配置(vector段)(暴露__Vectors地址)
- Reset_Handler(text段)
- 配置栈指针
- 把数据从只读的内存复制到RAM中,这里有两种方案(1.复制多个段;2.只复制一个段)(这里采用方案2)从代码看是将__etext段的内容复制到链接脚本中标定的data段
- (方案2)然后是将bss段的内容清0
- 跳转执行SystemInit(在system_pisces.c中), 这里主要初始化内容如下
- 将协处理寄存器CP11和CP10设为Full access,这两个协处理器是用来访问FPU寄存器的,主要是用来处理浮点数的计算。
- 将异常向量表的地址赋值到异常基址寄存器中
- 配置CACHE
- 跳转执行__start
- Default_Handler: 就是死循环
- 使用宏设置默认的Handler
startup.c: 从汇编代码跳转到c代码,操作系统的初始化
_start:
- 关闭中断
- 初始化中断,就是配置中断的优先级
- 初始化board
- 初始化tick
- 初始化kernel object
- 初始化定时器系统
- 初始化heap
- 初始化scheduler
- 初始化应用
- 初始化定时器线程
- 初始化idle线程
- 启动调度器
cpu上下文切换的实现context_gcc.S
操作的寄存器
-
PRIMASK(Priority Mask Register)
仅第0位有效,置1的时候,就是屏蔽所有优先级可配置的中断 -
LR(Link Register)
LR中保存的返回值
-
Execution Program Status Register
Cortex-m4提供的一些指令
-
LDR(Load Register) and STR(Store Register)
与mov指令的区别是:mov指令不能操作cpu寄存器和内存之间的数据交换。这种情况下只能使用ldr和str指令。
例子:LDR R8, [R10] ; Loads R8 from the address in R10. LDRNE R2, [R5, #960]! ; Loads (conditionally) R2 from a word ; 960 bytes above the address in R5, and ; increments R5 by 960 STR R2, [R9,#const-struc] ; const-struc is an expression evaluating ; to a constant in the range 0-4095. STRH R3, [R4], #4 ; Store R3 as halfword data into address in ; R4, then increment R4 by 4 LDRD R8, R9, [R3, #0x20] ; Load R8 from a word 32 bytes above the ; address in R3, and load R9 from a word 36 ; bytes above the address in R3 STRD R0, R1, [R8], #-16 ; Store R0 to address in R8, and store R1 to ; a word 4 bytes above the address in R8, ; and then decrement R8 by 16.
-
MSR
语法:MSR spec_reg, Rn
-
IT(If-Then condition instruction)
语法:IT{x{y{z}}} cond
x指定了第二条指令的条件
y指定了第三条指令的条件开关
z指定了第四条指令的条件开关
cond指定了第一条指令的条件开关x,y,z可选的值:
- T Then:应用cond设置的条件
- E Else:cond不成立的时候
例子:
/*
* if(R4 == R5)
* {
* R7 = R8 + R9;
* R7 /= 2;
* } else
* {
* R7 = R10 + R11;
* R7 *= 2;
* }
*/
CMP R4, R5
ITTEE EQ
ANDEQ R7, R8, R9
ASREQ R7, R7, #1
ADDNE R7, R10, R11
LSLNE R7, R7, #1
-
Branch instructions
语法:
B label ; BL label BX Rm BLX Rm
所有的这些指令都会产生一个跳转,跳转到label或者是寄存器Rm中指示的地址。除此之外:
- BL和BLX指令会把下一条指令的地址写入到LR寄存器中
- 如果寄存器的第0位为0的话,BX和BLX会触发一个UsageFault
限制:
- 不要对PC寄存器使用BLX指令
- 对于BX和BXL执行,只有当寄存器的第0位为1时才能正常执行,但是真正跳转的地址是将第0位改为0的值。
对外提供的接口
- rt_hw_interrupt_disable
MRS r0, PRIMASK ; 将特殊寄存器RRIMASK寄存器的值写到r0寄存器中
CPSID I ; 将RRIMASK寄存器的第0位置为1
BX LR ;
- rt_hw_interrupt_enable
MSR PRIMASK, r0 ; 将r0寄存器的值写入到特殊寄存器PRIMASK中
BX LR ;
-
rt_hw_context_switch_interrupt(or rt_hw_context_switch)
该函数的c语言形式定义为
void rt_hw_switch_interrupt(from, to)
r0 -> from
r1 -> to
逻辑:
- 判断flag是否为1,是的话,直接跳转到步骤2,否则将flag值置为1,将from的值赋给rt_interrupt_from_thread,然后跳转到步骤2
- 将to的值赋给rt_interrupt_to_thread,执行步骤3
- 将0x10000000写入到INT_CTRL寄存器中,触发PendSV异常,执行步骤4
- BX LR
-
PendSV_Handler
r0 -> switch from thread stack
r1 -> switch to thread stack
psr, pc, lr, r12, r3, r2, r1, r0 are pushed into [from] stack
流程是:- 上下文切换的时候关闭中断
- 获取flag值,如果flag为0,跳转到pendsv_exit,反之执行步骤3
- 将flag值置为0
- 获取rt_interrupt_from_thread的值,如果为0跳转到switch_to_thread,反之执行步骤5
- 获取线程栈指针psp,保存在r1寄存器中
- 寄存器压栈(r4-r11),栈顶的地址保存在r1寄存器中,然后将rt_interrupt_from_thread的值更新为当前r1寄存器的值
- switch_to_thread,加载to_thread的栈指针,保存在r1寄存器中。
- 将to_thread的栈中保存的寄存器状态pop出栈,然后更新r1寄存器的值(根据psp寄存器)
- pendsv_exit,恢复中断使能状态,返回
-
rt_hw_context_switch_to
-
rt_hw_interrupt_thread_switch
-
HardFault_Handler