I used to want to use the compiler to write the MCU's C code, what the compiler did to help us. How the compiler allocates variables and code. So just idle to see what the compiler's installation path is. The ICCAVR compiler and Atmel Atmega64 are used in the work. So I daoteng this compiler and mcu~~~.
To be honest ICCAVR the compiler is really simple and convenient, but powerful (of course, I didn't use the other compiler O (╯-╰) o). For its basic use again no longer repeat. Clicking on the Help menu in the compiler environment pops up the Show Library Source Code passwd, and then clicking will pop up a small prompt box: password is ICCAVR. The installation directory that comes to ICCAVR will see a compressed package libsrc.zip, which is in the Libsrc.avr folder. Oh presumably you already know this compressed package of decompression password. There are common C library function source code and common functions of the assembly code.
There is a Init.s file under the Libsrc.avr folder, which is a generic file for the Mega series MCU initialization. Inside is a dozens of-line assembly code. The code that executes the first time the MCU is power-up is the code, not the code you wrote ~ ~ ~ ~ Code as follows:
; Init.s
;
; To is included by the CRT*.S files
;
; Initialize Stacks
;
; Set the hardware stack to Ram_end, and the software stack some
; Bytes below that
Ldi R28,<ram_end
Ldi R29,>ram_end
Out $3d,r28
Out $3e,r29
Subi R28,SBCI r29,>hwstk_size
Ldi R16,0XAA ; Sentenial
Std Y+0,r16; Put sentenial at bottom of HW stack
CLR R0
Ldi R30,<__bss_start
Ldi R31,>__bss_start
Ldi R17,>__bss_end
; This loop zeros out all the data in the BSS area
;
Init_loop:
CPI R30,<__bss_end
CPC R31,r17
Breq Init_done
St Z+,r0
RJMP Init_loop
Init_done:
STD z+0,r16 ; Put sentenial at bottom of SW stack
; Copy data from Idata to data
; Idata contains the initialized values of the global variables
Ldi R30,<__idata_start
Ldi R31,>__idata_start
Ldi R26,<__data_start
Ldi R27,>__data_start
Ldi R17,>__idata_end
; Set Rampz always. If This is a main app and then Rampz needs to reset to
; Zero if invoked through the bootloader
Ldi R16,USE_ELPM
Out 0x3b,r16
Copy_loop:
CPI R30,<__idata_end
CPC R31,r17
Breq Copy_done
. If USE_ELPM
elpm ; Load (rampz:z)
. else
Lpm; Load (Z) byte into R0
. endif
ADIW r30,1
St X+,r0
RJMP Copy_loop
Copy_done:
Not familiar with the assembly, please add yourself, this code is also used in some of the ICC's own compiler pseudo-directive:<, > is to $ff to take the remainder and rounding operations. Constants such as Ram_end, hwstk_size, etc. are determined when we select the chip type at the time of the new project, or can be changed in the option option of project. Usually the size of the default hwstk_size hardware stack is 30,ram_end depending on the chip you are using AVR64 the value is 10ff. This is related to the memory organization inside the chip, which marks the end address of the MCU's SRAM. The . Text pseudo-directive calibrated the following generated is located in the code area, _start:: Label is the compiler inside the start label, and _main is our program entrance. NOTE:ICC in Compiler:: denotes external label,: indicates internal label.
First use the immediate number load LDI to save the high-end address of the RAM to the Y-pointer, and assign the Y-pointer to the SP stack pointer out $3d,r28 0x3d is the address of SPL. This allows the stack pointer to be set up with the first four instructions. Then use the same method to set the stack size. Because the Y pointer points to the high-end address of the stack, and then uses the subi instruction to subtract the stack size you set in the compiler environment, point the Y pointer to the stack bottom of the hardware stack, and the stack bottom of the hardware stack is near the top of the software stack. To prevent a stack overflow, the ICC compiler specifically stores 0xAA as a marker (LDI R16,0XAA std y+0,r16). In fact, you go to see the macro function in the file to check whether the stack overflow, it is to determine whether the location of the 0XAA is covered.
After the stack is set, the memory allocation of the variable is made. For the allocation of variable memory, the compiler does this: first define the first allocation, and do not optimize the memory speed (even if the byte is not the case, because SRAM is very scarce). The variables are divided into the variables with the initial value and the variables without the initial value (definition and declaration of the variables), and the ICC stores the two variables in the BSS and data areas respectively. The BSS stores variables that have no initial value (only declarations, no defined global variables, etc.), and the data area holds global variables with initial values (global variables and static modified variables with the initial value). It's very easy to deal with the BSS area.
CLR R0
Ldi R30,<__bss_start
Ldi R31,>__bss_start
Ldi R17,>__bss_end
Init_loop:
CPI R30,<__bss_end
CPC R31,r17
Breq Init_done
St Z+,r0
RJMP Init_loop
Using the processor feature directive, set the z-pointer, use the pointer self-increment storage mate jump, and the BSS variable in the C project you write is zeroed all by default. At this point, the STD z+0,r16 still stores the 0xAA tag at the top of the BSS area, which is the software stack area between the BSS area and the hardware stack area. This is true: when the compiler helps you plan your storage, your variable space begins with a low address, first planning the data area, then dividing the BSS area, the rest is the software stack area, and finally the hardware stack area. So when your global variables are too long, the hardware stack is certain, then the software stack is bound to be squeezed too small, the software stack is used for function calls when the stack out of the stack, interrupt the field protection and other operations. So this situation is particularly prone to stack overflow ~~~!!!
Ldi R30,<__idata_start
Ldi R31,>__idata_start
Ldi R26,<__data_start
Ldi R27,>__data_start
Ldi R17,>__idata_end
The difference between the Idata area and the data area appears again. The Idata zone specifies the location of your global variable with the initial value in Flash, or how does the MCU remember the initial value of the variable you define?
Copy_loop:
CPI R30,<__idata_end
CPC R31,r17
Breq Copy_done
. If USE_ELPM
ELPM ; Load (rampz:z)
. else
LPM ; Load (Z) byte into R0
. endif
ADIW r30,1
St X+,r0
RJMP Copy_loop
Copy_done:
This code is to copy the initial value all to the data area in RAM corresponding location, depending on the chip type to determine whether the need for ELPM.
This is the whole INIT.S code, which is also mentioned in the preceding note; To is included by the CRT*.S files. This generic code is referred to in the CRTXXBOOT.S code. The generic boot code is as follows:
; Make sure to assemble w/-n flag, e.g.
; Iasavr-n CRT ...
;
; bootloader startup file, same as CRTAVR.S except that vectors is routed
; To the bootloader sections and use jmp for the vector
;
. include "Area.s"
. text
__start:: ; Entry point
; Route vector
Ldi r16,1
Out 0x35,r16 ; MCUCR = 1, unlock Ivsel
Ldi r16,2
Out 0x35,r16 ; MCUCR = 2, set IvselThe above code is used to set the interrupt vector table position, whether it is in the starting position of Flash or the BLS area (this is discussed later)
USE_ELPM = 0;
. include "Init.s"
; Call User Main Routine
Call _mainlooking for your main function, now the compiler has helped you to get everything ready and give you the MCU.
_exit::
rjmp _exit
; Interrupt vectors. The first entry is the reset vector
;
. Area vector (ABS)calibration Interrupt Vector Absolute address, located at 00000H, a jump to find __start
. org 0
JMP __start
Well, it is my understanding that the initialization of ICCAVR to the MCU is complete. Hope to criticize. PS: Please specify the original source if you want to reprint.
The sky never leaves traces of wings, but the birds have flown.
AVR startup code under Imagecraft