【u-boot】uboot代码简要分析 (u-boot 移植)

uboot代码简要分析 (u-boot 移植)

2012-12-19 22:46:04

【转】

先来看看源码目录结构,再按照代码的执行顺序简单地分析源码


1.U-boot源码整体框架

源码解压以后,我们可以看到以下的文件和文件夹:

 cpu

与处理器相关的文件。每个子目录中都包括cpu.c和interrupt.c、start.S、u-boot.lds。

cpu.c初始化CPU、设置指令Cache和数据Cache等

interrupt.c设置系统的各种中断和异常

start.S是U-boot启动时执行的第一个文件,它主要做最早其的系统初始化,代码重定向和设置系统堆栈,为进入U-boot第二阶段的C程序奠定基础

u-boot.lds链接脚本文件,对于代码的最后组装非常重要

 board

已经支持的所有开发板相关文件,其中包含SDRAM初始化代码、Flash底层驱动、板级初始化文件

其中的config.mk文件定义了TEXT_BASE,也就是代码在内存的其实地址,非常重要。

 common

与处理器体系结构无关的通用代码,U-boot的命令解析代码/common/command.c、所有命令的上层代码cmd_*.c、U-boot环境变量处理代码env_*.c、等都位于该目录下

drivers

包含几乎所有外围芯片的驱动,网卡、USB、串口、LCD、Nand Flash等等

disk

fs

net

支持的CPU无关的重要子系统:

磁盘驱动的分区处理代码

文件系统:FAT、JFFS2、EXT2等

网络协议:NFS、TFTP、RARP、DHCP等等

include

头文件,包括各CPU的寄存器定义,文件系统、网络等等

configs子目录下的文件是与目标板相关的配置头文件

doc

U-Boot的说明文档,在修改配置文件的时候可能用得上

lib_arm

处理器体系相关的初始化文件

比较重要的是其中的board.c文件,几乎是U-boot的所有架构第二阶段代码入口函数和相关初始化函数存放的地方。

lib_avr32

lib_blackfin

lib_generic

lib_i386

lib_m68k

lib_microblaze

lib_mips lib_nios

lib_nios2

lib_ppc

lib_sh

lib_sparc

 api

examples

外部扩展应用程序的API和范例

nand_spl

onenand_ipl

post

一些特殊构架需要的启动代码和上电自检程序代码

libfdt

支持平坦设备树(flattened device trees)的库文件

tools

编译S-Record或U-Boot映像等相关工具,制作bootm引导的内核映像文件工具mkimage源码就在此

Makefile

MAKEALL

config.mk

rules.mk

mkconfig

控制整个编译过程的主Makefile文件和规则文件

CHANGELOG

CHANGELOG-before-U-Boot-1.1.5

COPYING

CREDITS

MAINTAINERS

README

一些介绍性的文档、版权说明

标为红色的是移植时比较重要的文件或文件夹。


2. U-boot代码的大致执行流程(以S3C24x0为例)

从链接脚本文件u-boot.lds中可以找到代码的起始:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")

OUTPUT_ARCH(arm)

ENTRY(_start)

SECTIONS

{

       . = 0x00000000;

       . = ALIGN(4);

       .text :

       {

              cpu/arm920t/start.o      (.text)

              *(.text)

       }

…...

从中知道程序的入口点是_start,定位于cpu/arm920t /start.S(即u-boot启动的第一阶段)。
下面我们来仔细分析一下 start.S。(请对照数据手册阅读源码):

#include

#include

.globl _start

_start:        b       start_code

         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:    .word software_interrupt

_prefetch_abort:          .word prefetch_abort

_data_abort:                .word data_abort

_not_used:                  .word not_used

_irq:                    .word irq

_fiq:                     .word fiq

         .balignl 16,0xdeadbeef

_TEXT_BASE:

         .word         TEXT_BASE

.globl _armboot_start

_armboot_start:

         .word _start

.globl _bss_start

_bss_start:

         .word __bss_start

.globl _bss_end

_bss_end:

         .word _end

#ifdef CONFIG_USE_IRQ

.globl IRQ_STACK_START

IRQ_STACK_START:

         .word         0x0badc0de

.globl FIQ_STACK_START

FIQ_STACK_START:

         .word 0x0badc0de

#endif

start_code:

        

         mrs   r0, cpsr

         bic    r0, r0, #0x1f

         orr    r0, r0, #0xd3

         msr   cpsr, r0

         bl      coloured_LED_init

         bl      red_LED_on

#if     defined(CONFIG_AT91RM9200DK) || defined(CONFIG_AT91RM9200EK)

        

         ldr     r0, =_start

         ldr     r1, =0x0

         mov  r2, #16

copyex:

         subs r2, r2, #1

         ldr     r3, [r0], #4

         str     r3, [r1], #4

         bne   copyex

#endif

#if defined(CONFIG_S3C2400) || defined(CONFIG_S3C2410)

        

# if defined(CONFIG_S3C2400)

#  define pWTCON    0x15300000

#  define INTMSK       0x14400008       

#  define CLKDIVN     0x14800014       

#else

#  define pWTCON    0x53000000

#  define INTMSK       0x4A000008       

#  define INTSUBMSK         0x4A00001C

#  define CLKDIVN     0x4C000014      

# endif

         ldr     r0, =pWTCON

         mov  r1, #0x0

         str     r1, [r0]

        

         mov  r1, #0xffffffff

         ldr     r0, =INTMSK

         str     r1, [r0]

# if defined(CONFIG_S3C2410)

         ldr     r1, =0x3ff

         ldr     r0, =INTSUBMSK

         str     r1, [r0]

# endif

        

        

         ldr     r0, =CLKDIVN

         mov  r1, #3

         str     r1, [r0]

#endif       

        

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

         bl      cpu_init_crit

#endif

#ifndef CONFIG_SKIP_RELOCATE_UBOOT

relocate:                               

         adr    r0, _start            

         ldr     r1, _TEXT_BASE                  

         cmp  r0, r1                  

         beq   stack_setup

         ldr     r2, _armboot_start

         ldr     r3, _bss_start

         sub   r2, r3, r2             

         add   r2, r0, r2             

copy_loop:

         ldmia         r0!, {r3-r10}                 

         stmia          r1!, {r3-r10}                 

         cmp  r0, r2                  

         ble    copy_loop

#endif       

        

stack_setup:

         ldr     r0, _TEXT_BASE        

         sub   r0, r0, #CONFIG_SYS_MALLOC_LEN  

         sub   r0, r0, #CONFIG_SYS_GBL_DATA_SIZE

#ifdef CONFIG_USE_IRQ

         sub   r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)

#endif

         sub   sp, r0, #12         

clear_bss:

         ldr     r0, _bss_start              

         ldr     r1, _bss_end              

         mov  r2, #0x00000000         

clbss_l:str r2, [r0]                

         add   r0, r0, #4

         cmp  r0, r1

         ble    clbss_l

         ldr     pc, _start_armboot

_start_armboot:  .word start_armboot

#ifndef CONFIG_SKIP_LOWLEVEL_INIT

cpu_init_crit:

        

         mov  r0, #0

         mcr   p15, 0, r0, c7, c7, 0     

         mcr   p15, 0, r0, c8, c7, 0     

        

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

         bic    r0, r0, #0x00002300     @ clear bits 13, 9:8 (--V- --RS)

         bic    r0, r0, #0x00000087     @ clear bits 7, 2:0 (B--- -CAM)

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

         orr    r0, r0, #0x00001000     @ set bit 12 (I) I-Cache

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

        

         mov  ip, lr

         bl      lowlevel_init

         mov  lr, ip

         mov  pc, lr

#endif

…...

//位于\include目录下是一个包含其他头文件的头文件

//位于\include\linux目录下

u-boot的主入口,跳入了后面的start_code

这些是跳转向量表,和芯片的体系结构有关

 ldr语句的意思是将第二个操作数(如:_undefined_instruction)指向的地址数据传给PC

.word 为定义一个4字节的空间

undefined_instruction 为地址, 即后面标号所对的偏移地址数据

16字节对齐,并以0xdeadbeef填充,它是个Magic number 。

这些和上面的一样,定义一个4字节的空间存放地址

代码从这里开始执行!!

让系统进入SVC(管理员模式)

这些都是为AT91RM9200写的

系统时钟的寄存器地址定义

关闭看门狗

关闭所有中断

设置时钟的分频比

跳入cpu_init_crit ,这是一个系统初始化函数,他还会调用boardlowlevel_init.S中的lowlevel_init函数,对系统总线的初始化,初始化了连接存储器的位宽、速度、刷新率等重要参数。经过这个函数的正确初始化,Nor Flash、SDRAM才可以被系统使用。

后面的代码略,主要是中断相关代码,但是U-boot基本不使用中断所以暂且略过。

现在我们再来看看lib_arm/board.c中的第二阶段入口函数start_armboot :

void start_armboot (void)                     

{

       init_fnc_t **init_fnc_ptr;

       char *s;

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)

       unsigned long addr;

#endif

      

       gd = (gd_t*)(_armboot_start - CONFIG_SYS_MALLOC_LEN - sizeof(gd_t));

      

       __asm__ __volatile__("": : :"memory");

       memset ((void*)gd, 0, sizeof (gd_t));

       gd->bd = (bd_t*)((char*)gd - sizeof(bd_t));

       memset (gd->bd, 0, sizeof (bd_t));

       gd->flags |= GD_FLG_RELOC;

       monitor_flash_len = _bss_start - _armboot_start;

       for (init_fnc_ptr = init_sequence; *init_fnc_ptr; ++init_fnc_ptr) {

              if ((*init_fnc_ptr)() != 0) {

                     hang ();

              }

       }

      

       mem_malloc_init (_armboot_start - CONFIG_SYS_MALLOC_LEN,

                     CONFIG_SYS_MALLOC_LEN);

#ifndef CONFIG_SYS_NO_FLASH

      

       display_flash_config (flash_init ());

#endif

#ifdef CONFIG_VFD

#     ifndef PAGE_SIZE

#       define PAGE_SIZE 4096

#     endif

      

      

       addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

       vfd_setmem (addr);

       gd->fb_base = addr;

#endif

#ifdef CONFIG_LCD

      

       if (!gd->fb_base) {

#            ifndef PAGE_SIZE

#              define PAGE_SIZE 4096

#            endif

             

             

              addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);

              lcd_setmem (addr);

              gd->fb_base = addr;

       }

#endif

#if defined(CONFIG_CMD_NAND)

       puts ("NAND:  ");

       nand_init();           

#endif

#if defined(CONFIG_CMD_ONENAND)

       onenand_init();

#endif

#ifdef CONFIG_HAS_DATAFLASH

       AT91F_DataflashInit();

       dataflash_print_info();

#endif

      

       env_relocate ();

#ifdef CONFIG_VFD

      

       drv_vfd_init();

#endif

#ifdef CONFIG_SERIAL_MULTI

       serial_initialize();

#endif

      

       gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");

       stdio_init ();   

       jumptable_init ();

#if defined(CONFIG_API)

      

       api_init ();

#endif

       console_init_r ();   

#if defined(CONFIG_ARCH_MISC_INIT)

      

       arch_misc_init ();

#endif

#if defined(CONFIG_MISC_INIT_R)

      

       misc_init_r ();

#endif

      

       enable_interrupts ();

      

#ifdef CONFIG_DRIVER_TI_EMAC

      

extern void davinci_eth_set_mac_addr (const u_int8_t *addr);

       if (getenv ("ethaddr")) {

              uchar enetaddr[6];

              eth_getenv_enetaddr("ethaddr", enetaddr);

              davinci_eth_set_mac_addr(enetaddr);

       }

#endif

#if defined(CONFIG_DRIVER_SMC91111) || defined (CONFIG_DRIVER_LAN91C96)

      

       if (getenv ("ethaddr")) {

              uchar enetaddr[6];

              eth_getenv_enetaddr("ethaddr", enetaddr);

              smc_set_mac_addr(enetaddr);

       }

#endif

      

       if ((s = getenv ("loadaddr")) != NULL) {

              load_addr = simple_strtoul (s, NULL, 16);

       }

#if defined(CONFIG_CMD_NET)

       if ((s = getenv ("bootfile")) != NULL) {

              copy_filename (BootFile, s, sizeof (BootFile));

       }

#endif

#ifdef BOARD_LATE_INIT

       board_late_init ();

#endif

#ifdef CONFIG_GENERIC_MMC

       puts ("MMC:   ");

       mmc_initialize (gd->bd);

#endif

#ifdef CONFIG_BITBANGMII

       bb_miiphy_init();

#endif

#if defined(CONFIG_CMD_NET)

#if defined(CONFIG_NET_MULTI)

       puts ("Net:   ");

#endif

       eth_initialize(gd->bd);

#if defined(CONFIG_RESET_PHY_R)

       debug ("Reset Ethernet PHY\n");

       reset_phy();

#endif

#endif

      

       for (;;) {

              main_loop ();

       }

      

}

 

gd_t bd_t这两个数据结构比较重要,建议大家看看。

分配一个存储全局数据的区域,地址给指针 gd

全局数据的区清零

gd->bd(指针)赋值(在gd的前面)并清零

gd->flags 赋值,表示已经重定向(在内存中)

monitor_flash_len为u-boot代码长度。

初始化循环

init_sequence 是一个初始化函数集的函数指针数组(后面讲解)

如果有任何一个函数失败就进入死循环。

这个始化函数集比较重要,建议大家认真跟踪一下。

初始化堆空间,清零。

初始化Nor Flash相关参数,并显示其大小。

初始化VFD存储区(LCD显示相关)

初始化LCD显存

初始化Nand Flash控制器,并显示其容量大小

初始化OneNand

初始化 DataFlash

初始化环境变量,如果认为没有找到存储其中的,就用默认值并打印:“*** Warning - bad CRC, using default environment”。这是我们常看到的。

初始化 VFD(LCD显示相关)

初始化串口。

从环境变量里获取IP地址

初始化标准输入输出设备。比如:串口、LCD、键盘等等

初始化全局数据表中的跳转表gd->jt。

跳转表是一个函数指针数组,定义了u-boot中基本的常用的函数库,gd->jt是这个函数指针数组的首指针。

初始化API,用于为U-boot编写的“应用程序”

