您的位置:首页 > 运维架构 > 网站架构

Bootloader架构设计

2016-05-21 00:00 281 查看
此部分为Uboot第一阶段,后期陆续补上第二阶段。

1.Bootloader核心初始化(硬件初始化)

在这部分还和开发板没关系,只和处理器有关

1.1 异常处理

start.S

.text

.global    _start

_start:

        b     reset                                         

        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

 

_undefined_instruction: .word undefined_instruction

_software_interrupt:   .wordsoftware_interrupt

_prefetch_abort:   .wordprefetch_abort

_data_abort:         .worddata_abort

_not_used:           .wordnot_used

_irq:               .wordirq

_fiq:               .wordfiq                                 

 

undefined_instruction:

        nop

 

software_interrupt:

        nop

 

prefetch_abort:

        nop

 

data_abort:

        nop

 

not_used:

        nop

 

irq:

        nop

 

fiq:

        nop

 

reset:

        nop

 

uboot.lds

OUTPUT_ARCH(arm)

ENTRY(_start)  入口

SECTIONS {

       . = 0x30008000;  设置起始位置

      

       . = ALIGN(4);  4字节对齐

       .text :   代码段

       {

       start.o (.text)   放start.o文件以及其他文件的代码段

       *(.text)

       }

 

       . = ALIGN(4);

       .data :

       {

       *(.data)

       }

      

       . = ALIGN(4);

       bss_start = .;    @记录bss起始位置

       .bss :

       {

       *(.bss)

       }

       bss_end = .;    @记录bss结束位置

}

 

Makefile

all: start.o

       arm-linux-ld -Tuboot.lds-o uboot.elf $^

       arm-linux-objcopy -Obinary uboot.elf uboot.bin

      

%.o : %.S

       arm-linux-gcc -g -c $^

      

%.o : %.c

       arm-linux-gcc -g -c $^

      

.PHONY: clean

clean:

       rm *.o *.elf *.bin

 

1.2 设置为SVC模式

将start.S中reset改写成

reset:

        bl set_svc

set_svc:

da15
        mrs r0, cpsr

        bic r0, r0,#0x1f  @后5位清零

        orr r0,#0xd3     @d3屏蔽了irq,frq

        msr cpsr, r0

 

1.3 关闭看门狗

将start.S中reset改写成

reset:

        bl set_svc

        bldisable_watchdog

set_svc:      @6440210 y也一样

        mrs r0, cpsr

        bic r0, r0,#0x1f  @后5位清零

        orr r0,#0xd3    @d3屏蔽了irq,fiq

              msr cpsr, r0

#define pWTCON 0x530000000  @看门狗时钟控制寄存器地址6440 210不同

disable_watchdog:

        ldr r0, = pWTCON

        mov r1, #0x0

        str r1, [r0]

1.4 关闭所有中断

将start.S中reset改写成

reset:

        bl set_svc

       bl disable_watchdog

        bldisable_interrupt

set_svc://6440 210 y  @也一样

        mrs r0, cpsr

        bic r0, r0,#0x1f  @后5位清零

        orr r0,#0xd3    @d3屏蔽了irq,fiq

              msr cpsr, r0

#define pWTCON 0x530000000  @看门狗时钟控制寄存器地址6440 210不同

disable_watchdog:

        ldr r0, = pWTCON

        mov r1, #0x0 

        str r1, [r0]

disable_interrupt:    @2440

        mvn r1, #0x0     @所有寄存器  将0x0取反

        ldr r0,=0x40000008   @INTIMSK寄存器地址

        str r1, [r0]

6410和210的话改下寄存器地址

disable_interrupt:  @6410

        mvn r1, #0x0

        ldr r0,=0x71200014   @两个中断使能寄存器地址

        str r1, [r0]

       ldr r0, =0x71300014

        str r1, [r0]

 

disable_interrupt:   @210

        mvn r1, #0x0

        ldr r0, =0xf2000014   @四个中断使能清除寄存器地址

        str r1, [r0]

        ldr r0, =0xf2100014

        str r1, [r0]

        ldr r0, =0xf2200014

        str r1, [r0]

        ldr r0, =0xf2300014

        str r1, [r0]

