Uboot19之start.S之CP15协处理器

本文详细解析了U-Boot启动过程中的关键步骤,包括内存重定位、关键寄存器初始化、缓存管理及MMU配置等内容。通过深入分析start.S文件中的汇编指令,阐述了如何设置栈、初始化内存及板级特性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

时间:2018.4.5  作者:Tom   工作:HWE 说明:如需转载,请注明出处。

/*

*************************************************************************

* Startup Code (reset vector)

* do important init only if we don't start from memory!

* setup Memory and board specific bits prior to relocation.

* relocate armboot to ram

* setup stack

*************************************************************************

*/

_TEXT_BASE:

    .word    TEXT_BASE

TEXT_BASE = 0xc3e00000

1.汇编是"绝对引用",即引用的就是地址

在start.s中

_TEXT_BASE:

.word TEXT_BASE /*uboot映像在SDRAM中的重定位地址,我设置为0xc3e00000 */

1)第100行这个TEXT_BASE就是上个课程中分析Makefile时讲到的那个配置阶段的TEXT_BASE,其实就是我们链接时指定的uboot的链接地址。(值就是0xc3e00000)

2)源代码中和配置Makefile中很多变量是可以互相运送的。简单来说有些符号的值可以从Makefile中传递到源代码中。

3) 此处和上面的类似,_TEXT_BASE是一个标号地址,此地址中是一个word类型的变量,变量名是TEXT_BASE,此值见名知意,是text的base,即代码的基地址,可以在Makefile中找到其定义:TEXT_BASE = 0x33D00000

/*

* Below variable is very important because we use MMU in U-Boot.

* Without it, we cannot run code correctly before MMU is ON.

* by scsuh.

*/

_TEXT_PHY_BASE:

    .word    CFG_PHY_UBOOT_BASE

X210_sd.h 配置文件中有定义:

结果就是CFG_PHY_UBOOT_BASE     0x33e00000    uboot在DDR中的物理地址,起始地址。

/*

* These are defined in the board-specific linker script.

*/

.globl _bss_start

_bss_start:

    .word __bss_start

__bss_start定义在和开发板相关的u-boot.lds中,_bss_start保存的是__bss_start标号所在的地址。

.globl _bss_end

_bss_end:

    .word _end

同上,这样赋值是因为代码所在地址非编译时的地址,直接取得该标号对应地址。

关于_bss_start和_bss_end都只是两个标号,对应着此处的地址。

而两个地址里面分别存放的值是__bss_start和_end,返两个的值,根据注释所说,是定义在开发板相关的链接脚本里面的,我们此处的开发板相关的链接脚本是:

C:\board\samsung\x210

其中可以找到__bss_start和_end的定义:

__bss_start = .;

.bss : { *(.bss) }

_end = .;

而关于_bss_start和_bss_end定义为.glogl全局变量,是因为uboot的其他源码中要用到返两个变量,详情请自己去搜索源码。

#if defined(CONFIG_USE_IRQ)

/* IRQ stack memory (calculated at run-time) */

.globl IRQ_STACK_START

IRQ_STACK_START:

    .word    0x0badc0de

堆栈指针开始的地方,但目前我们初始化,还不知道,随便放了一个badcode

/* IRQ stack memory (calculated at run-time) */

.globl FIQ_STACK_START

FIQ_STACK_START:

    .word 0x0badc0de

#endif

同上,IRQ_STACK_START和FIQ_STACK_START,也是在cpu_init中用到了。不过此处,是另有当定义了宏CONFIG_USE_IRQ的时候,才用到返两个变量,其含义也很明显,另有用到了中断IRQ,才会用到中断的堆栈,才有中断堆栈的起始地址。快速中断FIQ同理。在cpu_init中,根据我们的一些定义,比如堆栈大小等等,去修改了IRQ_STACK_START ,FIQ_STACK_START ,FREE_RAM_END和FREE_RAM_SIZE的值。至于为何这么修改,后面遇到的时候会具体再解释。

/*

*************************************************************************

*

* CPU_init_critical registers

*

* setup important registers

* setup memory timing

*

*************************************************************************

*/

/*

* we do sys-critical inits only at reboot,

* not when booting from ram!

*/

1)bl    disable_l2cache        // 禁止L2 cache

2)bl    set_l2cache_auxctrl_cycle    // l2 cache相关初始化

3)bl    enable_l2cache        // 使能l2 cache

4)刷新L1 cache的icache和dcache。

5)关闭MMU

总结:上面这5步都是和CPU的cache和mmu有关的,不用去细看,大概知道即可。

cpu_init_crit:

 

#ifndef CONFIG_EVT1

#if 0    

    bl    v7_flush_dcache_all

不用看

#else

    bl    disable_l2cache