初始化 console,平台无关,不一定是串口哦,如果把标准输出设为vga,字符会显示在LCD上。

平台相关的其他初始化,有的平台有

中断使能(一般不使用,很多平台此函数是空的)

TI芯片中的内置MAC初始化(平台相关)

一种网卡芯片初始化(平台相关)

获取 bootfile参数

一些板级初始化(有的板子有)

SD/MMC控制器初始化

MII相关初始化

网卡初始化

进入主循环,其中会读取bootdelay和bootcmd

在bootdelay时间内按下键进入命令行,否则执行bootcmd的命令。

标有红色的是比较重要的地方。

大致的U-boot启动流程就简单介绍到这。

1、 SPSR:程序状态保存寄存器

  SPSR用于保存CPSR的状态,以便异常返回后恢复异常发生时的工作状态。

  CPSR:Current Program Status Register

  SPSR:Saved Program Status Register

  SPSR用来进行异常处理,有以下功能:

  1.保存ALU中的当前操作信息。

  2.控制允许和禁止中断。

  3.设置处理器的运行模式。

uboot 移植步骤详解

( 2012-12-27 17:23:20 发布)

uboot移植到an2410全记录

http://www.eechina.com/thread-6690-1-1.html

http://wenku.baidu.com/view/c6c5fad049649b6648d74745.html

U-BOOT  编写流程

Bootloader的两部分:

1、NANFLASH 前4KB 一开机就会被复制到RAM中,然后跳到RAM 去运行。(第一部分IPL)

2、如果bootloader 大于4KB,则利用前4KB的代码将NANFLASH上大于4KB部分的bootloader复制到RAM中去继续运行。(第二部分SPL)

IPL:关闭看门狗、中断à设置CPU频率à初始化化SDRAMà将NADFLASH上的代码拷贝到SDRAM上—>跳到代码入口执行代码。

1、  WDT

#define   main_addr  0x0

Start.S

Void start_main()   //低级别的main

{
WTCON=0x0;          //关闭看门狗

__asm__ __volatile(      //关闭中断

“mrs r0,cpsr\n”

“orr  r0,r0,#0xc0\n”
“msr  cpsr,r0\n”

:  :  :”r0”

);

//----------------设置CPU频率-----------//

MPLLCON=0X7F021; //405Mhz

CAMDIVN=0;

CLKDIVN=1|(2<<1);  //FCLK:HCLK:PCLK=8:2:1

//---初始化UART0,用于输出信息--//

GPHCON&=~(0xf<<8);

GPHCON|=(0x0a<<8);

//------初始化协处理器CP15------//

__asm__ __volatile(  

“mcr p15,0,0%,c1,c0,0\n”

:“r”((1<<1)|(0xf<<3)|(1<<12)|(3<<30))

);

//---------------初始化SDRAM-----------//

BWSCON=(2<<24);

BANKCON6=(3<<15)|1;

REFRESH=(1269)|(1<<18)|(1<<23);

BANKSIZE=1|(1<<5)|(1<<7);

MRSRB6=(3<<4);

//------------拷贝第二部分代码至SDRAM------//

nand_read(0x33000000, 0,100);

//-----------跳到SDRAM中的第2部分运行---------//

__asm__ __volatile(  

“ldr r0,=main \n”

“mov lr,pc\n”

“mov pc,r0\n”

:

:

:”r0”

);

}

Void main()        //第二部分的mian

{
 

}

//拷贝代码,一页一页的去拷贝

Void nand_read(unsigned char *buf, unsigned long nand_addr,int page_cnt)

{
Int I,j;

NFCONF=(3<<8);

//enable flash

NFCONT=1;

For(j=0;j< page_cnt;j++, nand_addr+=512)

{
NFCMMD=0X00;

NFADDR=nand_addr&0xff;

NFADDR=( nand_addr>>9)&0xff;

NFADDR=( nand_addr>>17)&0xff;

NFADDR=( nand_addr>>25)&1;

}

While(!(NFSTAT&1))

;

For(i=0;i<512;i++){
*buf=NFDATA;

Buf++;

}

//disable flash

NFCONT=(1<<1);

Return 0;

}


一、Boot Loader 的概念和功能

1、嵌入式Linux 软件结构与分布
在一般情况下嵌入式Linux 系统中的软件主要分为以下及部分:
(1)引导加载程序:其中包括内部ROM 中的固化启动代码和Boot Loader 两部分。
而这个内部固化ROM 是厂家在芯片生产时候固化的,作用基本上是引导Boot Loader。有的芯片比较复
杂,比如Omap3,他在flash 中没有代码的时候有许多启动方式:USB、UART 或以太网等等。而S3C24x0 则很
简单,只有Norboot 和Nandboot。
(2)Linux kernel 和drivers。
(3)文件系统。包括根文件系统和建立于Flash 内存设备之上的文件系统(EXT4、UBI、CRAMFS 等等)。
它是提供管理系统的各种配置文件以及系统执行用户应用程序的良好运行环境的载体。
(4)应用程序。用户自定义的应用程序,存放于文件系统之中。
在Flash 存储器中,他们的一般分布如下:

2、在嵌入式Linux 中为什么要有BootLoader
在linux 内核的启动运行除了内核映像必须在主存的适当位置,CPU 还必须具备一定的条件:


1. CPU 寄存器的设置:
R0=0;
R1=Machine ID
(即Machine Type Number,定义在linux/arch/arm/tools/mach‐types);
R2=内核启动参数在 RAM 中起始基地址;

2. CPU 模式:
必须禁止中断(IRQs 和FIQs);
CPU 必须 SVC 模式;

3. Cache 和 MMU 的设置:
MMU 必须关闭;
指令 Cache 可以打开也可以关闭;
数据 Cache 必须关闭;

    但是在CPU 刚上电启动的时候,一般连内存控制器都没有配置过,根本无法在内存中运行程序,更不可
能处在Linux 内核的启动环境中。为了初始化CPU 及其他外设,使得Linux 内核可以在系统主存中跑起来,并
让系统符合Linux 内核启动的必备条件,必须要有一个先于内核运行的程序,他就是所谓的引导加载程序(Boot
Loader)。
而Boot Loader 并不是Linux 才需要,是几乎所有的运行操作系统的设备都具备的。我们的PC 的BOIS 就
是Boot Loader 的一部分(只是前期引导,后面一般还有外存中的各种Boot Loader),对于Linux PC 来说,Boot
Loader = BIOS + GRUB/LILO。

3、Boot Loader 的功能和选择

   通过上面的讲述,我们可以知道:Boot Loader 是在操作系统内核运行之前运行的一段小程序。通过这段
小程序,我们可以初始化硬件设备,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系
统内核准备好正确的环境,最后从别处(Flash、以太网、UART)载入内核映像并跳到入口地址。
由于BootLoader 直接操作硬件,所以她严重依赖于硬件,而且依据所引导的操作系统的不同,也有不同
的选择对于嵌入式世界中更是如此。

