KEIL MDK 分散加载的结构
1、我们先来解剖一只麻雀
很多人会说我做项目时没用过分散加载啊,可能有些人甚至都不知道它的存在。事实上,开发环境会默认生成一个分散加载文件(或者叫链接器描述文件),你使用的可能就是这个默认的分散加载文件,先来看一下Keil默认生成的分散加载文件,使用LPC54608随便找了一个示例代码用Keil生成了一个,如下图所示:
这个分散加载是keil自己生成的分散加载,可以说是最简单的分散加载,简单到RO、RW、ZI基本都是随意摆放的;里面红色字体的是各个段的起始地址以及大小,这些段的起始地址以及大小是由下图的红框框定的部分决定的,你可以把上图以及下面这张图合起来看,地址是一致的。
这个分散加载文件的基本意思如下图:
这里面Flash以及SRAM的地址以及大小都是可以修改的,其他的也可以修改,但起始地址以及大小都要在芯片真实存在的有效物理地址上,这部分需要参看芯片的用户手册里面的Memory MAP一节的内容,如下图就是LPC54608的Memory MAP,参看分散加载里面的地址,Flash以及SRAM的起始地址以及大小都落在有效的空间上了。如果你定义的Flash起始地址在0x40000000,那么肯定会出错,因为与AHB总线地址冲突了。
{
EXEC_ROM_1 0x0000 【运行域描述】这段是要告诉链接器,你的程序在哪里执行,在ARM Cortex-M系列的绝大多
{ 数MCU中加载域以及运行域是在同一个空间上的,即片内Flash。
program1.o (+RO) 【输入节描述】就是告诉链接器,具体把哪一个以及怎么把这一个obj文件放到运行域里面
}
DRAM 0x18000 0x8000 【运行域描述】这里指的是RAM空间的运行域,下面会解释为什么这里会有两个运行域
{
program1.o (+RW,+ZI) 【输入节描述】告诉链接器,去哪里找执行程序是需要使用的变量以及数据
}
}
{
EXEC_ROM_2 0x4000 【另一个运行域描述】
{
program2.o (+RO) 【另一个输入节】
}
SRAM 0x8000 0x8000 【另一个运行域描述】
{
program2.o (+RW,+ZI) 【另一个输入节】
}
}
你可能会问,这个分散加载文件是用什么语言编写的?是一种脚本语言,不是C语言也不是汇编,所以分散加载不能调试,它是一种叫做BNF的东东,官方文档里面把这东西描述的极其神秘,反正我是没太看懂这个所谓的BNF到底是个啥东东(据说是一个符号推导语言之类的一般存在于各种专业论文里面用于装B的东西),但是经过多个工程的磨练,小编我也基本弄明白了这东西该怎么用了,至于理论层面我没有深究,这个东东貌似做分散加载的描述脚本有些大材小用了,它能做的事情远不止于此,当然其他的跟我们没关系啦。
LR_IROM1就是【加载域】,指定了用户程序存储在0x00000000起始大小为0x80000的地址上,用户程序从这个区域内加载;
ER_IROM1就是【运行域】,是RO的运行域,因为LPC54608内部的Flash可以运行代码,所以用户代码可以从这个区域内运行;
RW_IRAM1就是【运行域】,是RW+ZI的运行域,这里指向的是片内SRAM的地址。
这样一解构,整个分散加载的基本结构就比较清晰了,下面我们再来看一个复杂一点的,同样的LPC54608的工程的分散加载
这个大家仔细看下这个结构,基本上上面所说的3个域结构的扩展。这里有几点不同的独特地方,我们解释下:
1、USB单独搞了两个运行域出来是干嘛的?
是这样的,USB由于要大量的数据吞吐,单独开辟一块SRAM作为数据BUFFER,所以你看到加载域2和加载域3的地址是在片内SRAM上,你可以对照一下上面的LPC5460的Memory MAP就会发现这两块地址是在片内SRAM上的。
2、分散加载还可以指定堆栈????
没错,确实可以,上面这个例子就在分散加载里面指定了堆栈其中:
ARM_LIB_HEAP域ARM_LIB_STACK是两个编译器之前就定义好的标号(Symbol),用于处理堆栈分配。当然堆栈也可以在启动代码里面指定分配,这是两种不同的方式,上面这个例子是在分散加载中指定堆栈。
关于分散加载,我们要讲的远不止于此,接下来,我们还会用几篇文档的篇幅来深入探讨这部分的技术。