uboot启动分析第一阶段(start.S)

2023-05-16

前面分析了启动脚本、Makefile、mkconfig,接下来就是uboot的start.S这个启动代码了,下面是本章的平台介绍:
单板:迅为4412开发板(Exynos 4412)
SDRAM:1G
EMMC:4G


Exynos 4412的启动过程可以在数据手册的Booting Sequence找到,下面只截取关键部分:

Exynos 4412 has 64 KB ROM (iROM) and 256 KB SRAM (iRAM) as internal memory.

You can select the booting device from the following list:
1. General NAND flash memory
2. SD/MMC memory card
3. eMMC memory
4. USB device

At the system reset, the program execution starts at iROM. The system reset may be asserted not
only on booting time, but also on wakeup from low power modes. Therefore, the boot loader code executes appropriate processes according to the reset status. Refer to Figure 5-1 for more information.

The boot loader is comprises the first and the second boot loaders. The characteristics of these boot loaders are:
1. iROM: It is a small and simple code to initiate SOC. It is implemented on internal ROM of SOC.
2. First boot loader (BL1): It is chip-specific and stored in external memory device.
3. Second boot loader (BL2): It is platform-specific and stored in external memory device. User should build and store this in an external memory device. It is not provided by Samsung.

这里写图片描述

从文字和图片可以总结出如下几点:

  • BL0:这段是固化的代码放置在 64K 的 iROM 里面,负责初始化基本的系统功能比如时钟和栈,并且加载 BL1 到内部 256KB 的 SRAM。

  • BL1:这段代码由三星提供,也更改不了,BL0 会根据 OM 引脚来判断当前 booting 设备是哪个,可以是 NAND_FLASH、SD卡、EMMC、USB设备,从选定好的设备中加载 BL1,BL1 主要负责初始化系统时钟和 DRAM 控制器,然后从 booting 设备中加载 OS 到 DRAM 中去运行,这里的 OS 其实就是指 BL2,DRAM 指的则是外部的 SDRAM。

  • BL2:BL2 的主要功能是去加载我们的 UBOOT 代码,此后 UBOOT 运行在 DRAM上, 同样加载的时候也需要校验,这里需要将 uboot.bin 合并(merge)进BL2里面,在迅为的启动脚本里面有描述,如下:

cat E4412.S.BL1.SSCR.EVT1.1.bin E4412.BL2.TZ.SSCR.EVT1.1.bin all00_padding.bin u-boot.bin E4412.TZ.SSCR.EVT1.1.bin > u-boot-iTOP-4412.bin

做好了一切准备以后就开始执行 start.S 了,这里标题指的第一阶段基本上都是用汇编去实现的,主要负责硬件的初始化,第二阶段都是C代码,实现一些比较复杂的内容,下一章节描述。

以 start.S 的代码开始描述:

.word 0x2000
.word 0x0
.word 0x0
.word 0x0

/*
  globl就是相当于C语言中的Extern,声明此变量,并且告诉链接器此变量是全局的,外部可以访问
  指定入口为_start
  u-boot.lds里面定义了ENTRY(_start),即指定入口为_start 
 */
.globl _start

/* 跳转到reset,这里的代码地址是00000010 */
_start: b   reset
/*
  ARM是RISC结构,数据从内存到CPU之间的移动只能通过L/S指令来完成,也就是ldr/str指令。
  比如想把数据从内存中某处读取到寄存器中,只能使用ldr
  将_undefined_instruction这个地址处的word(一字节)定义的值赋给pc。

  ARM体系结构规定在上电复位的起始位置必须有8条连续的跳转指令,
  通过硬件来实现。它们就是异常向量表。ARM在上电复位后是从0x0开始启动,
  如果bootloader存在,则是从_start开始执行上面的跳转没有执行。
  设置异常向量表的作用是识别bootloader,以后每当系统有异常出现时,
  cpu会根据异常号从内存0x0处开始查找并做相应的处理   
  下面8条即设置异常中断向量表
 */
    ldr pc, _undefined_instruction
    ldr pc, _software_interrupt
    ldr pc, _prefetch_abort
    ldr pc, _data_abort
    ldr pc, _not_used
    ldr pc, _irq
    ldr pc, _fiq

/*
当有异常出现ARM会自动执行以下步骤:

1 将下一条指令的地址存放在连接寄存器LR(通常是R14).---保存位置

2 将相应的CPSR(当前程序状态寄存器)复制到SPSR(备份的程序状态寄存器)中

3 根据异常类型,强制设置CPSR运行模式位

4 强制PC从相应异常向量地址取出下一条指令执行,从而跳转到异常处理函数中执行
 */

_undefined_instruction:
    .word undefined_instruction//“未定义指令”的时候,系统所要去执行的代码。
_software_interrupt:
    .word software_interrupt//软件中断
_prefetch_abort:
    .word prefetch_abort//预取指错误