四、U‐boot 的使用
我一贯认为:对于一个技术类的东西,你连用都不会用,那根本就不可能会开发。就好像一个人连Linux
不会用,那还谈何开发Linux 程序呢?
同样的要开发和移植U‐boot,首先要对U‐boot 有一定的了解,起码要会使用。
所以这里我们首先熟悉一下U‐boot 的使用以及如何将U‐boot 烧入mini2440。
当然在这之前首先必须保证你的板子上已经有了U‐boot。如果没有那就先烧上一个吧。针对mini2440 的
U‐boot‐2009.11 的bin 文件我已经上传到了博客中,可以直接烧入,请到这里下载:
u‐boot‐2009.11 for mini2440 Source release
把它烧到mini2440 的NAND 或者NOR Flash 的起始地址即可,你可以使用OpenJTAG 下载烧写:
关于OpenOCD 的安装和配置(以mini2440+OpenJTAG 为例)
也可以用板上已有的boottloader 烧写,也可以适用H‐JTAG 用并口烧(这是最直接最快的)。

1、常用的U‐boot 命令详解

(1)获取帮助
命令:help 或 ?
功能:查看当前U‐boot 版本中支持的所有命令。
[u‐boot@MINI2440]# help
? ‐ alias for 'help'
askenv ‐ get environment variables from stdin
base ‐ print or set address offset
bdinfo ‐ print Board Info structure
bmp ‐ manipulate BMP image data
boot ‐ boot default, i.e., run 'bootcmd'
bootd ‐ boot default, i.e., run 'bootcmd'
bootelf ‐ Boot from an ELF image in memory

……

如果你想获取某条命令的更详细的帮助,可以使用:
help <你想要查的指令>
或者 ? <你想要查的指令> ,
甚至 h <你想要查的指令缩写>。

以bmp 指令为例:
[u‐boot@MINI2440]# help bmp
bmp ‐ manipulate BMP image data
Usage:
bmp info <imageAddr> ‐ display image info
bmp display <imageAddr> [x y] ‐ display image at x,y
[u‐boot@MINI2440]# ? bmp
bmp ‐ manipulate BMP image data

(2)环境变量(environment variables,简称ENV)与相关指令
和shell 类似,U‐Boot 也有环境变量。一些U‐boot 默认的环境变量如下:

环境变量        解释说明
bootdelay       执行自动启动(bootcmd 中的命令)的等候秒数
baudrate        串口控制台的波特率
netmask         以太网的网络掩码
ethaddr         以太网的MAC 地址
bootfile        默认的下载文件名
bootargs        传递给Linux 内核的启动参数
bootcmd         自动启动时执行命令
serverip        TFTP 服务器端的IP 地址
ipaddr          本地的IP 地址
stdin           标准输入设备,一般是串口

stdout          标准输出,一般是串口,也可是LCD(VGA)
stderr          标准出错,一般是串口,也可是LCD(VGA)

要看到你的板上的ENV 值可使用printenv 命令,例如我的板子:


[u‐boot@MINI2440]# printenv
bootargs=noinitrd root=/dev/nfs rw nfsroot=192.168.0.1:/home/tekkaman/working/nfs/rootfs
ip=192.168.0.2:192.168.0.1::255.255.255.0 console=ttySAC0,115200 init=/linuxrc mem=64M
bootcmd=nfs 0x30008000 192.168.0.1:/home/tekkaman/working/nfs/zImage.img;bootm
bootdelay=1
baudrate=115200
ethaddr=08:08:11:18:12:27
ipaddr=192.168.0.2
serverip=192.168.0.1
gatewayip=192.168.0.1
netmask=255.255.255.0
tekkaman=bmp d 70000
stdin=serial
stdout=serial
stderr=serial

ethact=dm9000
Environment size: 470/131068 bytes

你会发现有些有的ENV 我没有,还有一个“tekkaman”的ENV。原因是如果你没有设置这个环境变量就
不会打印出,你也可以自己定义ENV,并在命令中使用${ENV}来调用它。同时你也可以删除这个ENV。设置
ENV 的命令是setenv,格式为:
setenv name value
第1 个参数是环境变量的名称。
第2 个参数是要设置的值,如果没有第2 个参数,表示删除这个环境变量。
例如:我先将”tekkaman”参数删除,再设置,最后在一个命令串中调用。

[u‐boot@MINI2440]# printenv tekkaman
tekkaman=bmp d 70000
[u‐boot@MINI2440]# setenv tekkaman
[u‐boot@MINI2440]# printenv tekkaman
## Error: "tekkaman" not defined
[u‐boot@MINI2440]# setenv tekkaman echo "I am Tekkaman Ninja!"
[u‐boot@MINI2440]# printenv tekkaman
tekkaman=echo I am Tekkaman
[u‐boot@MINI2440]# echo I Love Linux ;${tekkaman}
I Love Linux
I am Tekkaman
当你设置了ENV,它只保存在内存中,如果你要它保存在存放ENV 的固态存储器中,请使用:saveenv。

[u‐boot@MINI2440]# saveenv
Saving Environment to NAND...
Erasing Nand...
Erasing at 0x6000000000002 ‐‐ 0% complete.
Writing to Nand... done

如果在启动的时候会看到U‐boot 打印出:“Warning ‐ bad CRC, using default environment”,说明 U‐boot
没有在存放ENV 的固态存储器中找到有效的ENV,只好使用你在编译的时候定义的默认ENV。如果U‐boot 存
放ENV 的固态存储器的驱动是OK 的,那么只要运行 saveenv 就可以把默认ENV 写入固态存储器,下次启动
就不会有这个警告了。
ENV 可以放在许多固体存储器中,对于mini2440 来说Nor Flash、Nand Flash 或EEPROM 都可以,就看你
如何配置了(include/configs 下的配置文件)。例如:

Nor Flash:

#define CONFIG_ENV_IS_IN_FLASH 1
#define CONFIG_ENV_OFFSET 0X40000
#define CONFIG_ENV_SIZE 0x20000
Nand Flash:
#define CONFIG_ENV_IS_IN_NAND 1
#define CONFIG_ENV_OFFSET 0X40000
#define CONFIG_ENV_SIZE 0x20000
EEPROM:
#define CONFIG_ENV_IS_IN_EEPROM 1
#define CONFIG_ENV_OFFSET 0x000
#define CONFIG_ENV_SIZE 0x400
CONFIG_ENV_OFFSET 是在整个存储器中的偏移地址;
CONFIG_ENV_SIZE 是指其使用的大小。
注意 CONFIG_ENV_OFFSET 和 CONFIG_ENV_SIZE 的设置,不要覆盖了其他分区。


u-boot 移植步骤详解(必会)

1 U-Boot主要目录结构 
- board 目标板相关文件,主要包含SDRAM、FLASH驱动; 
- common 独立于处理器体系结构的通用代码,如内存大小探测与故障检测; 
- cpu 与处理器相关的文件。如mpc8xx子目录下含串口、网口、LCD驱动及中断初始化等文件; 
- driver 通用设备驱动,如CFI FLASH驱动(目前对INTEL FLASH支持较好) 
- doc U-Boot的说明文档; 
- examples可在U-Boot下运行的示例程序;如hello_world.c,timer.c; 
- include U-Boot头文件;尤其configs子目录下与目标板相关的配置头文件是移植过程中经常要修改的文件; 
- lib_xxx 处理器体系相关的文件,如lib_ppc, lib_arm目录分别包含与PowerPC、ARM体系结构相关的文件; 
- net 与网络功能相关的文件目录,如bootp,nfs,tftp; 
- post 上电自检文件目录。尚有待于进一步完善; 
- rtc RTC驱动程序; 
- tools 用于创建U-Boot S-RECORD和BIN镜像文件的工具; 

