joe 发表于 2022-10-26 15:32:15

MDK链接脚本控制问题

有人发我一个帖子,问了如下相同的问题:最近在玩STM32F7系列单片机,开发板上面有SDRAM,用指针可以对SDRAM正确地进行读写,但是我想在申请数据的时候不用自己计算数组的开始地址,而是由编译器进行安排。我的代码如下:
uint32_t sdram_show __attribute__ ((section ("SDRAM"))) ;

由此需要在链接脚本里添加section

LR_IROM1 0x08000000 0x00100000 { ; load region size_region
ER_IROM1 0x08000000 0x00100000 { ; load address = execution address
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x20010000 0x00040000 { ; RW data
.ANY (+RW +ZI)
}

SDRAM 0xC0000000 0x400000{
}
}

SDRAM大括号内 应该写什么呢?
现在提示以下错误
.\Objects\STM32F746NGH6_LCD.sct(15): warning: L6312W: Empty Execution region description for region SDRAM
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching lcd.o(.bss).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching uart.o(.bss).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching sdram.o(.bss).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching system_stm32f7xx.o(.data).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching stm32f7xx_hal.o(.data).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6406E: No space in execution regions with .ANY selector matching stdout.o(.data).
.\Objects\STM32F746NGH6_LCD.sct: Error: L6407E: Sections of aggregate size 0x184 bytes could not fit into .ANY selector(s).
Not enough information to list image symbols.
Not enough information to list the image map.
Finished: 2 information, 1 warning and 7 error messages.
".\Objects\STM32F746NGH6_LCD.axf" - 7 Error(s), 3 Warning(s).

如果我按照以下方法进行定义,则没有错误
uint32_t sdram_show __attribute__((at(0xC0000000)));
但是需要手动地去计算数组的开始地址,很不方便。各位大神有没有办法解决上面的错误问题?SDRAN大括号内应该写些什么东西?



joe 发表于 2022-10-26 15:34:26

可以用下面语法试试:
SDRAM 0xC0000000 0x400000{
   .ANY(+RW +ZI)
}

joe 发表于 2022-10-26 15:41:45

在这个网页中看到下面的代码:
0:链接脚本:
LR_IROM1 0x08000000 0x00040000{    ; load region size_region
ER_IROM1 0x08000000 0x00040000{; load address = execution address
   *.o (RESET, +First)
   *(InRoot$$Sections)
   .ANY (+RO)
}
RW_IRAM1 0x20000000   {; RW data
   .ANY (+RW +ZI)
}
RW_IRAM2 +0   {
   .ANY (+RW)
}
}

1:得到连接脚本段地址的地址:
extern unsigned int   Image$$RW_IRAM2$$RW$$Base;//Image$$RW_IRAM2$$Base ;
unsigned int CMD_point = (unsigned int) &Image$$RW_IRAM2$$RW$$Base ;//section ("RW_IRAM2");//|Image$$RW_IRAM2$$Base| ;
printf("%x",CMD_point);

2:指定代码段到对应地址
long rw1__attribute__((section ("RW_IRAM2")))=4;


我突然想到,能否将C文件中的函数都用__arrtibute__((section(...)))定义section,然后在链接脚本中固定这个section的基址,通过反汇编来查看是否是以在源C文件中出现的顺序放置???

joe 发表于 2022-10-26 16:05:12

Keil MDK的分散加载文件.sct出处:http://blog.csdn.net/tracing/article/details/9720157面对这样一个新东西,先去官网看看,或者看看IDE的帮助,基本上你想要的东西都有了,BAIDU来的都不全面,这是一种学习方法。    http://www.keil.com/support/man/docs/armlink/armlink_BABDDHBF.htm    这个链接是我在官网上找到的关于分散加载文件的资料。讲的比较详细了。这里通过一个例子记录下我学习的过程,通过分散加载文件把代码从flash里拷贝到ram里运行, 基于LPC1788。    先贴下我的sct文件: view plain copy





[*]LR_IROM1 0x00000000 0x00002000   
[*]{   
[*]    ER_IROM1 0x00000000 0x00020000   
[*]    {
[*]      *.o (RESET, +First)
[*]      *(InRoot$$Sections)
[*]      startup_lpc177x_8x.o (+RO)
[*]      system_LPC177x_8x.o (+RO)
[*]    }
[*]      
[*]    RW_IRAM1 0x20000000 0x00004000   
[*]    {
[*]      .ANY (+RW +ZI)
[*]    }
[*]}
[*]
[*]LR_IROM2 0x00002000 0x0007E000
[*]{
[*]    VECTOR 0x10000000 EMPTY 0xE4
[*]    {
[*]    }
[*]      
[*]    ER_IRAM1 +0
[*]    {
[*]      .ANY (+RO)
[*]    }
[*]}


这里有两个加载域(load region)LR_IROM1和LR_IROM2,LR_IROM1是初始化程序,拷贝代码等,从ROM的地址0开始,LR_ROM2是应用程序,从ROM的0x2000开始。+RO表示只读,代码或者只读数据,一般用来表示代码,+RW表示可读可写的数据,+ZI表示初始化为0的数据。大括号里面的为运行域(execution region),一个加载域可以包含几个运行域,LR_ROM2里面有两个运行域,VECTOR和ER_IRAM1,我用VECTOR来表示中断向量区域,ER_IRAM1来表示应用程序区,+0表示紧接着VECTOR排放,EMPTY表示空的,这里空出0xE4的大小,用来放中断向量,.ANY表示除了上面用到的代码之外的代码,官网上有专门解释.ANY的一节。    下面用一张图来表示这个程序的加载域和执行域: https://img-blog.csdn.net/20170504160557100其实加载域的empty这块区域是不用空出来的,主要是运行域要空出来,用来拷贝中断向量,看个人喜好了,我觉得空出来方便引用这块区域的执行域地址。
    这样框架就比较清楚了,拷贝的程序清单如下:
view plain copy





[*]extern unsigned char Image
Base;
[*]extern unsigned char Image
Length;
[*]
[*]extern unsigned char Load
Base;
[*]extern unsigned char Image
Base;
[*]extern unsigned char Image
Length;
[*]
[*]void CopyCode2Ram ()
[*]{
[*]    unsigned char *pSrc, *pDes;
[*]    unsigned int count;
[*]      
[*]    SCB->VTOR = 0x10000000;
[*]      
[*]    pSrc = 0;
[*]    pDes = (unsigned char*)&Image
Base;
[*]    count = 0xE4;
[*]      
[*]    while (count--)
[*]    {
[*]      *pDes++ = *pSrc++;
[*]    }
[*]      
[*]      
[*]    count = (unsigned int)&Image
Length;
[*]    pDes = (unsigned char*)&Image
Base;
[*]    pSrc = (unsigned char*)(&Load
Base + 0xE4);
[*]      
[*]    while (count--)
[*]    {
[*]      *pDes++ = *pSrc++;
[*]    }
[*]}


其中拷贝中断向量的时候要指定中断向量的偏移地址。Load
Base表示执行域ER_IRAM1的加载地址;Image
Base表示执行域ER_IRAM1的执行地址;Image
Length表示执行域ER_IRAM1的实际长度,VECTOR区域因为是EMPTY,所以实际长度是0,
而中断向量的长度是固定的,所以程序里就写了个常数。



出处:http://blog.163.com/yaochen_good/blog/static/5673636320131523020888/

分散加载能够将加载和运行时存储器中的代码和数据描述在被称为分散加载描述文件的一个文本描述文件中,以供连接时使用。
(1)分散加载区
分散加载区域分为两类:
? 加载区,包含应用程序复位和加载时的代码和数据。
? 执行区,包含应用程序执行时的代码和数据。应用程序启动过程中,从每个加载区可创建一个或多个执行区。
映象中所有的代码和数据准确地分为一个加载区和一个执行区。
(2)分散加载文件示例
ROM_LOAD 0x0000 0x4000
{
    ROM_EXEC 0x0000 0x4000; Root region
    {
      * (+RO); All code and constant data
    }
    RAM 0x10000 0x8000
    {
      * (+RW, +ZI); All non-constant data
    }
}https://img-blog.csdn.net/20170504161037760

(3)分散加载文件语法
load_region_namestart_address | "+"offset
{
    execution_region_namestart_address | "+"offset
    {
      module_select_pattern["("
                                    ("+" input_section_attr | input_section_pattern)
                                    ([","] "+" input_section_attr | "," input_section_pattern)) *
                               ")"]
    }
}
load_region:       加载区,用来保存永久性数据(程序和只读变量)的区域;
execution_region:执行区,程序执行时,从加载区域将数据复制到相应执行区后才能被正确执行;
load_region_name:加载区域名,用于“Linker”区别不同的加载区域,最多31个字符;
start_address:   起始地址,指示区域的首地址;
+offset:         前一个加载区域尾地址+offset 做为当前的起始地址,且“offset”应为“0”或“4”的倍数;
attributes:      区域属性,可设置如下属性:
                  PI       与地址无关方式存放;
                  RELOC    重新部署,保留定位信息,以便重新定位该段到新的执行区;
                  OVERLAY覆盖,允许多个可执行区域在同一个地址,ADS不支持;
                  ABSOLUTE 绝对地址(默认);
