分析的程序源码:
https://download.youkuaiyun.com/download/wowocpp/10831727
运行环境 :nu-lb-nuc140 keil 5.25
1 系统编译

程序编译完毕之后,查看:
Nu_LB_NUC140_RTX_SRC\project\KEIL\lst\GPIO_OutputInput.map

2 系统上电 复位



单步执行之后,
这个时候 SP = MSP = 0x20000F18
注意 这个时候 LR 的值0x0000 0EE5 ,是 上面的反汇编语句:BL.W os_set_env 设置的

再次 单步执行:
3 MSR PSP,R0

继续单步执行:
4 os_flags的由来
Nu_LB_NUC140_RTX_SRC\project\KEIL\lst\GPIO_OutputInput.map



开始执行 :LDRB R0,[R0]



取得的 R0的值为:0x0000 0000
下一步执行:LSLS R0,#31
5 Z标志位 和 bne 指令

cmp:算数处理指令,用于把一个寄存器的内容和另一个寄存器的内容或立即数进行减法比较,
不存储结果, 但会更改标志位
bne: 数据跳转指令,
not equal 不相等跳转,相减不等于0 ,Z = 0 ,跳转到BNE后标签处
beq: 数据跳转指令,标志寄存器中Z标志位等于1时, 跳转到BEQ后标签处
因为上一步执行结果为 0,Z =1
BNE PrivilegedE ; 不跳转
MOVS R0,#0x03 ; Unprivileged Thread mode, use PSP

6 MSR CONTROL,R0

注意 这个时候 LR 的值0x0000 0EE5 ,是 反汇编语句:BL.W os_set_env 设置的
7 执行BX LR

程序存储器 地址: 0x0000 0EE0中的 BL.W 跳转之后,会保存子函数返回的LR地址:0x0000 0EE5
执行BX LR 之后,跳转到0x0000 0EE4开始执行
8 源码 函数 存放地址



#define __SVC_0 __svc_indirect(0) // 位于 RTL.h中
prio_stksz







#define os_sys_init(init) os_set_env();
_os_sys_init((U32)rt_sys_init,init,0,NULL)
init函数的地址为:0x0000 0DE1 ------F0C
rt_sys_init 函数的地址为: 0x0000 168D — F10
r12 中保存的是rt_sys_init 函数的地址 -----
r0 中保存的是init 函数的地址
r1 中保存的是 prio_stksz的值

r12 中保存的是p 函数的地址 -----
r0 中保存的是task 函数的地址
r1 中保存的是 prio_stksz的值
r2 中应该保存的是 stk的地址
9 执行SVC 0x00

查看 此时的 堆栈 :

执行 SVC 0x00

CPU 的执行模式 变为 handler
Stack 指针 使用的是MSP
PSP 指针 变为 0x2000 0EF8
LR指针 变为 0xFFFF FFFD
这个时候的内存:

10 取得SVC号码 原理:

其中的
LDR R1,[R0,#24] ; Read Saved PC from Stack
是 从栈保存的lr_SVC中获取 函数返回的时候的执行地址。
该地址是下面分析中的 0x2000 0F10 对应的数据:0x0000 0EF0
0x0000 0EF0 是执行 SVC 0 任务,返回之后去执行的下一条指令的地址。
SVC 0 指令和其下一条指令,之间 是间隔2个字节,所以减去2。

init函数的地址为:0x0000 0DE1 ------F0C
rt_sys_init 函数的地址为: 0x0000 168D — F10
r12 中保存的是rt_sys_init 函数的地址 -----
r0 中保存的是init 函数的地址
r1 中保存的是 prio_stksz的值
r2 中应该是 参数 stack = NULL的值
r3 0x0000 0010 这个值是干嘛的?系统启动之后就是这个值
看下面的定义 只用到了 prio_stksz和 stk两个参数
所以r3 空闲了

加2的原因
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203ic/Bgbcjggh.html

__svc_indirect 原型
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.dui0203ic/Bgbcjggh.html

10 取得SVC 号码
执行:
MRS R0,PSP ; Read PSP
LDR R1,[R0,#24] ; Read Saved PC from Stack

SUBS R1,R1,#2 ; Point to SVC Instruction
执行的原因是:

这个2 应该是和流水线有关,因为是16位指令,所以提前多取了2个字节
继续执行:
LDRB R1,[R1] ; Load SVC Number
看上图中的,
0x0000 0EEE DF00 SVC 0x00
其中0x0000 0EEE 是 存放在程序存储器中的地址
0xDF00 是存放在该地址中的内容。
0xDF00 的含义是 汇编代码 SVC 0x00 编译之后的机器码。

LDRB R1,[R1] 就是从程序存储器中取出低字节的数据0x00

11 CMP 指令 与 Z标志位
继续执行:
CMP R1,#0
CMP 指令是 两个参数相减,结果为0 ,Z=1

BNE SVC_User ; User SVC Number > 0
由于 Z = 1, 所以 没有跳转到 SVC_User中去
继续执行:
CMP R1,#0
BNE SVC_User ; User SVC Number > 0
MOV LR,R4

12 LDMIA 指令
继续执行:
LDMIA R0,{R0-R3,R4} ; Read R0-R3,R12 from stack
R0 = 0x2000 0EF8


13 调用 rt_sys_init 函数
执行到:
BLX R12 ; Call SVC Function
之前:

之后:

调用完 还会回来。
NUC140 RTX系统初始化剖析
本文深入分析了NUC140 RTX系统初始化过程,详细解析了从系统编译到上电复位,再到SVC指令执行的每一个关键步骤,包括寄存器状态变化、堆栈使用、指令解析及函数调用流程。
367

被折叠的 条评论
为什么被折叠?



