我正在尝试在定时器中断处理程序中编写上下文切换。目前,上下文切换能够在命令(协作)上的上下文之间切换。在中断处理程序中,我试图:
我相信我可以正确地完成前两个,但我想知道:如何切换回中断模式,或者至少从中断处理程序修改SVC R13和R15?
我正在使用ARM v6处理器;非常感谢你的帮助!
编辑:这基本上就是我的开关:
void interrupt_yield() {
unsigned int old_mode;
__asm__("mrs %0, cpsr" : "=r" (old_mode));
__asm__("msr cpsr_c, %0" : : "r" (MODE_SVC));
PUSH_ALL; // Macro for push {r0-r12, lr}
__asm__("mov %0, sp" : "=r"(sp));
manager->threads[manager->current_thread].sp = sp;
unsigned nt = (manager->current_thread + 1) % manager->thread_counter;
if (CURRENT_THREAD.status == ACTIVE) {
CURRENT_THREAD.status = INACTIVE;
}
manager->current_thread = nt;
CURRENT_THREAD.status = ACTIVE;
SET_SP(CURRENT_THREAD.sp);
POP_ALL;
__asm__("msr cpsr, %0" : : "r" (old_mode));
}
void timer_vector() { // This is called by assembly in interrupt mode
armtimer_clear_interrupt(); // clear timer interrupt
interrupt_yield(); // Calls above function
}
目标是更改IRQ链接寄存器以返回到新功能。但是,我似乎无法切换回中断模式。
另外1次编辑:我从未真正切换IRQ链接寄存器;我意识到这一点,但我甚至没有切换回IRQ模式,所以这是一个后来修复的问题。
答案 0 :(得分:1)
对于ARMv6,您需要更改模式以获取存储寄存器。您的示例代码已经包含许多必要的详细信息。
#define MODE_IRQ 0x12
#define MODE_SVC 0x13
unsigned int mode; /* original mode */
/* target data... */
unsigned int lr_irq;
unsigned int sp_irq;
unsigned int spsr;
asm (" mrs %0, cpsr\n" /* Save mode. */
" msr cpsr_c,%4 \n" /* to irq mode */
" mov %1, lr\n" /* Get lr_irq */
" mov %2, sp\n" /* Get sp_irq */
" mrs %3, spsr\n" /* Get spsr_irq */
" msr cpsr, %0\n" /* back to old mode */
: "=&r" (mode), "=r"(lr_irq),
"=r"(sp_irq), "=r"(spsr)
: "I" (MODE_IRQ));
gcc会将lr_irq
等分配给通用寄存器(非库存),您可以跨模式传输数据。具有虚拟化扩展的ARMv7具有避免此切换的指令。
您应该知道定时器中断可能在许多情况下发生。至少从IRQ模式检查 spsr 并进行一些调试(断言)验证它是用户模式可能是谨慎的。如果这永远不会触发,并且您认为IRQ只能在用户模式下发生,则可以删除“debug”。
另一种方法是在IRQ处理程序的汇编程序中执行此操作,并将它们传递给r0-r2中的interrupt_yield()
例程。 ARM EABI将参数放在r0-r2中,因此中断产量需要参数。获得此数据后,无需返回IRQ模式。我强烈推荐这种方法用于生产代码。以上内容适用于原型设计。