1.5 关闭mmu和cache 都在cp15协处理器

将start.S中reset改写成

reset:

        bl set_svc

        bldisable_watchdog

        bldisable_interrupt

        bl disable_mmu

set_svc:  @6440 210 y也一样

        mrs r0, cpsr

        bic r0, r0,#0x1f  @后5位清零

        orr r0,#0xd3    @d3屏蔽了irq,fiq

              msr cpsr, r0

              mov pc, lr   @返回

#define pWTCON 0x530000000  @看门狗时钟控制寄存器地址6440 210不同

disable_watchdog:

        ldr r0, = pWTCON

        mov r1, #0x0

        str r1, [r0]

        mov pc,lr  @返回

disable_interrupt:    @2440

        mvn r1, #0x0

        ldr r0,=0x40000008   @INTIMSK寄存器地址

        str r1, [r0]

        mov pc,lr  @返回

disable_mmu:   @2440、6420、210一样

        mcr p15, 0,r0, c7, c7, 0  @使Icache和Dcache失效

        mrc p15, 0,r0. c1, c0, 0  @读控制寄存器

        bic r0, r0,#0x00000007  @第0/1/2位全变0

        mcr p15, 0,r0, c1, c0, 0  @写控制寄存器

        mov pc,lr  @返回

       这里的bl是跳转的意思,lr(r14)保存了返回地址,pc(r15)是当前地址,把lr给pc就是从子程序返回。Lr(r14)的一般有两个作用:

(1)当使用bl或blx跳转到子程序的时候,r14保存了返回地址,可以在调用过程结尾恢复。

(2)异常中断发生时,这个异常模式特定的物理r14被设置成该异常模式将要返回的地址。

       另外注意pc,在调试时显示的当前指令地址,而用mov lr, pc的时候lr保存的是此指令向后数两条指令的地址,大家可以试一下用mov pc, pc ,结果得到的是跳转两条指令,这个原因是由于arm的流水线造成的,预取两条指令的结果。

2.Led(初始化)驱动设计

