整套网站建设,网站开发中如何实现gps定位,wordpress 自带模板,怎么建设个人主页网站在上一篇文章IAR中ICF链接文件详解和实例分析中#xff0c;我通过I.MX RT1170的SDK中的内存映射关系#xff0c;分析了IAR中的ICF链接文件的语法。对于MCU编程所使用的IDE来说#xff0c;IAR和Keil用得比较多#xff0c;所以这一篇文章就来分析一下Keil的分散文件.scf(scat…在上一篇文章IAR中ICF链接文件详解和实例分析中我通过I.MX RT1170的SDK中的内存映射关系分析了IAR中的ICF链接文件的语法。对于MCU编程所使用的IDE来说IAR和Keil用得比较多所以这一篇文章就来分析一下Keil的分散文件.scf(scatter file)。 文章目录 1 内存映射2 SCF语法分析2.1 工程的SCF文件2.2 define2.3 加载区域和执行区域 1 内存映射
和上一篇文章一样同样使用I.MX RT1170的SDK中的链接文件进行分析通过实际的分散文件来学习里面的语法。和上一节也是同一个例程除了芯片自带的RAM外还有NOR Flash和SDRAM。首先来看一下整个工程的内存映射表格
类型名称起始地址大小FlashNOR Flash0x300000000x1000000RAMSDRAM0x800000000x3000000RAMNCACHE_REGION0x830000000x1000000RAMSRAM_DTC_cm70x200000000x40000RAMSRAM_ITC_cm70x00x40000RAMSRAM_OC10x202400000x80000RAMSRAM_OC20x202c00000x80000RAMSRAM_OC_ECC10x203400000x10000RAMSRAM_OC_ECC20x203500000x10000
对于我们的工程来说有以下几个内存
两个256KB的紧耦合内存DTCM和ITCM两个带ECC的片内RAMOC1和OC2。在映射的起始地址为0x30000000的FlexSPI1接口上接了一个16MB的NOR Flash在映射的起始地址为0x80000000的FlexSPI2接口上接了一个64MB的SDRAM。其中前48MB用于可缓存的区域后16MB(NCACHE_REGION)用于不可缓存区域通常直接与硬件进行交互的buffer需要设置为不可缓存。
2 SCF语法分析
2.1 工程的SCF文件
针对上面的内存映射官方的SDK中提供的SCF文件如下
#if (defined(__ram_vector_table__))#define __ram_vector_table_size__ 0x00000400
#else#define __ram_vector_table_size__ 0x00000000
#endif#define m_flash_config_start 0x30000400
#define m_flash_config_size 0x00000C00#define m_ivt_start 0x30001000
#define m_ivt_size 0x00000020#define m_boot_data_start 0x30001020
#define m_boot_data_size 0x00000010#define m_dcd_data_start 0x30001030
#define m_dcd_data_size 0x000006E8#define m_xmcd_data_start 0x30001040
#define m_xmcd_data_size 0x00000204#define m_interrupts_start 0x30002000
#define m_interrupts_size 0x00000400#define m_text_start 0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size 0x03FFDC00
#else
#define m_text_size 0x00FFDC00
#endif#define m_qacode_start 0x00000000
#define m_qacode_size 0x00040000#define m_interrupts_ram_start 0x80000000
#define m_interrupts_ram_size __ram_vector_table_size__#define m_data_start (m_interrupts_ram_start m_interrupts_ram_size)
#define m_data_size (0x03000000 - m_interrupts_ram_size)#define m_data2_start 0x20000000
#define m_data2_size 0x00040000#define m_data3_start 0x202C0000
#define m_data3_size 0x00080000#define m_ncache_start 0x83000000
#define m_ncache_size 0x01000000/* Sizes */
#if (defined(__stack_size__))#define Stack_Size __stack_size__
#else#define Stack_Size 0x0400
#endif#if (defined(__heap_size__))#define Heap_Size __heap_size__
#else#define Heap_Size 0x0400
#endif#if defined(XIP_BOOT_HEADER_ENABLE) (XIP_BOOT_HEADER_ENABLE 1)
LR_m_text m_flash_config_start m_text_startm_text_size-m_flash_config_start { ; load region size_regionRW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address execution address* (.boot_hdr.conf, FIRST)}RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address execution address* (.boot_hdr.ivt, FIRST)}RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address execution address* (.boot_hdr.boot_data, FIRST)}#if defined(XIP_BOOT_HEADER_DCD_ENABLE) (XIP_BOOT_HEADER_DCD_ENABLE 1)RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address execution address* (.boot_hdr.dcd_data, FIRST)}
#elif defined(XIP_BOOT_HEADER_XMCD_ENABLE) (XIP_BOOT_HEADER_XMCD_ENABLE 1)RW_m_xmcd_data_text m_xmcd_data_start FIXED m_xmcd_data_size { ; load address execution address* (.boot_hdr.xmcd_data, FIRST)}
#endif
#else
LR_m_text m_interrupts_start m_text_startm_text_size-m_interrupts_start { ; load region size_region
#endifVECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address execution address* (.isr_vector,FIRST)}ER_m_text m_text_start FIXED m_text_size { ; load address execution address* (InRoot$$Sections).ANY (RO)}
#if (defined(__ram_vector_table__))VECTOR_RAM m_interrupts_ram_start EMPTY m_interrupts_ram_size {}
#elseVECTOR_RAM m_interrupts_start EMPTY 0 {}
#endifRW_m_data2 m_data2_start m_data2_size {* (RamFunction)* (DataQuickAccess)}
#if (defined(__heap_noncacheable__))RW_m_data m_data_start m_data_size-Stack_Size { ; RW data
#elseRW_m_data m_data_start m_data_size-Stack_Size-Heap_Size { ; RW data
#endif.ANY (RW ZI)*(*m_usb_dma_init_data)*(*m_usb_dma_noninit_data)}
#if (!defined(__heap_noncacheable__))ARM_LIB_HEAP 0 EMPTY Heap_Size { ; Heap region growing up}
#endifARM_LIB_STACK m_data_startm_data_size EMPTY -Stack_Size { ; Stack region growing down}RW_m_ram_text m_qacode_start m_qacode_size { ;* (CodeQuickAccess)}
#if (defined(__heap_noncacheable__))RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
#elseRW_m_ncache m_ncache_start m_ncache_size { ; ncache data
#endif* (NonCacheable.init)* (*NonCacheable)}
#if (defined(__heap_noncacheable__))ARM_LIB_HEAP 0 EMPTY Heap_Size { ; Heap region growing up}RW_m_ncache_unused 0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
#elseRW_m_ncache_unused 0 EMPTY m_ncache_size-ImageLength(RW_m_ncache) { ; Empty region added for MPU configuration
#endif}
}2.2 define
先来分析第一段分散文件KEIL的分散文件的语法define和#if defined语句与C语言一致所以下面这一段还是很好理解的
#if (defined(__ram_vector_table__))#define __ram_vector_table_size__ 0x00000400
#else#define __ram_vector_table_size__ 0x00000000
#endif#define m_flash_config_start 0x30000400
#define m_flash_config_size 0x00000C00#define m_ivt_start 0x30001000
#define m_ivt_size 0x00000020#define m_boot_data_start 0x30001020
#define m_boot_data_size 0x00000010#define m_dcd_data_start 0x30001030
#define m_dcd_data_size 0x000006E8#define m_xmcd_data_start 0x30001040
#define m_xmcd_data_size 0x00000204#define m_interrupts_start 0x30002000
#define m_interrupts_size 0x00000400#define m_text_start 0x30002400
#if (defined(__use_flash64MB__))
#define m_text_size 0x03FFDC00
#else
#define m_text_size 0x00FFDC00
#endif#define m_qacode_start 0x00000000
#define m_qacode_size 0x00040000#define m_interrupts_ram_start 0x80000000
#define m_interrupts_ram_size __ram_vector_table_size__#define m_data_start (m_interrupts_ram_start m_interrupts_ram_size)
#define m_data_size (0x03000000 - m_interrupts_ram_size)#define m_data2_start 0x20000000
#define m_data2_size 0x00040000#define m_data3_start 0x202C0000
#define m_data3_size 0x00080000#define m_ncache_start 0x83000000
#define m_ncache_size 0x01000000/* Sizes */
#if (defined(__stack_size__))#define Stack_Size __stack_size__
#else#define Stack_Size 0x0400
#endif#if (defined(__heap_size__))#define Heap_Size __heap_size__
#else#define Heap_Size 0x0400
#endif先说明一下I.MX系列单片机上电会进入L1 BootLoader它用来引导程序如何启动比如说是否加密、加密密钥、是XIP还是non-XIP(就要拷贝到RAM)、是否要初始化时钟。在NOR Flash启动的情况下程序镜像的前0x2000字节就是用来给L1 BootLoader提供一些启动信息的这里不必过分关注这些字段的意义若想详细理解可以参考我的这篇文章I.MX RT1170启动详解Boot配置、Bootable image头的组成。 (1)__ram_vector_table__没有在别的地方定义所以__ram_vector_table_size__为0。这也很好理解因为这里有NOR Flash向量表就不放到RAM中了而是放在NOR Flash的最前面。 (2)m_flash_config_start和m_flash_config_size:用来给L1 BootLoader提供NOR Flash的配置信息因为上电后L1 BootLoader用最慢的最保险的配置来初始化NOR Flash如果用户希望自行配置一些参数比如时钟变快一些就可以在这个字段填充配置信息起始地址为0x30000400(NOR Flash的基地址为0x30000000)长度为0xC00 (3)m_ivt_start和m_ivt_sizeIVT(Image Vector Table)字段用来保存程序入口地址等参数 (4)m_boot_data_start和m_boot_data_size用来保存镜像的绝对起始地址和大小 (5)m_dcd_data_start和m_dcd_data_sizeDCD字段一般用来初始化SDRAM特别是希望程序在SDRAM运行的时候需要配置此字段 (6)m_xmcd_data_start和m_xmcd_data_size可以看到这里的起始地址和大小与上面的DCD字段重合了实际上二者的功能类似只不过DCD的配置是一个个寄存器配置的指令比较复杂而XMCD简化了这些配置操作这两个字段是二选一的。 (7)m_interrupts_start和m_interrupts_size前面说了L1 BootLoader的头信息的大小为0x2000所以从0x2000开始就是程序的开始最前面放置向量表长度为0x400。 (8)m_text_start和m_text_size代码段紧接着向量表后面起始地址为0x30002400这里__use_flash64MB__为假我们假设用的是16MB(0x1000000)的NOR Flash剩下的大小就是0x1000000-0x24000x00FFDC00。 (9)m_qacode_start和m_qacode_size即前面内存映射中芯片内部的SRAM_ITC_cm7 (10)m_interrupts_ram_start和m_interrupts_ram_size如果向量表没有放在NOR Flash就放在SDRAM的起始这里由于放在NOR Flash这两个字段没有用到 (11)m_data_start和m_data_sizedata数据段这里将data段放在了SDRAM。这里SDRAM的大小为64M这个字段占了前48M。 (12)m_data2_start、m_data2_size、m_data3_start和m_data3_size同样是data数据段分别为SRAM_ITC_cm7和SRAM_OC2即片内的RAM都可以作为data段放置变量
SRAM_OC1没有用到我们可以自行声明。因为L1 BootLoader运行时用到了这块SRAM所以使用时需要考虑使用这块SRAM的时间。
(13)m_ncache_start和m_ncache_size即SDRAM最后的16M用来做non-cacheable区域比如GUI绘制的Buffer、摄像头的Buffer和DMA的数据这种直接与硬件交互的内存需要定义在不可缓存的区域。这与MPU配置有关可以参考我的MPU系列的文章MPU内存保护单元详解及例子和L1 Cache之I-Cache和D-cache详解。 (14)Stack_Size和Heap_Size分别为栈和堆的大小由于程序中使用了FreeRTOS所以只要保证这里的栈和堆的大小能够成功初始化FreeRTOS即可初始化FreeRTOS过程应该没有内存分配所以Heap Size可以设置为0。
2.3 加载区域和执行区域
接下来开始涉及到一些分散文件的语法参考文档DUI0377G_02_mdk_armlink_user_guide.pdf(可以在KEIL安装目录下找到)。 相比IARKEIL的分散文件的语法简单地多和Linux的ld文件差不多分散文件就由一个或多个加载区域(Load Region)构成如下图所示 加载区域的语法如下
load_region_name (base_address | ( offset)) [attribute_list] [max_size]
{
execution_region_description
}load_region_name(名称) 用于由链接器识别不同加载区域的独特标签每个加载区域必须具有唯一的名称base_address(基地址)加载区域内的代码和数据在内存中放置的起始内存地址attribute_list(属性) 定义加载区域的特性和行为包括只读、读写、仅执行或其他内存保护属性max_size(最大大小) 可选用于限制加载区域的大小防止内存溢出execution_region_description(执行区域) 加载区域可以包含一个或多个执行区域。执行区域表示连续的代码和数据块作为一个单独的单元加载到内存中
如果要把所有语法都总结到文章中就太耗时了所以还是继续分析分散文件出现了什么语法或关键字我们再来去找它的意思。由于后面的分散文件中的宏定义太多而影响阅读这里假设XIP_BOOT_HEADER_ENABLE1、XIP_BOOT_HEADER_DCD_ENABLE1、XIP_BOOT_HEADER_XMCD_ENABLE0和__heap_noncacheable__(表示将堆放置在non-cacheable区域保证堆内存不会收到缓存的影响)。
剩下的分散文件实际上就是定义了一个加载区域LR_m_text它的起始地址为m_flash_config_start(0x30000400)最大的大小为m_text_startm_text_size-m_flash_config_start(16M-0x4000xFFFC00)即从0x30000400处开始链接大小为0xFFFC00这个大小仅限制加载区域的大小(下面属性为FIXED的执行区域)。0~0x400与NXP RT系列单片机的加密启动有关这些字段编译器无法进行填充所以这里就没有考虑。
LR_m_text m_flash_config_start m_text_startm_text_size-m_flash_config_start { ; load region size_region......
}在分散文件中;后面为注释
在加载区域LR_m_text下有非常多个执行区域下面来一个个分析一下
1、RW_m_config_text起始地址0x30000400大小0xC00
RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address execution address* (.boot_hdr.conf, FIRST)
}FIXED执行区域的属性表示让执行区域的执行地址与加载地址尽量保持相等。这意味着分配给这个执行区域的代码和数据在加载到内存时会尽量放置在指定的执行地址上。如果因为内存冲突或空间不足等原因无法满足则链接器会报错。FIRST表示把该section放在该执行区域的最开始的地方
所以这里就是从0x30000400开始处开始放置boot_hdr.conf段因为放置的位置必须固定才能被L1 BootLoader正确识别所以执行区域需要用FIXED属性。
2、RW_m_ivt_text起始地址0x30001000大小0x00000020
RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address execution address* (.boot_hdr.ivt, FIRST)
}同上放置L1 BootLoader的引导头。
3、RW_m_boot_data_text起始地址0x30001020大小0x00000010
RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address execution address* (.boot_hdr.boot_data, FIRST)
}同上放置L1 BootLoader的引导头。
4、RW_m_dcd_data_text起始地址0x30001030大小0x000006E8
RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address execution address* (.boot_hdr.dcd_data, FIRST)
}同上放置L1 BootLoader的引导头。
5、VECTOR_ROM起始地址0x30002000大小0x00000400
VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address execution address* (.isr_vector,FIRST)
}放置中断向量表。在启动文件startup_MIMXRT1176_cm7.S文件中定义了该段.section .isr_vector, a这里的a表示将该段标记为可分配(allocatable)的意味着它在链接时可以被分配到内存中的某个位置。
6、ER_m_text起始地址0x30002400大小0x00FFDC00
ER_m_text m_text_start FIXED m_text_size { ; load address execution address* (InRoot$$Sections).ANY (RO)
}InRoot$$Sections是在分散文件中使用的特殊标记用来将压缩数据段放置该执行区域中以确保这些数据段在运行时能够被自动解压缩并提供给程序使用。该特性是ARM为了减少存储空间占用设计的上电后ARM库会根据此段来进行解压。 参考文章Example of placing code in a root region .ANY可以理解为*表示所有段但.ANY可以用在多个执行区域中而*一般只用在一个执行区域中所以.ANY会更灵活一些。具体参考手册7.4章节Placement of unassigned sections with the .ANY module selector(RO)只读数据段
这里表示将所有的只读数据段放置在这个执行区域。
7、VECTOR_RAM 这里将中断向量表放置在NOR Flash了这个执行区域没有用到
VECTOR_RAM m_interrupts_start EMPTY 0 {
}EMPTY表示保留一个空区域但这里区域的大小设为0所以这段执行区域没有任何作用
8、RW_m_data2起始地址0x20000000大小0x00040000
RW_m_data2 m_data2_start m_data2_size {* (RamFunction)* (DataQuickAccess)
}这里定义了两个SectionRamFunction和DataQuickAccess在程序中都可以用__attribute__((section()))来定义函数或变量到内部的SRAM_DTCM中可以加快函数的执行速度和数据的访问速度。
9、RW_m_data起始地址0x80000000大小0x03000000-0x400
RW_m_data m_data_start m_data_size-Stack_Size { ; RW data.ANY (RW ZI)*(*m_usb_dma_init_data)*(*m_usb_dma_noninit_data)
}这个执行区域就是SDRAM的前48M将所有的读写数据段和bss段放置在此同时声明两个usb段用于SDK中对于USB相关功能的实现。实际上USB段放在non-cacheable区域肯定是可以运行的但是同时也意味着没有用到缓存速度就会降低很多。所以就可以将USB相关变量声明到cacheable的区域然后在代码中必要的地方手动缓存更新相关函数如SCB_CleanInvalidateDCache和SCB_CleanDCache。
10、ARM_LIB_STACK起始地址0x83000000大小0x400
ARM_LIB_STACK m_data_startm_data_size EMPTY -Stack_Size { ; Stack region growing down
}ARM_LIB_STACK栈的执行区域的固定名称
这里的Stack_Size的前面有一个-表示栈是向下生长的。
11、RW_m_ram_text起始地址0x00000000大小0x00040000
RW_m_ram_text m_qacode_start m_qacode_size { ;* (CodeQuickAccess)
}与上面的7类似声明一个CodeQuickAccess段用于将函数链接到内部的SRAM_ITCM中因为内部的SRAM的速度比NOR Flash或SDRAM的访问速度都快得多。
12、RW_m_ncache起始地址0x83000000大小0x01000000-0x400
RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data* (NonCacheable.init)* (*NonCacheable)
}定义non-cacheable区域的两个段NonCacheable.init和NonCacheable同时预留堆的空间因为这里我们假设堆空间也为non-cacheable。
13、ARM_LIB_HEAP大小0x400
ARM_LIB_HEAP 0 EMPTY Heap_Size { ; Heap region growing up
}0表示为上一个执行区域的结束地址根据用户放置到RW_m_ncache执行区域的变量的多少和大小来决定这个地址ARM_LIB_HEAP堆的执行区域的固定名称
定义堆空间的内存。
14、RW_m_ncache_unused
RW_m_ncache_unused 0 EMPTY m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size { ; Empty region added for MPU configuration
}同样放置在上一个执行区域的结束地址处用来给MPU进行配置大小m_ncache_size-ImageLength(RW_m_ncache)-Heap_Size即除去变量和堆外的剩下的non-cacheable区域。
ImageLength可以取某个执行区域占的大小