_data_abort:
    .word data_abort//数据错误
_not_used:
    .word not_used//未定义
_irq:
    .word irq//(普通)中断
_fiq:
    .word fiq//快速中断
_pad:
    .word 0x12345678 /* now 16*4=64 */

.global _end_vect
_end_vect:
/* 接下来的代码,都要16字节对齐,不足之处,用0xdeadbeef填充 */
    .balignl 16,0xdeadbeef

_TEXT_BASE:
/* _TEXT_BASE是一个标号地址,在board\samsung\smdkc210中定义,通过反汇编或路径可得知为"0xc3e00000" */
    .word   TEXT_BASE 
_TEXT_PHY_BASE:
    .word   CFG_PHY_UBOOT_BASE /* 反汇编得知为:43e00000 */

.globl _armboot_start
_armboot_start:
    /* 此含义可用C语言表示为:*(_armboot_start) = _start */
    .word _start

/* 以下这些地址跟u-boot.lds一一对应,声明地址标号 */
.globl _bss_start
_bss_start:
    /* bss段的起始地址 */
    .word __bss_start

.globl _bss_end
_bss_end:
    /* bss段的结束地址 */
    .word _end

/* 相当于一个无参数的宏cache_invalidate_dcache_v7,也就相当于一个函数了,似乎和cache有关,暂不细究 */
    .macro  cache_invalidate_dcache_v7
    MRC     p15, 1, r0, c0, c0, 1      @ read Cache Level ID register (clidr)
    ANDS    r3, r0, #0x7000000         @ extract level of coherency from clidr
    MOV     r3, r3, lsr #23            @ left align level of coherency bit field
    BEQ     finished_inval             @ if loc is 0, then no need to clean

    MOV     r10, #0                    @ start clean at cache level 0 (in r10)
loop_1:
    ADD     r2, r10, r10, lsr #1       @ work out 3x current cache level
    MOV     r1, r0, lsr r2             @ extract cache type bits from clidr
    AND     r1, r1, #7                 @ mask of the bits for current cache only
    CMP     r1, #2                     @ see what cache we have at this level
    BLT     skip_inval                 @ skip if no cache, or just i-cache
    MCR     p15, 2, r10, c0, c0, 0     @ select current cache level in cssr
    MOV     r1, #0
    MCR     p15, 0, r1, c7, c5, 4      @ prefetchflush to synch the new cssr&csidr
    MRC     p15, 1, r1, c0, c0, 0      @ read the new csidr
    AND     r2, r1, #7                 @ extract the length of the cache lines
    ADD     r2, r2, #4                 @ add 4 (line length offset)
    LDR     r6, =0x3ff
    ANDS    r6, r6, r1, lsr #3         @ find maximum number on the way size
    CLZ     r5,r6                       @ DCI 0xE16F5F16 , find bit position of way size increment
    LDR     r7, =0x7fff
    ANDS    r7, r7, r1, lsr #13        @ extract max number of the index size
loop_2:
    MOV     r8, r6                     @ create working copy of max way size
    loop_3:
    ORR     r11, r10, r8, lsl r5       @ factor way and cache number into r11
    ORR     r11, r11, r7, lsl r2       @ factor index number into r11
    MCR     p15, 0, r11, c7, c6, 2     @ invalidate by set/way
    SUBS    r8, r8, #1                 @ decrement the way
    BGE     loop_3

    SUBS    r7, r7, #1                 @ decrement the index
    BGE     loop_2
skip_inval:
    ADD     r10, r10, #2               @ increment cache number
    CMP     r3, r10
    BGT     loop_1
finished_inval:
    .endm

/*
 * the actual reset code
 */

reset:
/* 首先进入SVC管理模式,为什么要进行SVC管理模式而不是其它模式,主要因为SVC模式比其他模式有更多的硬件访问权限,并且多了影子寄存器,可以访问的硬件资源更多,详情:http://www.360doc.com/content/13/0514/11/7245213_285318786.shtml */

/*
 * MRS{条件} 通用寄存器,程序状态寄存器(CPSR或SPSR)
 * mrs :程序状态寄存器访问指令
 * 通用寄存器  程序状态寄存器(CPSR或SPSR)
 * 读取CPSR程序状态寄存器,保存到R0中
 */
    mrs r0, cpsr

/*
 * bic :BIC{条件}{S} 目的寄存器,操作数1,操作数2,
 * BIC指令用于清除操作数1的某些位,并把结果放置到目的寄存器中。操作数1应是一个寄存器
 * 操作数2可以是一个寄存器,被移位的寄存器,或一个立即数。操作数2为32位的掩码,如果在掩码中设置了某一
 * 位,则清除这一位。未设置的掩码位保持不变。
 * 0x1f=00011111,相当于清除低5位,刚好是模式位。
 */ 
    bic r0, r0, #0x1f

