RSIC-V堆和栈

原文转载,RISC-V MCU堆栈机制 - 知乎先作标记

1、什么是堆栈?

在嵌入式的世界里,堆栈通常指的是,严格来说,堆栈分为堆(Heap)栈(Stack)

  • 栈(Stack): 一种顺序数据结构,满足后进先出(Last-In / First-Out)的原则,由编译器自动分配和释放。使用一级缓存,调用完立即释放。
  • 堆(Heap):类似于链表结构,可对任意位置进行操作,通常由程序员手动分配,使用完需及时释放(free),不然容易造成内存泄漏。使用二级缓存。

2、堆栈的作用

  • 函数调用时,如果函数参数和局部变量很多,寄存器放不下,需要开辟栈空间存储。
  • 中断发生时,栈空间用于存放当前执行程序的现场数据(下一条指令地址、各种缓存数据),以便中断结束后恢复现场。

3、堆栈大小定义

RISC-V MCU的堆栈大小通常在ld链接脚本中定义,关于ld链接脚本可查看该文:RISC-V MCU ld链接脚本说明

ENTRY( _start ) /* 入口地址 */ 
 
__stack_size = 2048; /* 定义栈大小 */
PROVIDE( _stack_size = __stack_size );/* 定义_stack_size符号,类似于全局变量 */
 
MEMORY
{
    FLASH (rx) : ORIGIN = 0x00000000 , LENGTH = 0x10000
    RAM (xrw) : ORIGIN = 0x20000000 , LENGTH = 0x5000
}
/* 
...
中间省略 
...
*/
 
.stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :  /* 分配栈空间0x20004800 ~ 0x20005000,共2KB */
{
    . = ALIGN(4);
    PROVIDE(_susrstack = . );
    . = . + __stack_size;
    PROVIDE( _eusrstack = .);
} >RAM 

以RISC-V MCU CH32V103为例,在其ld链接脚本中,定义了_stack_size符号,值为 2048 Byte,后面使用该值在.stack段中分配栈空间,可更改此值调整栈空间大小。

CH32V103 的RAM共20KB,除去程序用到的databss段,剩下空间即为动态数据段,供堆栈的动态使用。

ld链接脚本中,没有明确定义heap堆的大小,按照其定义,动态数据段,除了stack占用的,剩下的都可用于heap,通过malloc进行动态管理。

4、压栈出栈过程

以CH32V103 printf函数调用为例,其反汇编代码如下:

000007a4 <iprintf>:
     7a4:	7139                	addi  sp,sp,-64         # 调整堆栈指针sp,分配64字节的栈空间
     7a6:	da3e                	sw	a5,52(sp)       # 压栈,保存a5寄存器的值
     7a8:	d22e                	sw	a1,36(sp)       # 压栈,按需保存相应的寄存器
     7aa:	d432                	sw	a2,40(sp)
     7ac:	d636                	sw	a3,44(sp)
     7ae:	d83a                	sw	a4,48(sp)
     7b0:	dc42                	sw	a6,56(sp)
     7b2:	de46                	sw	a7,60(sp)
     7b4:	80818793          	addi	a5,gp,-2040 # 20000078 <_impure_ptr>
     7b8:	cc22                	sw	s0,24(sp)       # 压栈,保存帧指针fp(s0)
     7ba:	4380                	lw	s0,0(a5)
     7bc:	ca26                	sw	s1,20(sp)
     7be:	ce06                	sw	ra,28(sp)       # 压栈,保存返回地址(ra寄存器)
     7c0:	84aa                	mv	s1,a0
     7c2:	c409                	beqz	s0,7cc <iprintf+0x28>
     7c4:	4c1c                	lw	a5,24(s0)
     7c6:	e399                	bnez	a5,7cc <iprintf+0x28>
     7c8:	8522                	mv	a0,s0
     7ca:	2315                	jal	cee <__sinit>
     7cc:	440c                	lw	a1,8(s0)
     7ce:	1054                	addi	a3,sp,36
     7d0:	8626                	mv	a2,s1
     7d2:	8522                	mv	a0,s0
     7d4:	c636                	sw	a3,12(sp)
     7d6:	167000ef          	jal	ra,113c <_vfiprintf_r>
     7da:	40f2                	lw	ra,28(sp)      # 出栈,恢复返回地址(ra寄存器)
     7dc:	4462                	lw	s0,24(sp)      # 出栈,恢复帧指针fp(s0)
     7de:	44d2                	lw	s1,20(sp)      # 出栈,按需恢复相应的寄存器
     7e0:	6121                	addi	sp,sp,64       # 释放栈空间
     7e2:	8082                	ret                    # 函数返回,根据ra寄存器地址返回