4 移植前的准备 

(1)、首先读读uboot自带的readme文件,了解了一个大概。 
(2)、看看common.h,这个文件定义了一些基本的东西,并包含了一些必要的头文件。再看看flash.h,这个文件里面定义了 flash_info_t为一个struct。包含了flash的一些属性定义。并且定义了所有的flash的属性,其中,AMD的有:AMD_ID_LV320B,定义为“#define AMD_ID_LV320B 0x22F922F9”。 
(3)、对于“./borad/at91rm9200dk/flash.c”的修改,有以下的方面: 
“void flash_identification(flash_info_t *info)”这个函数的目的是确认flash的型号。注意的是,这个函数里面有一些宏定义,直接读写了flash。并获得ID号。 
(4)、修改:”./board/at91rm9200dk/config.mk”为 
TEXT_BASE=0x21f80000 为TEXT_BASE=0x21f00000 (当然,你应该根据自己的板子来修改,和一级boot的定义的一致即可)。 
(5)、再修改”./include/configs/at91rm9200dk.h”为 
修改flash和SDRAM的大小。 
(6)、另外一个要修改的文件是: 
./borad/at91rm9200dk/flash.c。这个文件修改的部分比较的多。 
a. 首先是OrgDef的定义,加上目前的flash。 
b. 接下来,修改”#define FLASH_BANK_SIZE 0x200000”为自己flash的容量 
c. 在修改函数flash_identification(flash_info_t * info)里面的打印信息,这部分将在u-boot启动的时候显示。 
d. 然后修改函数flash_init(void)里面对一些变量的赋值。 
e. 最后修改的是函数flash_print_info(flash_info_t * info)里面实际打印的函数信息。 
f.还有一个函数需要修改,就是:“flash_erase”,这个函数要检测先前知道的flash类型是否匹配,否则,直接就返回了。把这里给注释掉。 

(7)、接下来看看SDRAM的修改。 
这个里面对于“SIZE”的定义都是基于字节计算的。 
只要修改”./include/configs/at91rm9200dk.h”里面的 
“#define PHYS_SDRAM_SIZE 0X200000”就可以了。注意,SIZE是以字节为单位的。 
(8)、还有一个地方要注意 
就是按照目前的设定,一级boot把u_boot加载到了SDRAM的空间为:21F00000 -> 21F16B10,这恰好是SDRAM的高端部分。另外,BSS为21F1AE34。 

(9)、编译后,可以写入flash了。 
a. 压缩这个u-boot.bin 
“gzip –c u-boot.bin > u-boot.gz” 
压缩后的文件大小为: 
43Kbytes 
b. 接着把boot.bin和u-boot.gz烧到flash里面去。 
Boot.bin大约11kBytes,在flash的0x1000 0000 ~ 0x1000 3fff 

5 U-Boot移植过程 
① 获得发布的最新版本U-Boot源码,与Linux内核源码类似,也是 bzip2的压缩格式。可从U-Boot的官方网站http://sourceforge.net/projects/U-Boot上获得; 
② 阅读相关文档,主要是U-Boot源码根目录下的README文档和U-Boot官方网站的DULG(The DENX U-Boot and Linux Guide)文档http://www.denx.de/twiki/bin/view/DULG/Manual。尤其是DULG文档,从如何安装建立交叉开发环境和解决U-Boot移植中常见问题都一一给出详尽的说明; 
③ 订阅U-Boot用户邮件列表http://lists.sourceforge.net/lists/listinfo/u-boot-users。在移植U-Boot过程中遇有问题,在参考相关文档和搜索U-Boot-User邮件档案库http://sourceforge.net/mailarchive/forum.php?forum_id=12898仍不能解决的情况下,第一时间提交所遇到的这些问题,众多热心的U-Boot开发人员会乐于迅速排查问题,而且很有可能,W.D本人会直接参与指导; 
④ 在建立的开发环境下进行移植工作。绝大多数的开发环境是交叉开发环境。在这方面,DENX 和MontaVista均提供了完整的开发工具集; 
⑤ 在目标板与开发主机间接入硬件调试器。这是进行U-Boot移植应当具备且非常关键的调试工具。因为在整个U-Boot的移植工作中,尤其是初始阶段,硬件调试器是我们了解目标板真实运行状态的唯一途径。在这方面,W.D本人和众多嵌入式开发人员倾向于使用BDI2000。一方面,其价格不如ICE调试器昂贵,同时其可靠性高,功能强大,完全能胜任移植和调试U-Boot。另外,网上也有不少关于BDI2000调试方面的参考文档。 
⑥ 如果在参考开发板上移植U-Boot,可能需要移除目标板上已有的BOOT LOADER。可以根据板上BOOT LOADER的说明文档,先着手解决在移除当前BOOT LOADER的情况下,如何进行恢复。以便今后在需要场合能重新装入原先的BOOT LOADER。 

6. U-Boot移植方法 
当前,对于U-Boot的移植方法,大致分为两种。一种是先用BDI2000创建目标板初始运行环境,将U- Boot镜像文件u-boot.bin下载到目标板RAM中的指定位置,然后,用BDI2000进行跟踪调试。其好处是不用将U-Boot镜像文件烧写到 FLASH中去。但弊端在于对移植开发人员的移植调试技能要求较高,BDI2000的配置文件较为复杂。另外一种方法是用BDI2000先将U-Boot 镜像文件烧写到FLASH中去,然后利用GDB和BDI2000进行调试。这种方法所用BDI2000的配置文件较为简单,调试过程与U-Boot移植后运行过程相吻合,即U-Boot先从FLASH中运行,再重载至RAM中相应位置,并从那里正式投入运行。唯一感到有些麻烦的就是需要不断烧写 FLASH。但考虑到FLASH常规擦写次数基本为10万次左右,作为移植U-Boot,不会占用太多的次数,应该不会为FLASH烧写有什么担忧。同时,W. D本人也极力推荐使用后一种方法。笔者建议,除非U-Boot移植资深人士或有强有力的技术支持,建议采用第二种移植方法。 


7. U-Boot移植主要修改的文件 
从移植U-Boot最小要求-U-Boot能正常启动的角度出发,主要考虑修改如下文件: 
① <目标板>.h头文件,如include/configs/RPXlite.h。可以是U-Boot源码中已有的目标板头文件,也可以是新命名的配置头文件;大多数的寄存器参数都是在这一文件中设置完成的; 
② <目标板>.c文件,如board/RPXlite/RPXlite.c。它是SDRAM的驱动程序,主要完成SDRAM的UPM表设置,上电初始化。 
③ FLASH的驱动程序,如board/RPXlite/flash.c,或common/cfi_flash.c。可在参考已有FLASH驱动的基础上,结合目标板FLASH数据手册,进行适当修改; 
④ 串口驱动,如修改cpu/mpc8xx/serial.c串口收发器芯片使能部分。 


