RT-1052学习笔记(3 )-分散加载文件

复习MDK链接加载程序过程

例子:各种数据所存放的段

#define       DATA      (0x10000000)     /* RO-Data                   */ 
char const    GcChar = 5;                /* RO-Data                   */
char          GcStr[] = "string.";       /* RW-Data                   */
char          GcZero;                    /* ZI-Data                   */ 

在随意一个MDK工程的listings文件夹中都会生成一个map文件,最底下会有链接信息,可知ROM会包括Code ,RO Data以及RW Data段。其中,前面两个段容易理解,代码段和只读数据段自然是存放在flash中。

==============================================================================
      Code (inc. data)   RO Data    RW Data    ZI Data      Debug   
     11080       1488       2976         28       2084    2984889   Grand Totals
     11080       1488       2976         28       2084    2984889   ELF Image Totals
     11080       1488       2976         28          0          0   ROM Totals
==============================================================================
    Total RO  Size (Code + RO Data)                14056 (  13.73kB)
    Total RW  Size (RW Data + ZI Data)              2112 (   2.06kB)
    Total ROM Size (Code + RO Data + RW Data)      14084 (  13.75kB)
==============================================================================
  • 问1:但是,RW?为什么数据段信息也存放在flash中?
  • 答1:RW段包含所有已经在代码中初始化的所有变量。在程序编译的时候把数据存放到FLASH中,然后在程序运行的时候,会把RW段数据复制到在分散加载文件中指定的地址空间上
  • 问2:这个动作是怎么发生的?谁来执行它?硬件还是软件?什么时候执行?
  • 答2:这个动作在启动文件的复位中断Reset_Handler中,在SystemInit函数执行完成之后会跳转到_main函数执行。然后在_main函数会完成RW段的重定位以及ZI段的清零,再跳转到main函数中运行

分散加载文件简介

i.MX-RT开发为什么需要自定义分散加载文件

由于RT1052没有内置flash,需要外界FLASH以实现程序的存放。再加上外扩RAM的存在,每个板子设计的flash和ram容量大小都不相同,使得在程序链接以及运行时必须根据这些信息以实现程序的链接以及初始化。也就是把不同的代码数据放在不同的存储空间,而且,在程序链接时,会包含有各种各样的域,标准的,自定义的域。

需要使用到自动以分散加载文件的情况:

  • 复杂的内存映射:不同区域存放不同数据和代码
  • 不同类型的内存:把需求不同的数据放置于不同的位置,ITCM,SDRAM,OCRAM
  • 内存映射I/O:把某些数据字节准确放置于某个内存,以便能够访问内存映射的外围设备,比如初始化SDRAM
  • 位于固定位置的函数:比如一些固定属性的函数列表
  • 使用符号标识堆和堆栈:可以在堆或者栈中定义一些自定义的符号。

加载文件语法

首先,分散加载的语法遵循 Backus-Naur Form规范.

作者:猫山王
来源:CSDN
原文:https://blog.csdn.net/PINBODEXIAOZHU/article/details/25389441
版权声明:本文为博主原创文章,转载请附上博文链接!


巴科斯范式的内容
在双引号中的字(“word”)代表着这些字符本身。而double_quote用来代表双引号。
在双引号外的字(有可能有下划线)代表着语法部分。
尖括号( < > )内包含的为必选项。
方括号( [ ] )内包含的为可选项。
大括号( { } )内包含的为可重复0至无数次的项。
竖线( | )表示在其左右两边任选一项,相当于"OR"的意思。
::= 是“被定义为”的意思。

基本概念

  • 加载时域:域所在的flash地址空间
  • 运行时域:程序运行时域所在的空间

加载时域:一个加载时域可以包含多个运行时域

load_region_name (base_address | ("+" offset)) [attribute_list] [max_size]
"{"
execution_region_description+
"}"
  • load_region_name :为本加载时域的名称,名称可以按照用户意愿自己定义,该名称中只有前 31 个字符有意义
  • base_address:表示本加载时域中的对象在连接时的起始地址,地址必须是字对齐的
  • +offset:表示本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移量 offset 字节处。本加载时域是第一个加载时域,则它的起始地址即为 offset, offset 的值必须能被 4 整除
  • attribute_list:指定本加载时域内容的属性
    • ABSOLUTE:绝对地址;PI:与位置无关;RELOC:可重定位;OVERLAY:覆盖;NOCOMPRESS:不能进行压缩
  • max_size:指定本加载时域的最大尺寸。如果本加载时域的实际尺寸超过了该值,连接器将报告错误, 默认取值为 0xFFFFFFFF;
  • execution_region_description:表示运行时域,后面有个+号,表示其可以有一个或者多个运行时域

运行时域语法