/*
 * ORR{条件}{S} 目的寄存器,操作数1,操作数2
 * ORR指令用于在两个操作数上进行逻辑或运算,并把结果放置到目的寄存器中。操作数1应是一个寄存器,操作数
 * 2可以是一个寄存器,被移位的寄存器,或一个立即数。该指令常用于设置操作数1的某些位。
 * 0xd3=11010011
 * 将r0与0xd3算数或运算,然后将结果给r0,即把r0的bit[7:6]和bit[4]和bit[2:0]置为1。
 */ 
    orr r0, r0, #0xd3

  /*
   * MSR{条件} 程序状态寄存器(CPSR或SPSR)_<域>,操作数
   * MSR指令用于将操作数的内容传送到程序状态寄存器的特定域中
   * 将r0中的值赋给状态寄存器cpsr
   */
    msr cpsr,r0

cache_init:
    /* mrc: 从协处理器读寄存器数据到ARM处理器的R0里面 */
    mrc p15, 0, r0, c0, c0, 0   @ read main ID register
    and r1, r0, #0x00f00000 @ variant
    and r2, r0, #0x0000000f @ revision
    orr r2, r2, r1, lsr #20-4   @ combine variant and revision
    cmp r2, #0x30
    mrceq   p15, 0, r0, c1, c0, 1   @ read ACTLR
    orreq   r0, r0, #0x6        @ Enable DP1(2), DP2(1)
    mcreq   p15, 0, r0, c1, c0, 1   @ write ACTLR

    /*
    CP15系统控制协处理器,CP15有很多个寄存器分别叫做寄存器0(Register 0),到寄存器15(Register 15)
    CP15 —系统控制协处理器 (the system control coprocessor)他通过协处理器指令MCR和MRC提供具体的寄存器来配置和控制caches、MMU、保护系统、配置时钟模式(在bootloader时钟初始化用到)
    CP15的寄存器只能被MRC和MCR(Move to Coprocessor from ARM Register )指令访问
     */
    /* Invalidate L1 I/D */
    mov r0, #0          @ set up for MCR
    /* 清空指令和数据的TLB */
    mcr p15, 0, r0, c8, c7, 0   @ invalidate TLBs
    /* 清除指令缓存ICache */
    mcr p15, 0, r0, c7, c5, 0   @ invalidate icache

    /* disable MMU stuff and caches */
    mrc p15, 0, r0, c1, c0, 0
    /* 此行代码是将r0的值,即0,写入到CP15的寄存器1中,向bit[0]写入0,即关MMU */

    bic r0, r0, #0x00002000 @ clear bits 13 (--V-)/* 清除bit[13],异常寄存器基地址 */
    bic r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)/* 清除bit[2-0],关闭Dcache */
    orr r0, r0, #0x00001000 @ set bit 12 (---I) Icache/* 设置bit[12],开启指令缓存Icache */
    orr r0, r0, #0x00000002 @ set bit 1  (--A-) Align/* 设置bit[1],打开数据地址对齐的错误检查,即如果数据地址为非法(奇数?)地址,就报错 */
    orr r0, r0, #0x00000800 @ set bit 11 (Z---) BTB
    mcr p15, 0, r0, c1, c0, 0