8. U-Boot移植要点 
① BDI2000的配置文件。如果采用第二种移植方法,即先烧入FLASH的方法,配置项只需很少几个,就可以进行U-Boot的烧写与调试了。对PPC 8xx系列的主板,可参考DULG文档中TQM8xx的配置文件进行相应的修改。下面,笔者以美国Embedded Planet公司的RPXlite DW板为例,给出在嵌入式Linux交叉开发环境下的BDI2000参考配置文件以作参考: 
; bdiGDB configuration file for RPXlite DW or LITE_DW 
; -------------------------------------------- 
[INIT] 
; init core register 
WSPR 149 0x2002000F ;DER : set debug enable register 
; WSPR 149 0x2006000F ;DER : enable SYSIE for BDI flash program 
WSPR 638 0xFA200000 ;IMMR : internal memory at 0xFA200000 
WM32 0xFA200004 0xFFFFFF89 ;SYPCR 
[TARGET] 
CPUCLOCK 40000000 ;the CPU clock rate after processing the init list 
BDIMODE AGENT ;the BDI working mode (LOADONLY | AGENT) 
BREAKMODE HARD ;SOFT or HARD, HARD uses PPC hardware breakpoints 
[HOST] 
IP 173.60.120.5 
FILE uImage.litedw 
FORMAT BIN 
LOAD MANUAL ;load code MANUAL or AUTO after reset 
DEBUGPORT 2001 
START 0x0100 
[FLASH] 
CHIPTYPE AM29BX8 ;;Flash type (AM29F | AM29BX8 | AM29BX16 | I28BX8 | I28BX16) 
CHIPSIZE 0x400000 ;;The size of one flash chip in bytes 
BUSWIDTH 32 ;The width of the flash memory bus in bits (8 | 16 | 32) 
WORKSPACE 0xFA202000 ; RAM buffer for fast flash programming 
FILE u-boot.bin ;The file to program 
FORMAT BIN 0x00000000 
ERASE 0x00000000 BLOCK 
ERASE 0x00008000 BLOCK 
ERASE 0x00010000 BLOCK 
ERASE 0x00018000 BLOCK 
[REGS] 
DMM1 0xFA200000 
FILE reg823.def 
② U-Boot移植参考板。这是进行U-Boot移植首先要明确的。可以根据目标板上CPU、FLASH、SDRAM的情况,以尽可能相一致为原则,先找出一个与所移植目标板为同一个或同一系列处理器的U-Boot支持板为移植参考板。如RPXlite DW板可选择U-Boot源码中RPXlite板作为U-Boot移植参考板。对U-Boot移植新手,建议依照循序渐进的原则,目标板文件名暂时先用移植参考板的名称,在逐步熟悉U-Boot移植基础上,再考虑给目标板重新命名。在实际移植过程中,可用Linux命令查找移植参考板的特定代码,如 grep –r RPXlite ./ 可确定出在U-Boot中与RPXlite板有关的代码,依此对照目标板实际进行屏蔽或修改。同时应不局限于移植参考板中的代码,要广泛借鉴U-Boot 中已有的代码更好地实现一些具体的功能。 
③ U-Boot烧写地址。不同目标板,对U-Boot在FLASH中存放地址要求不尽相同。事实上,这是由处理器中断复位向量来决定的,与主板硬件相关,对 MPC8xx主板来讲,就是由硬件配置字(HRCW)决定的。也就是说,U-Boot烧写具体位置是由硬件决定的,而不是程序设计来选择的。程序中相应 U-Boot起始地址必须与硬件所确定的硬件复位向量相吻合;如RPXlite DW板的中断复位向量设置为0x00000100。因此, U-Boot 的BIN镜像文件必须烧写到FLASH的起始位置。事实上,大多数的PPC系列的处理器中断复位向量是0x00000100和0xfff00100。这也是一般所说的高位启动和低位启动的BOOT LOADER所在位置。可通过修改U-Boot源码<目标板>.h头文件中CFG_MONITOR_BASE 和board/<目标板>/config.mk中的TEXT_BASE的设置来与硬件配置相对应。 

④ CPU寄存器参数设置。根据处理器系列、类型不同,寄存器名称与作用有一定差别。必须根据目标板的实际,进行合理配置。一个较为可行和有效的方法,就是借鉴参考移植板的配置,再根据目标板实际,进行合理修改。这是一个较费功夫和考验耐力的过程,需要仔细对照处理器各寄存器定义、参考设置、目标板实际作出选择并不断测试。MPC8xx处理器较为关键的寄存器设置为SIUMCR、PLPRCR、SCCR、BRx、ORx。 
⑤ 串口调试。能从串口输出信息,即使是乱码,也可以说U-Boot移植取得了实质性突破。依据笔者调试经历,串口是否有输出,除了与串口驱动相关外,还与 FLASH相关的寄存器设置有关。因为U-Boot是从FLASH中被引导启动的,如果FLASH设置不正确,U-Boot代码读取和执行就会出现一些问题。因此,还需要就FLASH的相关寄存器设置进行一些参数调试。同时,要注意串口收发芯片相关引脚工作波形。依据笔者调试情况,如果串口无输出或出现乱码,一种可能就是该芯片损坏或工作不正常。 
⑥ 与启动 FLASH相关的寄存器BR0、OR0的参数设置。应根据目标板FLASH的数据手册与BR0和OR0的相关位含义进行合理设置。这不仅关系到FLASH能否正常工作,而且与串口调试有直接的关联。 
⑦ 关于CPLD电路。目标板上是否有CPLD电路丝毫不会影响U-Boot的移植与嵌入式操作系统的正常运行。事实上,CPLD电路是一个集中将板上电路的一些逻辑关系可编程设置的一种实现方法。其本身所起的作用就是实现一些目标板所需的脉冲信号和电路逻辑,其功能完全可以用一些逻辑电路与CPU口线来实现。 
⑧ SDRAM的驱动。串口能输出以后,U-Boot移植是否顺利基本取决于SDRAM的驱动是否正确。与串口调试相比,这部分工作更为核心,难度更大。 MPC8xx目标板SDRAM驱动涉及三部分。一是相关寄存器的设置;二是UPM表;三是SDRAM上电初始化过程。任何一部分有问题,都会影响U- Boot、嵌入式操作系统甚至应用程序的稳定、可靠运行。所以说,SDRAM的驱动不仅关系到U-Boot本身能否正常运行,而且还与后续部分相关,是相当关键的部分。 
⑨ 补充功能的添加。在获得一个能工作的U-Boot后,就可以根据目标板和实际开发需要,添加一些其它功能支持。如以太网、LCD、NVRAM等。与串口和 SDRAM调试相比,在已有基础之上,这些功能添加还是较为容易的。大多只是在参考现有源码的基础上,进行一些修改和配置。 
另外,如果在自主设计的主板上移植U-Boot,那么除了考虑上述软件因素以外,还需要排查目标板硬件可能存在的问题。如原理设计、PCB布线、元件好坏。在移植过程中,敏锐判断出故障态是硬件还是软件问题,往往是关系到项目进度甚至移植成败的关键,相应难度会增加许多。 


