Embedded IDE (1): Detailed explanation and example analysis of ICF link files in IAR

Recently I have been using the MCUXPresso IDE provided by NXP. In addition to the inherent advantages of Eclipse, I think its biggest advantage is that it provides a very intuitive GUI configuration interface for the generation of link scripts. But this IDE only supports NXP-related products, and the debugging performance is not ideal in some cases. The IDEs we use more often are Keil and IAR. Both IDEs have their own formats for generating link scripts. This article will introduce the IAR configuration file with ( ) suffix related to IAR link script .icfgeneration IAR Configuration File.

I originally planned to introduce the format of each instruction in the ICF file in detail, but I found that there are too many instructions in it, and many of them are not used. For complete instructions, please refer to: Chapter in <EWARM_DevelopmentGuide.ENU.pdf> . Therefore, this article will take the files in the IAR project of I.MX RT1176 as an example to analyze, and then understand in detail the instruction format used. For the ICF example in this section, in addition to several RAMs inside RT1176, NOR Flash and SDRAM are also connected. So if you understand this ICF configuration file, there will not be much problem with other MCU configuration files.The linker configuration file
ICF

1 memory map

First, let’s take a 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 with the mapped starting address
  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 ICF syntax analysis

2.1 ICF file of the project

For the above memory mapping, the ICF file provided in the official SDK is as follows:

define symbol __ram_vector_table_size__        =  isdefinedsymbol(__ram_vector_table__) ? 0x00000400 : 0;
define symbol __ram_vector_table_offset__      =  isdefinedsymbol(__ram_vector_table__) ? 0x000003FF : 0;

define symbol m_interrupts_start       = 0x30002000;
define symbol m_interrupts_end         = 0x300023FF;

define symbol m_text_start             = 0x30002400;
if (isdefinedsymbol(__use_flash64MB__)) {
  define symbol m_text_end               = 0x33FFFFFF;
} else{
  define symbol m_text_end               = 0x30FFFFFF;
}

define symbol m_interrupts_ram_start   = 0x20000000;
define symbol m_interrupts_ram_end     = 0x20000000 + __ram_vector_table_offset__;

define symbol m_data_start             = m_interrupts_ram_start + __ram_vector_table_size__;
define symbol m_data_end               = 0x2003FFFF;

define symbol m_data2_start            = 0x202C0000;
define symbol m_data2_end              = 0x2033FFFF;

define symbol m_data3_start            = 0x80000000;
define symbol m_data3_end              = 0x82FFFFFF;

define symbol m_ncache_start                   = 0x83000000;
define symbol m_ncache_end                     = 0x83FFFFFF;

define exported symbol __NCACHE_REGION_START   = m_ncache_start;
define exported symbol __NCACHE_REGION_SIZE    = m_ncache_end - m_ncache_start + 1;

define symbol m_qacode_start           = 0x00000000;
define symbol m_qacode_end             = 0x0003FFFF;

define exported symbol m_boot_hdr_conf_start = 0x30000400;
define symbol m_boot_hdr_ivt_start           = 0x30001000;
define symbol m_boot_hdr_boot_data_start     = 0x30001020;
define symbol m_boot_hdr_dcd_data_start      = 0x30001030;
define symbol m_boot_hdr_xmcd_data_start      = 0x30001040;

/* Sizes */
if (isdefinedsymbol(__stack_size__)) {
  define symbol __size_cstack__        = __stack_size__;
} else {
  define symbol __size_cstack__        = 0x0400;
}

if (isdefinedsymbol(__heap_size__)) {
  define symbol __size_heap__          = __heap_size__;
} else {
  define symbol __size_heap__          = 0x0400;
}

define exported symbol __VECTOR_TABLE          = m_interrupts_start;
define exported symbol __VECTOR_RAM            = isdefinedsymbol(__ram_vector_table__) ? m_interrupts_ram_start : m_interrupts_start;
define exported symbol __RAM_VECTOR_TABLE_SIZE = __ram_vector_table_size__;

define memory mem with size = 4G;
define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end]
                          | mem:[from m_text_start to m_text_end];
define region QACODE_region = mem:[from m_qacode_start to m_qacode_end];
define region DATA_region = mem:[from m_data_start to m_data_end];
define region DATA2_region = mem:[from m_data2_start to m_data2_end];
define region DATA3_region  = mem:[from m_data3_start to m_data3_end-__size_cstack__];
define region CSTACK_region = mem:[from m_data3_end-__size_cstack__+1 to m_data3_end];
define region NCACHE_region = mem:[from m_ncache_start to m_ncache_end];

