Taskwechsel auf Cortext-M3 stürzt nach IRQ ab

Ich habe ein Exokernel-Modell für mein ARM-Betriebssystem verwendet. Wenn eine Aufgabe von einem UART lesen möchte, ruft sie eine Bibliotheksfunktion auf, die, wenn keine Daten vorhanden sind, einen SVC-Aufruf durchführt, um die Aufgabe zu blockieren (was dazu führt, dass der Kernel die Aufgabe in die Warteschlange für diesen IRQ stellt und den IRQ aktiviert ). Wenn der Interrupt auftritt, werden alle darauf wartenden Tasks in die ausführbare Warteschlange verschoben und der Interrupt wird wieder deaktiviert.

Dieses Modell funktionierte gut, als ich eine feste Reihe von Aufgaben hatte, aber jetzt bin ich zu verknüpften Listen übergegangen, um mehr Arten von Warteschlangen zu ermöglichen (z. B. IPC-Nachrichten). Etwas in der Änderung verursacht einen Absturz. Hier ist die Debug-Ausgabe:

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

Also alles gut bis zum Interrupt. Die tatsächliche Ausgabe variiert je nachdem, welcher UART zuerst Daten hat, aber das Muster ist das gleiche: Wenn ein Interrupt auftritt, tritt ein Fehler auf, wenn die nicht blockierte Aufgabe zum zweiten Mal umgeschaltet wird .

Hier sind die relevanten Code-Bits. Eine Montagescheibe:

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

Und die C-Funktionen:

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 und PendSV haben die Priorität 29, 30 bzw. 31.

Irgendwelche Vorschläge? Wo soll ich suchen?

Dies ist wahrscheinlich besser für StackOverflow geeignet. Wissen Sie, ob Ihre DL_XFunktionen/Makros atomar sind? Ich vermute, dass Ihre Linked-List-Implementierung nicht threadsicher ist. Bearbeiten: Wie sieht Ihr Kontextwechselcode aus?
Ich werde es mit einem Post auf SO versuchen, aber der hier vorgeschlagene EE-Chat war besser. Die DL_*Funktionen sind nicht atomar. Sie sind von uclist. Ich habe jedoch versucht, Interrupts um die Listenänderungen herum zu deaktivieren/aktivieren, was keinen Unterschied machte. Der Kontextumschaltcode ist zeptos_pendsv_isrund zeptos_schedule. Im Moment ist es ziemlich einfaches Round-Robin.
@tangrs Diese Frage ist auch hier in Ordnung

Antworten (2)

Ich habe das Problem endlich gefunden . Wenn mein SVC-Handler aufruft block, um eine Aufgabe auf die Sperrliste zu setzen, enthält der Stack dieser Aufgabe nur die von der Hardware gestapelten Register und nicht die, {r4-r11}die der Scheduler erwartet, wenn er sie später erneut ausführt.

Die schnelle Lösung besteht darin, einen Assembly-Shim für den SVC-ISR zu haben, der die zusätzlichen Register stapelt und entstapelt, und die C- zeptos_svc_isrFunktion ebenso einen Stapelzeiger zurückgeben zu lassen zeptos_schedule. Es funktioniert, aber einige Refactoring ist jetzt in Ordnung.

Hier gibt es einiges. Sie haben das Speichern von r12 im Store verpasst.

APCS schreibt auch nicht vor, r0-r3 zu speichern, Sie müssen auch etwas tun, um diese zu speichern.

IIRC, die Ausnahmen haben immer noch getrennte r13 und r14, daher müssen Sie währenddessen möglicherweise auch den normalen Modus r13/r14 speichern/wiederherstellen.

{r0-r3, r12, lr, pc} und PSR sind bereits von der Hardware gestapelt. Es gibt kein separates r14, aber das bewahre ich.