首先要明确下需求:
1. 分配的静态全局数组,会占用sram空间(希望放到flash上去);
2. 希望把某部分代码或数组,强制放到某个flash地址去;
3. 希望在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文件。
.
这里的语法都是标准的gcc ld语法,可自行网上搜索更全的使用方法。
下边只是个简单的举例供参考。
举例:把某个数组和某个函数指定编译到flash的某个位置,
比如,指定数组guSetting到152K的地方(地址为:0x80026000)
指定函数 testFuncToPos() 紧跟在数组后边。
需要做:
1. 在AgRV2K_FLASH.ld中定义一个新标签:
FLASH_E 和 FLASH_E_SIZE
分别为起始地址0x80026000和4K大小;
同时定义FLASH_EXT段,后续会来使用;
如下图:
这里的flash位置使用时最好放到code的结尾处。
并且要考虑fpga部分的flash使用空间。
(如果不对fgpa的位置指定,fpga会默认占用flash的最后100K)
2. 在mem.ld中,根据上边定义的标签,定义section,如:
.flash : ALIGN(4)
{
*(.testSectionData);
*(.testSectionCode);
} > FLASH_EXT
如下图:
这里定义出来两个section:testSectionData和testSectionCode。这两个section名称, 就是在代码中使用的关联section。
3. 在代码中,对数组和函数进行section指定,方法如:
__used uint8_t guSetting[16] __section(".testSectionData") =
__used __section(".testSectionCode") void testFuncToPos(void)
如下图:
并且在项目中调用函数testFuncToPos。
4. 编译后,可以从工程的\.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又刚好比较小,可以调整这个界限的值。调整方法如下:
board_logic.compress = true //(可选)对fpga部分进行压缩,更省空间
board_upload.logic_address = 0x80034000 //根据实际情况调整该边界值
flash的大小是在agrv2k_103.json 中定义的。
flash起始地址是0x80000000,ram是0x20000000。
另:
分散加载中,如果连AgRV2K_FLASH.ld都想自己指定,则需要修改platformio.ini中配置项:
board_build.ldscript = AgRV2K_FLASH.ld
.
分散加载在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区域即可。