硬汉嵌入式论坛

 找回密码
 立即注册
查看: 1309|回复: 10
收起左侧

[其它] GCC如何将变量编译到指定ROM地址

  [复制链接]

21

主题

61

回帖

124

积分

初级会员

积分
124
发表于 2023-6-8 15:03:56 | 显示全部楼层 |阅读模式
GCC如何将变量编译到指定ROM地址?比如定义:
const char Devname[16]  __attribute__((section(".demo.rodata")))={"demo"};
改行只能将变量Devname定义到指定的段中,但是不能指定固定的地址,比如指定到0x08000100,而在MDK中可以定义为:
const char Devname[]  __attribute__((at(0x08000400))) ={"demo"};

回复

使用道具 举报

0

主题

12

回帖

12

积分

新手上路

积分
12
发表于 2023-6-8 16:01:17 | 显示全部楼层
这个配合link文件(就是后缀为.ld的那个文件)实现,如在MEMORY定义中:
[C] 纯文本查看 复制代码
/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw)      : ORIGIN = 0x10000000, LENGTH = 64K
FLASH (rx)      : ORIGIN = 0x08000000, LENGTH = 128K
}


改为:
[C] 纯文本查看 复制代码
/* Specify the memory areas */
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 128K
CCMRAM (xrw)      : ORIGIN = 0x10000000, LENGTH = 64K
FLASH1 (rx)      : ORIGIN = 0x08000000, LENGTH = 64K
FLASH2 (rx)      : ORIGIN = 0x08010000, LENGTH = 64K
}


然后在SECTIONS中添加一个段,如:
[C] 纯文本查看 复制代码
  /* Constant data goes into FLASH */
  .rodata1 :
  {
    . = ALIGN(4);
    *(.rodata1)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata1*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(4);
  } >FLASH2


再在代码里定义:
[C] 纯文本查看 复制代码
const char Devname[16]  __attribute__((section(".rodata1")))={"demo"};


即可定义到指定的0x08010000位置,gcc的链接器通过link脚本来决定程序内存布局,理论上可以通过调整link脚本来实现任意内存布局(调整时要考虑MCU实际内存块分布和字节对齐情况),最后实际编译出来变量在内存哪个位置可以通过.map文件查看。
回复

使用道具 举报

1万

主题

6万

回帖

10万

积分

管理员

Rank: 9Rank: 9Rank: 9

积分
106746
QQ
发表于 2023-6-9 09:25:06 | 显示全部楼层
cking616 发表于 2023-6-8 16:01
这个配合link文件(就是后缀为.ld的那个文件)实现,如在MEMORY定义中:
[mw_shl_code=c,true]/* Specify th ...

回复

使用道具 举报

1

主题

52

回帖

55

积分

初级会员

积分
55
发表于 2023-6-9 11:17:15 | 显示全部楼层
赞赞赞!!!
回复

使用道具 举报

21

主题

61

回帖

124

积分

初级会员

积分
124
 楼主| 发表于 2023-6-12 11:02:35 | 显示全部楼层
本帖最后由 zhumx 于 2023-6-12 13:09 编辑
cking616 发表于 2023-6-8 16:01
这个配合link文件(就是后缀为.ld的那个文件)实现,如在MEMORY定义中:
[mw_shl_code=c,true]/* Specify th ...

你好,这样有个问题,假如我期望Devname编译到0x08000400位置,则ld脚本更改如下:
------------------------------------------------------------------------------------------------------------
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
FLASH1 (rx)    : ORIGIN = 0x8000000, LENGTH = 1K
FLASH2 (rx)    : ORIGIN = 0x8000400, LENGTH = 1K
FLASH (rx)     : ORIGIN = 0x8000800, LENGTH = 190K
}

  .isr_vector :
  {
    . = ALIGN(8);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(8);
  } >FLASH1

  .rodata1 :
  {
          KEEP(*(.rodata1))
  } >FLASH2

  .text :
{
    . = ALIGN(8);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(8);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH
代码中这样定义:const char Devname[16]  __attribute__((section(".rodata1")))={"demo"};
--------------------------------------------------------
这样改可以实现将Devname编译到0x08000400位置,但是0x08000000到0x08000400之间,除了中断向量信息,剩余的500多字节空间被空置,flash空间被浪费了。能否像keil中链接器那样,在实现变量链接到指定地址后,在该地址前后空间自动链接合适的代码信息,有效利用flash空间?





回复

使用道具 举报

0

主题

12

回帖

12

积分

新手上路

积分
12
发表于 2023-6-12 13:47:27 | 显示全部楼层
zhumx 发表于 2023-6-12 11:02
你好,这样有个问题,假如我期望Devname编译到0x08000400位置,则ld脚本更改如下:
------------------- ...

确实,像这样静态分配内存地址可能会导致存储空间的浪费。在GCC链接器脚本中,你可以通过配置SECTION命令来达到你想要的效果。理论上,你可以让链接器自动地把其他的代码或者数据填充到FLASH1和FLASH2区间,直到这些区间被完全使用。

为了实现这个目标,你需要更改你的链接器脚本,让它在`.rodata1`区段之后包含一些可能会占用剩余空间的段。例如,你可以尝试以下的配置:
[C] 纯文本查看 复制代码
MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
FLASH1 (rx)    : ORIGIN = 0x8000000, LENGTH = 1K
FLASH2 (rx)    : ORIGIN = 0x8000400, LENGTH = 1K
FLASH (rx)     : ORIGIN = 0x8000800, LENGTH = 190K
}

.isr_vector :
{
  . = ALIGN(8);
  KEEP(*(.isr_vector)) /* Startup code */
  . = ALIGN(8);
} >FLASH1