下面以移植u-boot 到44B0开发板的步骤为例,移植中上仅需要修改和硬件相关的部分。在代码结构上: 
1) 在board 目录下创建ev44b0ii 目录,创建ev44b0ii.c 以及flash.c,memsetup.S,u-boot.lds等。不需要从零开始,可选择一个相似的目录,直接复制过来,修改文件名以及内容。我在移植u-boot 过程中,选择的是ep7312 目录。由于u-boot 已经包含基于s3c24b0 的开发板目录,作为参考,也可以复制相应的目录。 
2) 在cpu 目录下创建arm7tdmi 目录,主要包含start.S,interrupts.c 以及cpu.c,serial.c几个文件。同样不需要从零开始建立文件,直接从arm720t 复制,然后修改相应内容。 
3) 在include/configs 目录下添加ev44b0ii.h,在这里放上全局的宏定义等。 
4) 找到u-boot 根目录下Makefile 修改加入 
ev44b0ii_config : unconfig 
@./mkconfig $(@:_config=) arm arm7tdmi ev44b0ii 
5) 运行make ev44bii_config,如果没有错误就可以开始硬件相关代码移植的工作 


3. u-boot 的体系结构 
1) 总体结构 
u-boot 是一个层次式结构。从上图也可以看出,做移植工作的软件人员应当提供串口驱动(UART Driver),以太网驱动(Ethernet Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。目前,通过USB 口下载程序显得不是十分必要,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的应用,command 通过串口提供人机界面。我们可以使用一些命令做一些常用的工作,比如内存查看命令md。 
Kermit 应用主要用来支持使用串口通过超级终端下载应用程序。TFTP 则是通过网络方式来下载应用程序,例如uclinux 操作系统。 
2) 内存分布 
在flash rom 中内存分布图ev44b0ii 的flash 大小2M(8bits),现在将0-40000 共256k 作为u-boot 的存储空间。由于u-boot 中有一些环境变量,例如ip 地址,引导文件名等,可在命令行通过setenv 配置好,通过saveenv 保存在40000-50000(共64k)这段空间里。如果存在保存好的环境变量,u-boot 引导将直接使用这些环境变量。正如从代码分析中可以看到,我们会把flash 引导代码搬移到DRAM 中运行。下图给出u-boot 的代码在DRAM中的位置。引导代码u-boot 将从0x0000 0000 处搬移到0x0C700000 处。特别注意的由于ev44b0ii uclinux 中断向量程序地址在0x0c00 0000 处,所以不能将程序下载到0x0c00 0000 出,通常下载到0x0c08 0000 处。 

4. start.S 代码结构 
1) 定义入口 
一个可执行的Image 必须有一个入口点并且只能有一个唯一的全局入口,通常这个入口放在Rom(flash)的0x0 地址。例如start.S 中的 
.globl _start 
_start: 
值得注意的是你必须告诉编译器知道这个入口,这个工作主要是修改连接器脚本文件(lds)。 
2) 设置异常向量(Exception Vector) 
异常向量表,也可称为中断向量表,必须是从0 地址开始,连续的存放。如下面的就包括了复位(reset),未定义处理(undef),软件中断(SWI),预去指令错误(Pabort),数据错误 (Dabort),保留,以及IRQ,FIQ 等。注意这里的值必须与uclinux 的vector_base 一致。这就是说如果uclinux 中vector_base(include/armnommu/proc-armv/system.h)定义为0x0c00 0000,则HandleUndef 应该在 
0x0c00 0004。 
b reset //for debug 
ldr pc,=HandleUndef 
ldr pc,=HandleSWI 
ldr pc,=HandlePabort 
ldr pc,=HandleDabort 
b . 
ldr pc,=HandleIRQ 
ldr pc,=HandleFIQ 
ldr pc,=HandleEINT0  
ldr pc,=HandleEINT1 
ldr pc,=HandleEINT2 
ldr pc,=HandleEINT3 
ldr pc,=HandleEINT4567 
ldr pc,=HandleTICK  
b . 
b . 
ldr pc,=HandleZDMA0  
ldr pc,=HandleZDMA1 
ldr pc,=HandleBDMA0 
ldr pc,=HandleBDMA1 
ldr pc,=HandleWDT 
ldr pc,=HandleUERR01  
b . 
b . 
ldr pc,=HandleTIMER0  
ldr pc,=HandleTIMER1 
ldr pc,=HandleTIMER2 
ldr pc,=HandleTIMER3 
ldr pc,=HandleTIMER4 
ldr pc,=HandleTIMER5  
b . 
b . 
ldr pc,=HandleURXD0  
ldr pc,=HandleURXD1 
ldr pc,=HandleIIC 
ldr pc,=HandleSIO 
ldr pc,=HandleUTXD0 
ldr pc,=HandleUTXD1  
b . 
b . 
ldr pc,=HandleRTC  
b . 
b . 
b . 
b . 
b .  
b . 
b . 
ldr pc,=HandleADC  
b . 
b . 
b . 
b . 
b .  
b . 
b . 
ldr pc,=EnterPWDN 
作为对照:请看以上标记的值: 
.equ HandleReset, 0xc000000 
.equ HandleUndef,0xc000004 
.equ HandleSWI, 0xc000008 
.equ HandlePabort, 0xc00000c 
.equ HandleDabort, 0xc000010 
.equ HandleReserved, 0xc000014 
.equ HandleIRQ, 0xc000018 
.equ HandleFIQ, 0xc00001c 
 
.equ HandleADC, 0xc000020 
.equ HandleRTC, 0xc000024 
.equ HandleUTXD1, 0xc000028 
.equ HandleUTXD0, 0xc00002c 
.equ HandleSIO, 0xc000030 
.equ HandleIIC, 0xc000034 
.equ HandleURXD1, 0xc000038 
.equ HandleURXD0, 0xc00003c 
.equ HandleTIMER5, 0xc000040 
.equ HandleTIMER4, 0xc000044 
.equ HandleTIMER3, 0xc000048 
.equ HandleTIMER2, 0xc00004c 
.equ HandleTIMER1, 0xc000050 
.equ HandleTIMER0, 0xc000054 
.equ HandleUERR01, 0xc000058 
.equ HandleWDT, 0xc00005c 
.equ HandleBDMA1, 0xc000060 
.equ HandleBDMA0, 0xc000064 
.equ HandleZDMA1, 0xc000068 
.equ HandleZDMA0, 0xc00006c 
.equ HandleTICK, 0xc000070 
.equ HandleEINT4567, 0xc000074 
.equ HandleEINT3, 0xc000078 
.equ HandleEINT2, 0xc00007c 
.equ HandleEINT1, 0xc000080 
.equ HandleEINT0, 0xc000084 
3) 初始化CPU 相关的pll,clock,中断控制寄存器 
依次为关闭watch dog timer,关闭中断,设置LockTime,PLL(phase lock loop),以及时钟。 
这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。 
ldr r0,WTCON //watch dog disable 
ldr r1,=0x0 
str r1,[r0] 
ldr r0,INTMSK 
ldr r1,MASKALL //all interrupt disable 
str r1,[r0] 
 
ldr r0,LOCKTIME 
ldr r1,=800 // count = t_lock * Fin (t_lock=200us, Fin=4MHz) = 800 
str r1,[r0] 
ldr r0,PLLCON  
ldr r1,PLLCON_DAT  
str r1,[r0] 
ldr r0,CLKCON 
ldr r1,=0x7ff8 //All unit block CLK enable 
str r1,[r0] 
4) 初始化内存控制器 
内存控制器,主要通过设置13 个从1c80000 开始的寄存器来设置,包括总线宽度, 
8 个内存bank,bank 大小,sclk,以及两个bank mode。 
 
