C语言 使用' __irq '指定的函数指针声明做什么?



我必须为一个项目做一点嵌入式编程,并通过查看其他一些项目来学习。我找到了下面声明向量表的代码:

typedef void (*const vect_t)(void) __irq;
vect_t vector_table[]
__attribute__ ((section("vectors"))) = {
(vect_t) (RAM_BASE + RAM_SIZE),
(vect_t) Reset_Handler,
// ...
};

reset处理程序声明如下:

void Reset_Handler(void) {
// ... no interesting
}

我读了__irq和ARM编译器文档说明如下:

编译器生成适合的函数入口和退出序列当此属性存在时,在中断处理程序中使用。

我猜vect_t应该是一个指向void函数的指针,不带参数,适合用作中断处理程序。这对我来说似乎很奇怪,因为__irq应该只是实现的编译器提示,而不是有助于函数类型的东西(如参数或返回类型)。

我的假设是__irq应该在Reset_Handler(以及所有其他中断处理程序)上使用,而不是在类型定义中使用。这是正确的吗?


请注意,我不是在问__irq做什么。我知道这不是C标准的一部分,它是一个ARM编译器扩展。我也明白,使用它时产生的代码取决于CPU架构。

一般来说,中断服务例程(ISR)使用不同的返回指令。普通函数只使用"从子程序返回";根据调用约定弹出堆栈的指令。然而,isr不是由程序调用,而是由硬件调用,因此它们通常具有不同的调用约定。为了正确地生成这些特殊的指令,你需要一些非标准的中断语法。

代码是一个中断向量表,所以类型定义是正确的。但是,如果将ISR声明为没有任何特殊关键字void Reset_Handler(void)的普通函数,则这将不起作用。这里不正确的转换(vect_t) Reset_Handler将确保该函数在中断时被调用,但它不会从该函数正确返回-可能崩溃。

我的假设是__irq应该在Reset_Handler(以及所有其他中断处理程序)上使用,而不是在类型定义中使用。这是正确的吗?

它应该在vector表和ISR函数定义中都有

以gcc为例(属性/指令/pragmas等是特定于某个工具而不是C语言)

struct interrupt_frame;
__attribute__ ((interrupt))
void x (struct interrupt_frame *frame)
{
}
void y ( void )
{
}

使用通用的aarch32类型的arm目标:

Disassembly of section .text:
00000000 <x>:
0:   e25ef004    subs    pc, lr, #4
00000004 <y>:
4:   e12fff1e    bx  lr

现在让我们进一步复杂化

struct interrupt_frame;
unsigned int k;
__attribute__ ((interrupt))
void x (struct interrupt_frame *frame)
{
k=5;
}
void y ( void )
{
k=5;
}
00000000 <x>:
0:   e92d000c    push    {r2, r3}
4:   e3a02005    mov r2, #5
8:   e59f3008    ldr r3, [pc, #8]    ; 18 <x+0x18>
c:   e5832000    str r2, [r3]
10:   e8bd000c    pop {r2, r3}
14:   e25ef004    subs    pc, lr, #4
0000001c <y>:
1c:   e3a02005    mov r2, #5
20:   e59f3004    ldr r3, [pc, #4]    ; 2c <y+0x10>
24:   e5832000    str r2, [r3]
28:   e12fff1e    bx  lr

对于中断,你需要在中断中保留所有寄存器,对于常规函数,调用约定规定了函数中哪些寄存器是易失的。因此,通过这个例子,你可以看到指令的主要原因,保存状态并使用中断指令的特定返回。

因为cortex-m架构(armv6-m, 7-m和8-m)被设计成可以直接将C函数放在向量表中,而不需要在它们周围包装任何asm(硬件负责保留状态和特殊返回问题)。编译器以同样的方式生成代码,基本上属性对目标没有影响:

00000000 <x>:
0:   2205        movs    r2, #5
2:   4b01        ldr r3, [pc, #4]    ; (8 <x+0x8>)
4:   601a        str r2, [r3, #0]
6:   4770        bx  lr
0000000c <y>:
c:   2205        movs    r2, #5
e:   4b01        ldr r3, [pc, #4]    ; (14 <y+0x8>)
10:   601a        str r2, [r3, #0]
12:   4770        bx  lr

最后要注意的是,您不会从重置向量返回,因此没有理由让text -m为重置向量费心使用这样的属性/指令。如果它真的是一个裸机向量表,那么你不应该从重置向量返回任何架构(与使用相同的方案用于位于操作系统上的一般应用程序条目相比,非裸机)(或者一个调用此代码的引导加载程序,你当然可以返回)。

其他架构不倾向于将重置放在"中断"列表中。或";exceptions"重置就是重置,ARM文档和代码倾向于将它们视为任何其他异常,因此您必须仍然以不同的方式考虑它。

最新更新