关于freeRtos中操作pxCurrentTCB部分汇编指令的理解
__asm void vPortSVCHandler( void )
{
/* *INDENT-OFF* */
PRESERVE8
ldr r3, = pxCurrentTCB /* Restore the context. */
ldr r1, [ r3 ] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
ldmia r0 !, { r4 - r11 } /* Pop the registers that are not automatically saved on exception entry and the critical nesting count. */
msr psp, r0 /* Restore the task stack pointer. */
isb
mov r0, # 0
msr basepri, r0
orr r14, # 0xd
bx r14
/* *INDENT-ON* */
}
一直对于这些指令莫名的脑袋浆糊,后来用stm32中freertos的demo代码中keil5中做简单的调试,对此感觉有了一点理解,做一下记录,代码还是要动手操练。
ldr r3, = pxCurrentTCB /* Restore the context. */
pxCurrentTCB 是个全局的结构体指针变量,这个指令的意思是取 指针变量的地址赋值给 r3
在c语言中,定义一个指针变量,直接对这个指针变量赋值操作,实际编译成汇编文件怎么处理的也不懂,但是在汇编语言中,ldr指令获取的是这个变量的链接地址。
ldr r1, [ r3 ] /* Use pxCurrentTCBConst to get the pxCurrentTCB address. */
获取pxCurrentTCB的值,比如定义一个int *p, a=100,这条指令意思相当于p=&a
ldr r0, [ r1 ] /* The first item in pxCurrentTCB is the task top of stack. */
pxCurrentTCB 是一个结构体指针,r0的值就是获取pxCurrentTCB指向的结构体的第一个元素,即栈顶指针pxTopOfStack,相当于获取*p的值,对于一个结构体变量就是第一项的值。
struct Test{
int a;
int b;
int c;
}
struct Test test;
struct Test *ptest=NULL;
void fun(void)
{
ptest = &test;
test.a = 1;
test b = 2;
test c = 3;
}
ldr r3, = pxCurrentTCB /* */
ldr r1, [ r3 ] /* 上面一步取的是pxCurrentTCB指针变量的地址,这两步相于 ptest = &test; */
ldr r0, [ r1 ] /* 结构体变量中第一个元素的值,即test.a 的值 */
xPortPendSVHandler中的任务切换的实现
__asm void xPortPendSVHandler( void )
{
extern uxCriticalNesting;
extern pxCurrentTCB;
extern vTaskSwitchContext;
/* *INDENT-OFF* */
PRESERVE8
mrs r0, psp
isb
ldr r3, =pxCurrentTCB /* 获取pxCurrentTCB变量的指针地址 */
ldr r2, [ r3 ] /* 获取当前正在运行的task的tcb指针 */
stmdb r0 !, { r4 - r11 } /* 保存现场寄存器压栈 */
str r0, [ r2 ] /* 保持当前栈的位置到tcb结构体的第一项中 即pxTopOfStack */
stmdb sp !, { r3, r14 } /* 下面有函数调用,防止r3,r14被更改,先保存到栈中 */
mov r0, #configMAX_SYSCALL_INTERRUPT_PRIORITY
msr basepri, r0 /* 屏蔽中断 */
dsb /* 数据同步 */
isb /* 指令中断 */
bl vTaskSwitchContext /* 进入此函数中查找需要切换到的就绪状态的最高优先级任务 ,然后更新pxCurrentTCB的值 */
mov r0, #0
msr basepri, r0 /* 开中断 */
ldmia sp !, { r3, r14 } /* 恢复r3,r14的值,r3保存的pxCurrentTCB的地址 */
ldr r1, [ r3 ] /* 获取pxCurrentTCB的值,在vTaskSwitchContext中pxCurrentTCB可能已经被赋予新的值,下面将进入一个新的任务的运行空间 */
ldr r0, [ r1 ] /* 获取新任务的栈的位置 */
ldmia r0 !, { r4 - r11 } /* 恢复现场,恢复寄存器的值 */
msr psp, r0 /* 把当前任务栈的指针赋值给psp寄存器 */
isb
bx r14 /* 硬件会自动把PC指针出堆栈(因为此时psp为新任务的堆栈顶指针所以出堆栈也是新任务的寄存器) */
nop
/* *INDENT-ON* */
}