【问题标题】:Data overriden when branch to PendSV分支到 PendSV 时覆盖数据
【发布时间】:2023-03-13 03:55:01
【问题描述】:

我有一个手臂皮质 m4 的小“操作系统”。我实现了一个等待功能。但从那以后,不知何故,上下文切换被破坏了。在逐步执行我注意到的指令时,无论出于何种原因,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 调用调度程序并将事情搞砸。

【问题讨论】:

    标签: c arm cortex-m


    【解决方案1】:

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

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

    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
      

      就是你所需要的。

    【讨论】:

    • 感谢您的回复。虽然我怀疑这是调试过程中的影响,但我会相应地更改代码,看看它是否有帮助。
    • 如果您想了解更多细节,很高兴通过编辑我的回答来提供更多帮助。
    猜你喜欢
    • 2015-02-08
    • 2018-05-16
    • 2012-09-03
    • 1970-01-01
    • 2016-11-23
    • 1970-01-01
    • 2021-10-01
    • 2019-08-04
    • 1970-01-01
    相关资源
    最近更新 更多