上回说到,ARM的thumb 指令集下关中断比较复杂,我们细细的讨论一下:
由于arm规定,thumb指令集操纵不了cpsr寄存器,所以必须要由thumb指令集切换到arm指令集上。
切换代码,由一段gcc 嵌入式的汇编代码完成。
这是从代码中截取出来的关键的切换代码,我用红色字做一下注释:
#define str(x) #x /*将x展开成字符串,str(abc),宏展开后为"abc", str(1234),宏展开后为"1234" */
#define xstr(x) str(x)
#define L(x) #x "_" xstr(__LINE__) /*该宏展开后与宏展开的位置有关系,如第255行写着宏 L(enable),这个宏展开后是:"enable_255"*/
/*该宏写着切换到ARM状态,虽然不好看,但安全*/
/*
* Switch to ARM mode Veneer,ugly but safe
*/
#define TO_ARM_MODE(x) /
asm volatile ( /
".code 16 /n" /
L(x) "_thumb: /n" /
".align 2 /n" /
"push {lr} /n" /
"adr %0, "L(x) "_arm /n" /
"bl " L(x)" /n" /
"pop {pc} /n" /
".balign 4 /n" /
L(x) ": /n" /
"bx %0 /n" /
"nop /n" /
".pool /n" /
".code 32 /n" /
L(x) "_arm: /n" /
: "=&r" (reg))
/*
该宏的确是不好看,假设代码的第100行写着 TO_ARM_MODE(enable),那么这个宏展开后是:
.code 16
enable_100_thumb:
.align 2
push {lr}
adr %0, enable_100_arm
bl enable_100 //注意这里是带返回的调用,是arm指令返回点
pop {pc}
.balign 4
enable_100:
bx %0 //注意这里是由thumb切换到ARM代码的跳转
nop
.pool
.code 32
enable_100_arm:
(其中 %0,是编译器指定的一个任意寄存器)
*/
结合上面展开的汇编,我们理解一下这个 Thumb 下的开中断函数。
void _CPU_ISR_Enable_Thumb(int _level )
{
int reg=0;
TO_ARM_MODE(enable);
asm volatile(
".code 32 /n"
"ENABLE_ARM: /n"
"MSR CPSR, %0 /n"
/* Return back to thumb.*/
"BX R14 /n"
".code 16 /n"
: : "r"(_level)
);
}
/* 这段嵌入式汇编:
asm volatile(
".code 32 /n"
"ENABLE_ARM: /n"
"MSR CPSR, %0 /n"
/* Return back to thumb.*/
"BX R14 /n"
".code 16 /n"
: : "r"(_level)
);
其中,MSR CPSR, %0,只是将 _level 输入到 cpsr 中。BX R14回到了arm指令返回点,即上面展开的pop {pc}的位置
并由 arm指令集切换到 thumb 指令集. .code 16 使接下来生成的代码可以又是 thumb指令集的。
*/
同理,也不难理解 闪开中断和 关闭中断。
void _CPU_ISR_Flash_Thumb(int _level )
{
int reg=0;
TO_ARM_MODE(flash);
asm volatile(
".code 32 /n"
"FLASH_ARM: /n"
"MRS %0, CPSR /n"
"BIC %0, %0, #0xC0 /n"
/* enable the irq*/
"MSR CPSR_c, %0 /n"
"ORR %0, %0, #0xc0 /n"
"MSR CPSR_c, %0 /n"
"BX R14 /n"
".code 16 /n"
:"=&r"(reg) : "r" (_level)
);
}
这里要特别注意几个问题:
1. ARM7、ARM9、Xscale使用这个代码都是没有问题的,xscale 实际上不需要使用veneer这段代码进行切换,它有一个新指令BLX(Branch and Link with Exchang)来实现状态切换。这条指令完成完成的任务是:在跳转时将返回的指令地址保存在LR寄存器中,同时将PC中的最低位的值拷贝到CPSR寄存器中的T位,从而改变处理器状态(Exchange)。一般来说,对于调用函数使用BLX指令即可,被调用函数则与V4T架构相同,也是使用BX指令来返回。
2.cortex-M3使用这个代码不行,因为Cortex-M3只支持thumb2指令集,整个汇编代码均不能使用,需要重新构建;
可使用CPSID/CPSIE指令完成。
3.由于ARM指令集向下兼容,arm指令集下的关中断,只要在支持ARM指令集的机器上使用都可以。