El cambio de tareas en Cortext-M3 se bloquea después de IRQ

3

He utilizado un modelo de exokernel para mi sistema operativo ARM. Cuando una tarea quiere leer desde un UART, llama a una función de biblioteca, la cual, si no hay datos, realiza una llamada SVC para bloquear la tarea (lo que hace que el núcleo ponga la tarea en la cola de espera para esa IRQ y habilite la IRQ). ). Cuando ocurre la interrupción, todas las tareas en espera se mueven a la cola ejecutable y la interrupción se desactiva nuevamente.

Este modelo funcionaba bien cuando tenía un conjunto fijo de tareas, pero ahora me he movido a listas vinculadas para permitir más tipos de cola de espera (por ejemplo, mensajes de IPC). Algo en el cambio está causando un choque. Aquí está la salida de depuración:

Creating task 0 (idle task)
task0 stack top is 2007cd20
Starting SysTick @ 100Hz
Becoming task 0
Switching to task gsm@2007c008 with SP 2007c3e8
GSM task starting
Switching to task rfid@2007c430 with SP 2007c810
Monitoring RFID reader
Blocking task rfid on IRQ 7
Switching to task gps@2007c858 with SP 2007cc38
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gsm@2007c008 with SP 2007c390
Blocking task gsm on IRQ 8
Switching to task gps@2007c858 with SP 2007cc38
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gps@2007c858 with SP 2007cc38
Starting GPS tracking
Blocking task gps on IRQ 6
Switching to task task0@2007cc80 with SP 2007ccd8
[... repeats...]
Switching to task task0@2007cc80 with SP 2007ccd8
Unblocking tasks waiting on IRQ 8
Switching to task gsm@2007c008 with SP 2007c3a0
Switching to task task0@2007cc80 with SP 2007ccd8
Switching to task gsm@2007c008 with SP 2007c3a0
Fault: �� �
   r0 = 2007c3a0
   r1 = 10007fb8
   r2 = 2007ccd8
   r3 = 10007fb8
  r12 = 00000008
   lr = fffffffd
   pc = 0070c858
  psr = 00000003
 BFAR = e000ed38
 CFSR = 00040000
 DFSR = 00000000
 AFSR = 00000000
SHCSR = 00070008

Así que todo está bien hasta la interrupción. La salida real varía dependiendo de qué UART tiene datos primero, pero el patrón es el mismo: cuando ocurre una interrupción, ocurre una falla cuando la tarea desbloqueada se cambia a la segunda vez .

Aquí están los bits relevantes de código. Un shim de montaje:

zeptos_pendsv_isr:
    push {lr}
    mrs r0, psp
    stmfd r0!, {r4-r11}
    bl zeptos_schedule
    ldmfd r0!, {r4-r11}
    msr psp, r0
    pop {pc}

Y las funciones de C:

static void pendsv(void) {
    SCB->ICSR |= 1 << 28;
}

void *zeptos_schedule(void *sp) {
    if (current_task) {
        current_task->sp = sp;
        DL_APPEND(runnable_tasks, current_task);
    }
    current_task = runnable_tasks;
    DL_DELETE(runnable_tasks, current_task);
    zeptos_printf("Switching to task %s@%p with SP %p\n", current_task->name, current_task, current_task->sp);
    return current_task->sp;
}

static void block(void *sp, uint8_t irq) {
    zeptos_printf("Blocking task %s on IRQ %i\n", current_task->name, irq);
    current_task->sp = sp;
    DL_APPEND(irq_blocked_tasks[irq], current_task);
    current_task = 0;
    NVIC_EnableIRQ(irq);
    pendsv();
}

void __attribute__((interrupt)) zeptos_isr(void) {
    int irq = (SCB->ICSR & 0xff) - 16;
    zeptos_printf("Unblocking tasks waiting on IRQ %i\n", irq);
    NVIC_DisableIRQ(irq);
    // NVIC_ClearPendingIRQ(irq);
    DL_CONCAT(runnable_tasks, irq_blocked_tasks[irq]);
    irq_blocked_tasks[irq] = 0;
    pendsv();
}

void __attribute__((interrupt)) zeptos_svc_isr(void) {
    __disable_irq();
    uint32_t *sp = (uint32_t *) __get_PSP();
    uint32_t pc = sp[6];
    uint8_t svc_type = *((uint8_t *) pc - 2);
    switch (svc_type) {
        case 0:
            sleep(sp[0]);
            break;

        case 1:
            block(sp, sp[0]);
            break;

        default:
            zeptos_puts("Bad SVC type\n");
    }
    __enable_irq();
}

void Zeptos_BlockOnIrq(uint8_t irq) {
    asm("svc 1");
}

SVC, SysTick y PendSV tienen prioridad 29, 30 y 31 respectivamente.

¿Alguna sugerencia? ¿Dónde debería estar buscando?

    
pregunta Dan Ellis

2 respuestas

1

Encontré el problema, finalmente . Cuando mi controlador SVC llama a block para poner una tarea en la lista bloqueada, la pila de esa tarea solo tiene los registros apilados por el hardware, y no el {r4-r11} que el programador espera que haya cuando vuelva a ejecutarse.

La solución rápida es tener un shim de ensamblaje para el ISR de SVC que apila y desapila los registros adicionales, y que la función C zeptos_svc_isr devuelva un puntero de pila como zeptos_schedule . Funciona, pero ahora hay algo de refactorización.

    
respondido por el Dan Ellis
0

Hay algunas cosas aquí. Te has perdido el guardado de r12 en la tienda.

APCS tampoco obliga a guardar r0-r3, también tendrá que hacer algo para guardarlos.

IIRC, las excepciones aún tienen r13 y r14 separados, por lo que es posible que deba almacenar / restaurar el modo normal r13 / r14 también durante este proceso.

    
respondido por el user34572

Lea otras preguntas en las etiquetas