STM32创建RAM段
STM32 create RAM section
我使用 STM32H7 微控制器和 GNU/GCC,在我的代码中,我仅使用 DTCM RAM,但我想将一些缓冲区存储在另一个可通过 DMA 访问的内存中。
我是链接描述文件的新手,我需要编辑启动代码吗?
这是我的链接描述文件,我在 SECTIONS
中添加了一些代码
/* Memories definition */
MEMORY
{
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
}
/* Sections */
SECTIONS
{
...
.ram1block 0x24000000 :
{
KEEP(*(.ram1section))
} > RAM_D1
...
}
我想使用这个属性:
uint8_t __attribute__(( section(".ram1section") )) ads_buf[READBACK_LENGTH];
我这样做对吗?
你有什么话题或建议吗?我是这些脚本的新手,有点迷茫
编辑:显然有一个更简单的解决方案:
在链接器文件中
SECTIONS
{
ads_buf=0x24000000;
...
}
来源
extern uint8_t ads_buf[READBACK_LENGTH];
任何人都可以确认此解决方案有效吗?
不,这不是有效的解决方案。为什么? - 因为它只声明了符号。链接器不知道内存中的这个地址有什么东西。它可以在那里放置另一个数据。一个非常非常糟糕的主意。
您的“更简单”解决方案意味着您必须手动为该内存部分中的所有对象分配地址,并格外小心以避免重叠。这正是链接器被发明出来要避免的事情,所以我建议不要那样做。
输出段不需要再指定地址,内存范围即可。是否需要修改启动代码取决于是否需要在启动时初始化该部分。
没有初始化
如果你不需要初始化因为你会,例如在读取之前用传入数据填充缓冲区,按如下方式修改链接描述文件:
.ram1block (NOLOAD) :
{
KEEP(*(.ram1section))
} > RAM_D1
大功告成。
简单的初始化
如果你确实需要简单地初始化它,例如全部为零,添加地址符号如下:
.ram1blockBss (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Bss = .;
KEEP(*(.ram1sectionBss))
. = ALIGN(4);
_EndRam1Bss = .;
} > RAM_D1
对齐地址使初始化更容易和更快。请注意,我将输入部分重命名为 ram1sectionBss
以表示该部分是零初始化的。在启动代码中添加类似这样的内容以零初始化内存:
ldr r0, =_BeginRam1Bss
ldr r1, =_EndRam1Bss
ldr r2, =0
b 2f
1: str r2, [r0], #4
2: cmp r0, r1
blo 1b
这有效地用零初始化了整个块,因此 C 变量定义为
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf[READBACK_LENGTH];
将被零初始化。
值初始化
如果您需要用不同的值初始化内存中的内容,请按如下方式修改链接器部分定义:
.ram1blockData (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Data = .;
KEEP(*(.ram1sectionData))
. = ALIGN(4);
_EndRam1Data = .;
} > RAM_D1 AT> FLASH
_InitRam1Data = LOADADDR (.ram1blockData);
也追加一个. = ALIGN(4);
到last部分before.ram1blockData
进入 FLASH
以确保闪存中的加载地址也对齐。
然后在启动代码中加入如下内容:
ldr r0, =_BeginRam1Data
ldr r1, =_EndRam1Data
ldr r2, =_InitRam1Data
b 2f
1: ldr r3, [r2], #4
str r3, [r0], #4
2: cmp r0, r1
blo 1b
如果您随后将 C 变量定义为
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf[READBACK_LENGTH] = { 1, 2, 3, 4 };
它将被正确初始化。再次注意重命名的部分(ram1sectionData
表示数据已初始化)。
琐碎 + 值初始化
如果您的内存块中同时需要零初始化和值初始化的数据,只需将两个段定义放在链接描述文件中,将两个汇编块放在启动代码中,即可使 C 定义像这样工作:
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf1[READBACK_LENGTH];
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf2[READBACK_LENGTH] = { 1, 2, 3, 4 };
PS:注意像_BeginRam1Bss
这样以下划线+大写字母开头的标识符,是保留的C标识符。这意味着您不会意外地在 C 代码中使用它们,否则会与链接描述文件发生冲突。 Linker-Script + Startup-Code 是实现的一部分,应该为常规 C 代码提供一致的运行时,而不会“占用”应该由 C 代码使用的非保留标识符。
我使用 STM32H7 微控制器和 GNU/GCC,在我的代码中,我仅使用 DTCM RAM,但我想将一些缓冲区存储在另一个可通过 DMA 访问的内存中。
我是链接描述文件的新手,我需要编辑启动代码吗?
这是我的链接描述文件,我在 SECTIONS
/* Memories definition */
MEMORY
{
DTCMRAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
ITCMRAM (xrw) : ORIGIN = 0x00000000, LENGTH = 64K
RAM_D1 (xrw) : ORIGIN = 0x24000000, LENGTH = 512K
RAM_D2 (xrw) : ORIGIN = 0x30000000, LENGTH = 288K
RAM_D3 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 2048K
}
/* Sections */
SECTIONS
{
...
.ram1block 0x24000000 :
{
KEEP(*(.ram1section))
} > RAM_D1
...
}
我想使用这个属性:
uint8_t __attribute__(( section(".ram1section") )) ads_buf[READBACK_LENGTH];
我这样做对吗?
你有什么话题或建议吗?我是这些脚本的新手,有点迷茫
编辑:显然有一个更简单的解决方案:
在链接器文件中
SECTIONS
{
ads_buf=0x24000000;
...
}
来源
extern uint8_t ads_buf[READBACK_LENGTH];
任何人都可以确认此解决方案有效吗?
不,这不是有效的解决方案。为什么? - 因为它只声明了符号。链接器不知道内存中的这个地址有什么东西。它可以在那里放置另一个数据。一个非常非常糟糕的主意。
您的“更简单”解决方案意味着您必须手动为该内存部分中的所有对象分配地址,并格外小心以避免重叠。这正是链接器被发明出来要避免的事情,所以我建议不要那样做。
输出段不需要再指定地址,内存范围即可。是否需要修改启动代码取决于是否需要在启动时初始化该部分。
没有初始化
如果你不需要初始化因为你会,例如在读取之前用传入数据填充缓冲区,按如下方式修改链接描述文件:
.ram1block (NOLOAD) :
{
KEEP(*(.ram1section))
} > RAM_D1
大功告成。
简单的初始化
如果你确实需要简单地初始化它,例如全部为零,添加地址符号如下:
.ram1blockBss (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Bss = .;
KEEP(*(.ram1sectionBss))
. = ALIGN(4);
_EndRam1Bss = .;
} > RAM_D1
对齐地址使初始化更容易和更快。请注意,我将输入部分重命名为 ram1sectionBss
以表示该部分是零初始化的。在启动代码中添加类似这样的内容以零初始化内存:
ldr r0, =_BeginRam1Bss
ldr r1, =_EndRam1Bss
ldr r2, =0
b 2f
1: str r2, [r0], #4
2: cmp r0, r1
blo 1b
这有效地用零初始化了整个块,因此 C 变量定义为
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf[READBACK_LENGTH];
将被零初始化。
值初始化
如果您需要用不同的值初始化内存中的内容,请按如下方式修改链接器部分定义:
.ram1blockData (NOLOAD) :
{
. = ALIGN(4);
_BeginRam1Data = .;
KEEP(*(.ram1sectionData))
. = ALIGN(4);
_EndRam1Data = .;
} > RAM_D1 AT> FLASH
_InitRam1Data = LOADADDR (.ram1blockData);
也追加一个. = ALIGN(4);
到last部分before.ram1blockData
进入 FLASH
以确保闪存中的加载地址也对齐。
然后在启动代码中加入如下内容:
ldr r0, =_BeginRam1Data
ldr r1, =_EndRam1Data
ldr r2, =_InitRam1Data
b 2f
1: ldr r3, [r2], #4
str r3, [r0], #4
2: cmp r0, r1
blo 1b
如果您随后将 C 变量定义为
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf[READBACK_LENGTH] = { 1, 2, 3, 4 };
它将被正确初始化。再次注意重命名的部分(ram1sectionData
表示数据已初始化)。
琐碎 + 值初始化
如果您的内存块中同时需要零初始化和值初始化的数据,只需将两个段定义放在链接描述文件中,将两个汇编块放在启动代码中,即可使 C 定义像这样工作:
uint8_t __attribute__(( section(".ram1sectionBss") )) ads_buf1[READBACK_LENGTH];
uint8_t __attribute__(( section(".ram1sectionData") )) ads_buf2[READBACK_LENGTH] = { 1, 2, 3, 4 };
PS:注意像_BeginRam1Bss
这样以下划线+大写字母开头的标识符,是保留的C标识符。这意味着您不会意外地在 C 代码中使用它们,否则会与链接描述文件发生冲突。 Linker-Script + Startup-Code 是实现的一部分,应该为常规 C 代码提供一致的运行时,而不会“占用”应该由 C 代码使用的非保留标识符。