memsetup: 
adr r0,SMRDATA 
ldmia r0,{r1-r13} 
ldr r0,=0x01c80000 //BWSCON Address 
stmia r0,{r1-r13} 
5) 将rom 中的程序复制到RAM 中 
首先利用PC 取得bootloader 在flash 的起始地址,再通过标号之差计算出这个程序代 
码的大小。这些标号,编译器会在连接(link)的时候生成正确的分布的值。取得正 
确信息后,通过寄存器(r3 到r10)做为复制的中间媒介,将代码复制到RAM 中。

relocate: 
 
adr r0, _start  
ldr r2, _armboot_start 
ldr r3, _armboot_end 
sub r2, r3, r2  
ldr r1, _TEXT_BASE  
add r2, r0, r2  
 
copy_loop: 
ldmia r0!, {r3-r10} 
stmia r1!, {r3-r10} 
cmp r0, r2 
ble copy_loop 
6) 初始化堆栈 
进入各种模式设置相应模式的堆栈。 
InitStacks: 
 
mrs r0,cpsr 
bic r0,r0,#0X1F 
orr r1,r0,#0xDB  
msr cpsr,r1  
ldr sp,UndefStack 
orr r1,r0,#0XD7  
msr cpsr,r1  
ldr sp,AbortStack 
orr r1,r0,#0XD2  
msr cpsr,r1  
ldr sp,IRQStack 
orr r1,r0,#0XD1  
msr cpsr,r1  
ldr sp,FIQStack 
bic r0,r0,#0XDF  
orr r1,r0,#0X13 
msr cpsr,r1  
ldr sp,SVCStack 
7) 转到RAM 中执行 
使用指令ldr,pc,RAM 中C 函数地址就可以转到RAM 中去执行。 
5. 系统初始化部分 
1. 串口部分 
串口的设置主要包括初始化串口部分,值得注意的串口的Baudrate 与时钟MCLK 有很大关系,是通过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 )计算得出。这可以在手册中查到。其他的函数包括发送,接收。这个时候没有中断,是通过循环等待来判断是否动作完成。 
例如,接收函数: 
while(!(rUTRSTAT0 & 0x1)); //Receive data read 
return RdURXH0(); 
2. 时钟部分 
实现了延时函数udelay。 
这里的get_timer 由于没有使用中断,是使用全局变量来累加的。 
3. flash 部分 
flash 作为内存的一部分,读肯定没有问题,关键是flash 的写部分。 
Flash 的写必须先擦除,然后再写。 
unsigned long flash_init (void) 

int i; 
u16 manId,devId; 
//first we init it as unknown,even if you forget assign it below,it's not a problem 
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){ 
flash_info[i].flash_id = FLASH_UNKNOWN; 
flash_info[i].sector_count=CFG_MAX_FLASH_SECT; 

 
_RESET(); 
_WR(0x555,0xaa); 
_WR(0x2aa,0x55); 
_WR(0x555,0x90); 
manId=_RD(0x0); 
_WR(0x555,0xaa); 
_WR(0x2aa,0x55); 
_WR(0x555,0x90); 
devId=_RD(0x1); 
_RESET(); 
printf("flashn"); 
printf("Manufacture ID=%4x(0x0004), Device ID(0x22c4)=%4xn",manId,devId); 
if(manId!=0x0004 && devId!=0x22c4){ 
printf("flash check faliluren"); 
return 0; 
}else{ 
for (i=0; i < CFG_MAX_FLASH_BANKS; ++i){ 
flash_info[i].flash_id=FLASH_AM160T; 


 
flash_get_offsets (CFG_FLASH_BASE, &flash_info[0]); 
 
flash_info[0].size =PHYS_FLASH_SIZE; 
return (PHYS_FLASH_SIZE); 

flash_init 完成初始化部分,这里的主要目的是检验flash 的型号是否正确。 
int flash_erase (flash_info_t *info, int s_first, int s_last) 

volatile unsigned char *addr = (volatile unsigned char *)(info->start[0]); 
int flag, prot, sect, l_sect; 
//ulong start, now, last; 
u32 targetAddr; 
u32 targetSize; 
 
rNCACHBE0=( (0x2000000>>12)<<16 )|(0>>12); //flash area(Bank0) must be non-cachable 
area. 
rSYSCFG=rSYSCFG & (~0x8); //write buffer has to be off for proper timing. 
if ((s_first < 0) || (s_first > s_last)) { 
if (info->flash_id == FLASH_UNKNOWN) { 
printf ("- missingn"); 
} else { 
printf ("- no sectors to erasen"); 

return 1; 

if ((info->flash_id == FLASH_UNKNOWN) || 
(info->flash_id > FLASH_AMD_COMP)) { 
printf ("Can't erase unknown flash type - abortedn"); 
return 1; 

prot = 0; 
for (sect=s_first; sect<=s_last; ++sect) { 
if (info->protect[sect]) { 
prot++; 


if (prot) { 
printf ("- Warning: %d protected sectors will not be erased!n", 
prot); 
} else { 
printf ("n"); 

l_sect = -1; 
 
flag = disable_interrupts(); 
 
for (sect = s_first; sect<=s_last; sect++) { 
if (info->protect[sect] == 0) { 
targetAddr=0x10000*sect; 
if(targetAddr<0x1F0000) 
targetSize=0x10000; 
else if(targetAddr<0x1F8000) 
targetSize=0x8000; 
else if(targetAddr<0x1FC000) 
targetSize=0x2000; 
else 
targetSize=0x4000; 
F29LV160_EraseSector(targetAddr); 
l_sect = sect; 
if(!BlankCheck(targetAddr, targetSize)) 
printf("BlankCheck Errorn"); 


 
if (flag) 
enable_interrupts(); 
 
udelay (1000); 
 
if (l_sect < 0) 
goto DONE; 
DONE: 
printf (" donen"); 
return 0; 

int BlankCheck(int targetAddr,int targetSize) 

int i,j; 
for(i=0;i{ 
j=*((u16 *)(i+targetAddr)); 
if( j!=0xffff) 

printf("E:%x=%xn",(i+targetAddr),j); 
return 0; 


return 1; 

flash_erase 擦除flash,BlankCheck 则检查该部分内容是否擦除成功。 
 
static int write_word (flash_info_t *info, ulong dest, ulong data) 

volatile u16 *tempPt; 
 
u16 low = data & 0xffff; 
u16 high = (data >> 16) & 0xffff; 
low=swap_16(low); 
high=swap_16(high); 
tempPt=(volatile u16 *)dest; 
_WR(0x555,0xaa); 
_WR(0x2aa,0x55); 
_WR(0x555,0xa0); 
*tempPt=high; 
_WAIT(); 
_WR(0x555,0xaa); 
_WR(0x2aa,0x55); 
_WR(0x555,0xa0); 
*(tempPt+1)=low; 
_WAIT(); 
return 0; 

wirte_word 则想flash 里面写入unsigned long 类型的data,因为flash 一次只能写入16bits,所以这里分两次写入。
 

posted on 2022-10-04 01:30  bdy  阅读(423)  评论(0编辑  收藏  举报

导航