ARM 微处理器可支持多达 16 个协处理器,用亍各种协处理操作,在程序执行的过程中,每个协处理器叧执行针对自身的协处理指令,忽略 ARM 处理器和其他协处理器的指令。 ARM 的协处理器指令主要用亍 ARM 处理器初始化 ARM 协处理器的数据处理操作,以及在ARM 处理器的寄存器和协处理器的寄存器之间传送数据,和在 ARM 协处理器的寄存器和存储器之间传送数据。 ARM 协处理器指令包括以下 5 条:
— CDP 协处理器数操作指令
— LDC 协处理器数据加载指令
— STC 协处理器数据存储指令
— MCR ARM 处理器寄存器到协处理器寄存器的数据传送指令
— MRC 协处理器寄存器到ARM 处理器寄存器的数据传送指令

CP15系统控制协处理器
CP15 —系统控制协处理器 (the system control coprocessor)他通过协处理器指令MCR和MRC提供具体的寄存器来配置和控制caches、 MMU、保护系统、配置时钟模式(在bootloader时钟初始化用到)CP15的寄存器叧能被MRC和MCR(Move to Coprocessor from ARM Register )指令访问"。

CP15有很多个寄存器,分别叨做寄存器0(Register 0),到寄存器15(Register 15),每个寄存器分别控制丌同的功能,而且有的是只读,有的是只写,有的是可读写。而且这些寄存器的含义,随着版本ARM内核版本变化而不断扩展,详情请参考返个网址:
http://www.heyrick.co.uk/assembler/coprocmnd.html,https://blog.youkuaiyun.com/xie0812/article/details/52628006,
其中,根据我们此处关心的内容,摘录部分内容如下:

用于系统存储管理的协处理器CP15

MCR{cond} coproc,opcode1,Rd,CRn,CRm,opcode2

MRC {cond} coproc,opcode1,Rd,CRn,CRm,opcode2

coproc 指令操作的协处理器名.标准名为pn,n,为0~15

opcode1 协处理器的特定操作码. 对于CP15寄存器来说,opcode1永远为0,不为0时,操作结果不可预知

<Rd>作为源寄存器的ARM寄存器,其值将被传送到协处理器寄存器中。
<CRn>作为目标寄存器的协处理器寄存器,其编号可能是C0,C1,…,C15。
<CRm>和<opcode_2>两者组合决定对协处理器寄存器迚行所需要的操作,如果没有指定,则将为<CRm>为C0,opcode_2为0"

在基于ARM的嵌入式系统中,存储系统通常是通过系统控制协处理器CP15完成的。CP15可以包含16个32位的寄存器,其编号为0-15。实际上对于某些编号的寄存器可能对应有多个物理寄存器。在指令中指定特定的标志位来区分这些物理寄存器。有些类似于ARM寄存器中,处于不同的处理器模式时,ARM某些寄存器可能不同。

.global disable_l2cache

1)bl    disable_l2cache        // 禁止L2 cache

disable_l2cache:

    mrc p15, 0, r0, c1, c0, 1

rd为r0=0
CRn为C1
CRm为C0
opcode_2为1,即#将主标示符寄存器c1中的内容读到AMR寄存器R0中

    bic r0, r0, #(1<<1)

将r0中第二位置0;

    mcr p15, 0, r0, c1, c0, 1

将r0写入C1

    mov    pc, lr

    mov    r0, #0x0    @

    mov    r1, #0x0    @ i    

    mov    r3, #0x0

    mov    r4, #0x0

对寄存器进行清零。

lp1:

    mov    r2, #0x0    @ j

lp2:    

    mov    r3, r1, LSL #29        @ r3 = r1(i) <<29

    mov    r4, r2, LSL #6        @ r4 = r2(j) <<6

    orr    r4, r4, #0x2        @ r3 = (i<<29)|(j<<6)|(1<<1)

    orr    r3, r3, r4

    mov    r0, r3            @ r0 = r3

    bl    CoInvalidateDCacheIndex

    add    r2, #0x1        @ r2(j)++

    cmp    r2, #1024        @ r2 < 1024

    bne    lp2            @ jump to lp2

    add    r1, #0x1        @ r1(i)++

    cmp    r1, #8            @ r1(i) < 8

    bne    lp1            @ jump to lp1

 

    bl    set_l2cache_auxctrl

2)bl    set_l2cache_auxctrl_cycle    // l2 cache相关初始化

    bl    enable_l2cache

3)bl    enable_l2cache        // 使能l2 cache

 

#endif

#endif

    

    bl    disable_l2cache

 

    bl    set_l2cache_auxctrl_cycle

 

    bl    enable_l2cache

4)刷新L1 cache的icache和dcache。

/*

* Invalidate L1 I/D

因为此时CPU还不能区分来的数据是指令还是数据,代码段内容会被修改然后加载到数据缓存,此时代码缓存中的数据和RAM中的不一致。所以清掉重新来过。

*/

