您的位置 首页 分销

s3c6410 uboot代码剖析

以下用以记录uboot代码的分析过程,目标是s3c6410,如有错误,欢迎指正。强调,内容与三星原厂提供的uboot-1.1.6有更改的地方,因为外接外…

以下用以记载uboot代码的剖析进程,方针是s3c6410,如有过错,欢迎纠正。

着重,内容与三星原厂供给的uboot-1.1.6有更改的当地,由于外接外设的差异,特别是nand_flash、外接网卡芯片和LCD芯片

以下纯代码情形剖析,请结合uboot的功用结构图和内存分布图检查代码,这样会愈加简单了解。

s3c-u-boot-1.1.6源代码能够在三星下面的网站取得,但条件是你有官方的email。

仍是百度google搜一下吧,当然我这也是有的哦。

http://www.samsung.com/global/business/semiconductor/productInfo.do?fmly_id=835&partnum=S3C6410


功用结构图(上图) uboot内存分布图(上图)

1.start.s代码剖析(榜首阶段)

/*以下是具有arm特征的反常向量表,为间断反常预备 */

——————–

.globl _start
_start: breset
ldrpc, _undefined_instruction
ldrpc, _software_interrupt
ldrpc, _prefetch_abort
ldrpc, _data_abort
ldrpc, _not_used
ldrpc, _irq
ldrpc, _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
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

.balignl 16,0xdeadbeef

——————–

/*当发生间断反常时,pc会跳转到.word的后边地址处 处理反常,

undefined反常由arm核译码单元检测,并触发未界说指令反常恳求,硬件设置pc的值为0x4,强制程序从内存0x4地址履行指令;

0x8寄存软件间断处理指令,arm中运用swi指令时触发软件间断,硬件设置PC的值为0x8,一同进入体系形式,多用在体系库的编写;

prefetch反常,预取指间断反常,导致正在取的指令无法正常取出,这儿需求留意流水线形成的pc值;

data间断,无法获取数据,发生的原因有或许是内存未预备好、内存无读或写权限等一些原因发生的反常;

0x14暂时未运用;

0x18供给体系硬件间断跳转接口,一般咱们的处理器都会引出许多的外部间断线,在这儿能做的便是判别体系间断线发生的间断,注册间断,初始化间断,调用间断函数等等;

0x1c地址为_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
_pad:
.word 0x12345678 /* now 16*4=64 */
.global _end_vect
_end_vect:

.balignl 16,0xdeadbeef

——————–

/*

_TEXT_BASE标号所代表的是uboot代码的运转地址,关于s3c6410

体系来说,假设nand flash发动办法,体系会把0xc000000里边前4KB的内容映射到引导镜像区,即0x0地址,可是咱们需求把

uboot代码放到咱们的SDRAM,原因是咱们代码里边需求对变量做更改而且添加代码履行功率等

下面代码的意义是界说uboot程序履行的运转地址,值为0xc7e00000,.word后边的值TEXT_BASE在编译的时分,

经过向编译器传递参数取得,-DTEXT_BASE办法向编译器传递宏参,在编译的时分能够留意下编译的时分都会指定它的值,值得界说在

config.mk中,Makefile会包含它。

*/

——————–

_TEXT_BASE:
.wordTEXT_BASE

——————–

/*

在uboot里边会敞开MMU,下面是在MMU敞开前uboot在内存寄存的实在物理地址,值为0x57e00000。着重一下,咱们做的开发板的SDRAM在DMC1上,即拜访物理内存的实践物理地址从0x50000000开端,SDRAM的巨细为256M,正好是一个DMC1,所以内存的拜访地址便是0x50000000-0x6FFFFFFF之间了。

*/

——————–

_TEXT_PHY_BASE:
.wordCFG_PHY_UBOOT_BASE

——————–

/*

这个不解说也是能够的,可是仍是要解说。许多人对_start的值有疑问,以为是0x0,由于看到_start的标号在代码段最开端处,其实是过错的,汇编代码里边的标号是和编译时指定的运转地址有联络的。咱们在编译程序的时分会经过-DTEXT_BASE=0xc7e00000参数奉告编译器咱们程序将会运转在0xc7e00000地址,那么天然编译器会以为代码开端的时分就运转在这个地址,那么_start的值天然便是0xc7e00000了。总结之,标号的值与编译时指定的程序地址有联络,而与程序实践寄存在内存出的方位无关。当心运用哦。特别在运用一些伪指令的时分

*/

——————–

.globl _armboot_start
_armboot_start:
.word _start

——————–

/*

下面的代码__bss_start的值是在u-boot.lds脚本里边界说的,尽管没给值,可是你要知道文件的巨细和方位是由

编译器指定的,那么还需求咱们奉告它值吗?所以没值胜有值啦,由编译时编译器决议它们的值

*/

——————–

.globl _bss_start
_bss_start:
.word __bss_start

.globl _bss_end
_bss_end:
.word _end

——————–

/*

uboot开端履行的第二条代码处即在这儿了,下面的代码使得cpu的形式为管理形式,假设想使得为cpu为管理形式,需求确保cpsr寄存

器的最低5位为10011,下面是把0xd3的值赋值给cpsr,0xd3即1101 0011,最高两方位1的意思为封闭间断和快间断,这是为了避免代码

正在履行时,发生外部间断,导致程序跳转到反常向量表而无法正常按次序履行。5位为0的意思是cpu的状况为arm状况,假设是1则cpu进入thumb态,thumb态处理16位指令代码和数据。

*/

