硬件堆栈和软件堆栈

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

硬件堆栈和软件堆栈在AVR中的理解

(2010-02-01 18:43:03)

首先是从理论上的东西。。网上转载来的,后面是看AVR代码时得出的一些东西。

硬件堆栈:或许也可以称作系统堆栈,是位于片内RAM区。有人说,只要能使用PUSH,POP指令的单片机,都可以说含有硬件堆栈。这样的说法我个人觉得不是很全面。通过指令进行压栈和出栈操作只是系统堆栈中的一种操做。系统堆栈还可以被隐含调用。例如,当调用子程序时,系统会主动把返回地址压入堆栈,并不需要用户通过指令操作。通常,栈底设在内存的高端,也就是把内存的最高一段空间划作栈区。这些都是向下生长栈。栈指针可能是专用的寄存器,也可能借用一通用寄存器。也有单片机是在数据区里划一块作栈区,可能是向上生长,也可能是向下生长。

硬件堆栈:是通过寄存器SPH,SPL做为索引指针的地址,是调用了CALL,RCALL等函数调用指令后硬件自动填充的堆栈!

软件堆栈:是编译器为了处理一些参数传递而做的堆栈,会由编译器自动产生和处理,可以通过相应的编译选项对其进行编辑。

简单一点说,硬件堆栈主要做为地址堆栈用,而软件堆栈主要会被分配成数据堆栈!

---摘自《AVR单片机C语言开发入门指导》- P169---

ICCAVR 使用两个堆栈:一个用于子程序调用和中断操作的硬件堆栈,一个用于传递参数、临时变量和局部变量的软件堆栈。可以使用堆栈检测函数检测两个堆栈是否溢出。

单片机在执行调用子程序的指令时,一般会把返回地址自动存入堆栈,而没有被单片机自动入栈但是也需要保存的内容比如状态寄存器、通用寄存器等,就得通过PUSH等指令把它们人为地保存到堆栈中。自动入栈和“人为入栈”可能使用的是一个堆栈指针。有的单片机可以分开,比如AVR,可以通过“ST -Y, R0”这样的指令把R0存入软件堆栈区(Y是由R28和R29两个寄存器的值组成的16位指针),有的单片机缺少这样的指令,就会把软件堆栈和硬件堆栈放在一个栈空间,都使用SP,比如51.

看其栈顶指针是否和CPU具有特殊的关联,有关联者(如SP)“硬”,而无关联者“软”。

如果没有硬堆栈,你可以选定一个寄存器作堆栈指针,通过软件实现堆栈操作。移植μC/OS-II也不一定要硬堆栈。ARM 就很难说它的堆栈是软的还是硬的。32位的ARM指令中没有PUSH、POP指令。ARM习惯上用R13作堆栈指针(SP),但用别的寄存器作堆栈指针也未常不可。ARM习惯上用LDM/STM(多寄存器加载/存储指令)来操作堆栈,压多少,按什么顺序都能选择。应该说ARM是软硬结合的堆栈。

C代码(AVR-GCC编译,优化等级-00):

#include

int add(int a,int b)

{

int c;

c=a+b;

return c;

}

int main(void)

{

int a=2,b=3,c=0;

c=add(a,b);

//c=sub(a,b);

}

汇编代码:

(省略一些boot代码)

。。。。。。。

00000054 <__ctors_end>:

54: 11 24 eor r1, r1

56: 1f be out 0x3f, r1 ; 63

58: cf e5 ldi r28, 0x5F ; 95 //此处Y指针和SP都指到了SRAM最高端

5a: d4 e0 ldi r29, 0x04 ; 4

5c: de bf out 0x3e, r29 ; 62

5e: cd bf out 0x3d, r28 ; 61

。。。

0000008e :

#include

int add(int a,int b)

{

8e: cf 93 push r28

90: df 93 push r29 //保存了Y指针,此时SP已经-2,这里再减2

92: cd b7 in r28, 0x3d ; 61 //重新定位Y指针跟SP一样。

94: de b7 in r29, 0x3e ; 62

96: 26 97 sbiw r28, 0x06 ; 6 //减掉6,即向下开了6字节的区域,存放3变量 98: 0f b6 in r0, 0x3f ; 63

9a: f8 94 cli

9c: de bf out 0x3e, r29 ; 62

9e: 0f be out 0x3f, r0 ; 63

a0: cd bf out 0x3d, r28 ; 61

a2: 9a 83 std Y+2, r25 ; 0x02

a4: 89 83 std Y+1, r24 ; 0x01

a6: 7c 83 std Y+4, r23 ; 0x04

a8: 6b 83 std Y+3, r22 ; 0x03

int c;

c=a+b;

aa: 29 81 ldd r18, Y+1 ; 0x01

b2: 82 0f add r24, r18

b4: 93 1f adc r25, r19

b6: 9e 83 std Y+6, r25 ; 0x06

b8: 8d 83 std Y+5, r24 ; 0x05

return c;

ba: 8d 81 ldd r24, Y+5 ; 0x05

bc: 9e 81 ldd r25, Y+6 ; 0x06

be: 26 96 adiw r28, 0x06 ; 6 //加了6个字节空间,Y指针恢复到减6之前

c0: 0f b6 in r0, 0x3f ; 63

c2: f8 94 cli

c4: de bf out 0x3e, r29 ; 62

c6: 0f be out 0x3f, r0 ; 63

c8: cd bf out 0x3d, r28 ; 61

ca: df 91 pop r29

cc: cf 91 pop r28

ce: 08 95 ret //弹出堆栈中2个字节

000000d0

:

}

int main(void)

{

d0: c9 e5 ldi r28, 0x59 ; 89 //这4句给SP和Y指针重新赋值了,很明显的在SP的 d2: d4 e0 ldi r29, 0x04 ; 4 //上面还有6个字节(SRAM最大到045E),这6个字节 d4: de bf out 0x3e, r29 ; 62 //被存放了a,b,c三个变量(可以与上面理论对应)

d6: cd bf out 0x3d, r28 ; 61 //通过Y指针来保存了这三个变量到这个区域

int a=2,b=3,c=0;

d8: 82 e0 ldi r24, 0x02 ; 2

da: 90 e0 ldi r25, 0x00 ; 0

dc: 9a 83 std Y+2, r25 ; 0x02

de: 89 83 std Y+1, r24 ; 0x01

e0: 83 e0 ldi r24, 0x03 ; 3

e2: 90 e0 ldi r25, 0x00 ; 0

e4: 9c 83 std Y+4, r25 ; 0x04

e6: 8b 83 std Y+3, r24 ; 0x03

e8: 1e 82 std Y+6, r1 ; 0x06

ea: 1d 82 std Y+5, r1 ; 0x05

c=add(a,b);

相关文档
最新文档