u-boot startup process——Based on S3C2410

Publisher:GoldenSunriseLatest update time:2016-07-26 Source: eefocusKeywords:u-boot  S3C2410 Reading articles on mobile phones Scan QR code
Read articles on your mobile phone anytime, anywhere
This article takes the popular Samsung S3C2410, openmoko platform and u-boot-1.3.2 (released in May 2008) as examples to introduce how to explore the u-boot startup process in the ZIX embedded development environment.

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 of the cpu/arm920t/start.S assembly language file :

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

  1. 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:

    1. cpu_init() - in the common/main.c file, performs initialization interrupt stack operations
    2. board_init() - in the board/neo1973/gta01/gta01.c file, performs board initialization, mainly updating GPIO and PLL settings
    3. interrupt_init() ——In the /cpu/arm920t/s3c24x0/interrupts.c file, perform clock interrupt initialization
    4. env_init() - in the common/env_nand.c file, sets the default environment variables
    5. init_baudrate() - In the lib_arm/board.c file, store the baudrate in the environment variable into the bd_info structure bd
    6. serial_init() - in the common/serial.c file, calls the real init() in the driver to initialize the serial port
    7. console_init_f() - In the common/console.c file, update the have_console flag of the global_data structure gd to 1
    8. 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
    9. init_func_i2c() - in lib_arm/board.c file, initializes the i2c bus
    10. 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
    11. 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>
Keywords:u-boot  S3C2410 Reference address:u-boot startup process——Based on S3C2410

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

Latest Microcontroller Articles
Change More Related Popular Components

EEWorld
subscription
account

EEWorld
service
account

Automotive
development
circle

About Us Customer Service Contact Information Datasheet Sitemap LatestNews


Room 1530, 15th Floor, Building B, No.18 Zhongguancun Street, Haidian District, Beijing, Postal Code: 100190 China Telephone: 008610 8235 0740

Copyright © 2005-2024 EEWORLD.com.cn, Inc. All rights reserved 京ICP证060456号 京ICP备10001474号-1 电信业务审批[2006]字第258号函 京公网安备 11010802033920号