【Bootloader学习理解学习--加强版】

笔者在接着聊一下bootloader,主要针对MCU的Bootloader。

笔者之前介绍过一篇Bootloader文章,主要是其概念、一些升级包的格式和升级流程,本次接着来说一下。

1、MCU代码运行方式

之前文章也介绍过,MCU的代码运行方式有两种,

  • XIP方式:(Excute In Place),在位置上执行,即在存储地址上执行代码,如果是这样,则对存储芯片有要求,则要求是支持memory map的方式,把存储空间当做是CPU的memory一样,可以通过总线获取到code指令,然后执行。
    在这里插入图片描述

内部ROM Code会跳转到Code中,然后Flash中的Code就会执行,接着把data段和bss段进行分散加载到对应的ram,然后就可以执行,STM32就是这种方式。

  • RAM方式:一般情况下,MCU Boot会把代码从存储空间搬到RAM上去运行,例如一些SPI Flash或者Nand Flash,无法直接执行指令,需要搬到RAM方式执行,然后再跳到RAM地址执行,RAM地址执行,还有个好处是速度执行更快,相对Flash上面执行。

下图以NXP的LPC54016为例进行展示,Boot将代码从SPI Flash搬到SRAMX,然后再将执行权交给SRAMX,至此代码就在Sramx上面执行,最后同样需要进行分散加载,将bss段和data段搬到Sram0-3上面执行。
在这里插入图片描述
分散加载可以参考笔者本篇文章。

2、NXP单片机运行方式修改

以NXP LPC54016单片机为例,笔者来介绍一下如何从RAM方式切到XIP方式。

2.1 代码启动流程

  1. 首先需要看一下手册,研究一下如何支持XIP方式,MCU的code都是rom code搬出来的,如果要执行XIP方式,则不搬code,所以需要看相关的配置

  2. 开始笔者想着是相关的寄存器设置,可以让ROM Code中的boot不搬代码,但是实际看手册没有找到相关的寄存器信息。

  3. 找到了相关的流程图,通过引脚来决定怎么启动
    在这里插入图片描述

  4. OTP BOOT SRC 引脚被设置,则会从外部flash(Parallel Mmeory)搬数据(512Byte)到SRAM,通过该512Byte来决定在怎么启动(XIP还是RAM方式)

  5. 如果OTP BOOT SRC没有被设置,则有ISP的引脚,来决定进入ISP模式下载数据到SRAM,然后BOOT,还是从外部Flash Boot(搬移)。

  6. 这里有个AUTO BOOT的方式,首先寻找有效的Image(0x0/0x20000000 within internal SRAM (SRAMX or SRAM0) or 0x10000000 for
    SPIFI XIP and 0x80000000 for parallel flash XIP image),如果地址都没找到,则进入ISP模式,等待命令下载代码。
    在这里插入图片描述

  7. 如何确定有效的Image呢,NXP巧妙的利用了中断向量表的信息,来装载NXP特有的Mark,从而确定是否有效的代码。

    1. 中断向量表中,有几个位置是保留,且中断向量表处于代码开始放置,方便确认,可参考笔者之前文章ARM学习(5) 异常模式学习(CortexM3/M4)
    2. 从0x20的位置确定是否是0xEDDC94BD
    3. 从0x24的位置,确定真正NXP的Image Header位置信息,从而跳转过去,找到Image Header决定走XIP还是SRAM等
      在这里插入图片描述
  8. NXP LPC单片机有自己的Image Header,可以设置一些属性,通过链接脚本的方式,包括分散加载的地址,也是链接脚本里面指定。
    在这里插入图片描述

    1. Image Type:选择ram方式还是xip方式
      在这里插入图片描述
    2. Load_adderess:加载地址,SRAMX还是SPIFI地址
    3. CRC check选择:设置是否进行CRC 校验
  9. 经过这些研究,就可以知道XIP的设置和RAM的设置表了,主要关心的还是 Image Type和Loadaddress。
    在这里插入图片描述
    在这里插入图片描述

2.2 链接脚本修改

中断向量表
extern void (* const g_pfnVectors[])(void);
extern void * __Vectors attribute ((alias (“g_pfnVectors”)));
WEAK extern void __imghdr_loadaddress();
WEAK extern void __imghdr_imagetype();