mov    r0, #0 @ set up for MCR    r0清零

mcr    p15, 0, r0, c8, c7, 0 @ invalidate TLBs        r0放在C8 C7中,这个TLB是虚拟内存管理,后面再说、

mcr    p15, 0, r0, c7, c5, 0 @ invalidate icache        使之无效

 

/*

* disable MMU stuff and caches

5)关闭MMU

*/

mrc    p15, 0, r0, c1, c0, 0

bic    r0, r0, #0x00002000 @ clear bits 13 (--V-)

bic    r0, r0, #0x00000007 @ clear bits 2:0 (-CAM)

orr    r0, r0, #0x00000002 @ set bit 1 (--A-) Align

orr    r0, r0, #0x00000800 @ set bit 12 (Z---) BTB

mcr     p15, 0, r0, c1, c0, 0

/* Read booting information */

ldr    r0, =PRO_ID_BASE

ldr    r1, [r0,#OMR_OFFSET]

bic    r2, r1, #0xffffffc1

 

/* Read booting information */

ldr    r0, =PRO_ID_BASE

ldr    r1, [r0,#OMR_OFFSET]

bic    r2, r1, #0xffffffc1

读取启动信息

#ifdef CONFIG_VOGUES

    /* PS_HOLD(GPH0_0) set to output high */

    ldr    r0, =ELFIN_GPIO_BASE

    ldr    r1, =0x00000001

    str    r1, [r0, #GPH0CON_OFFSET]

 

    ldr    r1, =0x5500

    str    r1, [r0, #GPH0PUD_OFFSET]

 

    ldr    r1, =0x01

    str    r1, [r0, #GPH0DAT_OFFSET]

#endif

这个是上电锁存。

/* NAND BOOT */

    cmp    r2, #0x0        @ 512B 4-cycle

    moveq    r3, #BOOT_NAND

 

    cmp    r2, #0x2        @ 2KB 5-cycle

    moveq    r3, #BOOT_NAND

 

    cmp    r2, #0x4        @ 4KB 5-cycle    8-bit ECC

    moveq    r3, #BOOT_NAND

 

    cmp    r2, #0x6        @ 4KB 5-cycle    16-bit ECC

    moveq    r3, #BOOT_NAND

 

    cmp    r2, #0x8        @ OneNAND Mux

    moveq    r3, #BOOT_ONENAND

 

    /* SD/MMC BOOT */

    cmp r2, #0xc

    moveq r3, #BOOT_MMCSD    

 

    /* NOR BOOT */

    cmp r2, #0x14

    moveq r3, #BOOT_NOR    

/* Uart BOOTONG failed */

    cmp r2, #(0x1<<4)

    moveq r3, #BOOT_SEC_DEV

    

    ldr    r0, =INF_REG_BASE

    str    r3, [r0, #INF_REG3_OFFSET] 将启动标识码写入INF_REG3中

1)从哪里启动是由SoC的OM5:OM0这6个引脚的高低电平决定的。

2)实际上在210内部有一个寄存器(地址是0xE0000004),这个寄存器中的值是硬件根据OM引脚的设置而自动设置值的。这个值反映的就是OM引脚的接法(电平高低),也就是真正的启动介质是谁。

3)我们代码中可以通过读取这个寄存器的值然后判断其值来确定当前选中的启动介质是Nand还是SD还是其他的。

4)start.S的225-227行执行完后,在r2寄存器中存储了一个数字,这个数字等于某个特定值时就表示SD启动,等于另一个特定值时表示从Nand启动••••

5)260行中给r3中赋值#BOOT_MMCSD(0x03),这个在SD启动时实际会被执行,因此执行完这一段代码后r3中存储了0x03,以后备用。

 

    /*

     * Go setup Memory and board specific bits prior to relocation. 15.重定位前初始化存储器和板特殊位

     */

 

    ldr    sp, =0xd0036000 /* end of sram dedicated to u-boot */

    sub    sp, sp, #12    /* set stack */

    mov    fp, #0

1)284-286行第一次设置栈。这次设置栈是在SRAM中设置的,因为当前整个代码还在SRAM中运行,此时DDR还未被初始化还不能用。栈地址0xd0036000是自己指定的,指定的原则就是这块空间只给栈用,不会被别人占用。

2)在调用函数前初始化栈,主要原因是在被调用的函数内还有再次调用函数,而BL只会将返回地址存储到LR中,但是我们只有一个LR,所以在第二层调用函数前要先将LR入栈,否则函数返回时第一层的返回地址就丢了。

    bl    lowlevel_init    /* go setup pll,mux,memory */ 17.调用lowlevel_init函数初始化pll memory等与板子相关的内容 函数位于board目录下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值