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?