——————–

reset:

mrsr0,cpsr
bicr0,r0,#0x1f
orrr0,r0,#0xd3
msrcpsr,r0

——————–

/* 以下标号地点处的代码比较多,将做逐渐剖析,这段代码首要的作业也便是改了一些硬件寄存器和内存初始化作业 */

——————–

cpu_init_crit:

——————–

/*
指令的意义为改写指令和数据缓存。mcr的意思是把arm寄存器的值赋值给coprocesser寄存器,拿榜首条指令来说,

p15代表协处理器,0为必定的值,指令中0b0000四位来表明,现在无详细效果,假设不是0则成果不知道,后边的r0是行将写入

c7方针寄存器中的值,后边还有个c7所代表的意思为额定操作码,假设不是c0,则表明的是同一个寄存器的不同物理寄存器,由于

同一个寄存器的姓名并不代码通一个物理内存,咱们在学rpsr的时分应该知道这点,终究的0供给附加信息,用于区别同一寄存器的

不同物理寄存器,如无附加信息,请坚持为0值,不然成果不行猜测

下面三行代码不难看出,c7、c8的值被清为0,为什么要清为零呢,你需求去看arm1176jzf-s芯片手册了,其间是有阐明的,不再累述,

arm1176jzf-sarm核芯片手册下载地址httop://www.arm.com,也能够与自己联络获取。
*/

——————–
movr0, #0
mcrp15, 0, r0, c7, c7, 0/* flush v3/v4 cache */
mcrp15, 0, r0, c8, c7, 0/* flush v4 TLB */

——————–

/*
实在不想解说这段,由于曾经看过芯片手册的解说,且不止一遍,关于这儿面要更改的内容便是不能悉数记下来,和作业有关了,

不能经心搞这块内容,最多2个月左右的时刻能回来回忆一下了。总归仍是去查arm11核芯片手册,由于以下改的内容是协处理器

c1,那么你就该去查c1是用来干什么的。检查得知,是操控寄存器,检查手册是online books12.2.2 Primary register allocation

一节,其间13,9,8位为V、R、S:V位是对高端反常向量表的支撑,假设挑选0反常向量表为0x00000000-0x0000001c,假设挑选

1反常向量表便是FFFF0000-FFFF001c;R位用于ROM维护的,详细的还要与c5里边的合作,这都是MMU惹的祸,很烦,可是现在

咱们还没有讲到MMU,所以为什么这样做,也有必要到讲到MMU的时分才见分晓了,S在这儿面的意思也是用于体系维护的,和MMU

又是有很大的联络,好吧,后边会找MMU算账的,这儿就先不深入了,接下来再剖析下下面的指令意义

bicr0, r0, #0x00000087@ clear bits 7, 2:0(B— -CAM) 的B位为0表明支撑小little-endian,1表明支撑big-endian格局的体系内存

CAM为第三位,M为0代表制止MMU,反之翻开,A代表地址对齐检查,0代表制止,C代表指令数据cache操控,0为制止

orrr0, r0, #0x00000002@ set bit 2 (A) Align 这段指令又比较犯贱了,翻开地址对齐检查了,这是应该的O(∩_∩)O~,后边又

设置12位为1,意义是假设数据cache和指令cache是分隔的话,这儿面置1的意义将会翻开指令缓存

*/

——————–
mrcp15, 0, r0, c1, c0, 0
bicr0, r0, #0x00002300@ clear bits 13, 9:8 (–V- –RS)
bicr0, r0, #0x00000087@ clear bits 7, 2:0 (B— -CAM)
orrr0, r0, #0x00000002@ set bit 2 (A) Align
orrr0, r0, #0x00001000@ set bit 12 (I) I-Cache
mcrp15, 0, r0, c1, c0, 0

——————–

/*

以下代码的效果是为了给256M的内存在MMU敞开的时分把0x70000000作为重映射的基地址

c15协处理器寄存器在s3c6410上有特殊效果,它是外部内存端口映射寄存器,32位,在开关MMU的时分发生效果,且优先级最高

这儿的0x70000000为外部端口的基地址,0x13的二进制为0x10011,0x10011的意思为256M,代表映射的

巨细为256M,0x10010为128M。假设你没开MMU,PHY和Peri port映射的地址将相同。经过下面的内容后,咱们知道咱们本来uboot

代码是放置到0x57e00000的,现在便只能经过0x57e00000+0x70000000虚拟地址来拜访uboot开端地址了。

运用C15的办法是:

1.Opcode_1 set to 0

2.CRn set to c15

3.CRm set to c2

4.Opcode_2 set to 4

还有问题请参阅arm1176jzfs芯片手册

*/

——————–

/* Peri port setup */
ldrr0, =0x70000000
orrr0, r0, #0x13
mcrp15,0,r0,c15,c2,4 @ 256M(0x70000000-0x7fffffff)

——————–

/*

下面是一条跳转指令,代码这儿不贴,可是其间的代码很重要,在lowlevel.S中完结比方说点亮LED灯、封闭watchdog、封闭间断、体系

时钟初始、nand flash初始化、内存操控器初始化。不过说实在的,去仔细剖析这些初始化的进程,关于你对怎么操控硬件有很大的帮

助,关于这个函数,所要说的东西太多,会在后边的文章中独自剖析它,现在先知道功用就好,没有它代码无法发动。

*/

——————–

bllowlevel_init

——————–

/*跳转出来今后,持续履行下面的代码,下面的代码是判别程序是否现已在ram中了,在的话就不复制,直接跳转到after_copy了,不然

持续履行下面的代码*/

——————–

ldrr0, =0xff000fff
bicr1, pc, r0/* r0 <- current base addr of code */
ldrr2, _TEXT_PHY_BASE/* r1 <- original base addr in ram */
bicr2, r2, r0/* r0 <- current base addr of code */
cmp r1, r2 /* compare r0, r1 */
beq after_copy/* r0 == r1 then skip flash copy */

——————–

/*

下面代码经过函数copy_from_nand函数把代码复制到ram中。steppingstone只能复制4KB,咱们需求把一切的代码搬运到内存中哦

咱们知道s3c6410能够经过SD、onenand、nand发动,可是咱们这儿做了简化,先只从nand发动,今后会再添加SD卡发动

copy_from_nand代码也在start.S中,做了修改以合适大页拜访,如有需求请留言奉告,将添加copy_from_nand代码剖析

*/

——————–

#ifdef CONFIG_BOOT_NAND
movr0, #0x1000
blcopy_from_nand
#endif

——————–

/*SD卡发动办法,这个宏我没有界说,先保存吧*/

——————–

#ifdef CONFIG_BOOT_MOVINAND
ldrsp, _TEXT_PHY_BASE
blmovi_bl2_copy
bafter_copy
#endif

——————–

/*这儿我啥都没做*/

after_copy:

/*

翻开MMU功用

协处理器c3的效果是存储的维护和操控,用在MMU中为内存的域拜访操控

c3为32位寄存器,每两位为一个拜访操控特权,0x00代表没有拜访权限,这时分拜访将失效;0x01为客户类型,将依据

地址改换条目中的拜访操控位决议是否容许特定内存拜访;0x10是保存的,暂时没有运用,0x11为管理者权限,不考虑

地址改换条目中的权限操控位,将不会拜访内存失效。

ldrr5, =0x0000ffff
mcrp15, 0, r5, c3, c0, 0,代码的意义为设置高8个域无拜访权限,低8个域为管理者权限。

接着下面经过mcrp15, 0, r1, c2, c0, 0指令给c2赋值,c2用于保存页表基地址。所谓页表基地址便是真假转化的内存页表的首地址。

这儿r1的值赋值给了c2,r1的值为0x57exxxxx,c2高14位是贮存页表的基地址

终究代码很简单,翻开MMU。

*/

——————–

#ifdef CONFIG_ENABLE_MMU
enable_mmu:
/* enable domain access */
ldrr5, =0x0000ffff
mcrp15, 0, r5, c3, c0, 0@ load domain access register

/* Set the TTB register */
ldrr0, _mmu_table_base
ldrr1, =CFG_PHY_UBOOT_BASE
ldrr2, =0xfff00000
bicr0, r0, r2
orrr1, r0, r1
mcrp15, 0, r1, c2, c0, 0

/* Enable the MMU */
mmu_on:
mrcp15, 0, r0, c1, c0, 0
orrr0, r0, #1/* Set CR_M to enable MMU */
mcrp15, 0, r0, c1, c0, 0
nop
nop
nop
nop
#endif

——————–

/*

仓库初始化代码,咱们在这儿界说了CONFIG_MEMORY_UPPER_CODE

sp的值为0xC7FFFFE8

*/

——————–

skip_hw_init:
/* Set up the stack */
stack_setup:
#ifdef CONFIG_MEMORY_UPPER_CODE
ldrsp, =(CFG_UBOOT_BASE + CFG_UBOOT_SIZE – 0xc)
#else
ldrr0, _TEXT_BASE/* upper 128 KiB: relocated uboot */
subr0, r0, #CFG_MALLOC_LEN/* malloc area */
subr0, r0, #CFG_GBL_DATA_SIZE /* bdinfo */
#ifdef CONFIG_USE_IRQ
subr0, r0, #(CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ)
#endif
subsp, r0, #12/* leave 3 words for abort-stack */

#endif

——————–

/*清零BSS段内容为0 */

——————–

clear_bss:
ldrr0, _bss_start/* find start of bss segment */
ldrr1, _bss_end/* stop here */
mov r2, #0x00000000/* clear */

clbss_l:
strr2, [r0]/* clear loop… */
addr0, r0, #4
cmpr0, r1
bleclbss_l

——————–

/*跳转到uboot代码的第二个阶段,第二阶段基本上都是用C完结的,幸亏前面sp的值现已设置好了 */

——————–

ldrpc, _start_armboot

_start_armboot:
.word start_armboot

——————–

2.第二阶段代码剖析(代码在lib_arm目录下的board.c里边,start_armboot函数)

1)初始化CPU及外围硬件

init_fnc_t init_fnc_ptr;
char *s;
#ifndef CFG_NO_FLASH
ulong size;
#endif

#if defined(CONFIG_VFD) || defined(CONFIG_LCD)
unsigned long addr;
#endif

#if defined(CONFIG_BOOT_MOVINAND)
uint *magic = (uint *) (PHYS_SDRAM_1);
#endif

/* Pointer is writable since we allocated a register for it */
#ifdef CONFIG_MEMORY_UPPER_CODE /* by scsuh */
ulong gd_base;

gd_base = CFG_UBOOT_BASE + CFG_UBOOT_SIZE – CFG_MALLOC_LEN – CFG_STACK_SIZE – sizeof(gd_t);
#ifdef CONFIG_USE_IRQ
gd_base -= (CONFIG_STACKSIZE_IRQ+CONFIG_STACKSIZE_FIQ);
#endif
gd = (gd_t*)gd_base;
#else
gd = (gd_t*)(_armboot_start – CFG_MALLOC_LEN – sizeof(gd_t));
#endif

/* compiler optimization barrier needed for GCC >= 3.4 */
__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));

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 ();
}
}

解说:界说二级指针init_fnc_ptr指向一个寄存函数指针的数组,init_fnc_ptr是typedef int (init_fnc_t) (void)类型,即函数类型,init_fnc_ptr能够指向一个没有参数,回来值为int型的函数指针的地址(很绕哦,),咱们看上面代码终究的for循环init_fnc_ptr = init_sequence,if中会运用(*init_fnc_ptr)()办法调用init_sequence中的函数(函数名能够看为一个地址),假设回来值不是0,则履行hang报错。

由于咱们界说了CONFIG_MEMORY_UPPER_CODE宏,所以gd = (gd_t*)gd_base,由gd_base的值咱们知道,malloc区域、stack区域、bdinfo数据在内存的方位是放在upper of uboot。

__asm__ __volatile__(“”: : :”memory”);这条是内嵌汇编,请检查另一篇介绍内嵌汇编的博文。

gd->bd指针指向数据类型为bd_t的结构体,bd_t结构体记载开发板的参数,例如串口波特率、ip地址、机器类型、发动参数、环境变量方位等。

下面剖析for循环履行的函数:

cpu_init:由于咱们没有界说CONFIG_USE_IRQ,所以这个函数直接回来0

board_init:

函数内首要履行dm9000_pre_init()函数,由于咱们把DM9000AEP网卡映射到内存的Xm0CSn[1]上,所以咱们要设置拜访CSn[1]的办法,SROM_BW_REG &= ~(0xf << 4);SROM_BW_REG |= (1<<7) | (1<<6) | (1<<4);两条代码的意义为设置0x70000000操控器的CSn[1]拜访办法为nBE enable、wait enable、16位数据总线拜访形式,SROM_BC1_REG = ((DM9000_Tacs<<28)+(DM9000_Tcos<<24)+(DM9000_Tacc<<16)+(DM9000_Tcoh<<12)+(DM9000_Tah<<8)+(DM9000_Tacp<<4)+(DM9000_PMC));这句话的意思是设置拜访的时序,s3c6410 datasheet中现已给出了时序代码,欢迎检查哦。由于我的zc6410开发箱接了4.3的TFT LCD,所以在代码中添加了对LCD的装备作业,代码如下:

writel(readl(MIFPCON) & (~(1 << 3)), MIFPCON);

writel(readl(MIFPCON) & (~(3 << 0)) | 0x1, SPCON);

writel(0xaaaaaaaa, GPICON);
writel(0xaaaaaa, GPJCON);

以上四行代码分别对MIFPCON、SPCON、GPICON、GPJCON四个寄存器赋值。为什么赋值?检查s3c6410 datasheet手册14.5.1节,给出了DisplayController的引脚装备,MIFPCON的[3]位设置为0(normal mode)instead of “1”(by-pass mode),SPCON[1:0]位的值设置为“01”(useRGB I/F Style)or “00” to use Host I/F Style,咱们设置的是01。GP%&&&&&%ON、GPJCON赋值的原因请看下面图:

榜首张图是LCD操控器接口衔接原理图,后边的是图是芯片手册,经过两个图咱们就知道为什么要写后边两行代码了吧。

好了,73-74行一个是记载机器类型,一个是指定向内核传参的地址。

interrupt_init:

184行:值0x0101的意义是设置env_init:如下图,由于没有界说ENV_IS_EMBEDDED,一切仅仅履行了142-143,把环境变量的首地址赋值给gd->env_addr。

init_baudrate:

139行运用getenv_r函数在default_environment里找baudrate要害字,找到后把“=”号后边的值赋值给gd->baudrate,然后

再放到gd->bd->bi_baudrate里边。simple_strtoul是uboot完结的字符串转UL类型。

serial_init:什么都没做,坚持默许的8位数据、无奇偶校验、1 中止位、无开端位。

console_init_f:gd->have_console = 1就这一句话

display_banner:串口打印uboot信息,便是uboot发动的时分咱们看到的信息,这儿运用的是printf,可是咱们追进去后,重视的函数

应该是serial_putc,它是实在向串口输出一个字符的函数,这个函数会递归调用,应该说自己调用自己,遇到\n完毕。

print_cpuinfo:打印CPU信息,CPU类型和速度 CPU:…

checkboard:打印开发板信息 BOARD:…

dram_init:

记载dram的开端地址,0x50000000,size为256M

display_dram_config:由于没有界说DEBUG,所以打印DRAM:256M

2)装备malloc空间

(由于CONFIG_LCD、CONFIG_VFD没有界说,所以越过这一部分)

咱们界说了UPPER_CODE,所以履行榜首个mem_malloc_init。这个函数的效果是记载仓库空间的开端地址、完毕地址、当时地址。

3)发动设备初始化(SD、NAND、ONENAND)

体系一开端测验从SD卡发动,由于本篇介绍的是NANDFLASH发动办法,所以SD卡部分暂不剖析,会独自拓荒章节介绍s3c6410

的SD卡发动办法(包含windows下SD flasher应用程序的编写、SD卡硬件电路剖析、SD寄存器操作和发动流程)。

下面剖析nand发动办法,咱们在自己的头文件里界说了CONFIG_COMMANDS和CFG_CMD_NAND,所以会履行nand_init函数

剖析代码中的368行nand_init函数,咱们知道在uboot发动起来之后,会显现NAND: 512M(你nandflash的巨细),所以不难想象,

nand_init会向终端打印NAND巨细的信息,以下是nand_init的完结:

nand_init函数的完结体在drivers/nand/nand.c中,nand_init函数不只会打印出nandflash的巨细,还会初始化描绘nand的结构体

nand_info以及代表“nand”的设备结构体nand_chip,这两个结构体前者是mtd层对设备的笼统和对块设备的接口一致,后者是

设备的实体,一切对设备的读写操控操作都终究经过这个结构体完结,下面咱们开端剖析nand_init函数:

64~69行:从表面看,终究会履行size += nand_info[i].size,由此最起码能够猜测到这个函数管帐算出nand的巨细。那么是怎样计算出

来的呢,咱们需求看nand_init_chip函数,留意,在进入这个函数之前,先看一下传入的三个参数,前两个参数咱们现已介绍过,第三

个参数是nand的数据寄存器,拜访地址为0x70200010。nand_init_chip函数会依据咱们传入的参数,去查找对应的nand设备,并初

始化一些功用接口为今后对nand操作做预备,下面看图:

47行:mtd->priv完结了mtd中间层对底层nand设备的接口,咱们今后在拜访nand硬件时,经过mtd的priv成员能够快速找到咱们的

nand设备。

49行:nand成员中保存了读写nand数据的数据寄存器基地址,咱们经过读写base_addr中的数据,完结对nand中数据的读和写,

后边的__iomem是个宏界说,这样界说的#define __iomem,只界说并没有给值,所以没有任何功用意义,可是关于咱们在看代码

的时分,很简单能判别出后边的变量是IO地址空间的寄存器地址。

50行:是对nand设备的初始化操作,咱们进入函数体

820~823行:判别是否是从nand_flash发动。看s3c6410寄存器就会理解:

820行:NFCONF界说的宏,其实是取0x70200000地址里边的内容,那么假设咱们把OM跳线设置为nand发动,这个[31]

位的值就会为1,这样的话NFCONF & 0x80000000的值便是1了,因此boot_nand的值为1;

825行:铲除0x70200004的[16]位,封闭软件锁存,假设此位设置为1,则NFSBLK(0x70200020)到NFEBLK

(0x70200024)-1被敞开,除了这部分区域,写或擦除指令是无效的,只要读指令是有用的(NFSBLK和NFEBLK)为可编程

可编程开端和完毕块地址寄存器;

826~827行:咱们在进入这个函数的时分就做过了;

828行:nand->cmd_ctrl = s3c_nand_hwcontrol,这个函数是用于向nand硬件发送指令的,比方发送00h,代表的是读指令。

829行:nand->dev_ready = s3c_nand_device_ready,是用于判别nand芯片处于忙/可读状况的。s3c_nand_device_ready

用一个循环去判别NFSTAT(0x70000028)的最低位(RnB输入引脚状况),假设是1表明现在nand可读,0代表正在忙不行读

830行:bbt(bad block table)坏块表,由于咱们没用到,所以s3c_nand_scan_bbt函数会直接回来;

咱们没有界说宏CFG_NAND_FLASH_BBT,所以nand->options|= NAND_SKIP_BBTSCAN,后边宏的意义代表在初始化期间

将越过坏块扫描;

839行:CFG_NAND_HWECC需求咱们自己界说,意义代表运用过错纠错码;

840行:代表运用NAND_FLASH模块内部的ECC模块发生纠错码;

841行:nand->ecc.hwctl= s3c_nand_enable_hwecc,设置对ECC的操控,这个函数应该在发生ECC编码前被调用。这个函数的

功用为承认SLC FLASH或是MLC FLASH,SLC代表single layer cell,MCL为multi-level cell,有关SLC和MLC的差异在容量、可

读写总次数、读写速度上,SLC的读写速度要快于MLC,但MCL的容量要比SLC大许多,由于1cell能够包容4bits,有爱好能够查阅

相关手册。此函数还有一个功用为:1.初始化主区ECC解码器/编码器(向0x70200004的[5]位写1)2.敞开主区ECC

(向0x70200004的[7]位写0);

842行:nand->ecc.calculate= s3c_nand_calculate_ecc,用于存储发生的校验纠错码;

843行:nand->ecc.correct= s3c_nand_correct_data,用生成的ECC码检测是否有过错,没有则回来,详细内容看函数阐明就好;

845~849行:向nand发送4条指令,849行为等候设备预备好;

851~852行:将读取到nand芯片的厂商信息和芯片ID编号;

854~859行:nand_flash_ids结构体保存了许多公司出产的nand芯片信息和编号,for循环将经过ID找到和咱们板子匹配的NAND芯片;

再往下尽管代码挺多的,但不必忧虑,只会履行877行的nand->ecc.layout = &s3c_nand_oob_16,这是在界说oob信息;

以下是nand芯片能够处理的指令以及指令的意义(下面是三星K9F4G08U0A-PCB0芯片的指令集)

剖析完board_nand_init函数后,咱们持续看nand_init_chip第52行,nand_scan函数:

这个函数的首要功用便是2768行的nand_scan_ident函数,功用是填充mtd结构体,装备对nand的接口。这样下次在拜访设备时,可

经过mtd层找到对应的底层设备,咱们看下nand_scan_ident函数:

到此,咱们不再往下追函数了,

2501行在设置mtd设备层的接口函数,

2504行nand_get_flash_type函数代码比较多,首要的功用仍是在取得nand芯片的厂商信息和ID,并判别是否支撑,假设支撑

为这个nand设备和mtd填充一些功用接口函数,

咱们再来看nand_scan_ident函数的后边代码,2512行是for循环,maxchips的值是nand_scan函数传递进来的1,所以咱们终究

看到的i值为1,在2526行chip->numchips=1,mtd->size=512(我的nandflash类型是K9F4G08U0A-PCB0,巨细512M,

咱们在board_nand_init函数中现已在结构体nand_flash_ids中找到咱们的nand类型,chipsize的值为512)。

nand初始化剖析完毕。。。

4)环境变量初始化

环境变量初始化,即start_armboot函数第379行的env_relocate ()函数,这个函数完结体在env_common.c中,咱们看本相:

这个函数的功用其实便是让env_ptr指向寄存环境变量的首地址,而且填充env_ptr->data成员变量。

208和212的两个宏咱们没有界说,所以直接看223行,223行malloc一个CFG_ENV_SIZE的空间用于寄存环境变量

230行是在环境变量被迁移到内存后,咱们能够运用内存中的环境变量

由于gd->env_valid咱们前面没有赋值,所以232行if会履行,打印236行的内容

240~244行是判别默许的环境变量是否大于咱们限制的环境变量巨细

247行是把uboot代码中现已存在的default_environment指针所指向的环境变量复制到env_ptr->data中

253行是crc32的校验,会对环境变量的内容运用CRC32校验表做每8字节的DO1校验,校验表界说在crc32.c的77行

259行是把内存中可操作的环境变量记载在大局变量

5)网络初始化

网络初始化分为IP地址初始化和MAC地址初始化,看下图:

387行:IP地址初始化,getenv_IPaddr会回来IPaddr_t(unsigned long)类型的IP地址赋值给gd->bd->bi_ip_addr大局变量。

进入getenv_IPaddr函数会履行return (string_to_ip(getenv(var))),咱们先看getenv(var):

497行:是给软件狗预备的,我的ZC6410板子上有硬件狗,所以这个WATCHDOG_RESET其实便是一个空的macro。

499~510行:会查找环境变量中的name,name是传递进来的ipaddr,进入查找旅程env_get_char函数,假设咱们没有忘掉的话

咱们在env_relocate ()函数的230行履行过env_get_char = env_get_char_memory,所以会履行env_get_char_memory函数,

这个函数在env_common.c中完结:

186行的值同样在env_relocate正确履行往后,被赋值为1,回来gd->env_addr+index,gd->env_addr指向default_environment

由于default_environment是个指针数组,index代表数组的索引,即环境变量名得索引,看default_environment数组就能够理解

假设你现已自己看过了这两段for循环,你或许知道外层for循环在遍历default_environment数组,内层判别是否超出环境变量的

巨细。后边外层循环会履行507行的envmatch函数,这个函数是真实匹配getenv传递进去的ipaddr参数的,里边运用while循环

去找环境变量中的‘=’号前面的是否有ipaddr字符串,有则回来ipaddr在环境变量中的索引,比方ipaddr=“。。。”\0这个字符串

被放在default_environment[1]中,则回来这个索引值1。

getenv的509行回来这个索引地点的环境环境变量的地址,即return ( ((uchar *)(gd->env_addr + index)) )。

咱们回来getenv_IPaddr函数,履行return (string_to_ip(getenv(var))),把上面回来的地址地点的字符串(IP)转化为int类型的数据

保存在gd->bd->bi_ip_addr中。

390~403:MAC地址初始化

和上面的IP初始化相同,做着换汤不换药的作业

6)设备列表初始化

start_armboot函数的417行:devices_init ()函数。函数完结的首要功用是把设备放入list列表。这种办法是被引荐的,由于下次在查找

设备的时分会经过这个列表来查询设备,便利一致管理。这个设备列表的接口的个二级指针变量devlist(list_t类型->二级指针)。

下面不贴代码了,由于不难了解。这个函数的169~171行for循环在把规范输入、规范输出、规范过错的姓名赋值到stdio_names大局

数组变量中。176行比较要害,是为uboot中的设备建立了list列表,今后添加设备时,能够刺进这个list中经过devlist指针。

再往下面是许多宏界说,可是咱们都没有界说,只要一个函数会履行,便是drv_system_init ()。这个函数注册了一个设备,设备名是

serial,填充设备的device_t结构体,指明设备的输入和输出设备等,终究运用device_register (&dev),把自己注册到devlist列表中。

7)装备功用函数表

start_armboot函数的423行履行jumptable_init (),为gd-jt大局变量界说了一些独立的函数接口,便利调用。

8)操控台初始化

start_armboot函数的437行履行console_init_r (),设定一个操控台。咱们知道,咱们的uboot也是有printf之类输出、有指令能够

让咱们输入的,所以咱们需求界说出一个操控台设备,让咱们的输入和输出都指向它,下面看函数:

491行:从devlist设备列表中取得当时devlist中所罗列的最大设备个数,咱们前面现已对这个接口做过赋值,并装备到devlist中,且

最初设置的值为0,代表榜首个设备,那么这儿items的值就应该是0了;

493和500行的宏我没有界说,不履行;

507~519行:查找输入输出设备,

511行:界说一个能够代表操控台设备的结构体,而且从devlist中为自己获取一个列表方位,ListGetPtrToItem函数如下图所示:

上面if和else if都没有得到履行,直接履行终究的return

return的值由最下面的define决议,咱们看到list->itemList数组其实便是devlist的设备挂载点

咱们将会把咱们的设备列表都罗列到这个数组中

可是需求留意一个问题,list->itemList界说是list->itemlist[1],仅仅分配了一个字段,怎么能存下多个设备列表呢

gnu编译器对规范C做了扩展,支撑动态巨细的数组界说,所以我想你应该运用gnu编译器来编译uboot

513和516行把这个取得的列表地址赋值给了inputdev和outputdev两个设备指针;

522和528行是真实的给咱们的规范输入和规范输入设备填充结构的函数,会把dev地址再赋值给大局结构体stdio_devices

这个结构体是终究记载规范输入输出结构体的;

534行:CFG_CONSOLE_INFO_QUIET宏没有界说,所以会履行下面的代码,咱们在发动uboot之后,会打印

In:serial

Out:serial

Error:serial

便是这段代码起的效果;

559行:运用setenv把规范输入输出和规范过错设备变量设置到环境变量中。

9)开间断反常向量表

start_armboot函数的433行履行enable_interrupts,内容如下:

以上代码是内嵌汇编,这个我不解说,由于我前面有文章专门介绍__asm__ __volatile__用法的,效果便是铲除cpsr中的

[7]位。这位的意义是间断使能/制止位,具有间断最高优先级,这儿代码用于敞开间断。

10)网卡芯片初始化

我的板子上运用的是DM9000AEP网卡,不是CS8900,所以代码有所改动,添加了对DM9000网卡的初始化支撑,这儿我调用了

DM9000里的一个函数eth_set_mac,用于初始化网卡芯片,代码如下:

注释写的很好,最起码咱们知道这段代码在干什么,首要打印出大局变量gd里边寄存的网卡设备的MAC地址,这个值是咱们在

板子头文件里边界说的CONFIG_ETHADDR获取的;

293行:在填充MAC地址的值到网卡芯片专门用于保存MAC地址的寄存器傍边,所以这儿咱们界说了DM9000_PAR,它的值是0x10

用于保存MAC地址,为什么是0x10这个地址保存MAC地址,咱们当然不知道,得去找DM9000网卡芯片厂商的datasheet,如下:

看倒数第二行,奉告你DM9000的物理网卡地址寄存器叫PAR,地址是10h-15h 6个字节用于保存MAC地址

那么咱们下面就把咱们的物理网卡地址写到这个寄存器里边来吧,运用294行的函数(留意咱们现已敞开MMU了,不能

不能直接拜访内存和IO内存了)。DM9000_iow的函数完结体如下图所示:

DM9000_outb是个宏,向IO内存中写入值,例如DM9000_outb(reg,DM9000_IO)的意义是把reg的值写入DM9000_IO这个寄存器

那么DM9000_IO和DM9000_DATA的值是多少呢?即你现在需求具有以下的剖析进程:

咱们想让DM9000网卡能用,就有必要接到板子上

不是随意接的,由于你需求能够操控DM9000的寄存器才干经过软件操控它

那么你又要想怎么才干操作到DM9000网卡的寄存器?

操作DM9000网卡寄存器就有对它的读和写,有必要经过总线拜访到DM9000网卡寄存器的数据地址

拜访数据地址有必要经过CPU的内存映射(CPU拜访外设的地址空间)

所以,终究咱们得让CPU和DM9000衔接,把DM9000接到CPU能拜访的地址空间上

咱们怎么知道衔接上了呢?得看DM9000网卡的衔接原理图,如下:

看上面的图,有一个叫NET_ncs的引脚,它便是拜访片选引脚,它接到了CPU的CS1上,这张图写的是CS#

CS#代表什么意思?幸亏我是知道接到CS1上的,这个‘#’应该是硬件工程师的失误了吧

不论怎么样,你要知道接到s3c6410cpu的CS1引脚上是什么意思,代表cpu能够经过这个地址来拜访DM9000哦

接下来咱们看一下,CS1的值是多少,即什么地址能够拜访到DM9000,检查s3c6410 datasheet:

看图中红线部分,本来接NET_nCS接到了s3c6410的SRAM1上,即CS1上,地址是0x18000000

好了,回到DM9000_iow函数持续剖析,咱们界说DM9000_IO和DM9000_DATA的值,如下图所示:

不难看出,DM9000_IO的值是0x18000300

?,为什么不是0x18000000呢?由于DM9000网卡选用pakeage机制,拜访DM9000的内部寄存器被映射到了