max_size:          该区域的大小;
execution_region_name:执行区域名;
start_address:   该执行区的首地址,必须字对齐;
+offset:         同上;
attributes:      同上;
                  PI          与地址无关,该区域的代码可任意移动后执行;
                  OVERLAY   覆盖;
                  ABSOLUTE    绝对地址(默认);
                  FIXED       固定地址;
                  UNINIT      不用初始化该区域的ZI段;
module_select_pattern: 目标文件滤波器,支持通配符“*”和“?”;
                        *.o匹配所有目标,* (或“.ANY”)匹配所有目标文件和库。
input_section_attr:    每个input_section_attr必须跟随在“+”后;且大小写不敏感;
                        RO-CODE 或 CODE
                        RO-DATA 或 CONST
                        RO或TEXT, selects both RO-CODE and RO-DATA
                        RW-DATA
                        RW-CODE
                        RW 或 DATA, selects both RW-CODE and RW-DATA
                        ZI 或 BSS
                        ENTRY, that is a section containing an ENTRY point.
                        FIRST,用于指定存放在一个执行区域的第一个或最后一个区域;
                        LAST,同上;
input_section_pattern: 段名;
汇编中指定段:
   AREA    vectors, CODE, READONLY
C中指定段:
#pragma arm section "name"]] [,sort_type="name"]*
sort_type:      code、rwdata、rodata、zidata
                如果“sort_type”指定了但没有指定“name”,那么之前的修改的段名将被恢复成默认值。
