[英]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.