define block CSTACK    with alignment = 8, size = __size_cstack__   { };
define block HEAP      with alignment = 8, size = __size_heap__     { };
define block RW        { first readwrite, section m_usb_dma_init_data };
define block ZI         with alignment = 32  { first zi, section m_usb_dma_noninit_data };
define block NCACHE_VAR    { section NonCacheable , section NonCacheable.init };
define block QACCESS_CODE  { section CodeQuickAccess };
define block QACCESS_DATA  { section DataQuickAccess };

initialize by copy { readwrite, section .textrw, section CodeQuickAccess, section DataQuickAccess };
do not initialize  { section .noinit };

place at address mem: m_interrupts_start    { readonly section .intvec };
place at address mem: m_boot_hdr_conf_start { section .boot_hdr.conf };
place at address mem: m_boot_hdr_ivt_start { section .boot_hdr.ivt };
place at address mem: m_boot_hdr_boot_data_start { readonly section .boot_hdr.boot_data };
place at address mem: m_boot_hdr_dcd_data_start { readonly section .boot_hdr.dcd_data };
place at address mem: m_boot_hdr_xmcd_data_start { readonly section .boot_hdr.xmcd_data };

keep{ section .boot_hdr.conf, section .boot_hdr.ivt, section .boot_hdr.boot_data, section .boot_hdr.dcd_data, section .boot_hdr.xmcd_data};

place in TEXT_region                        { readonly };
place in DATA3_region                       { block RW };
place in DATA3_region                       { block ZI };
if (isdefinedsymbol(__heap_noncacheable__)) {
  place in NCACHE_region                    { last block HEAP };
} else {
  place in DATA3_region                     { last block HEAP };
}
place in NCACHE_region                      { block NCACHE_VAR };
place in CSTACK_region                      { block CSTACK };
place in QACODE_region                      { block QACCESS_CODE };
place in DATA_region                        { block QACCESS_DATA };

Let’s analyze the above ICF file section by section.

2.2 define [exported] symbol和isdefinedsymbol

define symbol __ram_vector_table_size__        =  isdefinedsymbol(__ram_vector_table__) ? 0x00000400 : 0;
define symbol __ram_vector_table_offset__      =  isdefinedsymbol(__ram_vector_table__) ? 0x000003FF : 0;

define symbol m_interrupts_start       = 0x30002000;
define symbol m_interrupts_end         = 0x300023FF;

define symbol m_text_start             = 0x30002400;
if (isdefinedsymbol(__use_flash64MB__)) {
  define symbol m_text_end               = 0x33FFFFFF;
} else{
  define symbol m_text_end               = 0x30FFFFFF;
}

define symbol m_interrupts_ram_start   = 0x20000000;
define symbol m_interrupts_ram_end     = 0x20000000 + __ram_vector_table_offset__;

define symbol m_data_start             = m_interrupts_ram_start + __ram_vector_table_size__;
define symbol m_data_end               = 0x2003FFFF;

define symbol m_data2_start            = 0x202C0000;
define symbol m_data2_end              = 0x2033FFFF;

define symbol m_data3_start            = 0x80000000;
define symbol m_data3_end              = 0x82FFFFFF;

define symbol m_ncache_start                   = 0x83000000;
define symbol m_ncache_end                     = 0x83FFFFFF;

define exported symbol __NCACHE_REGION_START   = m_ncache_start;
define exported symbol __NCACHE_REGION_SIZE    = m_ncache_end - m_ncache_start + 1;

define symbol m_qacode_start           = 0x00000000;
define symbol m_qacode_end             = 0x0003FFFF;

Two ICF syntaxes appear in this paragraph:

(1)isdefinedsymbol(name) : nameReturns 1 when defined, otherwise returns 0

(2)define symbol : Define a variable

  • grammar:define [ exported ] symbol name = expr;
  • Parameters: namevariable name, exprvariable value, exportedcan be omitted, if defined, you can use extern in the program to get the value of this variable

Now let’s analyze the above linked file:

(1) __ram_vector_table__It is not defined in other places, that is, the values ​​of the sum __ram_vector_table_size__and __ram_vector_table_offset__are all 0. Therefore, m_interrupts_ram_startthe m_interrupts_ram_endsum is 0x20000000.

