虽然可用的存储空间看起来比section的长度要大,但是链接器为何提示“placement fails for object”?
这种情况一般是因为段的空间的分配是并不是我们想象中的连续的一个紧挨一个,而是被编译器给“分块”管理了。在内存地址分配时,一个段需要完全适配到页(page)中,或者从页的边界开始连续分配;为了满足这个要求,段在分配到页中时,可能无法完全利用某些页,导致内存地址中产生了间隙(hole),使得实际所需要的内存空间超过了根据变量大小计算出来的理论值。编译器这样做的目的是为了优化数据页(DP)寄存器的加载,达到减小代码尺寸和优化程序性能的目的。例如,针对一个数组,如果数组的长度小于64字(words),则编译器仅需安全地加载DP一次就可以访问数组的全部元素;如果数组长度大于64字,则在访问每64字的数组元素时,编译器仅需加载一次DP,当然如果访问多个64字的数组元素则仍需要多次加载DP。
举例说明:
在cmd里定义:
RAMM1 : origin = 0x000400, length = 0x000400 /* on-chip RAM block M1 */
commbuf : 》 RAMM1 PAGE = 1
在main.c里定义以下几个变量
#pragma DATA_SECTION(sendT, “commbuf”)
Uint16 sendT;
#pragma DATA_SECTION(receT, “commbuf”)
Uint16 receT;
#pragma DATA_SECTION(CntPPR, “commbuf”)
Uint32 CntPPR;
表面上共需260+260+250*2=1020,commbuf正好放得下。但ccs提示空间不够:
(run placement fails for object “commbuf”, size 0x474 (page 1)。
Available ranges: RAMM1 size: 0x400 unused: 0x400 max hole: 0x400)
产生错误的原因是根据DP加载的原则,page被划分为64word的小单元,而数组被存储在连续的、整块的单元上,未使用到的空间不会再分配给其它数组或者变量使用。所以16位260长度的数组实际占用了64*5=320 (64*4=256《260),32位500的长度实际占用了64*8=512,占用的总长度为:320*2+512=1152=0x480。
按照CCS的提示,commbuf占用空间是320*2+500=1140=0x474,但是事实上32位数组占据的最后那个page已经无法被别的变量使用了,所以如果还有新的变量出现的话,会提示RAMM1块缺少的地址更多。
根据我们的需要,可以在每次之间内存读取操作之前都加载DP,这样就可以禁用上面的“分块”管理特性了。这样做虽然可以减小内存地址空间中的“间隙”,但是每一次访问内存都需要加载DP,反而大大地增加了代码的尺寸,实在是得不偿失(看起来很少有人会这么做)。我们可以通过启用编译器的-disable_dp_load_opt,或者叫-md选项来实现这一方法。
确认某个段是否被编译器给分块管理的方法就是使用.bss和.usect指令。
虽然可用的存储空间看起来比section的长度要大,但是链接器为何提示“placement fails for object”?
这种情况一般是因为段的空间的分配是并不是我们想象中的连续的一个紧挨一个,而是被编译器给“分块”管理了。在内存地址分配时,一个段需要完全适配到页(page)中,或者从页的边界开始连续分配;为了满足这个要求,段在分配到页中时,可能无法完全利用某些页,导致内存地址中产生了间隙(hole),使得实际所需要的内存空间超过了根据变量大小计算出来的理论值。编译器这样做的目的是为了优化数据页(DP)寄存器的加载,达到减小代码尺寸和优化程序性能的目的。例如,针对一个数组,如果数组的长度小于64字(words),则编译器仅需安全地加载DP一次就可以访问数组的全部元素;如果数组长度大于64字,则在访问每64字的数组元素时,编译器仅需加载一次DP,当然如果访问多个64字的数组元素则仍需要多次加载DP。
举例说明:
在cmd里定义:
RAMM1 : origin = 0x000400, length = 0x000400 /* on-chip RAM block M1 */
commbuf : 》 RAMM1 PAGE = 1
在main.c里定义以下几个变量
#pragma DATA_SECTION(sendT, “commbuf”)
Uint16 sendT;
#pragma DATA_SECTION(receT, “commbuf”)
Uint16 receT;
#pragma DATA_SECTION(CntPPR, “commbuf”)
Uint32 CntPPR;
表面上共需260+260+250*2=1020,commbuf正好放得下。但ccs提示空间不够:
(run placement fails for object “commbuf”, size 0x474 (page 1)。
Available ranges: RAMM1 size: 0x400 unused: 0x400 max hole: 0x400)
产生错误的原因是根据DP加载的原则,page被划分为64word的小单元,而数组被存储在连续的、整块的单元上,未使用到的空间不会再分配给其它数组或者变量使用。所以16位260长度的数组实际占用了64*5=320 (64*4=256《260),32位500的长度实际占用了64*8=512,占用的总长度为:320*2+512=1152=0x480。
按照CCS的提示,commbuf占用空间是320*2+500=1140=0x474,但是事实上32位数组占据的最后那个page已经无法被别的变量使用了,所以如果还有新的变量出现的话,会提示RAMM1块缺少的地址更多。
根据我们的需要,可以在每次之间内存读取操作之前都加载DP,这样就可以禁用上面的“分块”管理特性了。这样做虽然可以减小内存地址空间中的“间隙”,但是每一次访问内存都需要加载DP,反而大大地增加了代码的尺寸,实在是得不偿失(看起来很少有人会这么做)。我们可以通过启用编译器的-disable_dp_load_opt,或者叫-md选项来实现这一方法。
确认某个段是否被编译器给分块管理的方法就是使用.bss和.usect指令。
举报