繁体   English   中英

分支到 PendSV 时覆盖数据

[英]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_tasknext_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 调用调度程序并将事情搞砸。

您观察到的行为可能有多种原因。 一是它并没有真正发生; 跨中断边界的调试很棘手,因为暂停和步进会禁用中断,因此很难跟踪事情发生的顺序。 鉴于所讨论的变量是静态分配的,它不太可能是堆栈损坏,从而缩小了范围。 但是从您提供的代码来看,原因并不明显。

如果可以的话,我想将大部分答案用于解决上下文切换和调度程序中的一些奇怪问题。 也许通过查看这些,您会找到原始问题的答案!

  1. 我建议您从上下文切换中调用调度程序。 这有几个好处:它简化了上下文切换的调用(您只需设置 PENDSV 位,无需先调用调度程序;您不再需要全局next_task变量,因为调度程序直接返回指向下一个任务的指针到上下文切换;您不再需要在上下文切换期间禁用中断(如果 ISR 触发上下文切换而另一个正在进行中,则可能发生的最坏情况是您只会连续获得两个中断);并且您不再需要调用任务进入等待状态时的调度程序(尽管您需要设置 PENDSV 位,最简单的方法是使用 SVC 处理程序)。

    在上下文切换中是这样的:

     LDR r0, =OS_Scheduler BLX r0 /* Pointer to next task is now in r0 */

    显然,您还必须重写您的调度程序,以便它返回一个指向任务结构的指针。

  2. 您保存和加载r4-r11有点奇怪。 为什么使用STMIA和指针算法,而不是STMDBSTMFDPUSH (所有同义词)? 为什么一次推四个寄存器? 已经完成例如

    MRS r1, PSP

    然后你可以简单地写

    STMFD r1!, {r4-r11}

    推动整个批次,与 pop 相同(使用LDMFDPOP )。

  3. 我不清楚您为什么要创建和使用特定的异常返回代码。 正确的代码应该已经在进入 PendSV 处理程序时加载到LR 一个简单的

    BX lr

    是你所需要的全部。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM