1. u-boot 介绍
u-boot 是一个open source 的bootloader,现在版本是1.1.2。u-boot 是在ppcboot 以及armboot 的基础上开展而来,适当的老练和安稳,现已在许多嵌入式体系开发进程中被选用。由于其开发源代码,其支撑的开发板很多。
为什么咱们需求u-boot?明显能够将uClinux 直接烧入flash,然后不需求额定的引导装载程序(bootloader)。可是从软件晋级的视点以及程序修补的来说,软件的主动更新非常重要。事实上,引导装载程序(bootloader)的用处不仅如此,但仅从软件的主动更新的需求就阐明咱们的开发是必要的。一起,u-boot 移植的进程也是一个对嵌入式体系包含软硬件以及操作体系加深了解的一个进程。
2. u-boot 移植的结构
移植u-boot 到新的开发板上仅需求修正和硬件相关的部分。在代码结构上:
1) 在board 目录下创立gold44b 目录,创立gold44b.c 以及flash.c,memsetup.S,u-boot.lds等。不需求从零开端,可挑选一个类似的目录,直接仿制过来,修正文件名以及内容。我在移植u-boot 进程中,挑选的是Dave/B2目录。由于u-boot 现已包含根据s3c24b0 的开发板目录,作为参阅,也能够仿制相应的目录。
2) 在cpu 目录下创立s3c44b0x 目录,首要包含start.S,interrupts.c 以及cpu.c,serial.c几个文件。相同不需求从零开端树立文件,直接从arm720t 仿制,然后修正相应内容。
3) 在include/configs 目录下增加gold44b.h,在这儿放上大局的宏界说等。
4) 找到u-boot 根目录下Makefile 修正参加
gold44b_config : unconfig
@./mkconfig $(@:_config=) arm s3c44b0 gold44b
5) 运转make ev44bii_config,假如没有过错就能够开端硬件相关代码移植的作业
3. u-boot 的体系结构
1) 整体结构
u-boot 是一个层次式结构。做移植作业的软件人员应当供给串口驱动(UART Driver),以太网驱动(Ethernet Driver),Flash 驱动(Flash 驱动),USB 驱动(USB Driver)。现在,经过USB 口下载程序显得不是十分必要,而且开发板上也没有USB接口,所以暂时没有移植USB 驱动。驱动层之上是u-boot 的运用,command 经过串口供给人机界面。咱们能够运用一些指令做一些常用的作业,比方内存查看指令md。
Kermit 运用首要用来支撑运用串口经过超级终端下载运用程序。TFTP 则是经过网络方法来下载运用程序,例如uClinux 操作体系。
2) 内存散布
gold44b 的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-1.1.2/board/gold44b/config.mk装备如下:TEXT_BASE = 0x0C700000。这样,引导代码u-boot将从0x0000 0000 处搬移到0x0C700000 处。特别留意的由于gold44b uClinux 中止向量程序地址在0x0c000 0000 处,所以不能将程序下载到0x0c000 0000 出,一般下载到0x0c008 0000 处。
4. start.S 代码结构
1) 界说进口
一个可履行的Image 有必要有一个进口点而且只能有一个仅有的大局进口,一般这个进口放在Rom(flash)的0x0 地址。例如start.S 中的
.globl _start
_start:
值得留意的是你有必要告知编译器知道这个进口,这个作业首要是修正衔接器脚本文件(lds)。
开发板上的u-boot.lds如下:
OUTPUT_FORMAT(“elf32-littlearm”, “elf32-littlearm”, “elf32-littlearm”)
OUTPUT_ARCH(arm)
ENTRY(_start)
SECTIONS
{
. = 0x00000000;
. = ALIGN(4);
.text :
{
cpu/s3c44b0/start.o (.text)
*(.text)
}
. = ALIGN(4);
.rodata : { *(.rodata) }
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
.got : { *(.got) }
__u_boot_cmd_start = .;
.u_boot_cmd : { *(.u_boot_cmd) }
__u_boot_cmd_end = .;
armboot_end_data = .;
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) }
_end = .;
}
2) 设置反常向量(Exception Vector)
反常中止矢量表(Exception Vector Table)是u-boot与uClinux内核产生联络要害的当地之一。即便uClinux内核现已得到处理器的操控权运转,一旦产生中止,处理器仍是会主动跳转到从0x0地址开端的榜首级反常中止矢量表中的某个表项(根据于中止类型)处读取指令运转。
反常中止矢量表有必要是从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。
.globl _start
_start: b reset
/*Modfied by zl 2005-2-21 */
/* add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
*/
ldr pc, =0x0c000004
ldr pc, =0x0c000008
ldr pc, =0x0c00000c
ldr pc, =0x0c000010
ldr pc, =0x0c000014
ldr pc, =0x0c000018
ldr pc, =0x0c00001c
.balignl 16,0xdeadbeef
这儿,地址0x0处的一级反常中止矢量表只简略地包含向二级反常中止矢量表的跳转指令就能够。这样,就能够正确地将产生的事情交给uClinux的中止处理程序来处理。这样规划是由于在本u-boot移植里不运用中止,8019和Timer都是轮询中止的,假如u-boot要运用中止(如要用到USB设备),就需求树立二级反常中止矢量表了。代码如下(没有调试经过):
.globl _start
_start: b reset
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
add pc, pc, #0x0c000000
.balignl 16,0xdeadbeef
….
在Reset是仿制中止矢量表
/*
now copy to sram the interrupt vector
*/
adr r0, real_vectors
add r2, r0, #1024
ldr r1, =0x0c000000
add r1, r1, #0x08
vector_copy_loop:
ldmia r0!, {r3-r10}
stmia r1!, {r3-r10}
cmp r0, r2
ble vector_copy_loop
….
树立三级中止跳转
/*************************************************/
/* interrupt vectors */
/*************************************************/
/*
real_vectors:
b reset
b undefined_instruction
b software_interrupt
b prefetch_abort
b data_abort
b not_used
b irq
b fiq
*/
/*************************************************/
undefined_instruction:
mov r6, #3
b reset
software_interrupt:
mov r6, #4
b reset
prefetch_abort:
mov r6, #5
b reset
data_abort:
mov r6, #6
b reset
not_used:
/* we *should* never reach this */
mov r6, #7
b reset
irq:
mov r6, #8
b reset
fiq:
mov r6, #9
b reset
3) 初始化CPU 相关的pll,clock,中止操控寄存器
依次为封闭watch dog timer,封闭中止,设置LockTime,PLL(phase lock loop),以及时钟。
这些值(除了LOCKTIME)都可从Samsung 44b0 的手册中查到。
/*
*************************************************************************
*
* CPU_init_critical registers
*
* setup important registers
* setup memory timing
*
*************************************************************************
*/
#define INTCON (0x01c00000+0x200000)
#define INTMSK (0x01c00000+0x20000c)
#define LOCKTIME (0x01c00000+0x18000c)
#define PLLCON (0x01c00000+0x180000)
#define CLKCON (0x01c00000+0x180004)
#define WTCON (0x01c00000+0x130000)
cpu_init_crit:
/* disable watch dog */
ldr r0, =WTCON
ldr r1, =0x0
str r1, [r0]
/*
* mask all IRQs by clearing all bits in the INTMRs
*/
ldr r1,=INTMSK
ldr r0, =0x03fffeff
str r0, [r1]
ldr r1, =INTCON
ldr r0, =0x05
str r0, [r1]
/* Set Clock Control Register */
ldr r1, =LOCKTIME
ldrb r0, =800
strb r0, [r1]
ldr r1, =PLLCON
#if CONFIG_S3C44B0_CLOCK_SPEED==64
ldr r0, =0x38021 /* smdk4110: Xtal=8MHz Fclk=64MHz */
#elif CONFIG_S3C44B0_CLOCK_SPEED==66
ldr r0, =0x34031 /* 66MHz (Quartz=11MHz) */
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
ldr r0, =0x610c1 /*B2: Xtal=20mhz Fclk=75MHz */
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
str r0, [r1]
ldr r1,=CLKCON
ldr r0, =0x7ff8
str r0, [r1]
mov pc, lr
4) 初始化SDRAM操控器
内存操控器(首要是SDRAM操控器),首要经过设置13 个从1c80000 开端的寄存器来设置,包含总线宽度,8 个内存bank,bank 巨细,sclk,以及两个bank mode。
#ifdef CONFIG_INIT_CRITICAL
bl cpu_init_crit
/*
* before relocating, we have to setup RAM timing
* because memory timing is board-dependend, you will
* find a memsetup.S in your board directory.
*/
bl memsetup
#endif
初始化内存操控器的代码寄存在u-boot-1.1.2/board/gold44b/memsetup.S中
与ADS或许SDT下的boot代码(memcfg.s和44binit.s)共同,仅仅汇编格局有点不一样。
5) 将rom 中的程序仿制到RAM 中
首要运用PC 获得bootloader 在flash 的开始地址,再经过标号之差核算出这个程序代
码的巨细。这些标号,编译器会在衔接(link)的时分生成正确的散布的值。获得正
确信息后,经过寄存器(r3 到r10)做为仿制的中心前言,将代码仿制到RAM 中。
relocate: /* relocate U-Boot to RAM */
adr r0, _start /* r0 <- current position of code */
ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
cmp r0, r1 /* dont reloc during debug */
beq stack_setup
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2 <- size of armboot */
add r2, r0, r2 /* r2 <- source end address */
copy_loop:
ldmia r0!, {r3-r10} /* copy from source address [r0] */
stmia r1!, {r3-r10} /* copy to target address [r1] */
cmp r0, r2 /* until source end addreee [r2] */
ble copy_loop
6) 初始化仓库
进入各种形式设置相应形式的仓库.
/* Set up the stack */
stack_setup:
ldr r0, _TEXT_BASE /* upper 128 KiB: relocated uboot */
sub r0, r0, #CFG_MALLOC_LEN /* malloc area */
sub r0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
sub r0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
sub sp, r0, #12 /* leave 3 words for abort-stack */
7) 转到RAM 中履行
运用指令ldr,pc,RAM 中C 函数地址就能够转到RAM 中去履行。
ldr pc, _start_armboot
5. 体系初始化部分
1) 串口部分(u-boot-1.1.2/cpu/s3c44b0/serial.c)
串口的设置首要包含初始化串口部分,值得留意的串口的Baudrate 与时钟MCLK 有很大联系,是经过:rUBRDIV0=( (int)(MCLK/16./(gd ->baudrate) + 0.5) -1 )核算得出。这能够在手册中查到。由于u-boot支撑可变的波特率,所以选用宏界说设置默许波特率(64Mhz,115200bps)和其他波特率。代码如下:
void serial_setbrg (void)
{
DECLARE_GLOBAL_DATA_PTR;
u32 divisor = 0;
/* get correct divisor */
switch(gd->baudrate) {
case 1200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 3124;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 3905;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默许
divisor = 3332;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 9600:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 390;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 487;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默许
divisor = 416;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 19200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 194;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 243;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默许
divisor = 207;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif
break;
case 38400:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 97;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 121;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默许
divisor = 103;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
case 57600:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 64;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75
divisor = 80;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64 /默许
divisor = 68;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
case 115200:
#if CONFIG_S3C44B0_CLOCK_SPEED==66
divisor = 32;
#elif CONFIG_S3C44B0_CLOCK_SPEED==64
divisor = 34;
#elif CONFIG_S3C44B0_CLOCK_SPEED==75 /默许
divisor = 40;
#else
# error CONFIG_S3C44B0_CLOCK_SPEED undefined
#endif break;
}
serial_flush_output();
serial_flush_input();
UFCON0 = 0x0;
ULCON0 = 0x03;
UCON0 = 0x05;
UBRDIV0 = divisor;
UFCON1 = 0x0;
ULCON1 = 0x03;
UCON1 = 0x05;
UBRDIV1 = divisor;
for(divisor=0; divisor<100; divisor++) {
/* NOP */
}
}
其他的函数包含发送,接纳。这个时分没有中止,是经过循环等候来判别是否动作完结。
例如,接纳函数:
static int serial_flush_input(void)
{
volatile u32 tmp;
/* keep on reading as long as the receiver is not empty */
while(UTRSTAT0&0x01) {
tmp = REGB(URXH0);
}
return 0;
}
2) 时钟部分(u-boot-1.1.2/cpu/s3c44b0/interrupt.c)
完结了延时函数udelay。
这儿的get_timer 由于没有运用中止,是运用大局变量来累加的。
void udelay (unsigned long usec)
{
ulong tmo;
tmo = usec / 1000;
tmo *= CFG_HZ;
tmo /= 8;
tmo += get_timer (0);
while (get_timer_masked () < tmo)
/*NOP*/;
}
3) flash 部分(u-boot-1.1.2/board/gold44b.c)
flash 作为内存的一部分,读必定没有问题,要害是flash 的写部分。
Flash 的写有必要先擦除,然后再写。
flash_init 完结初始化部分,这儿的首要意图是查验flash 的类型是否正确。
unsigned long flash_init (void)
{
#ifdef __DEBUG_START_FROM_SRAM__
return CFG_DUMMY_FLASH_SIZE;
#else
unsigned long size_b0;
int i;
/* Init: no FLASHes known */
for (i=0; i
}
/* Static FLASH Bank configuration here – FIXME XXX */
size_b0 = flash_get_size((vu_long *)CFG_FLASH_BASE, &flash_info[0]);
if (flash_info[0].flash_id == FLASH_UNKNOWN) {
printf (“## Unknown FLASH on Bank 0 – Size = 0x%08lx = %ld MB\n”,
size_b0, size_b0<<20);
}
/* Setup offsets */
flash_get_offsets (0, &flash_info[0]);
/* Monitor protection ON by default */
(void)flash_protect(FLAG_PROTECT_SET,
-CFG_MONITOR_LEN,
0xffffffff,
&flash_info[0]);
flash_info[0].size = size_b0;
return (size_b0);
#endif
}
flash_erase 擦除flash,BlankCheck 则查看该部分内容是否擦除成功。
int flash_erase (flash_info_t *info, int s_first, int s_last)
{
volatile CFG_FLASH_WORD_SIZE *addr = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *addr2;
int flag, prot, sect, l_sect;
ulong start, now, last;
int i;
if ((s_first < 0) || (s_first > s_last)) {
if (info->flash_id == FLASH_UNKNOWN) {
printf (“- missing\n”);
} else {
printf (“- no sectors to erase\n”);
}
return 1;
}
if (info->flash_id == FLASH_UNKNOWN) {
printf (“Cant erase unknown flash type – aborted\n”);
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;
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();
/* Start erase on unprotected sectors */
for (sect = s_first; sect<=s_last; sect++) {
if (info->protect[sect] == 0) { /* not protected */
addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[sect]);
if ((info->flash_id & FLASH_VENDMASK) == FLASH_MAN_SST) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00500050; /* block erase */
for (i=0; i<50; i++)
udelay(1000); /* wait 1 ms */
} else {
if (sect == s_first) {
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00800080;
addr[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
}
addr2[0] = (CFG_FLASH_WORD_SIZE)0x00300030; /* sector erase */
}
l_sect = sect;
}
}
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();
/* wait at least 80us – lets wait 1 ms */
udelay (1000);
/*
* We wait for the last triggered sector
*/
if (l_sect < 0)
goto DONE;
start = get_timer (0);
last = start;
addr = (CFG_FLASH_WORD_SIZE *)(info->start[l_sect]);
while ((addr[0] & (CFG_FLASH_WORD_SIZE)0x00800080) != (CFG_FLASH_WORD_SIZE)0x00800080) {
if ((now = get_timer(start)) > CFG_FLASH_ERASE_TOUT) {
printf (“Timeout\n”);
return 1;
}
/* show that were waiting */
if ((now – last) > 50000000) { /* every second */
putc (.);
last = now;
}
}
DONE:
/* reset to read mode */
addr = (CFG_FLASH_WORD_SIZE *)info->start[0];
addr[0] = (CFG_FLASH_WORD_SIZE)0x00F000F0; /* reset bank */
printf (” done\n”);
return 0;
}
wirte_word 则想flash 里边写入unsigned long 类型的data,由于flash 一次只能写入16bits,所以这儿分两次写入。
/*———————————————————————–
* Write a word to Flash, returns:
* 0 – OK
* 1 – write timeout
* 2 – Flash not erased
*/
static int write_word (flash_info_t *info, ulong dest, ulong data)
{
volatile CFG_FLASH_WORD_SIZE *addr2 = (CFG_FLASH_WORD_SIZE *)(info->start[0]);
volatile CFG_FLASH_WORD_SIZE *dest2 = (CFG_FLASH_WORD_SIZE *)dest;
volatile CFG_FLASH_WORD_SIZE *data2 = (CFG_FLASH_WORD_SIZE *)&data;
ulong start;
int flag;
int i;
/* Check if Flash is (sufficiently) erased */
if ((*((volatile ulong *)dest) & data) != data) {
return (2);
}
/* Disable interrupts which might cause a timeout here */
flag = disable_interrupts();
for (i=0; i<4/sizeof(CFG_FLASH_WORD_SIZE); i++)
{
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00AA00AA;
addr2[CFG_FLASH_ADDR1] = (CFG_FLASH_WORD_SIZE)0x00550055;
addr2[CFG_FLASH_ADDR0] = (CFG_FLASH_WORD_SIZE)0x00A000A0;
dest2[i] = data2[i];
/* re-enable interrupts if necessary */
if (flag)
enable_interrupts();
/* data polling for D7 */
start = get_timer (0);
while ((dest2[i] & (CFG_FLASH_WORD_SIZE)0x00800080) !=
(data2[i] & (CFG_FLASH_WORD_SIZE)0x00800080)) {
if (get_timer(start) > CFG_FLASH_WRITE_TOUT) {
return (1);
}
}
}
return (0);
}