Переключение задач на Cortext-M3 падает после IRQ
Я использовал модель exokernel для моей ОС ARM Cortex-M3. Когда задача хочет прочитать из UART, она вызывает библиотечную функцию, которая, если нет данных, делает SVC-вызов, чтобы заблокировать задачу (что заставляет ядро поместить задачу в очередь ожидания для этого IRQ и включает IRQ.). Когда происходит прерывание, все задачи, ожидающие его, перемещаются в рабочую очередь, и прерывание снова отключается.
Эта модель работала нормально, когда у меня был фиксированный массив задач, но теперь я перешел в связанные списки, чтобы учесть больше типов очереди ожидания (например, сообщения IPC). Что-то в изменении вызывает сбой. Вот отладочный вывод:
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: Usage 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
Так что все хорошо до перерыва. Фактический выходной сигнал изменяется в зависимости от того, какой из UART имеет данные первыми, но шаблон тот же: когда происходит прерывание, возникает ошибка, когда разблокированная задача переключается на второй раз.
Вот соответствующие биты кода. Монтажная прокладка:
zeptos_pendsv_isr:
push {lr}
mrs r0, psp
stmfd r0!, {r4-r11}
bl zeptos_schedule
ldmfd r0!, {r4-r11}
msr psp, r0
pop {pc}
И функции 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 и PendSV имеют приоритет 29, 30 и 31 соответственно.
Ошибка является ошибкой использования типа INVPC, что означает, что используется неправильный тип значения EXC_RETURN. Я проверил, и это 0xfffffffd каждый раз.
Какие-либо предложения? Где я должен искать?
2 ответа
Я нашел проблему, наконец. Когда мой обработчик SVC вызывает block
чтобы поместить задачу в список заблокированных, стек этой задачи содержит только регистры, составленные аппаратным обеспечением, а не {r4-r11}
что планировщик ожидает, что это произойдет, когда он снова запустит его позже.
Быстрое решение состоит в том, чтобы иметь сборочную прокладку для SVC ISR, которая укладывает и распаковывает дополнительные регистры, и иметь C zeptos_svc_isr
функция возвращает указатель стека как zeptos_schedule
делает. Это работает, но какой-то рефакторинг сейчас в порядке.
Как сохраняется состояние процессора задачи? Если я правильно помню, вам нужно сохранить CPSR при переключении. Возможно, вам придется сказать, измените его на:
mrs r12, epsr
stmfd r0!, { r4 - r11, r12 }
...
ldmfd r0!, { r4 - r11, r12 }
msr r12, epsr
EPSR будет содержать такие вещи, как флаги состояния процессора и любую информацию о состоянии блока IT.