cstartup.s与boot.link的学习

1、MCU上电后的启动流程

  1. 芯片上电
  2. 硬件依次以0x0、0x40000和0x80000为基地址并偏移到对应的0x20位置检查字符标记(“TLNK”,如果0x0位置没找到,则往后两个地址寻找,如果找到,则以该基地址为ram的启动基址)
  3. 直接从flash的启动基地址位置(步骤2中确定的)读取代码(flash - 1MB,0x20000000 ~ 0x20100000;以基址 + 偏移地址形式进行访问)
  4. 初始化 I / D-Cache(8KB大小;当 I / D-Cache初始化完成时,后续的代码运行则会转移至 ram 下进行;Note:大部分数据和代码不是直接从flash读到ram执行的,一般会先读到 I / D-Cache中,然后再根据实际读入ram中使用)
  5. 将所需各段信息(如retention_restet,aes_data,retention_data,ram_code)搬到 ram 中,以及做一些清理、初始化操作(如设置.bss、.sbss段初始值为0等)
  6. 将main函数指针压入t0寄存器
  7. 程序执行

放一张ram和flash的对应图相信各位就懂了(b91芯片的flash是1MB大小;ram为256KB大小,分为 I-RAM 和 D-RAM 两块,各占128KB,地址分别为0x00000000 ~ 0x00020000 和 0x00080000 ~ 0x000a0000,其中,I-RAM能够存放指令和数据,D-RAM只存放数据);从flash中搬移到ram的段有retention_reset、retention_data、ram_code:在这里插入图片描述

2、cstartup.s与boot.link的作用

在芯片的启动中,主要涉及cstartup.s与boot.link两个文件,简单的去看的话,就是,boot.link文件主要是为cstartup.s文件提供包括如各个段的起始地址、标识符等信息的(两个文件的具体交互过程我就不懂了,涉及到编译器的链接规则啥的,且每家编译器的链接规则都有区别),而cstartup.s则是程序启动前要执行的代码。另外,由于telink的b91芯片是基于risc-v架构搭建的,因此,启动代码中的一些指令与ram的还是有些区别的。

3、执行代码

3.1 boot.link
我们首先来看看boot.link里的内容长啥样:
在这里插入图片描述
图中,ENTRY(_RESET_ENTRY)用于指定程序的入口点,即程序开始执行的位置。另外,从图中可以明显看到,.link文件的整个操作都是在SECTIONS下操作的。

对于.link文件,我觉得主要弄清楚以下三点就没问题了:

  1. LMA和VMA是什么?
  2. 几个关键指令的作用,如图中的 ‘ . ’, ’ .retention_reset : AT( ALIGN(LOADADDR (.vectors) + SIZEOF (.vectors),8)) ’ 和 ’ (NOLOAD) ’
  3. .link中分配好的LMA和VMA具体在 .elk中是怎么体现的,如何计算?

第一点:LMA和VMA是什么?
LMA:Load Memory Address,加载地址,是指一个指令在内存中的加载地址,即指令的内容是从哪个地址加载进内存的。
VMA:Virtual Memory Address,虚拟地址,是实际代码执行时所对应的地址位置。

一般来说,LMA与VMA相同,但是存在一些情况是不同的,比如说LMA所处地址是只读属性,即代码无法在该地址下直接执行,因此,需要将LMA位置的代码 / 数据读到其他位置(VMA)使用。

第二点:几个主要指令如何理解?
对于图中红框的几个指令:

  1. ’ . ‘ 按我理解的话就是当前LMA与VMA的所处位置(地址值),如图中所示:. = 0x20000000; 即将LMA与VMA同时设置为 0x20000000;
  2. 第二个红框的两句指令需要合起来看,即先同时设置当前LMA与VMA为0x00000000,之后,针对.retention_reset段,将其LMA设置为.vectors段的LMA + .vectors段的长度(LOADADDR (.vectors) + SIZEOF (.vectors)),即对于.retention_reset段的内容,是从对应LMA下加载过来的。最后再对LMA的地址值以8字节进行对齐(ALIGN(xxx, 8));
  3. 第三个红框也比较重要,通常,对于.bss等段的内容是直接清0操作的,也就是说该段一般是不占信息量的。但是,当你在编写.link文件时,如果不加上NOLOAD声明,则实际编译出的对应段还是有长度的,也即占了位置,有信息量表示了。为此,通常会加上NOLOAD声明,告诉编译器该段不占信息量。

第三点:如何计算LMA和VMA?

首先我们来看看 .elk中分配的各个段的LMA和VMA是怎样的:
在这里插入图片描述
其中,size表示为对应段的大小,即len;
结合上下两张图可以看到,在加上NOLOAD声明后,对应的.aes_data段的LMA是没有增长的,也即说明了该指令的作用。

就举一个例子就好了,具体其他的计算就自己结合上下图进行计算了。另外,多注意字节对齐的问题,且不是全部位置都设置了字节对齐。

	. = 0x00000000;
	.retention_reset	: AT( ALIGN(LOADADDR (.vectors) + SIZEOF (.vectors),8)) 
	  	{ KEEP(*(.retention_reset )) }

首先,设置VMA和LMA的地址为0x00000000,然后,设置.retention_reset段的LMA地址为(0x20000000 + 0x18a = 2000 018A),又因为是8字节对齐,A大于8了,进位对齐,即LMA = 2000 0190。

3.2 cstartup.s
cstartup.s文件的话主要是一些启动指令代码,和其他的差不太多,这里只列出几个关键地方:

1、由于在进行bootloader阶段的时候,软件的初始化还没完成,即各种io口其实还不能使用,但是有时候为了调试 / 测试deep_retention / suspend等模式下的启动时间、启动顺序等,则需要一个高低电平指示,表示程序运行到相应位置,因此,这时候就引入了下图中的硬件io口来生成电平变化。
请添加图片描述
图中代码中 ‘ li t2, 0x10 ’ 是设置端口输出高电平,设为0x00时则是输出低电平。

2、另外,有时候栈空间是有富裕的,为了更加充分使用,可以通过下图的指令代码来计算栈的剩余空间。
具体操作即为:对于没有使用到的栈空间位置使用0x55555去填充,有数据的位置则不操作,以此不断循环填充,直达栈空间填满,然后通过手动计算即可知栈的剩余空间大小。
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值