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

关于栈和堆的一些资料。

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

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

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

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

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

---摘自《AVR单片机C语言开发入门指导》- P169---
    ICCAVR 使用两个堆栈:一个用于子程序调用和中断操作的硬件堆栈,一个用于传递参数、临时变量和局部变量的软件堆栈。可以使用堆栈检测函数检测两个堆栈是否溢出。

 

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

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

#include <avr/io.h>

int add(int a,int b)
{
    int c;
   c=a+b;   
    returnc;
}

int main(void)
{
    inta=2,b=3,c=0;

c=add(a,b);
//c=sub(a,b);
}
汇编代码:

(省略一些boot代码)

。。。。。。。

00000054 <__ctors_end>:
 54:    1124         eor    r1,r1
 56:    1fbe         out    0x3f,r1    ;63   
 58:    cfe5         ldi    r28,0x5F    ;95        //此处Y指针和SP都指到了SRAM最高端
 5a:    d4e0         ldi    r29,0x04    ;4
 5c:    debf         out    0x3e,r29    ;62
 5e:    cdbf         out    0x3d,r28    ; 61

。。。

0000008e <add>:
#include <avr/io.h>

int add(int a,int b)
{
 8e:    cf93         push   r28
 90:    df93         push   r29                  //保存了Y指针,此时SP已经-2,这里再减2
 92:    cdb7         in    r28,0x3d    ;61      //重新定位Y指针跟SP一样。
 94:    deb7         in    r29,0x3e    ;62
 96:    2697         sbiw    r28,0x06    ;6     //减掉6,即向下开了6字节的区域,存放3变量
 98:    0fb6         in    r0,0x3f    ;63
 9a:    f894          cli
 9c:    debf         out    0x3e,r29    ;62
 9e:    0fbe         out    0x3f,r0    ; 63
 a0:    cdbf         out    0x3d,r28    ;61
 a2:    9a83         std    Y+2,r25    ;0x02
 a4:    8983         std    Y+1,r24    ;0x01
 a6:    7c83         std    Y+4,r23    ;0x04
 a8:    6b83         std    Y+3,r22    ;0x03
    int c;
   c=a+b;   
 aa:    2981         ldd    r18,Y+1    ;0x01
 ac:    3a81         ldd    r19,Y+2    ;0x02
 ae:    8b81         ldd    r24,Y+3    ;0x03
 b0:    9c81         ldd    r25,Y+4    ;0x04
 b2:    820f         add    r24,r18
 b4:    931f         adc    r25,r19
 b6:    9e83         std    Y+6,r25    ;0x06
 b8:    8d83         std    Y+5,r24    ;0x05
    returnc;
 ba:    8d81         ldd    r24,Y+5    ;0x05
 bc:    9e81         ldd    r25,Y+6    ;0x06
 be:    2696         adiw    r28,0x06    ;6      //加了6个字节空间,Y指针恢复到减6之前
 c0:    0fb6         in    r0,0x3f    ;63
 c2:    f894          cli
 c4:    debf         out    0x3e,r29    ;62
 c6:    0fbe         out    0x3f,r0    ; 63
 c8:    cdbf         out    0x3d,r28    ;61
 ca:    df91         pop    r29
 cc:    cf91         pop    r28
 ce:    0895         ret                           //弹出堆栈中2个字节

000000d0 <main>:
}

int main(void)
{
 d0:    c9e5         ldi    r28,0x59    ;89   //这4句给SP和Y指针重新赋值了,很明显的在SP的
 d2:    d4e0         ldi    r29,0x04    ;4    //上面还有6个字节(SRAM最大到045E),这6个字节
 d4:    debf         out    0x3e,r29    ;62   //被存放了a,b,c三个变量(可以与上面理论对应)
 d6:    cdbf         out    0x3d,r28    ;61   //通过Y指针来保存了这三个变量到这个区域 
    inta=2,b=3,c=0;
 d8:    82e0         ldi    r24,0x02    ;2
 da:    90e0         ldi    r25,0x00    ;0
 dc:    9a83         std    Y+2,r25    ;0x02
 de:    8983         std    Y+1,r24    ;0x01
 e0:    83e0         ldi    r24,0x03    ;3
 e2:    90e0         ldi    r25,0x00    ;0
 e4:    9c83         std    Y+4,r25    ;0x04
 e6:    8b83         std    Y+3,r24    ;0x03
 e8:    1e82         std    Y+6,r1    ;0x06
 ea:    1d82         std    Y+5,r1    ;0x05

c=add(a,b);                                      
 ec:    6b81         ldd    r22,Y+3    ;0x03
 ee:    7c81         ldd    r23,Y+4    ;0x04
 f0:    8981         ldd    r24,Y+1    ;0x01
 f2:    9a81         ldd    r25,Y+2    ;0x02
 f4:    0e 94 4700    call    0x8e<add>                 //使用call时自动将PC+2的地址压到堆栈
 f8:    9e83         std    Y+6,r25    ;0x06
 fa:    8d83         std    Y+5,r24    ;0x05
//c=sub(a,b);
}
 fc:    80e0         ldi    r24,0x00    ;0
 fe:    90e0         ldi    r25,0x00    ;0
 100:   0c 94 82 00    jmp    0x104<_exit>

00000104 <_exit>:
 104:   ffcf         rjmp   .-2         ; 0x104<_exit>

r28和r29一起组成SP指针,Y指针可以作为间接寻址,很明显的刚开始的时候Y指针和SP都在045F这里,后来在高处开了6个字节的空间来存放临时变量,所以Y指针成了这个软件堆栈的栈顶,在这个过程中都是使用Y和SP的配合来实现变量和数据的改变,以及恢复,硬件堆栈和软件堆栈在这里已经不怎么区分了。。。不清楚流程可以画个图来加深理解,好了,看了那么久,总算有点感觉了。。


栈是可变的,要留足够的空间才行,如果没有操作系统用的会很少,主要取决于函数的嵌套深度参数类型。

一般情况RAM存放三种类型的数据:
1.全局变量
2.堆(典型的MALLOC函数调用),这个得看你用了还是没有
3.栈,这个必然要用到的,有操作系统的话用的就更多了,每一个任务都会有一个栈,根据任务的函数嵌套程度可分配不同的大小。

具体要看什么编译器了,所以首先要估计一下你的栈要用多少,然后,再计算一下你的全局变量有多少,最后定一下可能的动态分配内存(堆)有多少就可以了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值