Embedded IDE (2): Detailed explanation and example analysis of SCF scattered loading link files in KEIL

In the previous article ICF link file detailed explanation and example analysis in IAR , I analyzed the syntax of the ICF link file in IAR through the memory mapping relationship in the I.MX RT1170 SDK. For IDEs used in MCU programming, IAR and Keil are used more frequently, so this article will analyze Keil’s scattered files .scf( scatter file).

1 memory map

Like the previous article, I also use the link file in the SDK of I.MX RT1170 for analysis, and learn the grammar inside through the actual scattered file. It is the same routine as the previous section. In addition to the RAM that comes with the chip, there are NOR Flash and SDRAM. First look at the memory mapping table of the entire project:

type name initial address size
Flash NOR Flash 0x30000000 0x1000000
RAM SDRAM 0x80000000 0x3000000
RAM NCACHE_REGION 0x83000000 0x1000000
RAM SRAM_DTC_cm7 0x20000000 0x40000
RAM SRAM_ITC_cm7 0x0 0x40000
RAM SRAM_OC1 0x20240000 0x80000
RAM SRAM_OC2 0x202c0000 0x80000
RAM SRAM_OC_ECC1 0x20340000 0x10000
RAM SRAM_OC_ECC2 0x20350000 0x10000

For our project, there are the following memories:

  1. Two 256KB tightly coupled memories DTCMandITCM
  2. Two on-chip RAMs with ECC: OC1and OC2.
  3. 0x30000000A 16MB NOR Flash is connected to the FlexSPI1 interface whose starting address is
  4. 0x80000000A 64MB SDRAM is connected to the FlexSPI2 interface whose starting address is mapped . Among them, the first 48MB is used for the cacheable area, and the last 16MB ( NCACHE_REGION) is used for the uncacheable area. Generally, the buffer that directly interacts with the hardware needs to be set as uncacheable.

2 SCF syntax analysis

2.1 SCF file of the project

For the memory mapping above, the files provided in the official SDK SCFare as follows:

#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_start+m_text_size-m_flash_config_start {   ; load region size_region
  RW_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_start+m_text_size-m_interrupts_start {   ; load region size_region
#endif
  VECTOR_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 {
  }
#else
  VECTOR_RAM m_interrupts_start EMPTY 0 {
  }
#endif
  RW_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
#else
  RW_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
  }
#endif
  ARM_LIB_STACK m_data_start+m_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
#else
  RW_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
#else
  RW_m_ncache_unused +0 EMPTY m_ncache_size-ImageLength(RW_m_ncache) { ; Empty region added for MPU configuration
#endif
  }
}

2.2 define

Let’s first analyze the first paragraph of scattered files. The syntax defineand #if definedstatements of KEIL’s scattered files are consistent with the C language, so the following paragraph is easy to understand:

#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

Let me explain first, when the I.MX series microcontroller is powered on, it will enter the L1 BootLoader, which is used to boot how to start the program, such as whether it is encrypted, the encryption key, whether it is XIP or non-XIP (it needs to be copied to RAM), and whether it needs to be initialized. clock. In the case of NOR Flash startup, the first 0x2000 bytes of the program image are used to provide some startup information to the L1 BootLoader. There is no need to pay too much attention to the meaning of these fields. If you want to understand in detail, you can refer to my article I.MX Detailed explanation of RT1170 startup: Boot configuration and composition of Bootable image header .
(1) __ram_vector_table__is not defined elsewhere, so __ram_vector_table_size__it is 0. This is also easy to understand, because there is NOR Flash here, the vector table is not placed in RAM, but placed at the front of NOR Flash.
(2) m_flash_config_startand m_flash_config_size: used to provide NOR Flash configuration information to L1 BootLoader, because after power-on, L1 BootLoader uses the slowest and safest configuration to initialize NOR Flash. If the user wants to configure some parameters by himself, such as making the clock faster, You can fill in the configuration information in this field, the starting address is 0x30000400(the base address of NOR Flash is 0x30000000), the length is 0xC00
(3) m_ivt_startand m_ivt_size: IVT ( Image Vector Table) field, used to save parameters such as program entry address
(4) m_boot_data_startand m_boot_data_size: used Save the absolute starting address and size of the image
(5) m_dcd_data_startand m_dcd_data_size: DCDfields, which are generally used to initialize SDRAM. Especially if you want the program to run in SDRAM, you need to configure this field
(6) m_xmcd_data_startandm_xmcd_data_size: You can see that the starting address and size here DCDoverlap with the fields above. In fact, the functions of the two are similar, except that DCDthe configuration is a register configuration instruction, which is more complicated, and XMCD simplifies these configuration operations. This The two fields are optional.
(7) m_interrupts_startand m_interrupts_size: As mentioned earlier, the size of the header information of the L1 BootLoader is 0x2000, so 0x2000it is the beginning of the program from the beginning, and the vector table is placed at the front, and the length is 0x400.
(8) m_text_startand m_text_size: The code segment is immediately after the vector table, the starting address is 0x30002400, here __use_flash64MB__is false, we assume that 16MB ( ) NOR Flash is used 0x1000000, and the remaining size is 0x1000000-0x2400=0x00FFDC00.
(9) m_qacode_startSum : That is , (10) m_qacode_sizesum inside the chip in the previous memory map : If the vector table is not placed in NOR Flash, it is placed at the beginning of SDRAM. Since it is placed in NOR Flash, these two fields are not used ( 11) and : data data segment, where the data segment is placed in SDRAM. Here the size of SDRAM is 64M, and this field occupies the first 48M. (12) , , and : the same data segment, respectively and , that is, the on-chip RAM can be used as the data segment to place variablesSRAM_ITC_cm7
m_interrupts_ram_startm_interrupts_ram_size
m_data_startm_data_size
m_data2_startm_data2_sizem_data3_startm_data3_sizeSRAM_ITC_cm7SRAM_OC2

  • SRAM_OC1If it is not used, we can declare it ourselves. Because L1 BootLoader uses this SRAM when running, you need to consider the time when using this SRAM.

