A brief talk about RISC-V GCC: Linker script learning notes (Part 2)
[Copy link]
The previous article introduced some link script knowledge. This time we use the CH57x series template project built into MRS (MounRiver Studio) to practice it:
1Entrance
ENTRY keyword, to make sure the program entry is at _start
2 Memory Layout
Memory allocation, FLASH is read-only and executable, the starting address is 0x00000000. The size is 448K, 448K can also be written in hexadecimal; RAM is read-write and executable, the starting address is 0x20003800, and the size is 18K.
3 Output section
The .init output segment provides two symbols _sinit and _einit. _sinit is the starting address of FLASH. Immediately after the 4-byte alignment is the input segment .init. This input segment can be seen in start_CH573.S and is the starting jump.
These two segments put the code originally placed in FLASH into RAM for execution. The starting address of the .highcodelalign segment is 4-byte aligned. The symbol _highcode_lma is the address already arranged in FLASH. Note that the .highcode segment runs in RAM, where "." is the address in RAM, which is the starting address of RAM here. The same is true for _highcode_vma_start. In this output segment, there are .vector .vertor_handler .highcode input segments. After four-byte alignment, an address _highcode_vma_end is provided for the end of this segment. The three symbols defined in these two output segments are to move the code in FALSH to RAM for execution, which can also be seen in start_CH573.S
Using the same writing method as above, you can customize the functions or data in the source code to be executed in RAM. You only need to specify its section attribute in the source code.
__attribute__ (( section ( ". highcode " )))
UINT16 Get_Calibration_Cnt_RAM ( UINT16 loc ){
...
...
}
The .text segment is stored in FLASH, and the runtime address is also in FLASH.
First, let's talk about the symbol __global_pointer. There is a GP register in the RISC-V core that can be used to access data within +/- 2K of its address. Only one assembly statement is required. If it is not in range, auipc or lui will be used in conjunction with other instructions including the low 12-bit immediate value to access it. Its position can be manually adjusted so that frequently used data is within its access range, which can effectively reduce the code size.
These three segments can be considered as one part. First, the value of "." in .dalign is an expression. ORIGIN(RAM) is the starting address of RAM. MAX(0x800,SIZEOF(.highcode)) represents the maximum value of 0x800 and .highcode. "." equals the sum of the two values. .dlalign defines the symbol _data_lma, which is the address where FLASH is arranged. The address of _data_vma in the .data segment is the same as the address of "." in .dalign, because there is no data occupying the RAM area in the middle. The three symbols _data_vma, _data_lma, and _edata provide addresses for moving data to RAM, which can also be seen in start_CH573.S.
Among them, the input segment in data has specific types of variables and corresponds to which segment. If you are interested, you can check it in the corresponding MAP file, as shown in the following figure
.bss stores uninitialized variables or global variables initialized to 0. The COMMON segment is special and I will talk about it later. _sbss and _ebss provide addresses for clearing, which can also be seen in start_CH573.S
Set up the stack and put the starting address at the top of RAM.
|