void (* const g_pfnVectors[])(void) = {// Core Level - CM4&_vStackTop,                       // The initial stack pointerResetISR,                          // The reset handler.......,0,                                 // ECRP(void (*)(void))0xEDDC94BD,        // Reserved(void (*)(void))0x160,             // Reserved.....,(void (*)(void))0xFEEDA5A5, // Header Marker,0x160__imghdr_imagetype,         // (0x04) Image Type__imghdr_loadaddress,       // (0x08) Load_address(void (*)(void))(((unsigned)_image_size) - 4),            // (0x0C) load_length, exclude 4 bytes CRC field.

从上面中断向量表中可以看出,NXP LPC单片机在中断向量表中做了手脚,设置了一些参数,和启动方式结合起来,包括还可以知道ImageSize等。

注意观察,其声明是一个虚函数的方式(如果链接脚本里面没指定,也可以链接过),符合中断向量表数组的定义,其实际是链接脚本里面指定的数据。

RAM方式的链接脚本:
memory 布局定义,外部Flash地址,SRAMX,SRAM0-3地址,在链接脚本指定布局时,可以指定其位置。

MEMORY
{/* Define each memory region */BOARD_FLASH(rwx) : ORIGIN = 0x10000000, LENGTH = 0x1000000 /* 16M bytes (alias RAM) */  SRAMX (rwx) : ORIGIN = 0x0, LENGTH = 0x30000 /* 192K bytes (alias RAM) */  SRAM_0_1_2_3 (rwx) : ORIGIN = 0x20000000, LENGTH = 0x28000 /* 160K bytes (alias RAM2) */  USB_RAM (rwx) : ORIGIN = 0x40100000, LENGTH = 0x2000 /* 8K bytes (alias RAM3) */  
}/* Define a symbol for the top of each memory region */__base_SRAMX = 0x0  ; /* SRAMX */  __base_RAM = 0x0 ; /* RAM */  __top_SRAMX = 0x0 + 0x30000 ; /* 192K bytes */  __top_RAM = 0x0 + 0x30000 ; /* 192K bytes */  __base_SRAM_0_1_2_3 = 0x20000000  ; /* SRAM_0_1_2_3 */  __base_RAM2 = 0x20000000 ; /* RAM2 */  __top_SRAM_0_1_2_3 = 0x20000000 + 0x28000 ; /* 160K bytes */  __top_RAM2 = 0x20000000 + 0x28000 ; /* 160K bytes */  __base_USB_RAM = 0x40100000  ; /* USB_RAM */  __base_RAM3 = 0x40100000 ; /* RAM3 */  __top_USB_RAM = 0x40100000 + 0x2000 ; /* 8K bytes */  __top_RAM3 = 0x40100000 + 0x2000 ; /* 8K bytes */  

中断向量表定义,可看到在其前面定义了一些信息,data段的信息,data ram和data ram3都没有用到,我们就不考虑了,
“AT > xxxx”:加载地址
“> xxx”:执行地址
分散加载做的就是将加载地址数据搬到执行地址,如果本身两者地址一样,则无需处理。

     /* MAIN TEXT SECTION */.text : ALIGN(4){FILL(0xff)__vectors_start__ = ABSOLUTE(.) ;KEEP(*(.isr_vector))/* Global Section Table */. = ALIGN(4) ;__section_table_start = .;__data_section_table = .;LONG((LOADADDR(.data_RAM) - LOADADDR(.text)) + __base_SRAMX);LONG(    ADDR(.data_RAM));LONG(  SIZEOF(.data_RAM));LONG((LOADADDR(.data) - LOADADDR(.text)) + __base_SRAMX);LONG(    ADDR(.data));LONG(  SIZEOF(.data));LONG((LOADADDR(.data_RAM3) - LOADADDR(.text)) + __base_SRAMX);LONG(    ADDR(.data_RAM3));LONG(  SIZEOF(.data_RAM3));__data_section_table_end = .;__bss_section_table = .;LONG(    ADDR(.bss_RAM));LONG(  SIZEOF(.bss_RAM));LONG(    ADDR(.bss));LONG(  SIZEOF(.bss));LONG(    ADDR(.bss_RAM3));LONG(  SIZEOF(.bss_RAM3));__bss_section_table_end = .;__section_table_end = . ;/* End of Global Section Table */*(.after_vectors*)} > SRAMX AT> SRAMX

从上面脚本来分析,目前代码段的加载地址就处于SRAMX,所以直接写LOADADDR(.data)也行,看图2,加载地址和图1是一样的,
加载地址其实就是存放的位置,相对代码段的位置,但是却不是执行的地址。
在这里插入图片描述
在这里插入图片描述
代码段的定义,其加载地址和执行地址一样,均是SRAMX,同时一些只读数据和const数据,也存放在代码段。

 .text : ALIGN(4){*(.text*)*(.rodata .rodata.* .constdata .constdata.*). = ALIGN(4);} > SRAMX AT> SRAMX_etext = .;

data段的定义,加载地址和执行地址不一样,前者是执行地址,后者是加载地址,说明其正式执行的时候,在SRAM0-3,加载的时候在SRAMX。

加载地址和存储地址也不是一个概念,存储地址是真正存放代码的位置,程序运行需要再特殊的地方,比如内存,所以肯定需要搬到对应的位置,BOOT做的就是这个事情。搬到对应的位置后,就是真正执行的地址吗,也不竟然,BOOT通常是做很简单的事情,整体都搬过去,包括代码和数据,但是代码和数据往往要分开,所以就有了分散加载。

    /* Main DATA section (SRAM_0_1_2_3) */.data : ALIGN(4){FILL(0xff)_data = . ;PROVIDE(__start_data_RAM2 = .) ;PROVIDE(__start_data_SRAM_0_1_2_3 = .) ;*(vtable)*(.ramfunc*)KEEP(*(CodeQuickAccess))KEEP(*(DataQuickAccess))*(RamFunction)*(.data*). = ALIGN(4) ;_edata = . ;PROVIDE(__end_data_RAM2 = .) ;PROVIDE(__end_data_SRAM_0_1_2_3 = .) ;} > SRAM_0_1_2_3 AT>SRAMX

BSS段又不一样,其不用存储,加载地址和执行地址一样,只要初始化成0就行。

 /* MAIN BSS SECTION */.bss : ALIGN(4){_bss = .;PROVIDE(__start_bss_RAM2 = .) ;PROVIDE(__start_bss_SRAM_0_1_2_3 = .) ;*(.bss*)*(COMMON). = ALIGN(4) ;_ebss = .;PROVIDE(__end_bss_RAM2 = .) ;PROVIDE(__end_bss_SRAM_0_1_2_3 = .) ;PROVIDE(end = .);} > SRAM_0_1_2_3 AT> SRAM_0_1_2_3

堆栈区域,存放于SRAMX区域,堆后面预留了栈的4K空间,如果空间最后不够,链接脚本会报错。
.stack区域指明了栈的起始地址,没预留空间,由上面那个空间预留保证,很巧妙解决了栈递减的空间逻辑。

SECTIONS
{/* Reserve and place Heap within memory map */_HeapSize = 0x1000;.heap :  ALIGN(4){_pvHeapStart = .;. += _HeapSize;. = ALIGN(4);_pvHeapLimit = .;} > SRAMX_StackSize = 0x1000;/* Reserve space in memory for Stack */.heap2stackfill  :{. += _StackSize;} > SRAMX/* Locate actual Stack in memory map */.stack ORIGIN(SRAMX) + LENGTH(SRAMX) - _StackSize - 0:  ALIGN(4){_vStackBase = .;. = ALIGN(4);_vStackTop = . + _StackSize;} > SRAMX

最后这些都是符号,不占用任何空间size,如果程序用到了,直接将对于的数据连接进入,而不是变量进行加载。
如果确实是运行态的数据,就需要占用空间size。

    /* ## Create checksum value (used in startup) ## */PROVIDE(__valid_user_code_checksum = 0 - (_vStackTop + (ResetISR + 1) + (NMI_Handler + 1) + (HardFault_Handler + 1) + (( DEFINED(MemManage_Handler) ? MemManage_Handler : 0 ) + 1)   /* MemManage_Handler may not be defined */+ (( DEFINED(BusFault_Handler) ? BusFault_Handler : 0 ) + 1)     /* BusFault_Handler may not be defined */+ (( DEFINED(UsageFault_Handler) ? UsageFault_Handler : 0 ) + 1) /* UsageFault_Handler may not be defined */) );/* Provide basic symbols giving location and size of main text* block, including initial values of RW data sections. Note that* these will need extending to give a complete picture with* complex images (e.g multiple Flash banks).*/_image_start = LOADADDR(.text);_image_end = LOADADDR(.data) + SIZEOF(.data);_image_size = _image_end - _image_start;/* Provide symbols for LPC540xx parts for startup code to use* to set image to be plain load image or XIP.* Config : Plain load image = true*/__imghdr_loadaddress = ADDR(.text);__imghdr_imagetype = 1;
}

XIP方式链接脚本


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/177735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

水淼采集器-免费水淼采集器下载

在当今数字时代,随着信息的迅猛增长,许多网站管理员面临一个共同而具有挑战性的问题——如何在短时间内获取大量优质内容,以满足用户对信息的不断需求?水淼采集器,作为一个备受瞩目的解决方案,正成为许多人…

错误:FinalShell连接CentOs连接失败

需要说明的是:这个错误不是首次连接发生的,而是多次使用后可能发生的错误 正文: 可能的原因是虚拟机的ip地址发生了变更,原因有以下几点: 最最可能的原因:1.DHCP分配变更: 如果虚拟机使用DHCP来获取IP地址,那么DHCP服务器可能会分配给虚拟机一个新的I…

shell脚本正则表达式

目录 一. 正则表达式定义 二. 基本正则表达式 1. 元字符 2. 表示次数 3. 位置锚定 4. 分组或其他 二. 拓展正则表达式 1. 表示次数 2. 表示分组 一. 正则表达式定义 正则表达式(REGEXP ):由一类特殊字符及文本字符所编写的模式&…

常量指针和指针常量的区别

常量指针 常量指针&#xff0c;本质是指针&#xff0c;这个指针指向的内存块是常量&#xff0c;不能通过这个指针改变指向的内存块的值。 定义&#xff1a; const int *p; #include <iostream> using namespace std;int main(int argc, char *argv[]) {int a 10;int…

Logstash安装部署

目录 一、环境准备 二、安装部署 2.1 下载安装包到指定文件夹&#xff0c;并解压 2.2 复制证书文件 2.3 编辑配置文件 2.4 启动服务 一、环境准备 部署模式&#xff1a;单节点部署。 官网地址&#xff1a;Elasticsearch 平台 — 大规模查找实时答案 | Elastic 注意事项&am…

网站HTTPS证书

作为拥抱数字化时代的重要一环&#xff0c;建设一个安全可靠的网站对于吸引访问者并保持用户信任至关重要。在这方面&#xff0c;HTTPS证书是不可或缺的工具。HTTPS&#xff08;安全套接层传输协议&#xff09;证书是由受信任的第三方CA机构颁发的一种数字证书。它通过加密通信…

①实现基于session的登录流程:发送验证码、登录注册、校验登陆状态

个人简介&#xff1a;Java领域优质创作者&#xff1b;阿里云技术博主、星级博主、专家博主&#xff1b;正在Java学习的路上摸爬滚打&#xff0c;记录学习的过程~ 个人主页&#xff1a;.29.的博客 学习社区&#xff1a;进去逛一逛~ 登录功能 实现 实现基于session的登录流程&…

制作openeuler的livecd

下载该项目&#xff0c;执行下面的操作gitee openeuler livecd项目 基于openeuler环境 #安装工具&#xff0c;第一次可能报错&#xff0c;可以再执行一次 make installx86 livecd-creator -d -v --config./config/euler_x86_64.ks --fslabeleuler-LiveCD --cachecache --log…

HarmonyOS—ArkTS中@Observed和@ObjectLink装饰器的嵌套类对象属性变化【鸿蒙专栏-11】

文章目录 ARKTS中@Observed和@ObjectLink装饰器的嵌套类对象属性变化@Observed 类装饰器说明装饰器参数类装饰器的使用@ObjectLink 变量装饰器说明装饰器参数同步类型允许装饰的变量类型被装饰变量的初始值举例装饰器的限制条件观察变化和行为表现观察的变化框架行为使用场景1.…

docker安装各开发环境

docker安装 安装Docker # 环境安装&#xff1a; yum -y install gcc-c# 第一步&#xff1a;安装必要的一些系统工具 yum install -y yum-utils device-mapper-persistent-data lvm2# 第二步&#xff1a;添加软件源信息 yum-config-manager --add-repo http://mirrors.aliyun.…

没有哈希时间锁定合约的跨链原子交换

在上一篇文章中&#xff0c;我们介绍了使用哈希时间锁定合约&#xff08;HTLC&#xff09;的跨链原子交换实现。 今天&#xff0c;我们介绍一种无需 HTLC 即可实现的替代方法。 这将原子交换扩展到缺乏哈希锁和时间锁的区块链。 使用 SPV 证明交易已被挖掘 让我们按照商定的价…

windows10系统更新失败无法进入系统

用户反馈早上电脑重启&#xff0c;系统在更新卡好久好进去是否更新windows11&#xff0c;选否&#xff0c;重新就反复在更新中无法进入系统。我在测试的过程也是多次更新卡好久无法进入系统&#xff0c;而且出现下面提示 windows10系统更新失败无法进入系统&#xff0c;蓝屏提…

软件工程 课后题 acmakb 总结

让你一口气复习个爽&#xff01; 具体的应用题参考 软件工程学科出现的主要原因是软件危机的出现 20世纪60年代提出了软件工程的概念 软件危机的主要原因是软件本身的特点及开发方法 软件工程的三要素是方法、工具和过程 与计算机硬件相比&#xff0c;计算机软件有哪些特点…

设备管理的方法与思路

阅读本文你将了解设备管理的思路与方法&#xff1a;一、制定全面的管理计划&#xff1b;二、标准化管理流程&#xff1b;三、设备维护与保养&#xff1b;四、风险管理与预防&#xff1b;五、引入数字化工具。 设备管理在生产制造领域是保障生产安全和效率的核心环节。通过引入…

java分布式锁分布式锁

java分布式&锁&分布式锁 锁 锁的作用&#xff1a;有限资源的情况下&#xff0c;控制同一时间段&#xff0c;只有某些线程&#xff08;用户/服务器&#xff09;能访问到资源。 锁在java中的实现&#xff1a; synchronized关键字并发包的类 缺点&#xff1a;只对单个的…

强化学习中的“agent“

在强化学习中&#xff0c;"agent"&#xff08;智能体&#xff09;是指一个在环境中执行动作以达到某个目标的实体。强化学习是一种机器学习范式&#xff0c;其中智能体通过与环境的交互来学习最优的行为策略&#xff0c;以最大化累积的奖励信号。 以下是强化学习中 …

【Flutter】graphic图表的快速上手

简介 graphic是一个数据可视化语法和Flutter图表库。 官方github示例 网上可用资源很少,只有作者的几篇文章,并且没有特别详细的文档,使用的话还是需要一定的时间去调研,在此简单记录。 示例 以折线图为例(因为我只用到了折线图,但其他的图大差不差) 创建一个两个文…

邦芒宝典:职场中最重要的16种竞争力

​​职场竞争力包括哪几个方面&#xff0c;职场竞争力,简单地说,就是在职场中与他人相比&#xff0c;你所具备的优势及长处。以下小邦精心为大家准备了职场竞争力包括哪几个方面&#xff0c;快来一起看看吧。 ​1、技能和知识&#xff1a;具备相关的技能和知识&#xff0c;尤其…

世微 舞台灯深度调光 大功率 dc-dc降压恒流驱动IC APS54083

产品描述 APS54083 是一款 PWM 工作模式,高效率、外围简单、外置功率 MOS 管&#xff0c;适用于 5-220V 输入高精度降压 LED 恒流驱动芯片。输出最大功率150W最大电流 6A。APS54083 可实现线性调光和 PWM 调光&#xff0c;线性调光脚有效电压范围 0.5-2.5V.PWM 调光频率范围 10…

C++ :静态成员

静态成员 静态成员就是在成员变量和成员函数前加上关键字 static &#xff0c;称为静态成员 静态成员分为&#xff1a; 静态成员变量 1.所有对象共享同一份数据 2.在编译阶段分配内存 3.类内声明&#xff0c;类外初始化 静态成员函数 1.所有对象共享同一个函数 2.静态成…