英文:
Usage of __attribute((interrupt)) of arm-none-eabi-gcc for exception handlers
问题
在STM32H753(cortex M7)上使用arm-none-eabi-gcc进行裸机开发时,有一个编译选项__attribute((interrupt))
专用于IRQ处理程序。
通过一些测试,我确认唯一的区别是堆栈在8字节上对齐(即在函数将内容推送到堆栈之前,堆栈指针地址的最后3位被清零)。
我不理解的是:
-
为什么在线程模式和处理程序模式下堆栈对齐不同?
-
为什么STMicro HAL和我找到的其他示例代码中没有使用这个属性?
英文:
Context is: bare-metal development on STM32H753 (cortex M7) with arm-none-eabi-gcc
There is a compilation option __attribute((interrupt))
dedicated to IRQ handlers:
Doing some test, I confirmed the only difference is that the stack is aligned on 8 bytes (ie. the 3 least signficant bits of the stack pointer adress are zeroed before the function pushes on the stack).
What I don't understand is:
- why is the stack alignement different in thread mode and in handler mode ?
- why is this attribute not used in STMicro HAL and other sample code I've found ?
答案1
得分: 4
在Cortex-M中,“interrupt”属性没有任何区别。Cortex-M是这样构建的,中断处理程序只是常规的C函数,不需要像其他一些体系结构那样的特殊函数序言/尾声。因此,您根本不需要使用这个属性,HAL也不使用它。
ARMv7-M建议始终保持栈的8字节(2个字,64位)对齐,但不强制执行。如果您一次推送或弹出1个字,那么它将完全正常工作。尽管如此,这是建议。因此,如果您在汇编中编写一段代码,将一次推送/弹出一组寄存器被认为是一种良好的做法,但不是严格强制的,老实说,我从未遇到过任何情况,它在任何方面都有所影响。文档中实际上没有禁止它。作为一种纯粹的猜测,这可能是因为内部的AHB总线宽度为64位,但我对它在那个级别下的工作了解得太少。
当您处于线程模式并发生中断时,Cortex-M会自动堆叠R0-R3、R12、LR、PC(下一条指令的PC)和xPSR,无需在代码中添加任何指令来执行此操作。这正是为什么您不需要“interrupt”属性的原因,以及为什么Cortex-M中断处理程序是基本的C函数 - 自动堆叠的寄存器基本上与常规C代码线程中的调用者保存寄存器相同。除了在硬件中自动发生的堆叠/取消堆叠。因此,在进入中断处理程序时,所有调用者保存的寄存器已经保存在堆栈上,如果您正在使用专用线程堆栈指针,那么它将在中断中切换到主堆栈指针。如果在中断发生时,您的线程(或其他将被中断的中断)的堆栈是4字节对齐而不是8字节对齐,自动堆栈机制将在堆栈上多推送一个额外的虚拟寄存器,并在取消堆栈时将其丢弃。再次强调,不需要用户操作。
英文:
In Cortex-M the "interrupt" attribute doesn't make any difference. Cortex-M is built in such a way that interrupt handlers are just regular C functions, and don't require any special function prologue/epilogue like some other architectures do. Therefore, you don't need to use this attribute at all, and HAL doesn't use it.
ARMv7-M recommends to keep stack 8-byte (2 word, 64-bit) aligned at all times, but it doesn't force it. If you push or pop just 1 word at a time, it will work perfectly ok. Nevertheless, such is the recommendation. So if you write a piece in assembly, it's considered a good practice to push/pop an even number of registers at a time, but it's not strictly forced, and to be honest I've never had a situation where it would matter in any way at all. Nothing in the docs actually prohibits it. As a pure speculation, it could be due to internal AHB bus being 64-bit wide, but I know too little about how it works down on that level.
When you're in thread mode, and an interrupt occurs, Cortex-M automatically stacks R0-R3, R12, LR, PC (of the next instr.) and xPSR without any instructions in the code to do so. Which is exactly why you don't need an "interrupt" attribute, and why Cortex-M interrupt handlers are basic C functions - the registers automatically stacked are basically the same as caller-saved registers in regular C-code thread. Except that stacking/unstacking happens automatically in hardware. So by the time you enter interrupt handler, you have all caller-saved registers already saved on stack, and if you were using dedicated thread stack pointer, then it will switch to main stack pointer in the interrupts. If at the moment of interrupt your thread (or other interrupt that will be interrupted) had stack 4-byte aligned and not 8, the automatic stacking mechanism will push one extra dummy register on stack, and it will be thrown out when unstacking. Again, no user action required.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论