ARMv7-A 移植 FreeRTOS 上文保存和下文恢复
上文保存
上文保存其实就是将当前模式的 CPSR,PC,R14(LR), R12, R11…R0,保存在栈上。
.macro portSAVE_CONTEXT
/* Save current mode SPSR and LR onto the system mode stack before switching to system mode to save the remaining system mode registers. */
SRSDB sp!, #Mode_SYS
CPS #Mode_SYS
PUSH {R0-R12, R14}
/* Save the stack pointer in the TCB. */
LDR R0, =pxCurrentTCB
LDR R1, [R0]
STR SP, [R1]
.endm
- SRSDB sp!, #Mode_SYS
- 执行这条语句的时候是
SVC模式,也就是SWI中断中,所以下面这两个寄存器会保存- CPSR_svc - 异常进入前的状态,这里也就是 SYS 模式下的 CPSR
- LR_svc - 异常返回地址
- 所以这条语句执行完成之后
sys_sp -4中存的是 sys_cpsrssys_spp -8中存的是 sys_lr
- 执行这条语句的时候是
- cps #Mode_SYS
- 切换到 sys 模式
- PUSH {R0-R12, R14}
- 将 sys 模式下的通用寄存器保存在栈上
- LDR R0, =pxCurrentTCB
- 将全局变量 pxCurrentTCB 的地址放到 R0 寄存器,相当于 R0 = &pxCurrentTCB
- LDR R1, [R0]
- 将 R0 指向的内存地址做载入,[R0] 表示读取内存中地址为 R0 的字(word)所以 R1 = pxCurrentTCB
- STR SP, [R1]
- 将 SP 存入到 R1 所指向的地址,等价于 *(uint32_t *)R1 = SP,由于 pxCurrentTCB 首地址放的是 pxTopOfStack,也就是将当前的 SP 指针存入到 pxTopOfStack。
上文保存完成之后 pxCurrentTCB->pxTopOfStack 指向 sys 模式的栈顶,保存的是 R0 寄存器的值。
下文恢复
找到当前任务的 TCB 之后,pxCurrentTCB->pxTopOfStack 指向 sys 模式的栈顶,保存的是 R0 寄存器的值。
.macro portRESTORE_CONTEXT
/* Set the SP to point to the stack of the task being restored. */
LDR R0, =pxCurrentTCB
LDR R1, [R0]
LDR SP, [R1]
/* Restore all system mode registers other than the SP (which is already being used). */
POP {R0-R12, R14}
/* Return to the task code, loading CPSR on the way. */
RFEIA sp! /* CPSR = 0x1f, System mode, ARM mode, IRQ enabled, FIQ enabled. */
.endm
- LDR R0, =pxCurrentTCB
- 将全局变量 pxCurrentTCB 的地址放到 R0 寄存器,相当于 R0 = &pxCurrentTCB
- LDR R1, [R0]
- 将 R0 指向的内存地址做载入,[R0] 表示读取内存中地址为 R0 的字(word)所以 R1 = pxCurrentTCB
- LDR SP, [R1]
- 把 R1 指向结构体(TCB)开头的字读取出来并写入堆栈指针 SP,等价于 SP = *(uint32_t *)R1,这样做的原因是 TCB 的第一个成员保存的是任务栈顶指针,所以此时 SP 执行当前任务栈顶。
- POP {R0-R12, R14}
- 将栈中的数据依次恢复到 R0-R12, R14
RFEIA sp! - 从 sp 地址处读取两个字(PC 和 CPSR),写入 PC 和 CPSR(恢复到异常前的状态),sp 向上移动(因为是 IA 模式,所以每次取完之后自增)。
- 将栈中的数据依次恢复到 R0-R12, R14
上文保存与下文恢复栈示意图
从前面的上文保存和下文恢复可以总结出
- 上文保存:将 sys 模式下的,CPSR, PC, LR(R14), R12, R11, R10, R9, R8, R7, R6, R5, R4, R3, R2, R1, R0 寄存器依次保存在 sys 模式的栈上,并更新
pxCurrentTCB->pxTopOfStack的值 。 - 下文恢复:找到
pxCurrentTCB->pxTopOfStack的值,依次恢复 R0, R1, R2, R3, R4, R5, R6, R7, R8, R9, R10, R11, R12, LR(R14),PC, CPSR
栈的分布如下图
← 高地址
┌───────────────────────────────┐
│ 0x1F │ ← 当前模式的 CPSR 值
│ (System mode, ARM, IRQ/FIQ │
│ enabled) │
├───────────────────────────────┤
│ current mode return address │ ← PC
├───────────────────────────────┤
│ portTASK_RETURN_ADDRESS │ ← LR (R14)
├───────────────────────────────┤
│ 0x12121212 │ ← R12
├───────────────────────────────┤
│ 0x11111111 │ ← R11
├───────────────────────────────┤
│ 0x10101010 │ ← R10
├───────────────────────────────┤
│ 0x09090909 │ ← R9
├───────────────────────────────┤
│ 0x08080808 │ ← R8
├───────────────────────────────┤
│ 0x07070707 │ ← R7
├───────────────────────────────┤
│ 0x06060606 │ ← R6
├───────────────────────────────┤
│ 0x05050505 │ ← R5
├───────────────────────────────┤
│ 0x04040404 │ ← R4
├───────────────────────────────┤
│ 0x03030303 │ ← R3
├───────────────────────────────┤
│ 0x02020202 │ ← R2
├───────────────────────────────┤
│ 0x01010101 │ ← R1
├───────────────────────────────┤
│ pvParameters │ ← R0( pxCurrentTCB -> pxTopOfStack )
└───────────────────────────────┘
← 低地址
portYIELD 适配
#define portYIELD() __asm volatile("SWI 0" ::: "memory");
portYIELD 的适配其实就是触发一次 SWI 异常
在 SWI 中断中有如下处理
FreeRTOS_SWI_Handler:
portSAVE_CONTEXT
/* Ensure bit2 of the stack pointer is clear. */
MOV r2, sp
AND r2, r2, #4
SUB sp, sp, r2
LDR R0, =vTaskSwitchContext
BLX R0
portRESTORE_CONTEXT
- portSAVE_CONTEXT 保存
sys模式的寄存器任务 - 栈对齐处理
- 找到就绪的任务,更新
pxCurrentTCB的值 - portRESTORE_CONTEXT 恢复
sys模式的寄存器,跳转到就绪任务
portYIELD 测试
新建两个优先级相同的任务
unsigned char task1_param[] = "my log1 task";
#define TASK1_STACK_DEPTH 256
#define TASK1_PRIORITY 1
TaskHandle_t task1_handle;
__attribute__((aligned(8))) StackType_t task1_stack[TASK1_STACK_DEPTH]; // portBYTE_ALIGNMENT
StaticTask_t task1_tcb;
unsigned char task2_param[] = "my log2 task";
#define TASK2_STACK_DEPTH 256
#define TASK2_PRIORITY 1
TaskHandle_t task2_handle;
__attribute__((aligned(8))) StackType_t task2_stack[TASK2_STACK_DEPTH]; // portBYTE_ALIGNMENT
StaticTask_t task2_tcb;
void log1_task(void *param)
{
(void)param;
printf("log1 task start\r\n");
printf("log1 task start\r\n");
printf("log1 task start\r\n");
unsigned int task_cnt = 0;
while (1) {
task_cnt++;
printf("log1 task running %d \r\n", task_cnt);
taskYIELD_WITHIN_API();
// vTaskDelay(5);
}
}
void log2_task(void *param)
{
(void)param;
printf("log2 task start\r\n");
printf("log2 task start\r\n");
printf("log2 task start\r\n");
unsigned int task_cnt = 0;
while (1) {
task_cnt++;
printf("log2 task running %d \r\n", task_cnt);
taskYIELD_WITHIN_API();
// vTaskDelay(5);
}
}
task1_handle = xTaskCreateStatic((TaskFunction_t)log1_task,
(char *)"log1_task",
(uint32_t)TASK1_STACK_DEPTH,
(void *)task1_param,
(UBaseType_t)TASK1_PRIORITY,
(StackType_t *) task1_stack,
(StaticTask_t *) &task1_tcb);
if (task1_handle == NULL) {
printf("create log1 task failed!\r\n");
while (1)
;
}
task2_handle = xTaskCreateStatic((TaskFunction_t)log2_task,
(char *)"log2_task",
(uint32_t)TASK2_STACK_DEPTH,
(void *)task2_param,
(UBaseType_t)TASK2_PRIORITY,
(StackType_t *) task2_stack,
(StaticTask_t *) &task2_tcb);
if (task2_handle == NULL) {
printf("create log2 task failed!\r\n");
while (1)
;
}
/* Start the scheduler. */
vTaskStartScheduler();
printf("never reach here!\r\n");
任务运行日志
log2 task running 19523
log1 task running 19523
log2 task running 19524
log1 task running 19524
log2 task running 19525
log1 task running 19525
log2 task running 19526
log1 task running 19526
log2 task running 19527
log1 task running 19527
log2 task running 19528
log1 task running 19528
log2 task running 19529
log1 task running 19529
log2 task running 19530
从 log 可以看到 task1 和 task2 交替运行。
浙公网安备 33010602011771号