(13) m_ncache_startand m_ncache_size: The last 16M of SDRAM is used as a non-cacheable area, such as the Buffer drawn by the GUI, the Buffer of the camera, and the DMA data. This kind of memory that directly interacts with the hardware needs to be defined in a non-cacheable area. This is related to the MPU configuration. You can refer to my MPU series articles MPU memory protection unit detailed explanation and examples and L1 Cache I-Cache and D-cache detailed explanation .
(14) Stack_Sizeand Heap_Size: the size of the stack and heap respectively. Since FreeRTOS is used in the program, it is only necessary to ensure that the size of the stack and heap here can successfully initialize FreeRTOS. There should be no memory allocation during the initialization of FreeRTOS, so it can be set to Heap Size0 .

2.3 Loading area and execution area

Next, we will start to talk about the syntax of some scattered files. Please refer to the documentation: <DUI0377G_02_mdk_armlink_user_guide.pdf>(can be found in the KEIL installation directory).
Compared with IAR, the syntax of KEIL's scatter file is much simpler, similar to that of Linux's ld file. The scatter file consists of one or more loading areas ( Load Region), as shown in the figure below:
Insert image description here
the syntax of the loading area is as follows:

load_region_name (base_address | ("+" offset)) [attribute_list] [max_size]
"{"
execution_region_description+
"}"
  • load_region_name(name): A unique label used to identify different load areas by the linker, each load area must have a unique name
  • base_address(Base address): The starting memory address where the code and data in the loading area are placed in memory.
  • attribute_list(property): Defines the characteristics and behavior of the load area, including read-only, read-write, execute-only, or other memory protection attributes
  • max_size(maximum size): Optional, used to limit the size of the loading area to prevent memory overflow
  • execution_region_description(Execution Region): The load region can contain one or more execution regions. The execution region represents a contiguous block of code and data loaded into memory as a single unit

It would be too time-consuming to summarize all the grammars into the article, so continue to analyze the scattered files, what grammar or keywords appear, and then we will find its meaning. Because there are too many macro definitions in the following scatter files, the reading is affected. Here, it is assumed that XIP_BOOT_HEADER_ENABLE=1, XIP_BOOT_HEADER_DCD_ENABLE=1, XIP_BOOT_HEADER_XMCD_ENABLE=0and __heap_noncacheable__(indicate that the heap is placed in a non-cacheable area to ensure that the heap memory will not be affected by the cache).

The rest of the scatter file actually defines a loading area LR_m_text, its starting address is m_flash_config_start( 0x30000400), and its maximum size is m_text_start+m_text_size-m_flash_config_start( 16M-0x400=0xFFFC00), that is, 0x30000400the link starts from there, and the size is 0xFFFC00, this size only limits the size of the loading area (the following attribute for FIXEDthe execution region). 0~0x400Relevant to the encrypted startup of NXP RT series microcontrollers, these fields cannot be filled by the compiler, so they are not considered here.

LR_m_text m_flash_config_start m_text_start+m_text_size-m_flash_config_start {   ; load region size_region
	......
}
  • In scatter files, ;followed by comments

LR_m_textThere are many execution areas under the loading area . Let’s analyze them one by one:

1.RW_m_config_text : Starting address 0x30000400, size0xC00

RW_m_config_text m_flash_config_start FIXED m_flash_config_size { ; load address = execution address
	* (.boot_hdr.conf, +FIRST)
}
  • FIXED: Attributes of the execution area, indicating that the execution address and the load address of the execution area should be kept equal as much as possible. This means that the code and data allocated to this execution region will try to be placed at the specified execution address when loaded into memory. If it cannot be satisfied due to memory conflicts or insufficient space, the linker will report an error.
  • +FIRST: Indicates sectionplacing the function at the beginning of the execution area

So here is to start placing the segment from 0x30000400 boot_hdr.conf, because the placement position must be fixed to be correctly recognized by the L1 BootLoader, so the execution area needs to use FIXEDattributes.

2.RW_m_ivt_text : Starting address 0x30001000, size0x00000020

RW_m_ivt_text m_ivt_start FIXED m_ivt_size { ; load address = execution address
	* (.boot_hdr.ivt, +FIRST)
}