exec_region_name(base_address|"+"offset)[attribute_list][max_size|" "length]
{
	input_section_description*
}
  • exec_region_name:为本加载时域的名称,名称可以按照用户意愿自己定义,该名称中只有前31个字符有意义;
  • base_address:表示本加载时域中的对象在连接时的起始地址,地址必须是字对齐的;
  • +offset:表示本加载时域中的对象在连接时的起始地址是在前一个加载时域的结束地址后偏移量offset 字节处,offset的值必须能被4整除。
  • attribute_list: 指定本加载时域内容的属性:
    • ABSOLUTE:绝对地址;
    • PI:与位置无关;
    • RELOC:可重定位;
    • OVERLAY:覆盖;
    • FIXED:固定地址。区加载地址和执行地址都是由基址指示符指定的,基址指示符必须是绝对基址,或者偏移为 0。
    • ALIGNalignment:将执行区的对齐约束从4增加到alignment。alignment必须为2的正数幂。如果执行区具有base_address,则它必须为alignment对齐。如果执行区具有offset,则链接器将计算的区基址与alignment边界对齐;
    • EMPTY:在执行区中保留一个给定长度的空白内存块,通常供堆或堆栈使用。
    • ZEROPAD:零初始化的段作为零填充块写入ELF文件,因此,运行时无需使用零进行填充;
    • PADVALUE:定义任何填充的值。如果指定 PADVALUE,则必须为其赋值;
    • NOCOMPRESS:不能进行压缩;
    • UNINIT:未初始化的数据。
    • max_size:指定本加载时域的最大尺寸。如果本加载时域的实际尺寸超过了该值,连接器将报告错误,默认取值为0xFFFFFFFF
    • length:如果指定的长度为负值,则将 base_address 作为区结束地址。它通常与EMPTY 一起使用,以表示在内存中变小的堆栈。
  • input_section_description:指定输入段的内容。

输入段描述

module_select_pattern["("input_section_selector(","input_section_selector)*")"]
	("+" input_section_attr | input_section_pattern | input_symbol_pattern)
  • module_select_pattern:目标文件滤波器,支持使用通配符“”与“?”。其中符号“”代表零个或多个字符,符号“?”代表单个字符。进行匹配时所有字符不区分大小写。当module_select_pattern与以下内容之一相匹配时,输入段将与模块选择器模式相匹配:
    • 包含段和目标文件的名称;
    • 库成员名称(不带前导路径名);
    • 库的完整名称(包括路径名)。如果名称包含空格,则可以使用通配符简化搜索。例如,使用*libname.lib 匹配 C:\lib dir\libname.lib。
  • nput_section_attr:属性选择器与输入段属性相匹配。每个input_section_attr的前面有一个“+”号。如果指定一个模式以匹配输入段名称,名称前面必须有一个“+”号。可以省略紧靠“+”号前面的任何逗号。 选择器不区分大小写。可以识别以下选择器:
    • RO-CODE;
    • RO-DATA;
    • RO,同时选择 RO-CODE 和 RO-DATA;
    • RW-DATA;
    • RW-CODE;
    • RW,同时选择 RW-CODE 和 RW-DATA;
    • ZI;
    • ENTRY:即包含 ENTRY 段。
    • 可以识别以下同义词:
      • CODE 表示 RO-CODE;
      • CONST 表示 RO-DATA;
      • TEXT 表示 RO;
      • DATA 表示 RW;
      • BSS 表示 ZI。
    • 可以识别以下伪属性:
      • FIRST;
      • LAST。
      • 使用FIRST和LAST去标志一个运行域的第一个段个最后一个段
  • 通过使用特殊模块选择器模式.ANY 可以将输入段分配给执行区,而无需考虑其父模块。可以使用一个或多个.ANY 模式以任意分配方式填充运行时域。在大多数情况下,使用单个.ANY 等效于使用*模块选择器。

分散加载文件分析

  • 目的:能看懂一个分散加载文件。

上面介绍了三个分散加载文件的语法,现在先不直接上RT1052复杂的分散加载文件,先来个简单的STM32F103C8T6的分散加载文件,分散加载文件主要由一个加载时域和多个运行时域组成。

LR_IROM1 0x08000000 0x00010000{;定义一个加载时域,域基地址:0x08000000,大小:0x00010000
								;这个空间就是STM32flash的实际信息	
	ER_IROM1 0x08000000 0x00010000 {	;定义第一个一个运行时域,第一个运行时域必须和加载
										;时域起始地址相同,否则库不能加载到该时域的
										;错误,其域大小一般也和加载时域大小相同
		*.o (RESET, +First)				;RESET在启动文件中有定义,为一个异常向量表
		*(InRoot$$Sections)				;MDK自动生成的段,主要在_main中执行
		.ANY (+RO)						;剩下所有代码段
	}
	 RW_IRAM1 0x20000000 0x00005000  {;定义第二个运行时域,域基地址:0x08000000,大小:0x00005000
		.ANY (+RW +ZI)					;所有数据段
	}
}