#endif

    /* Read booting information */
    /* 这个寄存器就是去读取OM(从哪启动)寄存器的值 */
    ldr r0, =POWER_BASE /* 将地址0x10020000中的值放入r0 */
    ldr r1, [r0,#OMR_OFFSET] /* 将存储器地址为r0+OMR_OFFSET(0x10020000)的字数据读入寄存器R1 */
    bic r2, r1, #0xffffffc1 /* 清除r1这个寄存器的bit[0]和bit[6:31],把结果放入r2 */

    /* NAND BOOT */
    cmp     r2, #0xA
        moveq   r3, #BOOT_ONENAND//如果等于0xA了,执行这个将BOOT_ONENAND(0x1)存到R3

    /* SD/MMC_CH2 BOOT */
    cmp     r2, #0x4
        moveq   r3, #BOOT_MMCSD //如果等于0x4了,执行这个将BOOT_MMCSD(0x3)存到R3

    /* eMMC43_CH0/USB BOOT */
    cmp     r2, #0x6
    moveq   r3, #BOOT_EMMC43 //如果等于0x6了,执行这个将BOOT_EMMC43(0x6)存到R3 

    /* eMMC44_CH4/SDMMC_CH2 BOOT */
    cmp     r2, #0x28
    moveq   r3, #BOOT_EMMC441 //如果等于0x28了,执行这个将BOOT_EMMC43(0x7)存到R3 

    /*
    User-defined information register. By asserting XnRESET pin, PMU clears INFORM0 to 3 registers
    */
    ldr r0, =INF_REG_BASE /* 将配置写入相应的寄存器 */
    str r3, [r0, #INF_REG3_OFFSET]     

    /*
     * Go setup Memory and board specific bits prior to relocation.
     */
     /* 这段等下单独截出来 */
    bl  lowlevel_init   /* go setup pll,mux,memory */

    ldr r0, =0x1002330C  /* PS_HOLD_CONTROL register */
    ldr r1, =0x00005300  /* PS_HOLD output high */
    str r1, [r0]

    /* get ready to call C functions */
    ldr sp, _TEXT_PHY_BASE  /* setup temp stack pointer 43e00000*/
    sub sp, sp, #12
    mov fp, #0          /* no previous frame, so fp=0 */

    /* 如果已经在DRAM里面跑了就不需要重载了,实际上SDRAM在BL1的时候就初始化了,所以uboot已经运行在了SDRAM上,但具体在哪运行可能不是我们想定义的地址,所以还是得重载。下面会进行判断是否重载
     */
    ldr r0, =0xff000fff

    /*
      bic指令用于清除操作数1的某些位,并把结果放置到目的寄存器中
      清除pc指针的低24位和高16位,其他位保持不变,即[24:47]不变
      判断uboot是不是在我们想要的位置上,如果不是就重载uboot到指定位置
     */
    bic r1, pc, r0      /* r0 <- current base addr of code 当前PC地址*/
    ldr r2, _TEXT_BASE      /* r1 <- original base addr in ram  代码段在内存中的位置*/
    bic r2, r2, r0      /* r0 <- current base addr of code */
    cmp     r1, r2                  /* compare r0, r1                  */
    beq     after_copy      /* r0 == r1 then skip flash copy   */
    /* 
    这里不相等代表当前PC不在该有的位置上,可能是因为UBOOT代码前面还有BL2的代码,现在不需要BL2了,所以重载uBOOT到0地址 
   */

    /* light led2 如果需要重载则点亮LED作为指示 */
    ldr r0, =0x11000104  /* GPL2(0)  */
    ldr r1, =0x00000001  /* GPL2(0 set output   */
    str r1, [r0]

    ldr r0, =0x11000100  /* GPL2(0)  */
    ldr r1, =0x00000001  /* GPL2(0 output high  */
    str r1, [r0]

    /* wait us 延时 */
    mov r1, #0x10000
9:  subs    r1, r1, #1
    bne 9b

    ldr r0, =INF_REG_BASE /* 读出寄存器值,可知从哪里启动的 */
    ldr r1, [r0, #INF_REG3_OFFSET]

    /* 这里以EMMC启动为例,不一一列出各个选项了 */
    ....

    /* eMMC43_CH0/USB BOOT */
    cmp     r1, #BOOT_EMMC43
    beq     emmc_boot

    ....

emmc_boot:
#if defined(CONFIG_CLK_1000_400_200) || defined(CONFIG_CLK_1000_200_200) || defined(CONFIG_CLK_800_400_200)
    ldr r0, =CMU_BASE
    ldr r2, =CLK_DIV_FSYS1_OFFSET /* 设置分频 */
    ldr r1, [r0, r2]
    orr r1, r1, #0x3 /* 进行或运算  DOUTMMC0 = MOUTMMC0/(3 + 1) */
    str r1, [r0, r2]
#endif
    bl  emmc_uboot_copy /* 确定重定位地址及大小 */
    b   after_copy      /* 重定位代码 */

#if defined(CONFIG_ENABLE_MMU)
enable_mmu: /* 使能MMU */
    /* enable domain access */
    ldr r5, =0x0000ffff
    mcr p15, 0, r5, c3, c0, 0       @load domain access register

    /* Set the TTB register */
    ldr r0, _mmu_table_base
    ldr r1, =CFG_PHY_UBOOT_BASE
    ldr r2, =0xfff00000
    bic r0, r0, r2
    orr r1, r0, r1
    mcr p15, 0, r1, c2, c0, 0

    /* Enable the MMU */
mmu_on:
    mrc p15, 0, r0, c1, c0, 0
    orr r0, r0, #1
    mcr p15, 0, r0, c1, c0, 0
    nop
    nop
    nop
    nop
#endif

#ifdef CONFIG_EVT1
    /* store DMC density information in u-boot C level variable */
    ldr r0, = CFG_UBOOT_BASE /* 把uboot地址0x43e00000写入r0 */
    sub r0, r0, #4 /* uboot的代码基地址减去4后的字数据写入r1*/
    ldr r1, [r0]   
    ldr r0, _dmc_density /* 0xFFFFFFFF = 4G的EMMC */
    str r1, [r0]  /* C表示: *r0 = r1 将这个字数据保存到EMMC? */
#endif


skip_hw_init:
    /* Set up the stack */
stack_setup:
/* 内存中uboot的区域空间(2M),减去的4K用于存放bd信息结构体数据 */
    ldr sp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE - 0x1000)//0x43e00000 + 2M - 4K

/* 请 BSS 段 */
clear_bss:
    ldr r0, _bss_start      /* find start of bss segment        */
    ldr r1, _bss_end        /* stop here                        */
    mov     r2, #0x00000000     /* clear                            */

/* 循环清除BSS段的内容,即置0 */
clbss_l:
    str r2, [r0]        /* clear loop... */
    add r0, r0, #4
    cmp r0, r1
    ble clbss_l

    ldr pc, _start_armboot

_start_armboot:
    .word start_armboot /* 进入这个C函数后就属于第二阶段的代码了,汇编到此结束 */     

补充说明一下 lowlevel_init 做了什么:

#define check_mem /* 判断是否需要重载的宏 */

    .globl lowlevel_init
lowlevel_init:
    /* use iRAM stack in bl2 */
    /* 设置成iram的栈保险一些,因为不用初始化,uboot刚开始并不知道dram是否初始化了,所以使用iram */
    ldr sp, =0x02060000

    push    {lr}

    /* check reset status  */
    ldr     r0, =(INF_REG_BASE + INF_REG1_OFFSET)//INFORM1: 0x10020800+4
    ldr     r1, [r0]

    /* AFTR wakeup reset */
    ldr r2, =S5P_CHECK_DIDLE //0xBAD00000
    cmp r1, r2
    beq exit_wakeup

    /* Sleep wakeup reset */
    ldr r2, =S5P_CHECK_SLEEP//0x00000BAD
    cmp r1, r2
    beq wakeup_reset

    /* PS-Hold high PS_HOLD_CONTROL */
    ldr r0, =0x1002330c
    ldr r1, [r0]
    orr r1, r1, #0x300  /* r1和0x300或运算 */
    str r1, [r0] /* r1写回地址r0 */

    /* 0x0 = Disables Pull-up/Pull-down */
    ldr     r0, =0x11000c08
    ldr r1, =0x0
    str r1, [r0]/* r1写回地址r0 */

    /* Clear  MASK_WDT_RESET_REQUEST  */
    ldr r0, =0x1002040c
    ldr r1, =0x00
    str r1, [r0]

    #ifdef check_mem /* 检测是否需要重载 */
    /* when we already run in ram, we don't need to relocate U-Boot.
     * and actually, memory controller must be configured before U-Boot
     * is running in ram.
     */
    ldr r0, =0xff000fff
    bic r1, pc, r0      /* r0 <- current base addr of code */
    ldr r2, _TEXT_BASE      /* r1 <- original base addr in ram */
    bic r2, r2, r0      /* r0 <- current base addr of code */
    cmp     r1, r2                  /* compare r0, r1                  */
    beq     1f          /* r0 == r1 then skip sdram init   */
#endif
    /* 这里需要重载所以要进行如下初始化 */
    /* Memory initialize */
    bl mem_ctrl_asm_init

    /* init system clock */
    bl system_clock_init

    bl tzpc_init
    b   1f

1:
    /*wenpin.cui: headphone and sw uart switch init*/
    ldr r0, =0x11000C44
    ldr r1, [r0]
    and r1, r1, #0x4    
    cmp r1, #0x4    /*uart*/    
    beq out 

    ldr     r0, =0x11400084  /* GPC1(0)  */
    ldr     r1, [r0]    /* read GPC1DAT status*/
    orr r1, r1, #0x1    /* GPC1(0) output high  */
    str     r1, [r0]

    ldr     r0, =0x11400080  /* GPC1(0)  */
    ldr r1, [r0]
    and r1, r1, #0xfffffff0
    orr     r1, r1, #0x1    /* GPC1(0) output  */
    str     r1, [r0]
out:
    /* for UART */
    bl uart_asm_init

    bl onenandcon_init

    /* Print 'K' */
    ldr r0, =ELFIN_UART_CONSOLE_BASE
    ldr r1, =0x4b4b4b4b
    str r1, [r0, #UTXH_OFFSET]

    /* 2010.08.27 by icarus : for temporary 3D clock fix */
    ldr r1, =0x1
    ldr r2, =0x1003C22C
    str r1, [r2]
    ldr r1, =0x2
    ldr r2, =0x1003C52C
    str r1, [r2]

    /* 2010.10.17 by icarus : for temporary MFC clock fix */
    ldr r1, =0x3
    ldr r2, =0x1003C528
    str r1, [r2]

/*
其中保存的寄存器中,也包括lr的值(因为用bl指令进行跳转的话,那么之前的pc的值是存在lr中的),
然后在子程序执行完毕的时候,再把堆栈中的lr的值pop出来,赋值给pc,这样就实现了子函数的正确的返回。
*/
    pop {pc}

上面的代码都进行了注解,可以总结uboot启动第一阶段的所做的事如下:

  1. 中断向量地址定义

  2. uboot代码段、数据段、bss段定义

  3. 上电进入复位中断处理

  4. 把CPU的工作模式设置为SVC32模式

  5. 清空指令和数据的TLB

  6. 清除指令缓存ICache

  7. 关MMU

  8. 关Dcache

  9. 读取boot启动方式

  10. 选择相应boot(NAND/SD/EMMC)启动模式

  11. 进入 lowlevel_init :set sp in bl2、Memory initialize、init system clock 、uart_asm_init、onenandcon_init

  12. 设置SDRAM栈,判断是否需要重载,需要重载则点灯,读取启动信息判断从哪里复制bin,进行重载,复制uboot到指定地址即重定位uboot,使能MMU,保存 dmc density 信息,设置栈,腾出4K存放bd信息,清BSS段,执行 “start_armboot”。

针对上面可能还有些疑问:

问:根据手册,IROM初始化了系统时钟和栈,BL1初始化了系统时钟和DRAM,是不是初始化这些后 UBOOT 可以不用再次初始化了呢?直接操作即可?

答:虽然说BL1初始化了DRAM和时钟,但是这些设置我们并不清楚,也不知道是不是处在最优状态,所以最好的解决方式就是再初始化一遍,设置为我们想要的配置值

不了解各个段区别的可以看下下面的 TIPS

TIPS:

  • BSS段
    在采用段式内存管理的架构中,BSS段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。
  • 数据段
    在采用段式内存管理的架构中,数据段(data segment)通常是指用来存放程序中已初始化的全局变量的一块内存区域。数据段属于静态内存分配。
  • 代码段
    在采用段式内存管理的架构中,代码段(text segment)通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域属于只读。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

uboot启动分析第一阶段(start.S) 的相关文章

  • shell脚本第一行:#!/bin/bash的含义

    相信有接触过shell脚本的同学们都应该知道 xff0c shell脚本的第一行一般会写有以下字样 xff1a bin bash或者 bin sh或者 bin awk 比较常见的说法是 xff1a 第一行的内容指定了shell脚本解释器的路
  • LPMS-IMU姿态解算

    参考文章 xff1a AHRS姿态解算说明 加速度 43 陀螺仪 43 磁力计原理及原始数据分析 AHRS俗称航姿参考系统 xff0c AHRS由加速度计 xff0c 磁场计 xff0c 陀螺仪构成 xff0c AHRS的真正参考来自于地球
  • 如何在我的VsCode中集成Git

    在VsCode中配置Git后 xff0c 我们就可以简单快速管理我们的代码仓库 一 下载安装Git 如果没有Git xff0c 这里可以参考我之前的文章安装 配置Git 精讲Git xff08 从安装到熟练使用一文全解 xff0c 看完轻松
  • 虚拟机:xshell连接虚拟机Ubuntu失败解决方法

    当我们买不起服务器但却想模拟服务器环境进行学习时 xff0c 再好不过的就是直接装Linux虚拟机了 xff0c 非常简单快捷 xff01 首先我们可以通过ifconfig a来查看Ubuntu Server的IP地址 xff08 前提是你
  • SVM原理:超平面方程

    xff08 1 xff09 超平面方程 3维空间中平面方程的一般形式 xff1a 1 我们都知道为平面到原点的距离 这里简单证明超平面的法向量为 d维空间平面方程的一般形式 xff1a 2 平面的法向量为 xff0c xff08 分号表示列
  • windows11编译OpenCV4.5.0 with CUDA(附注意事项)

    windows11编译OpenCV4 5 0 with CUDA 从OpenCV4 2 0 版本开始允许使用 Nvidia GPU 来加速推理 本文介绍最近使用windows11系统编译带CUDA的OpenCV4 5 0的过程 文中使用 特
  • Windows11安装Detectron2(附详细操作指南)

    Windows11安装Detectron2 0 简介 Detectron2 是 Facebook AI Research 的下一代目标检测库 xff0c 可提供最先进的检测和分割算法 它是 Detectron 和 maskrcnn benc
  • 四轴飞行器F450+Futaba 14SG+好盈电调油门行程校准

    F450 43 Futaba 14SG油门行程校准 装机前忘了进行油门校准 xff0c 装好后进行校准一开始老出现电机接收不到油门信号的提示音 因为Futaba 14SG也是新入手的控 xff0c 有些模式和操作不熟悉 xff0c 为此花了
  • NTKO控件安装:“不能装载文档控件,请在检查浏览器的选项中检查浏览器的安全设置”问题

    上礼拜手欠把OFFICE文档控件 NTKO给卸载了 xff0c 结果这周通知基金结题网上填写报告 本以为就装个控件 xff0c 没想到各种问题 xff0c 几经尝试终于解决 xff0c 总结如下 xff1a 1 问题 xff1a 不能装载文
  • Arduino - 串口操作函数与示例代码大全

    Arduino 串口操作函数与示例代码大全 本文总结了Arduino常用串口操作函数 xff0c 函数说明部分来源于Arduino 官网串口使用指南 xff0c 示例与实验部分为自编 本文是对Arduino串口操作函数的较全面总结 xff0
  • vs2010 出错:error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏

    LNK1123 转换到 COFF期间失败 文件无效或损坏 的解决方法 一 错误描述 之前写的程序隔段时间使用VS2010再次运行时出现如下错误 xff1a LINK fatal error LNK1123 转换到 COFF 期间失败 文件无
  • OpenCV—基本矩阵操作与示例

    OpenCV的基本矩阵操作与示例 OpenCV中的矩阵操作非常重要 xff0c 本文总结了矩阵的创建 初始化以及基本矩阵操作 xff0c 给出了示例代码 xff0c 主要内容包括 xff1a 创建与初始化 矩阵加减法 矩阵乘法 矩阵转置 矩
  • ubuntu16.04下利用ROS启动LPMS-CURS、CURS2等型号IMU;用imu控制turtlesim--教程

    文章目录 IMU型号及协议第一步 下载安装 LPsensor library第二步 设置ROS和carkin工作空间第三步 下载编译LPMS IMU的ROS驱动第四步 启动IMU xff08 可能也会遇到问题 xff09 遇到的问题1 ub
  • PixHawk飞控和Mission Planner地面站安装调试

    PixHawk飞控和Mission Planner地面站安装调试 PixHawk是著名飞控厂商3DR推出的新一代独立 开源 高效的飞行控制器 xff0c 前身为APM飞控 xff0c 不仅提供了丰富的外设模块和可靠的飞行体验 xff0c 有
  • 飞越650四轴无人机安装全程详解(多图)

    飞越650四轴无人机安装全程详解 xff08 多图 xff09 本文根据自己的安装实际过程 xff0c 总结了开箱后一个比较合理的650四轴无人机安装顺序 xff0c 以及各个步骤的注意事项 xff0c 主要内容包括 xff1a 系统基本配
  • DIY一个基于树莓派和Python的无人机视觉跟踪系统

    DIY 一个基于树莓派和Python的无人机视觉跟踪系统 无人机通过图传将航拍到的图像存储并实时传送回地面站几乎已经是标配 如果想来点高级的 在无人机上直接处理拍摄的图像并实现自动控制要怎么实现呢 xff1f 其实视觉跟踪已经在一些高端的消
  • windows环境下的Anaconda安装与OpenCV机器视觉环境搭建

    windows环境下的Anaconda安装与OpenCV机器视觉环境搭建 本文介绍win7和win10系统下通过Anaconda配置基于python语言的机器视觉编程环境 xff08 博主测试了两个系统下的安装基本相同 xff09 xff0
  • OpenCV—轮廓操作一站式详解:查找/筛选/绘制/形状描述与重心标注(C++版)

    OpenCV 轮廓操作一站式详解 xff1a 查找 筛选 绘制 形状描述与重心标注 C 43 43 版 轮廓 是定义或限定形状或对象的边或线 xff0c 是机器视觉中的常用的概念 xff0c 多用于目标检测 识别 等任务 关于OpenCV轮
  • 正太分布函数和反函数 标量值函数 (借鉴)

    标准正态分布函数 CREATE function dbo normcdf 64 p decimal 28 18 AS begin
  • 离散时间傅里叶变换(一)

    一 非周期信号的表示 xff1a 离散时间博里叶变换 1 1 离散时间傅里叶变换的导出 1 离散时间傅里叶变换对 要清楚推导过程 X ejw 称为离散时间傅里叶变换 xff0c 这一对式子就是离散时间傅里叶变换对 上式称为综合公式 xff0

随机推荐

  • HuskyLens摄像头系列 | 写给小学生看的视觉PID巡线算法

    Hello xff0c 大家好 xff0c 光天化日之下我又来撸狗了 距离上次撸狗已经过去了个把月时间了 xff0c 那么这次又有什么新惊喜呢 xff1f 先来看一下本期的演示视频吧 https www bilibili com video
  • 时空行为检测数据集 JHMDB & UCF101_24 详解

    文章目录 0 前言1 JHMDB1 1 基本情况1 2 数据准备以及标签详解 2 UDF101 242 1 基本情况2 2 数据准备与标签详解 3 数据集可视化代码 0 前言 现在常用的时空行为检测数据集只有AVA JHMDB UCF101
  • Lock与RLock的区别

    目录 往期推荐介绍区别一区别二 往期推荐 Python多线程的使用 Python线程池的使用 Python多线程的安全问题 B站同名 有温度的算法 已经上线 想观看视频讲解的同学 点击此处直达B站 介绍 在上节中为大家说明了线程访问临界资源
  • ROS 小技巧 - OpenCV4 与 CV_Bridge 配合使用

    1 现象 ROS默认的Python版本是3 3 xff0c 但我系统安装的是OpenCV4 5 如果直接在pkg中使用cv bridge和opencv4 5就会有问题 会有一些undefined reference问题 参考资料 xff1a
  • 【做题系统】后端设计

    目录 一 设计思路 1 项目背景 2 技术栈选择 二 系统设计 1 系统结构图 2 项目结构 3 数据建模 4 数据流图 5 主要流程图 三 问题及解决办法 1 实现安全登录 访问 2 数据库中的信息安全问题 3 Mybatis plus如
  • C/C++字符串查找函数

    C C 43 43 string库 xff08 string h xff09 提供了几个字符串查找函数 xff0c 如下 xff1a memchr在指定内存里定位给定字符strchr在指定字符串里定位给定字符strcspn返回在字符串str
  • ssh命令-manpage

    SSH Section User Commands 1 Index Return to Main Contents BSD mandoc NAME ssh OpenSSH SSH 客户端 远程登录程序 总览 SYNOPSIS ssh l l
  • 一小时做出Java实战项目——飞翔的小鸟

    学姐又来啦 xff0c 今日分享一个Java实战项目 飞翔的小鸟 相信大家都玩过这个游戏 xff0c 这个游戏陪伴了我们整整一个童年 xff0c 是我们青春的回忆 飞翔的小鸟 xff0c 游戏中玩家只需通过点击方向键操纵让小鸟避开绿色管道等
  • 搭建本地仓库源

    一 如何搭建仓库源 之前讲了定制ISO的方法 xff1a 使用chroot定制系统 xff0c 但有时候我们想自定义的安装包不在上游的仓库源中 xff0c 在我们本地应该怎么办呢 xff1f 如果我们将deb包拷贝到iso目录再安装有点过于
  • 节点操作案例

    1 下拉菜单 xff08 仿微博 xff09 lt DOCTYPE html gt lt html lang 61 34 en 34 gt lt head gt lt meta charset 61 34 UTF 8 34 gt lt me
  • document获取对象的三种三方法

    Document对象中有几个常用的方法 xff0c 我们在Dom简介中提到过 说到获取JavaScript对象的方法 xff0c 最常用的可能就是getElementById了 xff0c 它是Document中最常用的获取对象的方式之一
  • 程序员,最关键的跨越是什么?做到了月薪可能翻上几番~

    黑马程序员视频库 播妞微信号 xff1a boniu236 传智播客旗下互联网资讯 学习资源免费分享平台 作为一名程序员 xff0c 最关键的跨越是什么 xff1f 从普通程序员进阶为熟练开发者 xff0c 从熟练开发者跃升到技术专家或架构
  • 黑马程序员:3分钟带你读懂C/C++学习路线

    随着互联网及互联网 43 深入蓬勃的发展 xff0c 经过40余年的时间洗礼 xff0c C C 43 43 俨然已成为一门贵族语言 xff0c 出色的性能使之成为高级语言中的性能王者 而在今天 xff0c 它又扮演着什么样重要的角色呢 x
  • 数据归一化

    原文链接 xff1a 从公式出发 xff1a 什么是模型收敛的有效方法 xff1f 大家好 xff0c 我是泰哥 数据归一化在模型收敛中起着至关重要的作用 xff0c 从经典机器学习到深度学习的数据归一化方法是如何一步步演变的呢 xff1f
  • 【Python面试】 说说Python变量、函数、类的命名规则?

    最近公众号新增加了一个栏目 xff0c 就是每天给大家解答一道Python常见的面试题 xff0c 反正每天不贪多 xff0c 一天一题 xff0c 正好合适 xff0c 只希望这个面试栏目 xff0c 给那些正在准备面试的同学 xff0c
  • ​LeetCode刷题实战46:全排列

    算法的重要性 xff0c 我就不多说了吧 xff0c 想去大厂 xff0c 就必须要经过基础知识和业务逻辑面试 43 算法面试 所以 xff0c 为了提高大家的算法能力 xff0c 这个公众号后续每天带大家做一道算法题 xff0c 题目就从
  • Android硬件访问服务-Service

    Android有四大组件 xff1a 一 Activity 二 Service 三 Broadcast Receiver 四 Content Provider Service是Android中一个类 xff0c 它是Android四大组件之
  • android6.0第三方APP获得设备节点的访问权限

    之前使用android4 4的系统进行开发时 system app xff08 系统自带APP xff09 目录下的 app 可以直接访问 dev 目录下的设备节点 xff0c Android 5 0 以后 xff0c 因为采取了 SEAn
  • U-boot取消或修改启动延时bootdelay

    在我们的实际项目中都希望uboot尽量能够快速启动 xff0c 这就涉及到uboot的裁剪工作 xff0c 由于裁剪的工作量和内容比较多 xff0c 这里暂不描述 但是uboot有个启动延时bootdelay xff0c 在我们进入linu
  • uboot启动分析第一阶段(start.S)

    前面分析了启动脚本 Makefile mkconfig xff0c 接下来就是uboot的start S这个启动代码了 xff0c 下面是本章的平台介绍 xff1a 单板 xff1a 迅为4412开发板 Exynos 4412 SDRAM