He utilizado un modelo de exokernel para mi sistema operativo ARM. Cuando una tarea quiere leer de una UART llama a una función de la biblioteca, que, si no hay datos, hace una llamada SVC para bloquear la tarea (lo que hace que el kernel ponga la tarea en la cola de espera para esa IRQ y habilita la IRQ). Cuando se produce la interrupción, todas las tareas que la esperan se mueven a la cola de ejecutables y la interrupción se desactiva de nuevo.
Este modelo funcionaba bien cuando tenía una matriz fija de tareas, pero ahora he pasado a listas enlazadas para permitir más tipos de colas de espera (por ejemplo, mensajes IPC). Algo en el cambio está causando una caída. 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 va bien hasta la interrupción. La salida real varía dependiendo de qué UART tenga datos primero, pero el patrón es el mismo: cuando ocurre una interrupción, se produce un fallo cuando la tarea desbloqueada pasa a la segunda vez .
Aquí están las partes relevantes del código. Una cuña 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 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 buscar?