1.      设置GPIO控制寄存器,把引脚设置为输出功能(#define GPBCON 0x56000010)

2.      根据原理图设置GPIO数据寄存器,点亮LED(#define GPBDAT 0x56000014)

reset:

       bl light_led

#define GPBCON 0x56000010 //控制寄存器地址

#define GPBDAT 0x56000014    @数据寄存器地址

light_led:

       ldr r0, =GPBCON

       ldr r1,=0x15400  @用的是5、6、7、8引脚  0101010100000000

       str r1, [r0]

      

       ldr r0, =GPBDAT

       ldr r1,=0x6BF  //11010111111 @用的是5、6、7、8引脚

       str r1, [r0]

       mov pc, lr

3.时钟初始化

1.      配置lock time

2.      设置分频系数(Uboot  采用1:4:8)

3.      设置cpu异步模式

4.      设置FCLK

reset:

bl init_clock

#define CLKDIVN 0x4c000014

#define MPLLCON 0x4c000004

#define MPLL_405MHZ((127<<12)|(2<<4)|(1<<0))

init_clock:

ldr r0, =CLKDIVN   @设置分频系数

mov r1, #0x5  HDIVN  2(10)   PDIVN  1(1) 1:4:8

str r1, [r0]

mrc p15,0,r0,c1,c0,0  @设置cpu异步模式

orr r0,r0,#0xc0000000   1100 0000 0000

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

       ldr r0, =MPLLCON  @设置FCLK

ldr r1, =MPLL_405MHZ

str r1, [r0]

mov pc, lr

 

4.内存初始化

reset:

        bl init_sdram

#define mem_contrl 0x48000000  @13三个寄存器第一个寄存器地址

init_sdram:

       ldr r0, =mem_contrl  @初始地址

       add r3, r0, #4*13  @结束地址,初始地址加上4个字节,13个寄存器

       adrl r1, mem_data   @r1的指针指向基地址,13个寄存器设置参数

 

0:  @循环记入13个寄存器设置参数

       ldr r2, [r1], #4

       str r2, [r0], #4

       cmp r0, r3

       bne 0b @往前跳转到0

       mov pc, lr

 

mem_data:  @13个寄存器设置

       .long 0x22000000

       .long 0x00000700

       .long 0x00000700

       .long 0x00000700

       .long 0x00000700

       .long 0x00000700

       .long 0x00000700

       .long 0x00018001

       .long 0x00018001

       .long 0x008c04f5

       .long 0x000000b1

       .long 0x00000030

       .long 0x00000030

 

下面是6410内存初始化,和2440有很大的区别

图中是6410 DRAM 控制器初始化流程  第3步多余的

新建mem.S,这里新建这个文件主要是实现内存初始化,其实在start.S添加也可以,只是浏览不方便。注意要在start.S中reset添加bl mem_init,Makefile中将all: start.o改为all: start.o mem.o

.text    @修改在代码段

.global mem_init  @设mem_init为全局

mem_init:

   

    ldr r0,=0x7e00f120

    mov r1, #0x8

    str r1, [r0]

 

    ldr r0,=0x7e001004  @内存控制命令寄存器

   

    mov r1, #0x4         @根据手册得知需要先进入配置模式

    str r1, [r0]

 

    ldr r0,=0x7e001010  @刷新寄存器地址

    ldr r1, =( ( 7800/ ( 1000000000/133000000 ) + 1 ) )      @设置刷新时间

    str r1, [r0]

 

    ldr r0,=0x7e001014  @CAS latency寄存器

    mov r1, #(3<< 1)

    str r1, [r0]

 

    ldr r0,=0x7e001018  @t_DQSS寄存器

    mov r1, #0x1

    str r1, [r0]

 

    ldr r0,=0x7e00101c  @T_MRD寄存器

    mov r1, #0x2

    str r1, [r0]

 

    ldr r0,=0x7e001020   @t_RAS寄存器

    ldr r1, =( ( 45 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001024   @t_RC寄存器

    ldr r1, =( ( 68 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001028   @t_RCD寄存器

    ldr r1, =( ( 23 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e00102c   @t_RFC寄存器

    ldr r1, =( ( 80 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001030   @t_RP寄存器

    ldr r1, =( ( 23 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001034   @t_rrd寄存器

    ldr r1, =( ( 15 /( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001038   @t_wr寄存器

    ldr r1, =( ( 15 /( 1000000000 / 133000000 ) + 1 ) )

 @  ldr r2, [r0]

    str r1, [r0]

 

    ldr r0,=0x7e00103c   @t_wtr寄存器

    mov r1, #0x07

    str r1, [r0]

 

    ldr r0, =0x7e001040   @t_xp寄存器

    mov r1, #0x02

    str r1, [r0]

 

    ldr r0,=0x7e001044   @t_xsr寄存器

    ldr r1, =( ( 120/ ( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e001048   @t_esr寄存器

    ldr r1, =( ( 120/ ( 1000000000 / 133000000 ) + 1 ) )

    str r1, [r0]

 

    ldr r0,=0x7e00100c   @内存控制配置寄存器

    ldr r1,=0x00010012   @配置控制器

    str r1, [r0]

 

    ldr r0,=0x7e00104c   @32位DRAM配置控制寄存器

    ldr r1, =0x0b45

    str r1, [r0]

 

    ldr r0,=0x7e001200   @片选寄存器

    ldr r1, =0x150f8

    str r1, [r0]

 

    ldr r0,=0x7e001304   @用户配置寄存器

    mov r1, #0x0

    str r1, [r0]

 

   @issue NOP

        ldr r0,=0x7e001008

    ldr r1,=0x000c0000

str r1, [r0]

 

    ldr r1,=0x00000000

    str r1, [r0]

 

    ldr r1,=0x00040000

    str r1, [r0]

 

    ldr r1, =0x000a0000

    str r1, [r0]

 

    ldr r1,=0x00080032

    str r1, [r0]

 

    ldr r0,=0x7e001004

    mov r1, #0x0

    str r1, [r0]

 

check_dmc1_ready:

   

    ldr r0,=0x7e001000

    ldr r1, [r0]

    mov r2, #0x3

    and r1, r1, r2

    cmp r1, #0x1

    bne check_dmc1_ready

    nop

mov pc, lr

5.复制nand flash中的bl到内存

在reset:添加

           bl copy_to_ram

copy_to_ram:

           ldrr0, =0x0  @2440起点地址

           ldrr1, =0x30008000  @终点地址

           addr3, r0, #1024*4

copy_loop:

           ldrr2, [r0], #4  @读下一内存

           strr2, [r1], #4  @写下一内存

           cmpr0, r3  @是否到了尾部

           bnecopy_loop

           movpc, lr

这部分2440、6410、210只在地点地址和终点地址不同具体如下

2440          起点 0x0          终点 30008000

6410        0x0c000000             0x50008000

210      0xd0020000            0x20008000

Ldr pc,=reset  <==>  mov pc, 0x3008058 (反汇编reset地址是0x3008058)

注意:考虑到的垫脚石,210的起点地址要设置为0xd0020010(0xd0020000+16)

5.C语言编程环境设置

1.堆栈的初始化

根据SP栈可分为满栈和空栈。满栈:当堆栈指针SP总是指向最后一个如堆栈的数据.ARM采用满栈。空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置。

根据SP移动方向分为升栈和降栈。升栈:SP从低地址è 高地址。ARM采用升栈。降栈:SP从高地址è低地址。

reset:

     bl init_stack

init_stack:

            ldr sp, =0x34000000  @sp指向从起始地址到64M的地方

            mov pc ,lr

sp设置6410 :0x54000000           210:0x24000000

2.清除bss段(bss:未初始化的全局变量)[sp,#-4]  <==> sp=sp-4

clean_bss:

            ldr r0, =bss_start

            ldr r1, =bss_end

            cmp r0, r1

            moveq pc, lr  @如果相同,直接返回

 

clean_loop:

            mov r2, #0

            str r2, [r0], #4

            cmp r0, r1

            bne clean_loop  @还不相等,继续

            mov pc, lr   @直到移到bss段,返回

第二阶段

将在reset中添加ldr pc, = uboot_main,删除bl light_led,

将Makefile中all: start.o mem.o 改为all: start.o mem.o main.o

新建C代码main.c

#define GPBCON (volatile unsigned long*)0x56000010  //控制寄存器地址

#define GPBDAT (volatile unsigned long*)0x56000014  //数据寄存器地址

 

int uboot_main()

{

    *(GPBCON) =0x15400;   @用的是5、6、7、8引脚  0101010100000000

    *(GPBDAT) =0x6BF;   @11010111111  用的是5、6、7、8引脚

    return 0;   

}

在汇编程序中light_led设这样的:

reset:

       bl light_led

#define GPBCON 0x56000010   @控制寄存器地址

#define GPBDAT 0x56000014   @数据寄存器地址

light_led:

       ldr r0, =GPBCON

       ldr r1,=0x15400  @用的是5、6、7、8引脚  0101010100000000

       str r1, [r0]

      

       ldr r0, =GPBDAT

       ldr r1,=0x6BF  @11010111111 用的是5、6、7、8引脚

       str r1, [r0]

       mov pc, lr

 

用内联函数汇编实现上面代码,可以这样做:将在reset中添加ldr pc, =uboot_main,删除bllight_led,

将Makefile中all: start.o mem.o 改为all: start.o mem.o main.o

新建C代码main.c

#define GPBCON 0x7f008800

#define GPBDAT 0x7f008808

 

int main()

{

     __asm__(

            “ldr r1,=0x11110000\n”  @汇编语句部分

            “str r1,[%0]\n”        @汇编语句部分

            “ldr r1,=0xa0\n”       @汇编语句部分

            “str r1,[%1]\n”   @汇编语句部分

            :   @输出部分

            :”r”(GPBCON),“r”(GPBDAT)  @输入部分

            :”r1”  @破坏描述部分

     );

     return 0;

}

      有个问题:如何判断是否从norflash启动?如果从norflash启动的话,0地址对应于norflash,但是norflash不能像内存一样去写;如果从nandflash启动的话0地址对应于片内内存sram,0地址是可以写的,判断思路就是:把零地址读出来,然后往0地址写一个特定的数,判断0地址等不等于写进去的特定数,即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: