我为atmega328编写了一个非常简单的中断服务程序(ISR),并使用avrgcc(使用-Os)使用AVR studio进行编译。
ISR (TIMER0_OVF_vect) {
txofcnt++; //count overflows and store in uint16_t
}
如果你注意到生成的程序集(如下),它使用r24,r25来获取增加易失性uint16_t txofcnt的作业,但它也是push-write-pop r1,r28,r29而没有读取它们。它还有一个额外的r0推/弹,而不会在它们之间使用它。
我没理解为什么r1被推动,清理然后最终成功。 但是为什么gcc觉得需要将EIMSK和GPIOR0加载到寄存器中然后不使用它们。如果你能告诉我GPIOR0的用途是什么,那么数据表说它存在但没有描述。
00000258 <__vector_16>:
ISR (TIMER0_OVF_vect) {
258: 1f 92 push r1
25a: 0f 92 push r0
25c: 00 90 5f 00 lds r0, 0x005F
260: 0f 92 push r0
262: 11 24 eor r1, r1
264: 8f 93 push r24
266: 9f 93 push r25
268: cf 93 push r28
26a: df 93 push r29
26c: cd b7 in r28, 0x3d ; 61 reads register EIMSK
26e: de b7 in r29, 0x3e ; 62 reads register GPIOR0
txofcnt++;
270: 80 91 0a 01 lds r24, 0x010A
274: 90 91 0b 01 lds r25, 0x010B
278: 01 96 adiw r24, 0x01 ; 1
27a: 90 93 0b 01 sts 0x010B, r25
27e: 80 93 0a 01 sts 0x010A, r24
}
282: df 91 pop r29
284: cf 91 pop r28
286: 9f 91 pop r25
288: 8f 91 pop r24
28a: 0f 90 pop r0
28c: 00 92 5f 00 sts 0x005F, r0
290: 0f 90 pop r0
292: 1f 90 pop r1
294: 18 95 reti
答案 0 :(得分:3)
请记住,ISR 中断当前在CPU上执行的任何代码。他们必须小心保存当前CPU状态(序言)并在退出(结尾)时恢复它,以便被中断的代码可以正常继续。
在AVR上,有几个寄存器不需要在正常的功能序言/结尾中保存/恢复,但确实需要由ISR保存:
这解释了为什么保存r0和r1。 GPIOR0显然只是一个函数可能正在使用的另一个通用寄存器,因此它也需要保存。如果优化器可以判断某些寄存器实际上没有被ISR修改,那么可能会优化其中一些,但优化器可能不那么聪明,或者您可能需要尝试更高的优化级别。
答案 1 :(得分:3)
有一些关于GCC在https://gcc.gnu.org/wiki/avr-gcc
的AVR注册用法的文档与您的问题相关的一些段落:
固定注册
固定寄存器是GCC不会分配的寄存器 注册分配器。寄存器R0和R1是固定的并且是隐式使用的 打印出汇编程序指令时:
R0
用作临时寄存器,使用后无需恢复。它必须在中断服务程序中保存和恢复 (ISR)序幕和结语。在内联汇编程序中,您可以使用
__tmp_reg__
用于临时寄存器。R1
始终包含零。在insn期间,内容可能被破坏,例如通过使用R0 / R1作为隐式的MUL指令 输出寄存器。如果insn销毁R1,那么insn必须将R1恢复为 之后为零。该寄存器必须保存在ISR序言中并且必须保存 然后设置为零,因为R1可能包含非零值。 ISR结局恢复了价值。在内联汇编程序中,您可以使用 零寄存器
__zero_reg__
。...
使用呼叫的注册
呼叫使用或呼叫破坏的通用寄存器(GPR)是 可能被函数调用破坏(破坏)的寄存器。
R18-R27,R30,R31
这些GPR被称为破坏。普通功能可以在不恢复内容的情况下使用它们。中断服务程序(ISR)必须 保存并恢复他们使用的每个寄存器。
...
呼叫保存的注册
R2-R17,R28,R29
其余的GPR被呼叫保存,即使用这种寄存器的功能必须恢复其原始内容。即使寄存器用于传递函数参数,这也适用。
以下是我对编译器为什么在ISR序言/结语中执行一些显然不必要的寄存器保存/恢复的猜测:
r0
和r1
,因为编译器生成或调用的代码将在上面列出关于它们的假设。由于它们没有被GCC的注册分配器跟踪,序言必须确保它们被保存(并且在r1
的情况下被初始化为0)。
r28
和r29
用于保存堆栈指针(0x3d
/ SPL
和0x3e
/ SPH
)。我猜测(并且我想强调这是猜测)编译器编写者认为中断处理程序交换堆栈可能很常见,这可以确保ISR可以恢复正在使用的堆栈。中断发生了。编译器可以假设这些寄存器不会被被调用的函数改变,因为它们是“调用保存”寄存器。
另外,你应该注意到显然是“额外”的推动&amp; pop r0
用于将SREG
状态寄存器保存在堆栈中。即使在r0
和push
指令之间没有使用pop
,请记住r0
寄存器是暂存寄存器,寄存器分配器不会跟踪它,所以编译器不会认为r0
在加载SREG
后不会发生变化。
作为旁注,0x3d
和0x3e
的读取是SPL
和SPH
堆栈指针寄存器,而不是EIMSK
和{{1寄存器。有关使用GPIOR0
/ IN
指令而不是加载或存储指令时寄存器寻址方式有何不同的详细信息,请参见reference manual here中的寄存器汇总表的注释4。 / p>
关于OUT
的奖励积分:
8.5.1通用I / O寄存器
ATmega48A / PA / 88A / PA / 168A / PA / 328 / P包含三个通用目的 I / O寄存器。这些寄存器可用于存储任何寄存器 信息,它们对于存储全局特别有用 变量和状态标志。通用I / O寄存器 地址范围0x00 - 0x1F可以使用SBI直接进行位访问, CBI,SBIS和SBIC指令。