下面来分析RT1052的分散加载文件

  • 工程配置:RT1052的分散加载文件当然是要自己写啦。
    RT-1052学习笔记(3 )-分散加载文件_第1张图片
; 外部QSPI FLASH 起始地址:0x60000000
; 存放QSPI FLASH 参数,如几线制的 SPI Flash, SPI 的时钟频率, LUT 查找表, DDR/SDR 模式以及片选 CS管脚的 hold/setup time 等信息
; 0x00001000 = 4KB
#define m_flash_config_start           0x60000000
#define m_flash_config_size            0x00001000

; 存放Image Vector Table(IVT)
#define m_ivt_start                    0x60001000
#define m_ivt_size                     0x00001000

; 存放中断向量表
#define m_interrupts_start             0x60002000
#define m_interrupts_size              0x00000400

; 程序从这里开始存储
#define m_text_start                   0x60002400
#define m_text_size                    0x01FFDC00


; ITCM :起始地址:0000_0000  ,大小128KB
; DTCM :起始地址:2000_0000  ,大小128KB
; OCRAM:起始地址:2020_0000  ,大小256KB

; ITCM,128KB,用来存放指令
;#define m_data_start                   0x00000000
;#define m_data_size                    0x00020000


; DTCM,128KB,用来存放数据
#define m_data_start                   0x20000000
#define m_data_size                    0x00020000


; OCRAM,256KB,用来存放数据
#define m_data2_start                  0x20200000
#define m_data2_size                   0x00040000

/* Sizes */
#if (defined(__stack_size__))
  #define Stack_Size                   __stack_size__
#else
  ; 0x0400 = 1KB  
  #define Stack_Size                   0x0400
#endif

#if (defined(__heap_size__))
  #define Heap_Size                    __heap_size__
#else
  #define Heap_Size                    0x0400
#endif



LR_m_rom_config m_flash_config_start m_flash_config_size {   ; load region size_region
	RW_m_config_text m_flash_config_start m_flash_config_size { ; load address = execution address
	* (.boot_hdr.conf, +FIRST)
	}
}

LR_m_rom_ivt m_ivt_start m_ivt_size {   ; load region size_region
	RW_m_ivt_text m_ivt_start m_ivt_size { ; load address = execution address
	* (.boot_hdr.ivt, +FIRST)
	* (.boot_hdr.boot_data)
	* (.boot_hdr.dcd_data)
	}
}

LR_m_text m_interrupts_start m_text_start+m_text_size-m_interrupts_size {   ; load region size_region
  VECTOR_ROM m_interrupts_start m_interrupts_size { ; load address = execution address
    * (RESET,+FIRST)
  }
  ER_m_text m_text_start m_text_size { ; load address = execution address
    * (InRoot$$Sections)
    .ANY (+RO)
  }
  RW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data
    .ANY (+RW +ZI)
    * (NonCacheable.init)
    * (NonCacheable)
  }
  ARM_LIB_HEAP +0 EMPTY Heap_Size { ; Heap region growing up
  }
  ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
  }
}

FLASH和SRAM分析

加载域 域描述
0x60000000~0x60000FFF .boot_hdr_conf 外部QSPI FLASH 起始地址:0x60000000。存放QSPI FLASH 参数,如几线制的 SPI Flash, SPI 的时钟频率, LUT 查找表, DDR/SDR 模式以及片选 CS管脚的 hold/setup time 等信息
0x60001000~0x60001FFF .boot_hdr.ivt + .boot_hdr.boot_data + .boot_hdr.dcd_data 存放Image Vector Table(IVT)
0x60002000~0x600023FF RESET 存放中断向量表
0x60002400~0x‭62000000‬ InRoot$$Sections + .ANY (+RO) 程序从这里开始存储
0x00000000~0x0001FFFF ITCM ITCM 128KB,用来缓存指令。因为在程序连接时不需要,所以没写在scf上
0x20000000~0x2001F7FF DTCM DTCM,126KB,用来存放数据
0x2001F800~0x2001FBFF Heap Heap,1KB,系统堆区,往上增长
0x2001FC00~0x2001FFFF Stack Stack,1KB,系统栈区,往下增长

参考文献

C:\Keil_v5\ARM\Hlp\DUI0377G_02_mdk_armlink_user_guide.pdf
分散加载文件浅析-周立功.pdf

你可能感兴趣的:(C语言,i.MX,RT,RT1052)