[英]Data overriden when branch to PendSV
我有一個手臂皮質 m4 的小“os”。 我實現了一個等待功能。 但從那以后不知何故,上下文切換被破壞了。 在逐步執行我注意到的指令時,無論出於何種原因, current_task
變量都會在進入 PendSV 中斷時被覆蓋。
這些是全局變量
volatile struct OS_task * current_task;
volatile struct OS_task * next_task;
以下類型:
struct OS_task{
volatile unsigned int *sp;
void (*handler)(void * params);
void * params;
volatile enum task_state state;
volatile unsigned char number;
volatile unsigned int delay;
};
這是調度程序功能。 它也是從 Systick 中斷調用的。
void OS_Scheduler(void)
{
current_task = &OS_tasktable.task_list[OS_tasktable.current_task];
current_task->state = OS_TASK_STATE_IDLE;
int next = OS_GetNextTask(OS_tasktable.current_task);
while (1)
{
if (OS_tasktable.task_list[next].delay == 0)
break;
OS_tasktable.task_list[next].delay--;
next = OS_GetNextTask(next);
}
OS_tasktable.current_task = next;
next_task = &OS_tasktable.task_list[OS_tasktable.current_task];
next_task->state = OS_TASK_STATE_ACTIVE;
S32_SCB->ICSR |= S32_SCB_ICSR_PENDSVSET_MASK;
}
這是 PendSV 處理程序。 雖然我正在研究 Cortex-M4F,但我不會僅僅因為我不需要浮點運算就保存 FPU 寄存器。
.syntax unified
.thumb
.global PendSV_Handler
.type PendSV_Handler, %function
PendSV_Handler:
/* Disable interrupts: */
cpsid i
/* Save registers R4-R11 (32 bytes) onto current PSP (process stack
pointer) and make the PSP point to the last stacked register (R8).*/
mrs r0, psp
subs r0, #16
stmia r0!,{r4-r7}
mov r4, r8
mov r5, r9
mov r6, r10
mov r7, r11
subs r0, #32
stmia r0!,{r4-r7}
subs r0, #16
/* Save current task's SP: */
ldr r2, =current_task
ldr r1, [r2]
str r0, [r1]
/* Load next task's SP: */
ldr r2, =next_task
ldr r1, [r2]
ldr r0, [r1]
/* Load registers R4-R11 (32 bytes) from the new PSP and make the PSP
point to the end of the exception stack frame. */
ldmia r0!,{r4-r7}
mov r8, r4
mov r9, r5
mov r10, r6
mov r11, r7
ldmia r0!,{r4-r7}
msr psp, r0
/* EXC_RETURN - Thread mode with PSP: */
ldr r0, =0xFFFFFFFD
/* Enable interrupts: */
cpsie i
bx r0
.size PendSV_Handler, .-PendSV_Handler
這是等待功能。 它通過 SVC 中斷調用。 執行此操作后, current_task
和next_task
變量將被正確設置。 只有在進入以下 PendSV 中斷時,才會以某種方式覆蓋current_task
。 這導致兩個任務都設置為相同的堆棧-> 不好。
void __os_wait_ms(unsigned int ms)
{
struct OS_task * current;
current = &OS_tasktable.task_list[OS_tasktable.current_task];
current->delay = ms * OS_tasktable.delay_factor;
OS_Scheduler();
return;
}
如果有幫助:准確地說,我使用 NXP 的 S32K146EVB。
編輯:我在等待函數執行期間禁用中斷,以避免 Systick 調用調度程序並將事情搞砸。
您觀察到的行為可能有多種原因。 一是它並沒有真正發生; 跨中斷邊界的調試很棘手,因為暫停和步進會禁用中斷,因此很難跟蹤事情發生的順序。 鑒於所討論的變量是靜態分配的,它不太可能是堆棧損壞,從而縮小了范圍。 但是從您提供的代碼來看,原因並不明顯。
如果可以的話,我想將大部分答案用於解決上下文切換和調度程序中的一些奇怪問題。 也許通過查看這些,您會找到原始問題的答案!
我建議您從上下文切換中調用調度程序。 這有幾個好處:它簡化了上下文切換的調用(您只需設置 PENDSV 位,無需先調用調度程序;您不再需要全局next_task
變量,因為調度程序直接返回指向下一個任務的指針到上下文切換;您不再需要在上下文切換期間禁用中斷(如果 ISR 觸發上下文切換而另一個正在進行中,則可能發生的最壞情況是您只會連續獲得兩個中斷);並且您不再需要調用任務進入等待狀態時的調度程序(盡管您需要設置 PENDSV 位,最簡單的方法是使用 SVC 處理程序)。
在上下文切換中是這樣的:
LDR r0, =OS_Scheduler BLX r0 /* Pointer to next task is now in r0 */
顯然,您還必須重寫您的調度程序,以便它返回一個指向任務結構的指針。
您保存和加載r4-r11
有點奇怪。 為什么使用STMIA
和指針算法,而不是STMDB
或STMFD
或PUSH
(所有同義詞)? 為什么一次推四個寄存器? 已經完成例如
MRS r1, PSP
然后你可以簡單地寫
STMFD r1!, {r4-r11}
推動整個批次,與 pop 相同(使用LDMFD
或POP
)。
我不清楚您為什么要創建和使用特定的異常返回代碼。 正確的代碼應該已經在進入 PendSV 處理程序時加載到LR
。 一個簡單的
BX lr
是你所需要的全部。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.