In fact, since the program runs in NOR Flash, the interrupt vector table at the beginning of the program image is also mapped to NOR Flash instead of being stored in RAM. So in fact the above variables are not used and can be ignored directly.

The vector table variables actually used are m_interrupts_start( 0x30002000) and m_interrupts_end( 0x300023FF), with a length of 0x3FF+1=0x400. You can count them at startup .s. The vector length at the beginning of the program is indeed filled to 0x400.

  • As for why the vector table starts from the 0x2000 offset of NOR Flash, this is because the I.MX series microcontrollers need an IVT header for the chip's inherent ROM BootLoader to boot. When using NOR Flash XIP, the length of this header is 0x2000. No need to worry too much here.

(2) m_text_start( 0x30002400) and m_text_end( 0x30FFFFFF) are followed by the vector table, which is the location of the subsequent code segment link, and the size is 16MB.

(3) m_data_startsum m_data_end; m_data2_startsum m_data2_end; m_data3_startsum m_data3_end; m_ncache_startsum m_ncache_end; m_qacode_startsumm_qacode_end

These three variables define the memory starting and ending addresses of DTCM, OC2, SDRAM(cacheable part), SDRAM(non-cacheable part) and , respectively. (4) and : Define the start and end addresses of non-cacheable memory. This part is used because the non-cacheable part needs to be configured in the MPU code of the program.ITCM
__NCACHE_REGION_START__NCACHE_REGION_SIZEexport


Then analyze it below:

/* 这里定义的是IVT头中不同参数的偏移,这里不做分析 */
define exported symbol m_boot_hdr_conf_start = 0x30000400;
define symbol m_boot_hdr_ivt_start           = 0x30001000;
define symbol m_boot_hdr_boot_data_start     = 0x30001020;
define symbol m_boot_hdr_dcd_data_start      = 0x30001030;
define symbol m_boot_hdr_xmcd_data_start      = 0x30001040;

/* Sizes */
if (isdefinedsymbol(__stack_size__)) {
  define symbol __size_cstack__        = __stack_size__;
} else {
  define symbol __size_cstack__        = 0x0400;
}

if (isdefinedsymbol(__heap_size__)) {
  define symbol __size_heap__          = __heap_size__;
} else {
  define symbol __size_heap__          = 0x0400;
}

define exported symbol __VECTOR_TABLE          = m_interrupts_start;
define exported symbol __VECTOR_RAM            = isdefinedsymbol(__ram_vector_table__) ? m_interrupts_ram_start : m_interrupts_start;
define exported symbol __RAM_VECTOR_TABLE_SIZE = __ram_vector_table_size__;

(1) __size_cstack__and __size_heap__are variables related to the stack and heap size of the program, which will be used later.

  • In fact, FreeRTOS is used in this project, so you only need to ensure that the stack size can run the initialization function of FreeRTOS. The subsequent heap and stack are managed by FreeRTOS and allocated from the space allocated to FreeRTOS.

(2) __VECTOR_TABLE( 0x30002000), __VECTOR_RAM( 0x30002000) and __RAM_VECTOR_TABLE_SIZE( 0)
give these three variables exportto the program. In fact, these three variables are not used in this project. These three variables were originally used to copy the vector table stored in Flash to RAM, so if you are using non-XIP Flash, such as NAND Flash , these three variables will be used.

2.3 define memory, define region, region expression and define block

First, let’s take a look at the new ICF syntax that will appear below:
(1)define memory : Define a piece of memory

  • grammar:define memory [ name ] with size = size_expr [ ,unit-size ];
  • Parameters: nameIt is the memory name and exprthe memory size, unit-sizewhich can be omitted. If it is defined, it must be assigned a value of bitsize_expr(bit) or bytesize_expr(byte), indicating the unit of the previous memory size. The default is byte.

(2)define region : Define an area where specific code segments and data segments can be placed. A region consists of one or more memory ranges, each of which is a contiguous sequence of bytes in a particular memory.

  • grammar:define [ ram | rom ] region_name = region-expr;
  • Parameter: region_nameIt is the name of the region, [ ram | rom ]which can be omitted, indicating that the region is RAM or ROM respectively. region-exprIt is a region expression. You can use region expressions to combine multiple memory ranges. These memory ranges may not be consecutive or even in the same memory. Introduced in (3).

(3) Area expression

  • grammar:[ memory-name: ][from expr { to expr | size expr } [ repeat expr [ displacement expr ]]]
  • Parameter: memory-nameIt is the name of the memory area. If there is only one memory, this item can be omitted; from expr { to expr | size exprthey are the starting address, ending address and size repeat exprof the memory area; they represent multiple memory ranges divided into the same memory; displacement exprthey are repeatthe previous memory in the sequence. Offset from start of range, default size is size.

At the same time, there can also be some operations between areas:

  • A|B: the union of A and B
  • A & B: the intersection of A and B
  • A - B: The set in which A excludes B

(4)define block : The block directive defines a contiguous region of memory that may contain a set of possibly empty segments or other blocks.
grammar:

define [ movable ] block name
[ with param, param... ]
{
extended-selectors
}
[ except
{
section-selectors
} ];

其中param可以为下面之一:
size = expr
minimum size = expr
maximum size = expr
expanding size
alignment = expr
end alignment = expr
fixed order
alphabetical order
static base [basename]

Block instructions are more complex and have many parameters, many of which are not used. I will not explain each parameter in detail here. The following will directly explain the meaning of several block instructions in the example to help everyone understand the ICF file. For the definition of specific instructions, please refer to manual p521 define block directive.


Continuing to analyze the ICF file, the next step is to define some memory, areas and blocks:

/* 定义一整个大小为2^32=4G的内存,即芯片的最大寻址范围 */
define memory mem with size = 4G;
define region TEXT_region = mem:[from m_interrupts_start to m_interrupts_end]
                          | mem:[from m_text_start to m_text_end];
define region QACODE_region = mem:[from m_qacode_start to m_qacode_end];
define region DATA_region = mem:[from m_data_start to m_data_end];
define region DATA2_region = mem:[from m_data2_start to m_data2_end];
define region DATA3_region  = mem:[from m_data3_start to m_data3_end-__size_cstack__];
define region CSTACK_region = mem:[from m_data3_end-__size_cstack__+1 to m_data3_end];
define region NCACHE_region = mem:[from m_ncache_start to m_ncache_end];

define block CSTACK    with alignment = 8, size = __size_cstack__   { };
define block HEAP      with alignment = 8, size = __size_heap__     { };
define block RW        { first readwrite, section m_usb_dma_init_data };
define block ZI         with alignment = 32  { first zi, section m_usb_dma_noninit_data };
define block NCACHE_VAR    { section NonCacheable , section NonCacheable.init };
define block QACCESS_CODE  { section CodeQuickAccess };
define block QACCESS_DATA  { section DataQuickAccess };

The above script defines multiple memory areas, including TEXT_regionthe range of the code segment, which is the area where the code starts after 0x30002000 in NOR Flash; QACODE_regionthat is, the area of ​​ITCM; DATA_regionthat is, the area of ​​DTCM; DATA2_regionthat is, the area of ​​SRAM_OC2; DATA3_regionthat is, the cacheable area of ​​SDRAM area; CSTACK_regionit is the area of ​​the stack, here defined as DATA3_regionthe last __size_cstack__byte area; NCACHE_regionthat is, the non-cacheable area of ​​SDRAM.

The next step is to define multiple blocks, of which CSTACKand HEAPare the stack and heap blocks respectively. It requires that the memory inside is 8-byte aligned, and the sizes are respectively and __size_cstack__; __size_heap__in RWis firstan extended-selectorsexpression (refer to p540), which means that readwritethe block Placed at the front of the containing RWblock (i.e., RWthe parent set of the block), multiple definitions can be defined here section-selectors, separated by commas, so the latter section m_usb_dma_init_datadefines a named m_usb_dma_init_datasection in this block; ZIsimilarly RW, it requires an additional 32 bytes Alignment; NCACHE_VAR, QACCESS_CODEand QACCESS_DATA all define a specific name section in this block.

  • sectionFor example, what is defined here m_usb_dma_init_datacan be used in the program #pragma(location=m_usb_dma_init_data)or __attribute__((section("m_usb_dma_init_data")))to define variables RWin
  • readwrite( RW), readonlyand ziare the three built-in ICF files block, which are read-write segment, read-only segment and bss segment respectively. readwriteBy default, a segment contains variables in the program that have initial values. By default, readonlythe segment contains the code of the program. By default, zithe segment contains variables that have no initial values ​​in the program.

2.4 initialize by copy和do not initialize

(1)initialize by copy
Grammar (refer to P527 for details):

initialize { by copy | manually }
[ with param, param... ]
{
section-selectors
}
[ except
{
section-selectors
} ];

This by copymeans copying a segment, which is also easy to understand. For example, for the RW segment, as long as it is not a variable with an initial value in the bss segment, these initial values ​​will occupy the size of the compiled image, that is, these initial values It is saved in Flash, and then copied to RAM after power-on. RWThe segment defined here is RAM, so "copy" a segment to Flash.

(2)do not initialize : On initialize by copythe contrary, generally used in the bss section

Continue reading the ICF file:

initialize by copy { readwrite, section .textrw, section CodeQuickAccess, section DataQuickAccess };
do not initialize  { section .noinit };

It is to manually define or manually define a variable based on whether it sectionwill store an initial value .sectioninitialize by copydo not initialize

2.5 place at、keep和place in

(1)place at

[ "name": ]
place [ noload ] at { address [ memory: ] address |
start of region_expr [ with mirroring to mirror_address ] |
end of region_expr [ with mirroring to mirror_address ] }
{
extended-selectors
}
[ except
{
section-selectors
} ];

This instruction is used to sectionsplace blocksthe sum at the beginning or end of a specific address or range.
(2)keep

keep
{
[ { section-selectors | block name }
[ , {section-selectors | block name }... ] ]
}
[ except
{
section-selectors
} ];

The function here is the same as keepthat in the link script ld file . It is used to control the linker to retain specific sums keepwhen generating executable files or libraries , preventing the linker from discarding unreferenced sums during optimization . (3)sectionsblockssectionsblocks
place in

[ "name": ]
place [ noload ] in region-expr
[ with mirroring to mirror_address ]
{
extended-selectors
}
[ except{
section-selectors
} ];

place inWill prevent sectionand blockto a specific area. If there are multiple sectionsums block, the order in which they are placed is random. If you want to specify this order, you can use blockexpressions, which are generally not used.


Continue to look at the link script:

place at address mem: m_interrupts_start    { readonly section .intvec };
place at address mem: m_boot_hdr_conf_start { section .boot_hdr.conf };
place at address mem: m_boot_hdr_ivt_start { section .boot_hdr.ivt };
place at address mem: m_boot_hdr_boot_data_start { readonly section .boot_hdr.boot_data };
place at address mem: m_boot_hdr_dcd_data_start { readonly section .boot_hdr.dcd_data };
place at address mem: m_boot_hdr_xmcd_data_start { readonly section .boot_hdr.xmcd_data };
keep{ section .boot_hdr.conf, section .boot_hdr.ivt, section .boot_hdr.boot_data, section .boot_hdr.dcd_data, section .boot_hdr.xmcd_data};

place in TEXT_region                        { readonly };
place in DATA3_region                       { block RW };
place in DATA3_region                       { block ZI };
if (isdefinedsymbol(__heap_noncacheable__)) {
  place in NCACHE_region                    { last block HEAP };
} else {
  place in DATA3_region                     { last block HEAP };
}
place in NCACHE_region                      { block NCACHE_VAR };
place in CSTACK_region                      { block CSTACK };
place in QACODE_region                      { block QACCESS_CODE };
place in DATA_region                        { block QACCESS_DATA };

The above place atis to place the following curly brackets sectioninto the previously specified address, and these can be placed into the specified section sectionusing statements such as this in the program . __attribute__((section(".boot_hdr.boot_data"), used))Several segments here are actually the startup headers of the I.MX RT series microcontrollers. In this way, the contents of the startup header can be changed in the C file.

  • The 6 defined above sectionshould all be readonlycorrect. My guess is that they can be written or not, because the placement address has been forced here. The addresses here are all in Flash, so they should be all indicated by default readonly. It's a pity that IAR does not generate a standard link script like Makefile, otherwise you can compare the difference before and after.

The latter place inis to place the previously defined ones block( sectioncomposed of multiple) into the previously defined ones region(representing one or more address ranges). Among last block HEAPthem, "in " is the same as lastwhat we encountered before first, and it is also extended-selectorsthe definition of "in", which means that it is placed at regionthe end of the "."

Guess you like

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