5、malloc使用注意事项

CH32V103默认工程中,heap只有起始地址,没有结束地址约束,这样最终会导致malloc永远都不会返回NULL。

如果使用malloc时,需进行如下操作:

  1. 重写_sbrk函数,代码如下,放在工程任意位置,推荐放在debug.c 文件中。
    _sbrk代码原型:https://github.com/riscv/riscv-newlib/blob/riscv-newlib-3.1.0/libgloss/libnosys/sbrk.c
void *_sbrk(ptrdiff_t incr)
{
	extern char _end[];
	extern char _heap_end[];
	static char *curbrk = _end;
 
	if ((curbrk + incr < _end) || (curbrk + incr > _heap_end))
	return NULL - 1;
 
	curbrk += incr;
	return curbrk - incr;
}

2. 修改ld链接脚本,定义heap大小

    • 默认RAM中除去data、bss、stack等剩余的都为heap空间
      增加PROVIDE( _heap_end = . ); 定义,位置如下:
      PROVIDE( _end = _ebss);
      PROVIDE( end = . );  /* 定义heap起始位置 */
    
      .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
        {
             PROVIDE( _heap_end = . );   /* 定义heap结束位置,默认到栈底结束 */ 
        
            . = ALIGN(4);
            PROVIDE(_susrstack = . );
            /*ASSERT ((. > 0x20005000),"ERROR:No room left for the stack");*/
            . = . + __stack_size;
            PROVIDE( _eusrstack = .);
        } >RAM 
    • 指定heap大小的修改方式如下:
      增加 PROVIDE( _heap_end = . + 0x400); 定义,位置如下:
  PROVIDE( _end = _ebss);
  PROVIDE( end = . );  /* 定义heap起始位置 */
  PROVIDE( _heap_end = . + 0x400);   /* 定义heap结束位置,长度为1KB */ 
 
  .stack ORIGIN(RAM) + LENGTH(RAM) - __stack_size :
    {
        . = ALIGN(4);
        PROVIDE(_susrstack = . );
        /*ASSERT ((. > 0x20005000),"ERROR:No room left for the stack");*/
        . = . + __stack_size;
        PROVIDE( _eusrstack = .);
    } >RAM
  • 31
    点赞
  • 35
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
【优质项目推荐】 1、项目代码均经过严格本地测试,运行OK,确保功能稳定后才上传平台。可放心下载并立即投入使用,若遇到任何使用问题,随时欢迎私信反馈与沟通,博主会第一时间回复。 2、项目适用于计算机相关专业(如计科、信息安全、数据科学、人工智能、通信、物联网、自动化、电子信息等)的在校学生、专业教师,或企业员工,小白入门等都适用。 3、该项目不仅具有很高的学习借鉴价值,对于初学者来说,也是入门进阶的绝佳选择;当然也可以直接用于 毕设、课设、期末大作业或项目初期立项演示等。 3、开放创新:如果您有一定基础,且热爱探索钻研,可以在此代码基础上二次开发,进行修改、扩展,创造出属于自己的独特应用。 欢迎下载使用优质资源!欢迎借鉴使用,并欢迎学习交流,共同探索编程的无穷魅力! 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip 基于业务逻辑生成特征变量python实现源码+数据集+超详细注释.zip
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值