.rodata1 :
{
  KEEP(*(.rodata1))
} >FLASH2

.text :
{
  . = ALIGN(8);
  *(.text)           /* .text sections (code) */
  *(.text*)          /* .text* sections (code) */
  *(.glue_7)         /* glue arm to thumb code */
  *(.glue_7t)        /* glue thumb to arm code */
  *(.eh_frame)

  KEEP (*(.init))
  KEEP (*(.fini))

  . = ALIGN(8);
  _etext = .;        /* define a global symbols at end of code */
} >FLASH

/* After the specifically placed sections, we add all remaining sections. */
.filled :
{
  *(.rodata*)    /* Consecutive read-only data. */
  *(.data)           /* .data sections */
  . = ALIGN(8);
} >FLASH2 AT> FLASH

这种配置将让.rodata和.data尽可能将FLASH2空间用尽,又由于.rodata1在前会优先分配在0x8000400地址上,FLASH2空间用尽后会把剩余的.rodata和.data的数据会链接到FLASH区。但是要注意,这可能会打乱你的内存布局,特别是如果你的程序依赖于某些特定的内存布局的话。你应该根据自己的需要进行适当的修改。

注意,这些配置都基于你理解你的程序的内存需求和你的目标硬件的内存布局。如果可能的话,最好还是尽量避免硬编码特定的内存地址,除非你有明确的理由要这样做。
回复

使用道具 举报

21

主题

61

回帖

124

积分

初级会员

积分
124
 楼主| 发表于 2023-6-12 16:27:16 | 显示全部楼层
cking616 发表于 2023-6-12 13:47
确实,像这样静态分配内存地址可能会导致存储空间的浪费。在GCC链接器脚本中,你可以通过配置SECTION命令 ...

谢谢,但是按上述办法编写ld脚本后提示编译错误:

我的ld脚本如下:

MEMORY
{
RAM (xrw)      : ORIGIN = 0x20000000, LENGTH = 64K
FLASH1 (rx)    : ORIGIN = 0x8010000, LENGTH = 1K
FLASH2 (rx)    : ORIGIN = 0x8010400, LENGTH = 1K
FLASH (rx)     : ORIGIN = 0x8010800, LENGTH = 190K
}


