Although u-boot has been widely used, due to its relative complexity, users are still reluctant to understand its internal structure.freely browsed and downloaded at forum.linuxbj.com . This u-boot represents a high level in the industry, can directly build a new version of embedded product design, and has high application value.
The overall startup process of u-boot is as follows
-> reset
-> set CPU mode
-> turn off watchdog/interrupt
-> set processor clock/on-chip bus
-> initialize debug serial port
-> initialize MMU/external bus/SDRAM, etc.
-> move rom code/data to ram
-> initialize function call stack
-> initialize peripherals/parameters
-> startup completed, enter main_loop
Embedded systems cannot do without bootloaders to initialize hardware and boot the operating system.
Nowadays, dedicated embedded boards running embedded Linux systems have become very popular, and u-boot is a bootloader that is very suitable for such systems.
u-boot mainly provides the following functions:
- Set the target board hardware parameters and initialize;
- Pass necessary information to the operating system;
- Perform interactive low-level operations;
- Intelligent loading operating system;
- The firmware program that boots and runs;
- Supports mass storage and USB interface
By using the ZIX development environment, you can observe the internal structure of u-boot in a more intuitive way, and perform code debugging and analysis at the same time. It is a powerful tool for understanding and porting u-boot.
Use the arm tool chain to compile the u-boot source code and get the u-boot.bin file that can be burned.
In the ZIX development environment, you can load u-boot.bin into the s3c2410 board and use gdb to debug.
gdb can access hardware through the JTAG interface and can also access virtual hardware through TCP/IP. After establishing a debugging connection, you can use gdb to manipulate the u-boot startup process. You can follow the execution sequence of the code below to understand which operations are performed from the previous point.
After s3c2410 is reset, the pc pointer will point to address 0x0. In the u-boot code, the 0x0 address is a vector table, and
the first instruction jumps to the reset code start_code . Located in line 53
DE>.globl _start _start: DE> DE>b start_co de DE> DE> DE>ldr pc, _undefined_instruction DE> DE>ldr pc, _software_interrupt DE> DE>ldr pc, _prefetch_abort DE> DE>ldr pc, _da ta_abort DE > DE>ldr pc, _not_used DE> DE>ldr pc, _irq DE> DE>ldr pc, _fiq
After the reset instruction jumps to line 154 , the basic initialization of the arm920t processor begins.
First, the current program status register CPSR is modified to make the processor enter the Supervisor|32 bit ARM mode and
disable the ARM9TDMI interrupt and fast interrupt. This is achieved by setting the corresponding CPSR mask:
DE>start_co de: DE> DE> DE> DE>/* DE> DE> DE> DE> * set the cpu to SVC32 mode DE> DE> DE> DE> */ DE> DE> DE> DE>mrs r0 ,cpsr DE> DE> DE> DE>bic r0,r0,#0x1f DE> DE> DE> DE>orr r0,r0,#0xd3 DE> DE> DE> DE>msr cpsr,r0 DE>
Next, clear the WTCON register unique to S3C2410 to zero. This is only to turn off the watchdog. The code location is line 234 :
DE> DE> DE>ldr r0, =pWTCON DE> DE> DE> DE>mov r1, #0x0 DE> DE> DE> DE>str r1, [r0] DE>
Then , at line 241 , the S3C2410 interrupt controller INTMSK register is set to all 1s,
INTSUBMSK is set to 0x7ff, and all interrupt sources are disabled. The S3C2410 manual has a detailed description starting from page 358:
DE> DE> DE>mov r1, #0xffffffff DE> DE> DE> DE>ldr r0, =INTMSK DE> DE> DE> DE>str r1, [r0] # if defined(CONFIG_S3C2410) || defined(CONFIG_S3C2440) || defined(CONFIG_S3C2442) || \ DE> DE> DE> DE>defined(CONFIG_S3C2443) DE> DE> DE> DE>ldr r1, =INTSUBMSK_val DE> DE> DE> DE>ldr r0, =INTSUBMSK DE> DE> DE> DE>str r1, [ r0] # endif DE>
Next, at line 259 , the arm920t control register CP15 is accessed and the highest two bits [31,30] are set.
After these two bits are set to 0b11, the processor clock is set to asynchronous mode, allowing the processor to access the bus asynchronously:
DE> DE> DE>mrc p15, 0, r1, c1, c0, 0 DE> DE> DE> DE>orr r1, r1, #0xc0000000 DE> DE> DE> DE>mcr p15, 0, r1, c1, c0, 0 DE>
At this point, the configuration of arm920t is completed, and then we start to set the S3C2410 clock synthesis parameters.
By setting the UPLL, MPLL and CLKDIVN registers (described in the S3C2410 manual from page 237),
we can get the required processor operating frequency, which is in line 308 :
DE> DE> DE>ldr r0, =UPLLCON DE> DE> DE> DE>ldr r1, =UPLLCON_val DE> DE> DE> DE>str r1, [r0] DE>
Line 321 :
DE> DE> DE>ldr r1, =MPLLCON_val DE> DE> DE> DE>str r1, [r0, #-4] DE> DE> DE> DE>/* MPLLCON */ DE> DE> DE> DE> DE> DE>/* FCLK:HCLK:PCLK = 1:2:4 */ DE> DE> DE> DE>ldr r0, =CLKDIVN DE> DE> DE> DE>mov r1, #CLKDIVN_val DE > DE> DE> DE>str r1, [r0] DE>
The UART0 of S3C2410 is initialized so that information can be printed through UART0 as soon as possible.
This code starts from line 332. The registers involved can be found in the S3C2410 manual starting from page 293:
DE> DE> DE>/* enable uart */ DE> DE> DE> DE>ldr r0, =0x4c00000c /* clkcon */ DE> DE> DE> DE>ldr r1, =0x7fff0 /* all clocks on */ DE> DE> DE> DE>str r1, [r0] DE> DE> DE> DE> DE> DE>/* gpio UART0 init */ DE> DE> DE> DE>ldr r0, =0x56000070 DE> DE> DE> DE>mov r1, #0xaa DE> DE> DE> DE>str r1, [r0] DE> DE> DE> DE> DE> DE>/* init uart */ DE> DE> DE> DE>ldr r0, =0x50000000 DE> DE> DE> DE>mov r1, #0x03 DE> DE> DE> DE>str r1, [r0] DE> DE> DE> DE>ldr r1, =0x245 DE> DE> DE> DE>str r1, [r0, #0x04] DE> DE> DE> DE>mov r1, #0x01 DE> DE> DE> DE >str r1, [r0, #0x08] DE> DE> DE> DE>mov r1, #0x00 DE> DE> DE> DE>str r1, [r0, #0x0c] DE> DE> DE> DE>mov r1 , #0x1a DE> DE> DE> DE>str r1, [r0, #0x28] DE>
After completing the UART0 settings, the code will enter the corresponding branch at line 360 according to different compile-time options and runtime parameters , which are
- fromline 572 to execute cpu_init_crit, and
flush the cache and tlb of the processor arm920t by operating CP15, and close the mmu and i-cache:DE>cpu_init_crit: DE> DE> DE> DE>/* DE> DE> DE> DE> * flush v4 I/D caches DE> DE> DE> DE>*/ DE> DE> DE> DE>mov r0, #0 DE> DE> DE> DE>mcr p15, 0, r0, c7, c7, 0 /* flush v3/v4 cache */ DE> DE> DE> DE>mcr p15, 0, r0, c8, c7, 0 /* flush v4 TLB */ DE> DE> DE> DE> DE> DE>/* DE> DE> DE> DE> * disable MMU stuff and caches DE> DE> DE> DE> */ DE> DE> DE> DE>mrc p15, 0, r0, c1, c0, 0 DE> DE> DE> DE>bic r0, r0, #0x00002300 DE> DE> DE> DE>@ clear bits 13, 9:8 (--V- --RS) DE> DE> DE> DE>bic r0, r0, #0x00000087 DE> DE> DE> DE>@ clear bits 7, 2:0 (B--- -CAM ) DE> DE> DE> DE>orr r0, r0, #0x00000002 DE> DE> DE> DE>@ set bit 2 (A) Align DE> DE> DE> DE>orr r0, r0, #0x00001000 DE> DE > DE> DE>@ set bit 12 (I) I-Cache DE> DE> DE> DE>mcr p15, 0, r0, c1, c0, 0 DE> Then jump to line 139 of the board/neo1973/common/lowlevel_init.S file to configure
bus data width, timing, SDRAM control, GPIO, etc. After configuration, it will return to start.S to continue execution.
Because this code is related to the board, it is placed in the board directory. Since there are many codes, only the beginning part is pasted:DE> DE> DE>/* memory control configuration */ DE> DE> DE> DE>/* make r0 relative the current location so that it */ DE> DE> DE> DE>/* reads SMRDATA out of FLASH rather than memory ! */ DE> DE> DE> DE>adr r0, SMRDATA DE> DE> DE> DE>ldr r1, =BWSCON /* Bus Width Status Controller */ DE> DE> DE> DE>add r2, r0 , #13*4 DE> After completing the board-level settings, determine the execution location of the code itself in line 373 of cpu/arm920t/start.S . If it is executed from the stepping stone
and u-boot is configured as nand boot mode, it jumps to nand_load copy code:DE> DE> DE>ldr r1, =BWSCON /* Z = CPU booted from NAND */ DE> DE> DE> DE>ldr r1, [r1] DE> DE> DE> DE>tst r1, #6 /* BWSCON[2:1] = OM[1:0] */ DE> DE> DE> DE>teqeq r0, #0 /* Z &= running at address 0 */ DE> DE> DE> DE>beq nand_load DE > Line 417 is the nand_load code, which will first jump to line 614 to execute may_resume
to detect whether the system is awakened from standby mode or powered on. If awakened, it will perform corresponding processing according to the previously saved scene, which
will not be described in this article; if it is powered on, it will return to nand_load to continue execution. In nand_load, the nand controller of s3c2410 is initialized,
involving memory mapping and register NFCONF, etc., see page 215 of the S3C2410 manual . Again, only paste the beginning part of the code:DE> DE> DE>mov r1, #S3C2410_NAND_BASE DE> DE> DE> DE>ldr r2, =0xf842 @ initial value enable tacls=3,rph0=6,rph1=0 DE> DE> DE> DE>str r2, [r1, #oN FCONF] DE> DE> DE> DE>ldr r2, [r1, #oN FCONF] DE> DE> DE> DE>bic r2, r2, #0x800 @ enable chip DE> In line 451, the stack pointer is set according to the configuration to prepare for the subsequent call to the C function to perform the copy:
DE> DE> DE>ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */ DE> DE> DE> DE>sub r0, r0, #CFG_MALLOC_LEN /* malloc area */ DE> DE > DE> DE >sub r0, r0, #CFG_GBL_DA TA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ DE> DE> DE> DE>sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif DE> DE> DE> DE>sub sp, r0, #12 /* leave 3 words for abort-stack */ DE> Then at line 460 , the target address in SDRAM is stored in r0, the 0x0 address is stored in r1, the u-boot length is stored in r2, and the nand_read_ll function is executed at line 154
of the cpu/arm920t/s3c24x0/nand_read.c file. This function accepts the values in the first three registers as parameters and puts the return value back in r0:DE> DE> DE>ldr r0, _TEXT_BASE DE> DE> DE> DE>mov r1, #0x0 DE> DE> DE> DE>mov r2, #CFG_UBOOT_SIZE DE> DE> DE> DE>bl nand_read_ll DE> The nand flash access code is implemented in the nand_read_ll function, and the feature of automatically skipping bad blocks is supported. The function loops through the nand page reads and stores them in SDRAM until u-boot has copied all of them and returns 0. The C code is left for the reader to read. After nand_read_ll returns 0, it jumps to ok_nand_read, and verifies the first 4K bytes of the copy in line 482 :
@verify mov r0, #0 @ldr r1, =0x33f00000 ldr r1, _TEXT_BASE mov r2, #0x400 DE> DE> DE> DE>@ 4 bytes * 1024 = 4K-bytes go_next: DE> DE> DE> DE>ldr r3, [r0], #4 DE> DE> DE> DE>ldr r4, [r1], #4 DE> DE> DE> DE>teq r3, r4 DE> DE> DE> DE>bne notmatch DE> DE> DE> DE>subs r2, r2, #4 DE> DE> DE> DE>beq done_nand_read DE> DE> DE> DE>bne go_next DE> After the verification is passed, the code line 506 saves 1 in the SDRAM location at address _booted_from_nand to tell the upper-layer software that this startup is booted from nand:
DE>done_nand_read: DE> DE> DE> DE>ldr r0, _booted_from_nand DE> DE> DE> DE>mov r1, #1 DE> DE> DE> DE>strb r1, [r0] DE> Then at line 518 , copy the interrupt vector table to 0x0:
DE> DE> DE>mov r0, #0 DE> DE> DE> DE>ldr r1, _TEXT_BASE DE> DE> DE> DE>mov r2, #0x40 irqvec_cpy_next: DE> DE> DE> DE>ldr r3, [r1], #4 DE> DE> DE> DE>str r3, [r0], #4 DE> DE> DE> DE>subs r2, r2, #4 DE > DE> DE> DE>bne irqvec_cpy_next DE> On line 532 , the stack pointer is set:
DE> DE> DE>ldr r0, _TEXT_BASE DE> DE> DE> DE>/* upper 128 KiB: relocated uboot */ DE> DE> DE> DE>sub r0, r0, #CFG_MALLOC_LEN DE> DE> DE > DE >/* malloc area */ DE> DE> DE> DE>sub r0, r0, #CFG_GBL_DA TA_SIZE /* bdinfo */ #ifdef CONFIG_USE_IRQ DE> DE> DE> DE>sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ) #endif DE> DE> DE> DE>sub sp, r0, #12 DE> DE> DE> DE>/* leave 3 words for abort-stack */ DE> At line 541 , the bss segment is cleared and the real C function start_armboot is jumped to enter the more advanced hardware initialization code. The assembly initialization part also completes its mission:
DE> DE> DE>ldr r0, _bss_start /* find start of bss segment */ DE> DE> DE> DE>ldr r1, _bss_end /* stop here */ DE> DE> DE> DE>mov r2, #0x00000000 /* clear */ DE> DE> clbss_l: DE> DE> DE> DE>str r2, [r0] /* clear loop... */ DE> DE> DE> DE>add r0, r0, #4 DE> DE> DE> DE>cmp r0, r1 DE> DE> DE> DE>ble clbss_l DE> DE > DE > DE> DE> DE>ldr pc, _start_armboot DE> The start_armboot function is located in line 277 of the lib_arm/board.c file. First, it initializes
the variable gd of type global_data. This variable is a structure, and most of its members are some basic settings of the board, such as serial number, IP address, MAC address, etc. (For the prototype of the structure, please refer to include/asm-arm/globel_data.h and include/asm-arm/u-boot.h): DE> DE> DE>gd = (gd_t*)(_armboot_start - CFG_MALLOC_LEN - sizeof(gd_t)); DE> DE> DE> DE>/* compiler optimization barrier needed for GCC >= 3.4 */ __asm__ __volatile__("": : :"memory"); DE> DE> DE> DE>memset ((void*)gd, 0, sizeof (gd_t)); gd->bd = (bd_t*)((char*)gd - sizeof(bd_t)); DE> DE> DE> DE>memset (gd->bd, 0, sizeof (bd_t)); DE> Then, in a for loop, starting from the init_sequence address, the initialization C functions are called one by one until the number reaches NULL. These routine functions are as follows in the order of calling:
- cpu_init() - in the common/main.c file, performs initialization interrupt stack operations
- board_init() - in the board/neo1973/gta01/gta01.c file, performs board initialization, mainly updating GPIO and PLL settings
- interrupt_init() ——In the /cpu/arm920t/s3c24x0/interrupts.c file, perform clock interrupt initialization
- env_init() - in the common/env_nand.c file, sets the default environment variables
- init_baudrate() - In the lib_arm/board.c file, store the baudrate in the environment variable into the bd_info structure bd
- serial_init() - in the common/serial.c file, calls the real init() in the driver to initialize the serial port
- console_init_f() - In the common/console.c file, update
the have_console flag of the global_data structure gd to 1 - display_banner() ——In the lib_arm/board.c file, print the u-boot banner, output version, running address and other information. For example, what you see on the console
- init_func_i2c() - in lib_arm/board.c file, initializes the i2c bus
- dram_init() - in the board/neo1973/gta01/gta01.c file, fill in the start and size members of bd->bi_dram[0] to describe the ram available to u-boot
- display_dram_config() - In the board/neo1973/gta01/gta01.c file, print the current ram configuration. The corresponding DRAM can be seen in the console: 128 MB
Using gdb, you can clearly see the calling process:
DE > DE> DE>for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) { DE> DE> DE> DE> DE> DE>if ((*init_fnc_ptr)() != 0) { DE> DE> DE > DE> DE> DE> DE> DE>hang (); DE> DE> DE > DE> DE > DE>} DE> DE> DE> DE>} DE> Next is the initialization of some optional peripherals, such as display, NOR, NAND, dataflash, network card, etc. After this process is executed, all initialization work is completed. Only the NOR code is pasted below:
DE>#ifndef CFG_NO_FLASH DE> DE> DE> DE>/* configure available FLASH banks */ DE> DE> DE> DE>size = flash_init (); DE> DE> DE> DE>display_flash_config (size); #endif /* CFG_NO_FLASH * / Then, an infinite loop is entered at line 457 , calling the main_loop() function at line 278 of the common/main.c file , and u-boot completes the boot process. main_loop provides an interactive command line that can be operated through the serial port or USB console, and can also further boot the operating system:
DE> DE> DE>for (;;) { DE> DE> DE > DE> DE> DE>main_loop (); DE> DE> DE> DE>} DE>
Previous article:About the problem of s3c2410 booting from NAND Flash --bootrom 4KB limit
Next article:Solve the 2.95.3 arm-linux-gcc compilation error problem
- Popular Resources
- Popular amplifiers
- Learn ARM development(16)
- Learn ARM development(17)
- Learn ARM development(18)
- Embedded system debugging simulation tool
- A small question that has been bothering me recently has finally been solved~~
- Learn ARM development (1)
- Learn ARM development (2)
- Learn ARM development (4)
- Learn ARM development (6)
Professor at Beihang University, dedicated to promoting microcontrollers and embedded systems for over 20 years.
- LED chemical incompatibility test to see which chemicals LEDs can be used with
- Application of ARM9 hardware coprocessor on WinCE embedded motherboard
- What are the key points for selecting rotor flowmeter?
- LM317 high power charger circuit
- A brief analysis of Embest's application and development of embedded medical devices
- Single-phase RC protection circuit
- stm32 PVD programmable voltage monitor
- Introduction and measurement of edge trigger and level trigger of 51 single chip microcomputer
- Improved design of Linux system software shell protection technology
- What to do if the ABB robot protection device stops
- CGD and Qorvo to jointly revolutionize motor control solutions
- CGD and Qorvo to jointly revolutionize motor control solutions
- Keysight Technologies FieldFox handheld analyzer with VDI spread spectrum module to achieve millimeter wave analysis function
- Infineon's PASCO2V15 XENSIV PAS CO2 5V Sensor Now Available at Mouser for Accurate CO2 Level Measurement
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- Advanced gameplay, Harting takes your PCB board connection to a new level!
- A new chapter in Great Wall Motors R&D: solid-state battery technology leads the future
- Naxin Micro provides full-scenario GaN driver IC solutions
- Interpreting Huawei’s new solid-state battery patent, will it challenge CATL in 2030?
- Are pure electric/plug-in hybrid vehicles going crazy? A Chinese company has launched the world's first -40℃ dischargeable hybrid battery that is not afraid of cold
- [IOT harmful gas detection device based on STM32F7508-DK] Part 1: touchGFX trial
- Free gifts: Aim TTi current measurement probe, professional signal generator, JD card and other small gifts are waiting for you
- Happy Mid-Autumn Festival and National Day friends!
- TangDynasty Software Manual for Anlu FPGA IDE
- Looking for a single chip microcomputer
- [TOPWAY 5-inch smart TFT module review] First experience
- [Hua Diao Experience] 20 Music Visualization: A Series of Trials with ESP32_C3 and WS2812B
- One week evaluation information delivered on time~
- EEWORLD University ---- 2021_Digikey KOL Series: Application of New Intelligent Exposure Algorithm in Face Recognition
- How to use Bluetooth 4.2 to implement the Internet of Things