Same as above, place the boot header of L1 BootLoader.

3.RW_m_boot_data_text : start address 0x30001020, size0x00000010

RW_m_boot_data_text m_boot_data_start FIXED m_boot_data_size { ; load address = execution address
	* (.boot_hdr.boot_data, +FIRST)
}

Same as above, place the boot header of L1 BootLoader.

4.RW_m_dcd_data_text : start address 0x30001030, size0x000006E8

RW_m_dcd_data_text m_dcd_data_start FIXED m_dcd_data_size { ; load address = execution address
	* (.boot_hdr.dcd_data, +FIRST)
}

Same as above, place the boot header of L1 BootLoader.

5.VECTOR_ROM : start address 0x30002000, size0x00000400

VECTOR_ROM m_interrupts_start FIXED m_interrupts_size { ; load address = execution address
	* (.isr_vector,+FIRST)
}

Place the interrupt vector table. startup_MIMXRT1176_cm7.SThe section is defined in the startup file: .section .isr_vector, "a", where athe section is marked as allocatable ( allocatable), which means that it can be allocated to a certain location in memory when linking.

6.ER_m_text : start address 0x30002400, size0x00FFDC00

ER_m_text m_text_start FIXED m_text_size { ; load address = execution address
	* (InRoot$$Sections)
	.ANY (+RO)
}
  • InRoot$$SectionsIt is a special mark used in the scatter file to place compressed data segments in the execution area to ensure that these data segments can be automatically decompressed and provided to the program at runtime. This feature is designed by ARM to reduce the storage space occupied. After power-on, the ARM library will decompress according to this section.
  • .ANY: It can be understood as *representing all segments, but .ANYit can be used in multiple execution areas, but *generally only used in one execution area, so .ANYit will be more flexible. For details, refer to Chapter 7.4 of the manual<Placement of unassigned sections with the .ANY module selector>
  • (+RO): read-only data segment

This means placing all read-only data segments in this execution area.

7.VECTOR_RAM : The interrupt vector table is placed in NOR Flash here. This execution area is not used.

VECTOR_RAM m_interrupts_start EMPTY 0 {
}
  • EMPTYIndicates that an empty area is reserved, but the size of this area is set to 0, so this execution area has no effect.

8.RW_m_data2 : start address 0x20000000, size0x00040000

RW_m_data2 m_data2_start m_data2_size {
	* (RamFunction)
	* (DataQuickAccess)
}

Two Sections are defined here: RamFunctionand DataQuickAccess, which can __attribute__((section("")))be used to SRAM_DTCM, which can speed up the execution speed of functions and the access speed of data.

9.RW_m_data : Starting address 0x80000000, size0x03000000-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)
}

This execution area is the first 48M of SDRAM, where all read and write data segments and bss segments are placed, and two usb segments are declared at the same time, which are used to implement USB-related functions in the SDK. In fact, the USB segment non-cacheablecan definitely run if placed in the zone, but it also means that the cache is not used and the speed will be reduced a lot. Therefore, you can declare USB related variables to cacheablethe area, and then manually cache and update related functions in necessary places in the code, such as SCB_CleanInvalidateDCacheand SCB_CleanDCache.

10.ARM_LIB_STACK : Starting address 0x83000000, size0x400

ARM_LIB_STACK m_data_start+m_data_size EMPTY -Stack_Size { ; Stack region growing down
}
  • ARM_LIB_STACK: Fixed name of the execution area of ​​the stack

Stack_SizeThere is a in front of it -, indicating that the stack is growing downward.

11.RW_m_ram_text : Starting address 0x00000000, size0x00040000

RW_m_ram_text m_qacode_start m_qacode_size { ;
	* (CodeQuickAccess)
}

Similar to 7 above, declare a CodeQuickAccesssegment to link the function to the internal SRAM_ITCM, because the internal SRAM is much faster than the access speed of NOR Flash or SDRAM.

12.RW_m_ncache : start address 0x83000000, size0x01000000-0x400

RW_m_ncache m_ncache_start m_ncache_size - Heap_Size { ; ncache data
	* (NonCacheable.init)
	* (*NonCacheable)
}

Define the sum non-cacheableof the two segments of the area NonCacheable.initand NonCacheablereserve heap space, because here we assume that the heap space is also non-cacheable.

13.ARM_LIB_HEAP : Size0x400

ARM_LIB_HEAP +0 EMPTY Heap_Size {    ; Heap region growing up
}
  • +0: Represents the end address of the previous execution area. RW_m_ncacheThis address is determined based on the number and size of variables placed by the user in the execution area.
  • ARM_LIB_HEAP: fixed name of the execution region of the heap

Defines the memory of the heap space.

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
}

It is also placed at the end address of the previous execution area and is used to configure the MPU. The size is the m_ncache_size-ImageLength(RW_m_ncache)-Heap_Sizeremaining non-cacheablearea except variables and heap.

  • ImageLengthYou can take the size of an execution area

Guess you like

Origin blog.csdn.net/tilblackout/article/details/132715348