CS1+0x300偏移地址处,DM9000_DATA寄存器的地址是DM9000_IO的值+4(都是32位)

DM9000_outb(reg, DM9000_IO);
DM9000_outb(value, DM9000_DATA);

上面两行代码应该知道什么意思了吧,榜首行是指定往DM9000网卡的哪一个地址写,reg是0x10,便是用于存储
MAC地址的DM9000内部PAR寄存器,那么DM9000_outb(reg, DM9000_IO)相当于说要向

0x18000310地址写入一个字节,DM9000_outb(value, DM9000_DATA)相当于说0x18000310被写入的内容是value

好了,经过eth_set_mac函数的293~294行,咱们把MAC地址写入了DM9000的PAR寄存器中

295~296行:向播送地址寄存器中赋值全1;

299~301行:是用于调试的,看能不能从方才的寄存器中读出咱们写入的MAC地址。

网卡剖析比较复杂,要害是你要找到IO拜访基地址,还要知道DM9000的间断线,由于咱们读网卡数据,体系是经过间断的办法

来取得的(linux体系,其他的不能太确认),还有DM9000网卡是数据和地址复用的,经过NET_CMD引脚来操控。我的DM9000

间断引脚接的是EINT7,看上面的DM9000网卡的衔接原理图就知道了。

11)一些后续初始化

看下面的本相:

好吧,这个后续初始化作业咱们如同都没有界说,等等,除了BOARD_LATE_INIT。不过board_late_init函数里边我什么都没写,

2)main_loop详解

这个挺重要的,由于它是uboot和咱们用户交互的接口。让咱们一同进去剖析吧(代码有点小长哦)

297行:CFG_HUSH_PARSER没有界说,所以会界说大局变量lastcommand[CFG_CBSIZE],CFG_CBSIZE的值为256,用于记载

console buffer size;

304行:咱们界说了CONFIG_BOOTDELAY在头文件中,默许是3,我改成了1。后边会界说指针s和int型bootdelay。用于uboot判别

无任何按键按下,就履行CONFIG_BOOTCOMMAND宏所对应的指令,这个指令一般用于加载发动操作体系;

308行:CONFIG_PREBOOT宏没有界说,不论;

311行:CONFIG_BOOTCOUNT_LIMIT没有界说,不论;

318行:没有界说CONFIG_VFD,所以也不论,但我后边会加上这个宏,由于我想在发动的时分在我的LCD上显现我的LOGO;

329行:CONFIG_BOOTCOUNT_LIMIT没有界说,不论;

339行:CONFIG_MODEM_SUPPORT没有界说,不论;

350行:CONFIG_VERSION_VARIABLE没有界说,不论;

。。。再没有界说的就不贴了,直接找咱们界说的看;

362行:其实CONFIG_AUTO_COMPLETE宏我也没有界说,可是这个东西挺好用的,能够协助咱们主动补齐指令

390行:咱们界说了CONFIG_BOOTDELAY,所以391行s的值将保存bootdelay环境变量的值得地址,392行把地址的值给bootdelay

408行:s保存bootcmd等于号后边字符串的地址;

412行:if判别为真,履行418行的run_command函数,这个函数很重要,是查找解析指令并履行的,所以咱们仍是看图吧:

run_command函数的1265行,设置ctrlc_was_pressed = 0,,这个变量符号ctrl+c没有被按下

1267行判别没有输入指令则回来-1

1271行判别咱们输入的指令字符串是否大于咱们界说的操控台buffer

1276行把指令复制到cmdbuf数组中

1285行是一个while循环,str指针指向cmdbuf

1291~1312用于查找“;”号,由于咱们的指令能够运用“;”号来履行多条指令,就如同

在shell环境里你输入ls;cd,会先履行ls,然后又会履行cd

1324行process_macros是判别假设一条指令里边有两条指令,把第二条指令分离出来放到finaltoken数组中

1327行:parse_line函数解析字符串,并把解析出来的指令和参数放到argv中,parse_line是字符串解析,不贴图

1333行:是到.u_boot_cmd(u-boot.lds中界说)代码段中找匹配的指令

__u_boot_cmd_start和__u_boot_cmd_end两个大局变量是这个段的开端地址和完毕地址

1340行:判别参数,假设所给的参数与完结指令的结构体中所指定的参数巨细不相同,将调用指令中供给的usage函数

1346行假设界说了CFG_CMD_BOOTD,则在1348行判别履行的是do_bootd指令则履行1356行的flag

1363行,是真实去履行咱们指令行所输入的指令

1370行,判别是否有ctrl+c按下,假设有run_command履行完毕

再次退回main_loop函数,

449行:开端履行等候用户输入的操作,CFG_HUSH_PARSER没有界说,463行的代码会被履行,readline函数会容许指令行提示符

比方我设置的CFG_PROMPT值为ZC6410 #,将会在指令行打印ZC6410 #,然后等候用户输入;

467行:把console_buffer中的数据复制到lastcommand数组中,484行判别len是否为0,假设按下ctrl+c按键,这个值将会是-1,

由于前面代码serial驱动代码中是有这部分代码判别的,假设判别ctrl+c按下,则回来-1,这儿的len值天然便是-1,假设ctrl+c按键

没有被按下,将履行run_command,履行用户输入的指令。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/bandaoti/fenxiao/277157.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部