最近搞Dialog的BLE SDK,发现空间不够了,询问原厂,得知可以通过调整分散加载文件而增加空间,一方面是有42KB+8KB的硬件基础,另一方面是原有的程序限制为38KB+8KB。故顺便学习了下把一个程序存放在不同的存储空间的问题。
简单的单片机是不用了解程序分散加载机制的,至少我搞了四五年(加上大学三年哈)没有碰到要特别需要了解的,空间不够换更大容量的单片机嘛。期间仅仅是听说过而已。还听说过搞能够跑Linux的开发板裸奔的时候需要了解下,因为程序比较大,为了兼顾性能,需要把程序分别存放在NAND Flash、NOR Flash、SDRAM等。
1、知识普及,怎么解读分散加载文件
分散加载,当然是把一个整体分成几个部分,放在不同的位置。怎么分割呢?可以按照RO、RW、ZI属性区分,还可以自定义代码属性,然后根据自定义属性划分。这个在后面介绍怎么自定义代码属性。
我可以按照代码属性随便放几个位置就可以嘛?有什么放置依据嘛?如果空间够大,CPU取指令速度差不多或没要求,可以随便放。当然,怎么放也是有一些规则的。比如RO属性的代码,放在ROM里,CPU只是读出来而已,比如RW和ZI属性的CPU要频繁的读写,可以放在RAM里,甚至是CPU的寄存器里,可以提高CPU的处理速度。对!这些规则的目的只有一个:怎么提高CPU的效率!
分散加载文件的语法规则:
(加载取名称) (基地址) (属性) (大小)
{
(执行区名称) (基地址) (属性) (大小)
{
;放置某种属性的代码
;放置某种属性的代码
;。。。。。。
}
。。。。。。
}
。。。。。。
其中(属性)是可选的,加载区的基地址跟第一个执行区的基地址必须是相同的。
2、抛砖引玉,DA14580的程序空间划分
LR_IROM5 (0x20000440) (0x8BE0) ;定义了名称为LR_IROM5的加载域,起始地址为0x20000440,大小为0x8BE0,接近35KB { ; ER_IROM5 (0x20000440) (0x8BC0) ;定义了名称为ER_IROM5的执行域,此域为LR_IROM5的第一个执行域,起始地址跟
{ ;LR_IROM5的起始地址一样,大小为0x8BC0 *(InRoot$$Sections) ;*为通配符,放置位于根区的所有库的部分如__main.o、__scatter*.o等 boot_vectors.o (+RO) ;放置boot_vectors.o中的RO属性代码 system_ARMCM0.o (+RO) ; .ANY (+RO) ;放置所有的RO属性的代码 } ; RW_IRAM52 (0x20009000) EMPTY 0x20 {} ;RMPTY为属性字段,这里表示空0x20这么多空间 } LR_IROM7 (0x20009020) (0x17E0) ; { ; ER_IROM7 (0x20009020)(0x11E0-NON_RET_HEAP_SIZE) ; { ; .ANY(+RW) ;放置所有的RW属性的代码 } ; RW_IRAM72 (0x2000A200 - NON_RET_HEAP_SIZE) UNINIT NON_RET_HEAP_SIZE ; { ; jump_table.o (heap_mem_area_not_ret) ;放置jump_table.o中的heap_mem_area_not_ret属性的代码 } ; RW_IRAM73 (0x2000A200) UNINIT 0x600 ;未初始化的0x600空间 { ; .ANY (STACK) ;作为栈使用 } }
3、追根溯源,遇到的问题以及原因分析
先贴张编译失败的图片:
no space in execution regions with .ANY selector matching ****
还有那种 ****(执行域名称) 缺少多少字节的编译错误
以上都是某个执行域分配的空间不够的问题,解决的关键是,先找到是哪个执行域空间不够,再想办法扩大,或者把这个执行域内的某个属性的代码放到其他执行域内。
这个是程序编译没问题,但是调试运行,根本进不了main(),就进入硬件中断了。其堆栈信息如上图。说明是初始化变量时,空间不够,解决办法一样,扩大那个执行域,或把那个执行域内一个或几个属性的代码放到其他执行域内。
4、码随心动,自由安排代码位置
根据以上学习,是否学会了自己编写分散加载文件了呢?