#pragma arm section   // 恢复所有段名为默认设置。
应用:
    #pragma arm section rwdata = "SRAM",zidata = "SRAM"
      static OS_STKSecondTaskStk;            // “rwdata”“zidata”将定位在“sram”段中。
    #pragma arm section                                 // 恢复默认设置
(4)程序中对区域地址引用的方法
Loadregionnameregionname
Base             Load address of the region.
Imageregionnameregionname
Base            Execution address of the region.
Imageregionnameregionname
Length          Execution region length in bytes (multiple of 4).
Imageregionnameregionname
Limit         Address of the byte beyond the end of the execution region.
Imageregionnameregionname
ZIBaseExecutionaddressoftheZIoutputsectioninthisregion.ImageBaseExecutionaddressoftheZIoutputsectioninthisregion.Image
region_nameZIZI
Length      Length of the ZI output section in bytes (multiple of 4).
Imageregionnameregionname
ZILimitAddressofthebytebeyondtheendoftheZIoutputsectionintheexecutionregion.SectionNameLimitAddressofthebytebeyondtheendoftheZIoutputsectionintheexecutionregion.SectionName
Base                   Input Address of the start of the consolidated section called SectionName.
SectionNameLimitInputAddressofthebytebeyondtheendoftheconsolidatedsectioncalledSectionName.Load:加载区,即存放地址;Image:执行区,即运行地址;Base:区首地址;Limit:区尾地址;Length:区长度;regionname:RO、RW、ZI、loadregionname、executionregionname;例如:“RAM1”区域的首地址:ImageLimitInputAddressofthebytebeyondtheendoftheconsolidatedsectioncalledSectionName.Load:加载区,即存放地址;Image:执行区,即运行地址;Base:区首地址;Limit:区尾地址;Length:区长度;regionname:RO、RW、ZI、loadregionname、executionregionname;例如:“RAM1”区域的首地址:Image
RAM1Base上例中“sram”段首地址:sramBase上例中“sram”段首地址:sram
Base
汇编引用示例:
IMPORT |LoadExecRAM1ExecRAM1
Base|            // Exec_RAM1 为“RW”段
IMPORT |ImageExecRAM1ExecRAM1
Base|
IMPORT |ImageExecRAM1ExecRAM1
Length|
IMPORT |ImageExecRAM1ExecRAM1
Limit|
LDRR0, =|LoadExecRAM1ExecRAM1
Base|
LDRR1, =|ImageExecRAM1ExecRAM1
Base|
LDRR2, =|ImageExecRAM1ExecRAM1
Limit|
0
CMPR1,   R2
LDRCC R3,   , #4
STRCC R3,   , #4
BCC%b0
C 引用:
extern unsigned char LoadExecRAM1ExecRAM1
Base;
extern unsigned char ImageExecRAM1ExecRAM1
Base;
extern unsigned char ImageExecRAM1ExecRAM1
Length;
void MoveRO(void)
{
unsigned char * psrc, *pdst;
unsigned intcount;
count = (unsigned int)   &ImageExecRAM1ExecRAM1
Length;
psrc= (unsigned char *)&LoadExecRAM1ExecRAM1
Base;
pdst= (unsigned char *)&ImageExecRAM1ExecRAM1
Base;
while (count--) {
*pdst++ = *psrc++;
}
}
二.分散加载应用
前面提到过,从NAND Flash启动,对于S3C2410而言,由于片内具有4K的称作
"SteppingStone"的SRAM,NAND FLASH的最低4K代码可以自动复制到"SteppingStone",因此可以将初始化等代码放在NAND FLASH的低4K区域内,其他的代码放置在4K以外,在初始化代码内将这些代码复制到外部SDRAM,从而这些代码可以在外部SDRAM内运行。
1.应用实例描述
先完成初始化操作,并且在初始化代码中将NAND FLASH的4K范围以外的代码(简单起见,这部分代码可以操作LED灯)复制到外部SDRAM中。主要目的是使用分散加载文件以及将NAND FLASH中的数据代码复制到SDRAM中。
2.分散加载文件
NAND_FLASH_LOAD 0x0 0x1000
{
    RAM_EXEC +0 0x1000
    {
      ;参见前面的加载文件语法
    }
}
NAND_FLASH_LOAD2 0x1000
{
    SDRAM_EXEC 0x30000000
    {
      ;参见前面的加载文件语法
    }
}
(1)将一些初始化代码放在第一个加载区(根区:加载地址和执行地址相同的区域,每一个分散加载描述文件必须至少要有一个根区。),地址范围为:0x0000~0x0fff的4K,其执行区的地址范围也是0x0000~0x0fff的4K,这正好是NAND FLASH启动时自动复制的地址范围。
(2)其他代码放在第2个加载区,从地址0x1000开始,由于这一部分不能自动复制,因此在初始化代码中应该将这一部分复制到外部SDRAM中,其执行区的起始地址为外部SRDAM的地址。
3. 二进制文件烧录
由于有2个加载区,因此生成的二进制文件有2个,文件名对应于相应的执行区名,分别是RAM_EXEC和SDRAM_EXEC,需要注意的是,应该将存放初始化代码的加载区对应的二进制文件RAM_EXEC烧录NAND FLASH的低4K区域,第二个加载区对应的二进制文件SDRAM_EXEC烧录到4K以后的区域。这个可以通过修改Samsuang的sjf烧录程序实现,原来的烧录程序是按BLOCK(16K)烧录,可以修改为按4K的Section烧录,即将1个Block分为4个Section(4K)。主要修改 k9s1208.c中的K9S1208_Program函数,需要注意的是,由于NAND FLASH写入前应该擦除,擦除是按Block擦除,由于现在是按Section写,因此应该注意只有在第1次写某一块中的Section前进行擦除,以后再写着一块的其它Section前不能再进行擦除。
这样RAM_EXEC烧录到0 SECTION,SDRAM_EXEC烧录到1 SECTION开始的以后的区域中,完成后复位即可。

