模式
AG32下的分散加载与数组存放
关于资源 > AG32下的分散加载与数组存放
需求分析
首先要明确下需求:
- 分配的静态全局数组,会占用SRAM空间(希望放到Flash上去);
- 希望把某部分代码或数组,强制放到某个Flash地址去;
- 希望在Flash某段区域插入一个标记位。
需求解决方案
需求1:将数组从SRAM中去除,减少SRAM使用
这部分可以参考
example_flash
里的样例,在数组前,用attribute((section(".flash.readonly")))
修饰即可:这种情况不需要使用分散加载那么麻烦的设定。
需求2:将某段代码或数组强制放到Flash指定地址
- 如果这段代码和数组要插入的地方在正常编译的代码段之外,可以使用“分散加载”来实现。
- 也就是说,如果
code.bin
编译完后是128K。那么你分散加载指定的这个地方,不能是0~128K之间的某个位置,必须要在128K以后。这是GCC编译器本身的限制,目前没有绕开的方法。 - 比较合理的一个地方,是确定好大小后,可以放在
logic.bin
之前。
需求3:在Flash某段区域插入一个标记位
- 这个需求其实和“需求2”是一样的。只是存放固定的一个数据。
- 答案也和需求2是一样的:如果存放位置在
code.bin
之后是可以的,不能存放在code.bin
中间。
分散加载
在实际应用中,有时候需要强制把一段数据,或一段代码,固定到指定的一个地址。这个时候,就不能使用默认的编译配置,需要自己来配置连接属性。
AG32中可以通过修改ld
配置,达成这样的目的。
分散加载在GCC下的限制
- 不能插入到代码段,只能放到正常编译的
bin
后边(参后详述)。
默认配置
在默认情况下,AG32编译时使用的连接配置文件是AgRV2K_FLASH.ld
,位于:
AgRV_pio\packages\framework-agrv_sdk\misc\devices
下。
打开该文件会看到,关联了另外两个文件:
其中,在AgRV2K_mem.ld
中是多个自定义的标签:

而在mem.ld
的定义中,则是对section
的使用和分配:

总结
AgRV2K_FLASH.ld
:关联到工程的,一般不要改动;AgRV2K_mem.ld
:自定义标签的定义,如果要新增标签,需要改动;mem.ld
:设置编译地址分配的地方,需要改动。
这里,相当于三个文件加在一起,组成一个完整的scat
文件。
举例:分散加载的实现
举例:把某个数组和某个函数指定编译到Flash的某个位置,
比如,指定数组guSetting
到152K的地方(地址为:0x80026000
),
指定函数 testFuncToPos()
紧跟在数组后边。
步骤:
在
AgRV2K_FLASH.ld
中定义一个新标签:plaintextFLASH_E 和 FLASH_E_SIZE
分别为起始地址
0x80026000
和4K大小;同时定义
FLASH_EXT
段,后续会来使用;这里的Flash位置使用时最好放到
code
的结尾处。并且要考虑FPGA部分的Flash使用空间。
(如果不对FPGA的位置指定,FPGA会默认占用Flash的最后100K)
在
mem.ld
中,根据上边定义的标签,定义section
:plaintext.flash : ALIGN(4) { *(.testSectionData); *(.testSectionCode); } > FLASH_EXT
这里定义出来两个
section
:testSectionData
和testSectionCode
。这两个section
名称,就是在代码中使用的关联section
。在代码中,对数组和函数进行
section
指定:c__used uint8_t guSetting[16] __section(".testSectionData") = {0}; __used __section(".testSectionCode") void testFuncToPos(void) { // 函数实现 }
并且在项目中调用函数
testFuncToPos
。编译后,查看
.readelf
文件:编译后,可以从工程的
\.pio\build\release\
路径下查看.readelf
文件,(如果是
dev
方式编译,路径是:\.pio\build\dev\
)打开该文件后,可以看到
guSetting
和testFuncToPos
正是在我们所期望的位置上:
注意事项
Flash空间规划:需要增加FPGA(CPLD)对空间的使用考虑。
芯片Flash大小:
- 不管所选型号的Flash是多大,请注意最后100K是留给FPGA使用的。
- 如果使用的芯片是256K的Flash空间,那么就是156K程序+100K FPGA,用户程序不能超过156K。如果超过156K编译是可以通过的,但烧录后会冲掉VE配置部分。
- VE配置被冲掉后,程序运行会表现出各种异常(连系统时钟初始化都跑不过)。
调整FPGA边界值:
如果程序使用的空间较大,FPGA又刚好比较小,可以调整这个界限的值。调整方法如下:
plaintextboard_logic.compress = true //(可选)对FPGA部分进行压缩,更省空间 board_upload.logic_address = 0x80034000 //根据实际情况调整该边界值
Flash大小定义:Flash的大小是在
agrv2k_103.json
中定义的。- Flash起始地址是
0x80000000
,RAM是0x20000000
。
- Flash起始地址是
分散加载在GCC下的限制
- 在ARM中,可以通过
__attribute__( at(绝对地址) )
的方式,把某个数组或某个函数强制编译到某个地址,并且不影响其他代码的编译。编译后整个bin
空间看上去是连续的。 - 这是ARM编译器支持的一个特性。
- 但GCC不支持这样的指定。
- GCC使用
ld
指定的地址,不能在正常编译的code
的区域内。这个地址只能放在没有用到的Flash部分。 - 也就是说,新指定的位置,必须在正常的编译区域的后边。
- 实际操作时,代码些许的改动都会影响到
bin
的大小,所以这个指定的位置,往往会跟正常bin
之间预留一些空间。因此,自定义ld
文件的方式bin
文件会增大一些。 - 从
bin
的角度看,code
和指定区域,两段中间空出来的区域,会被填充成00
。 - 最终
bin
增大的部分,就是这部分填充成0
的size
大小。 - 如果对
bin
的大小比较敏感,那就要缩小这段0
的区域。可以在最终出包时,看下其他代码编译后的大小,然后把这段指定地址放的尽量近。 - 如果对
bin
的大小不太敏感,那就随意指定到没用到的Flash区域即可。