/* Define output sections */
SECTIONS
{

  /* The startup code goes first into FLASH */
  .isr_vector :
  {
    . = ALIGN(8);
    KEEP(*(.isr_vector)) /* Startup code */
    . = ALIGN(8);
  } >FLASH1
  
.rodata1 :
{
  KEEP(*(.rodata1))
} >FLASH2

  /* The program code and other data goes into FLASH */
  .text :
  {
    . = ALIGN(8);
    *(.text)           /* .text sections (code) */
    *(.text*)          /* .text* sections (code) */
    *(.glue_7)         /* glue arm to thumb code */
    *(.glue_7t)        /* glue thumb to arm code */
    *(.eh_frame)

    KEEP (*(.init))
    KEEP (*(.fini))

    . = ALIGN(8);
    _etext = .;        /* define a global symbols at end of code */
  } >FLASH

  
  /* Constant data goes into FLASH */
  .rodata :
  {
    . = ALIGN(8);
    *(.rodata)         /* .rodata sections (constants, strings, etc.) */
    *(.rodata*)        /* .rodata* sections (constants, strings, etc.) */
    . = ALIGN(8);
  } >FLASH2 AT>FLASH

回复

使用道具 举报

0

主题

12

回帖

12

积分

新手上路

积分
12
发表于 2023-6-13 10:04:25 | 显示全部楼层
zhumx 发表于 2023-6-12 16:27
谢谢,但是按上述办法编写ld脚本后提示编译错误:

我的ld脚本如下:

你这个报错没图,然后ld脚本不全,你还可以保持原来的ld不变,把FLASH2的LENGTH调小,弄成10字节,调下FLASH的起始地址和LENGTH,LENGTH最小不是1k,比方说你看vesc工程的bldc的源码中就有:
[C] 纯文本查看 复制代码
MEMORY
{
    flash : org = 0x08000000, len = 16k
    flash2 : org = 0x0800C000, len = 524288 - 16	/* NEW_APP_MAX_SIZE - CRC_INFO */
    crcinfo : org = 0x0807FFF0, len = 8		/* CRC info */
    ram0  : org = 0x20000000, len = 128k    /* SRAM1 + SRAM2 */
    ram1  : org = 0x20000000, len = 112k    /* SRAM1 */
    ram2  : org = 0x2001C000, len = 16k     /* SRAM2 */
    ram3  : org = 0x00000000, len = 0
    ram4  : org = 0x10000000, len = 62k     /* CCM SRAM */
    libif : org = 0x1000F800, len = 2k
    ram5  : org = 0x40024000, len = 4k      /* BCKP SRAM */
    ram6  : org = 0x00000000, len = 0
    ram7  : org = 0x00000000, len = 0
}


分配crcinfo这个区域8字节的:
[C] 纯文本查看 复制代码
    _crcinfo_start_address = 0x0807FFF0;
    
    .crcinfo _crcinfo_start_address :
	{
		KEEP(*(.crcinfo))
	}  > crcinfo

开源基本都是gcc+ld脚本的,可以多参考多看看。
回复

使用道具 举报

21

主题

61

回帖

124

积分

初级会员

积分
124
 楼主| 发表于 2023-6-13 21:24:45 | 显示全部楼层
cking616 发表于 2023-6-13 10:04
你这个报错没图,然后ld脚本不全,你还可以保持原来的ld不变,把FLASH2的LENGTH调小,弄成10字节,调下FL ...

好的,谢谢!
回复

使用道具 举报

0

主题

1

回帖

1

积分

新手上路

积分
1
发表于 2023-12-18 13:19:52 | 显示全部楼层
扩展一下,MDK里面可以设置.c文件到rom2,这个在GCC上如何实现呢
回复

使用道具 举报

1

主题

49

回帖

52

积分

初级会员

积分
52
发表于 2023-12-19 08:49:02 | 显示全部楼层
linda 发表于 2023-12-18 13:19
扩展一下,MDK里面可以设置.c文件到rom2,这个在GCC上如何实现呢

就和上面的 FLASH 、RAM 一样,定义一个 Memory 地址是你的 ROM2 ,然后把你想放的内容放在这个 Memory 就可以了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|小黑屋|Archiver|手机版|硬汉嵌入式论坛

GMT+8, 2024-5-3 19:03 , Processed in 0.226864 second(s), 26 queries .

Powered by Discuz! X3.4 Licensed

Copyright © 2001-2023, Tencent Cloud.

快速回复 返回顶部 返回列表