下面是找了一篇值得参考的文章:原文地址:http://hi.baidu.com/pengjj0807/blog/item/ef73e287a212453cc65cc3be.htmlKEIL下分散加载文件的使用*************************************************************
; *** Scatter-Loading Description File generated by uVision ***
; *************************************************************LR_IROM1 0x08000000 0x00004000 ; load region size_region 第一个加载域,起始地址0x08000000,{ 大小0x00004000ER_IROM1 0x08000000 0x00004000 ; load address = execution address第一个运行时域,{ 起始0x08000000,大小0x00004000*.o (RESET, +First) IAP第一阶段还是在FLASH中运行
*(InRoot$$Sections)
startup_stm32f10x_md.o
}
ER_IROM2 0x20008000 0x00004000 ; load address = execution address第二个运行时域,
{ 起始0x20008000,大小0x00004000.ANY (+RO) IAP第二阶段加载到SDRAM中运行
}
RW_IRAM1 0x20000000 0x00008000 ; RW data 把可读写的数据和初始化为0的数据放在内存SDRAM的开头{.ANY (+RW +ZI)
}
}让MDK自己分配--选linker-usexxx


对于分散加载的概念,在《ARM体系结构与编程》书中第11章有明确介绍。分散加载文件(即scatter file 后缀为.scf)是一个文本文件,通过编写一个分散加载文件来指定
ARM连接器在生成映像文件时如何分配RO,RW,ZI等数据的存放地址。如果不用SCATTER文件指定,那么
ARM连接器会按照默认的方式来生成映像文件,一般情况下我们是不需要使用分散加载文件的。但在某些场合,我们希望把某些数据放在指定的地址处,那么这时候SCATTER文件就发挥了非常大的作用
而且SCATTER文件用起来非常简单好用。举个例子:比如像LPC2378芯片具有多个不连续的SRAM,通用的RAM是32KB,可是32KB不够用,我想把
某个.C中的RW数据放在USB的SRAM中,那么就可以通过SCATTER文件来完成这个功能。
下面是就这个例子作的说明:
这是一个标准的常用的分散加载文件,现在加注释于后,方便以后查阅:
;******************************************************************************
;
; SCATTER LOADING DEION
; ARM
; KEIL's uVision3
; (RealView Microprocessor Developer Kit)
;
; Filename : LPC2378_Flash.scat
;******************************************************************************LR_IROM1 0x00000000 0x00080000 ;; 第一个加载域,名字为LR_IROM1,起始
{                  ;;地址为0x0,大小为0x80000
ER_IROM1 0x00000000 0x00080000 ;;加载域中的运行时域,名字为ER_IROM1
{ ;; 起始地址为0x0,大小为0x80000
vectors.o (VECT, +First) ;;将vectors.c编译后生成的文件vectors.o中的代码
init.o (INIT) ;;以及init.o中的代码
* (+RO) ;;以及所有编译生成的RO属性的代码全部存放在
} ;;运行时域ER_IROM1指定的地址范围内,存放方式:顺序存放RW_IRAM1 0x40000000 0x0000e800  ;;这是第二个运行时域,功能同上
{ ;;其中 *是代表具有()里面指定的属性的全部数据
*(+RW,+ZI) ;;与*功能相似的有.ANY,后面说明
} ;; The following declarations select the "two region model" ;

;; A default __user_initial_stackheap() will be used ;
ARM_LIB_HEAP 0x40007000 EMPTY 0x00000100 {} ;;指定堆栈地址
ARM_LIB_STACK 0x40008000 EMPTY -0x00000E00 {}
}
下面是针对LPC2378的USB SRAM作数据RAM使用的配置:;******************************************************************************
;
; SCATTER LOADING DEION
; ARM
; KEIL's uVision3
; (RealView Microprocessor Developer Kit)
;
; Filename : LPC2378_Flash.scat
;******************************************************************************LR_IROM1 0x00000000 0x00080000 ;; 第一个加载域,名字为LR_IROM1,起始
{                  ;;地址为0x0,大小为0x80000
ER_IROM1 0x00000000 0x00080000 ;;加载域中的运行时域,名字为ER_IROM1
{ ;; 起始地址为0x0,大小为0x80000
vectors.o (VECT, +First)
init.o (INIT)
* (+RO)
}RW_IRAM1 0x40000000 0x0000e800
{
.ANY(+RW,+ZI)     ;; 此处.ANY替换原来的*,是因为下面的一个执行域对指定的模块中的RW,ZI数据指定了存放地址
;;用.ANY就可以把已经被指定的具有RW,ZI属性的数据排除
} ;; The following declarations select the "two region model" ;




找了3个分散加载文件来分析:1、7x256的flash.sct分散加载文件:Load_region 0x100000 0x40000 {//ro起始地址为0x100000,大小为0x40000

Fixed_region 0x100000 0x40000 {
*(cstartup +First)
.ANY (+RO)
}

Relocate_region 0x200000 {//rw和zi段的地址为0x200000*.o (VECTOR, +First)
.ANY (+RW +ZI)
}

ARM_LIB_HEAP 0x20E000 EMPTY 0x1000 {
}

ARM_LIB_STACK 0x210000 EMPTY -0x1000 {
}
}2、sram.sct文件Load_region 0x200000 0x10000 {

Fixed_region 0x200000 {
*.o (VECTOR, +First)
.ANY (+RO)
}

Relocate_region +0 {
*(cstartup +First)
.ANY (+RW +ZI)
}

ScatterAssert((ImageLength(Fixed_region) + ImageLength(Relocate_region)) < 0xE000)

ARM_LIB_HEAP 0x20E000 EMPTY 0x1000 {
}

ARM_LIB_STACK 0x210000 EMPTY -0x1000 {
}
}3、自定义的sram.sctLR_IROM1 0x00200000 0x00008000 { ; load region size_region
ER_IROM1 0x00200000 0x00008000 { ; load address = execution address//加载域等于运行域
*.o (RESET, +First)
*(InRoot$$Sections)
.ANY (+RO)
}
RW_IRAM1 0x00208000 UNINIT 0x00008000 { ; RW data//rw和zi段
.ANY (+RW +ZI)
}
}什么是分散加载文件这里就不赘述了。前面两个分散加载文件是从别的地方拷过来的,用在自己的程序中可能会有问题,因为如果不修改它的话它就固定了加载地址和运行地址,如果程序简单又比较小的话可能不会有问题,但是如果程序代码比较大,超出了那两个加载文件的定义大小可能就会出问题,解决办法也很简单,直接修改.sct文件直到适合你的代码。更好的办法是自己定义一个分散加载文件,在keil中勾选Use Memory Layout from Target Dialog,那么加载文件就是从你定义irom和iram等地址得到的,如果不勾选的话就是通过你自己指定的加载文件来加载。如果分散加载文件不对的话,可能出现的问题就是明明是在sram中调试程序,但是却能神奇的通过flash downloader下载到flash中去,刚开始也是不解,后来才发现是分散加载文件有错误,我使用了一个指定的flash.sct分散加载文件,这样的话我设置的irom和iram都无效了,编译器直接根据我指定的flash.sct来分布代码和加载代码,又查看了一下flash.sct文件是加代码加载到flash地址空间的,这就是为什么在jlink-sram工程中也能通过flash downloader工具烧写代码到flash中去的原因

页: [1]
查看完整版本: MDK链接脚本控制问题