Skip to content

AG32下的分散加载与数组存放

关于资源 > AG32下的分散加载与数组存放

需求分析

首先要明确下需求:

  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文件。


举例:分散加载的实现

举例:把某个数组和某个函数指定编译到Flash的某个位置,

比如,指定数组guSetting到152K的地方(地址为:0x80026000),

指定函数 testFuncToPos() 紧跟在数组后边。

步骤:

  1. AgRV2K_FLASH.ld中定义一个新标签

    plaintext
    FLASH_E 和 FLASH_E_SIZE

    分别为起始地址0x80026000和4K大小;

    同时定义FLASH_EXT段,后续会来使用;

    这里的Flash位置使用时最好放到code的结尾处。

    并且要考虑FPGA部分的Flash使用空间。

    (如果不对FPGA的位置指定,FPGA会默认占用Flash的最后100K)

  2. mem.ld中,根据上边定义的标签,定义section

    plaintext
    .flash : ALIGN(4)
    {
      *(.testSectionData);
      *(.testSectionCode);
    } > FLASH_EXT

    这里定义出来两个sectiontestSectionDatatestSectionCode。这两个section名称,就是在代码中使用的关联section

  3. 在代码中,对数组和函数进行section指定

    c
    __used uint8_t guSetting[16] __section(".testSectionData") = {0};
    __used __section(".testSectionCode") void testFuncToPos(void) {
      // 函数实现
    }

    并且在项目中调用函数testFuncToPos

  4. 编译后,查看.readelf文件

    编译后,可以从工程的\.pio\build\release\路径下查看.readelf文件,

    (如果是dev方式编译,路径是:\.pio\build\dev\

    打开该文件后,可以看到guSettingtestFuncToPos正是在我们所期望的位置上:


注意事项

  • Flash空间规划:需要增加FPGA(CPLD)对空间的使用考虑。

  • 芯片Flash大小

    • 不管所选型号的Flash是多大,请注意最后100K是留给FPGA使用的。
    • 如果使用的芯片是256K的Flash空间,那么就是156K程序+100K FPGA,用户程序不能超过156K。如果超过156K编译是可以通过的,但烧录后会冲掉VE配置部分。
    • VE配置被冲掉后,程序运行会表现出各种异常(连系统时钟初始化都跑不过)。
  • 调整FPGA边界值

    • 如果程序使用的空间较大,FPGA又刚好比较小,可以调整这个界限的值。调整方法如下:

      plaintext
      board_logic.compress = true //(可选)对FPGA部分进行压缩,更省空间
      board_upload.logic_address = 0x80034000 //根据实际情况调整该边界值
  • Flash大小定义:Flash的大小是在agrv2k_103.json中定义的。

    • Flash起始地址是0x80000000,RAM是0x20000000

分散加载在GCC下的限制

  • 在ARM中,可以通过__attribute__( at(绝对地址) )的方式,把某个数组或某个函数强制编译到某个地址,并且不影响其他代码的编译。编译后整个bin空间看上去是连续的。
  • 这是ARM编译器支持的一个特性。
  • 但GCC不支持这样的指定。
  • GCC使用ld指定的地址,不能在正常编译的code的区域内。这个地址只能放在没有用到的Flash部分。
  • 也就是说,新指定的位置,必须在正常的编译区域的后边。
  • 实际操作时,代码些许的改动都会影响到bin的大小,所以这个指定的位置,往往会跟正常bin之间预留一些空间。因此,自定义ld文件的方式bin文件会增大一些。
  • bin的角度看,code和指定区域,两段中间空出来的区域,会被填充成00
  • 最终bin增大的部分,就是这部分填充成0size大小。
  • 如果对bin的大小比较敏感,那就要缩小这段0的区域。可以在最终出包时,看下其他代码编译后的大小,然后把这段指定地址放的尽量近。
  • 如果对bin的大小不太敏感,那就随意指定到没用到的Flash区域即可。