文档章节

u-boot第二阶段简要讲解

季风_落地窗
 季风_落地窗
发布于 2014/05/28 22:23
字数 2301
阅读 186
收藏 6

tq2440的uboot目录有点繁琐. 用nor中的uboot下载uboot到nand中可以使用tftp方式. 1.连接开发板与pc 2.选择tftp下载

 3. 设置pc与开发板同一个网段

之后在pc上安装tftp server软件. 然后在uboot的tftp下载模式下输入 1. 因为tq2440的uboot源码写死了通过tftp下载的uboot文件名称为 u-boot.bin, 所以将天嵌提供的uboot改名为 u-boot.bin 放到 tftp工具的同级目录里即可自动完成下载, 下载前最好先格式化nand.


个人感觉uboot第一阶段就是为了芯片能够运行起来保证最最低的运行要求, 第二阶段才是真正干活的代码.

第二阶段总体概览

uboot启动的第二阶段流程大致如图所示:


进入 start_armboot 之后首先为 gd , gd->bd 安排存储空间, 然后将该空间清0. 

一 init_sequence中的函数

1. board_init

int board_init (void)
{
	S3C24X0_GPIO * const gpio = S3C24X0_GetBase_GPIO();//获取S3C24X0_GPIO_BASE, GPIO寄存器的基地址

	/* set up the I/O ports */
	gpio->GPACON = 0x007FFFFF;
        ... ...
	gpio->GPJUP = 0x00001fff;

	/* arch number of TQ2440-Board */
	gd->bd->bi_arch_number = MACH_TYPE_S3C2440;

	/* adress of boot parameters */
	gd->bd->bi_boot_params = 0x30000100;        //机器ID和ram中taglist的基地址在do_boot_linux中作为参数传递给kernel, [theKernel(0, machid, bd->bi_boot_params)]
        
        /* 开启 指令/数据 cache*/
	icache_enable();
	dcache_enable();

	return 0;
}

2. interrupt_init

int interrupt_init (void)
{
	S3C24X0_TIMERS * const timers = S3C24X0_GetBase_TIMERS();

	/* use PWM Timer 4 because it has no output */
	/* prescaler for Timer 4 is 16 */
	timers->TCFG0 = 0x0f00;
	if (timer_load_val == 0)
	{
	    /* for 10 ms clock period @ PCLK with 4 bit divider = 1/2 (default) and prescaler = 16. Should be 10390 @33.25MHz and 15625 @ 50 MHz */
	    timer_load_val = get_PCLK()/(2 * 16 * 100);
	}
	/* load value for 10 ms timeout */
	lastdec = timers->TCNTB4 = timer_load_val;
	/* auto load, manual update of Timer 4 */
	timers->TCON = (timers->TCON & ~0x0700000) | 0x600000;
	/* auto load, start Timer 4 , 自动重装*/
	timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
	timestamp = 0;

	return (0);
}


(1) 预分频值计算

这里使用没有输出引脚的定时器 TIM4, 定时 10ms, 自动重装. 

TCFG0              Bit      Description             
Prescaler 1       [15:8]    These 8 bits determine prescaler value for Timer 2, 3 and 4.    
Prescaler 0       [7:0]     These 8 bits determine prescaler value for Timer 0 and 1.        0x00

TCFG0=0x0F00, 预分频值Prescaler 1 = 1/16;

TCFG1=0x0000, 分频值  1/2 (默认值0000为2分频)

(2) 定时器初值计算
timer_load_val = get_PCLK()/(2 * 16 * 100);

get_PCLK()位于cpu/arm920t/s3c24x0/speed.c 中,取得pclk的过程如下

 FCLK => HCLK = FCLK / 2 => PCLK = HCLK / 2, FCLK : HCLK : PCLK = 4 : 2 : 1

即先通过MPLLCON的值计算得出FCLK,然后根据CLKDIVN的分频率比得出HCLK 和 PCLK. 要做一个10ms的定时器,所以这里计算初值。上面prescaler1 = 16, 且此处没有设置TCFG1,所以TCFG1为默认值0, 则divider选择了1/2; 1s = 10ms * 100;

于是,需要的三个参数全都出来了, 分频后频率为 (clk /(2 * 16)),即每秒要做(clk/(2 * 16))次减计数,那么10ms就要做 clk / (2 * 16 * 100)次减计数。

(3) TCNTB4
lastdec = timers->TCNTB4 = timer_load_val;

将定时器计数初值保存在 TCNTB4 中, 在减计数完成时自动将 TCNTB4 赋值给 TCNT4

(4) TCON
timers->TCON = (timers->TCON & ~0x0700000) | 0x600000; // ==> bit[22:20] = 110    auto reload mode, update TCNTB4
timers->TCON = (timers->TCON & ~0x0700000) | 0x500000; // ==> bit[22:20] = 101    auto reload mode, start for timer4 此时,真正开始定时器4

后期版本的uboot中的interrupt_init貌似更新为timer_init了

3. env_init

环境变量应该是如果在flash里有环境变量那就使用flash里的, 如果没有就使用默认的环境变量. 

这个函数在多个文件中定义, 但是每个文件开头都有一个宏定义来将整个文件包含起来, 如在env_nand.c中就有

#if defined(CFG_ENV_IS_IN_NAND)     /* Environment is in Nand Flash */

在 include/configs/smdk2440.h(EmbedSky.h)中如果定义了 CFG_ENV_IS_IN_NAND 就表示参数保存在nand中.

int env_init(){
    gd->env_addr  = (ulong)&default_environment[0];
    gd->env_valid = 1;
}

将默认的环境变量数组地址赋值给gd->env_addr;

default_environment[]定义在 common/env_common.c

4. init_baudrate

 getenv_r ("baudrate", tmp, sizeof (tmp)); 这里是对应上一步的 env_init, 如果设置了环境变量就依据 env_ptr 去寻找 "baudrate" 这个变量, 如果找到就用环境变量配置的值, 如果没有找到就用 default_environment[]里的默认值: 115200 

5. serial_init 

/*
 * 使用给定的 波特率 初始化串口. 始终设置为 8个数据位  无校验 1个停止位 无开始位
 */
int serial_init (void)
{
	serial_setbrg ();

	return (0);
}

void serial_setbrg (void)
{
	S3C24X0_UART * const uart = S3C24X0_GetBase_UART(UART_NR);
	int i;
	unsigned int reg = 0;

	/* value is calculated so : (int)(PCLK/16./baudrate) -1 , 这里是为了计算波特率寄存器的值: RBRDIV*/
	reg = get_PCLK() / (16 * gd->baudrate) - 1;

	/* FIFO enable, Tx/Rx FIFO clear */
	uart->UFCON = 0x07;
	uart->UMCON = 0x0;
	/* Normal,No parity,1 stop,8 bit */
	uart->ULCON = 0x3;
	/*
	 * tx=level,rx=edge,disable timeout int.,enable rx error int.,
	 * normal,interrupt or polling
	 */
	uart->UCON = 0x245;
	uart->UBRDIV = reg;

#ifdef CONFIG_HWFLOW
	uart->UMCON = 0x1; /* RTS up */
#endif
	for (i = 0; i < 100; i++);
}

使用上一步获取到的波特率来初始化串口, 串口默认设置为 8 N 1 无. 

6. console_init_f 

gd->have_console = 1; 等于设置了一个标志位, 表示uboot使用命令终端. 

7. display_banner & print_cpuinfo

这两个函数都是调试使用的, 他们打印出调试信息, 这里要注意的是打印函数, printf ;  在/common/console.c中它调用puts来实现输出 

void puts (const char *s)
{
    //GD_FLG_DEVINIT表示了是向自己还是串口输出
    if (gd->flags & GD_FLG_DEVINIT) {
        /* Send to the standard output */
	fputs (stdout, s);
    } else {
	/* Send directly to the handler */
	serial_puts (s);
    }
}

8. dram_init

首先要在 /include/configs/smdk2440.h(embedsky.h)中定义:
#define CONFIG_NR_DRAM_BANKS		1	   /* 只有一块sdram */
#define PHYS_SDRAM_1			0x30000000 /* SDRAM Bank #1 起始地址*/
#define PHYS_SDRAM_1_SIZE		0x08000000 /* 128 MB */


int dram_init (void)
{
	gd->bd->bi_dram[0].start = PHYS_SDRAM_1;        //bi_dram[0] 表示了 dram 起始地址和大小
	gd->bd->bi_dram[0].size = PHYS_SDRAM_1_SIZE;

	return 0;
}

最后总结一下最重要的两个结构体:

(1) /include/asm-arm/global_data.h  中的 gd_t , 其中保存了 u-boot 的配置信息

typedef	struct	global_data {
	bd_t		*bd;
	unsigned long	flags;
	unsigned long	baudrate;
	unsigned long	have_console;	/* serial_init() was called */
	unsigned long	reloc_off;	/* Relocation Offset */
	unsigned long	env_addr;	/* Address  of Environment struct */
	unsigned long	env_valid;	/* Checksum of Environment valid? */
	unsigned long	fb_base;	/* base address of frame buffer */
	void		**jt;		/* jump table */
} gd_t;

(2) /include/asm-arm/u-boot.h 中的 bd_t

typedef struct bd_info {
    int			bi_baudrate;	/* serial console baudrate */
    unsigned long	bi_ip_addr;	/* IP Address */
    unsigned char	bi_enetaddr[6]; /* Ethernet adress */
    struct environment_s *bi_env;
    //下面两个参数在进入内核时候会作为参数 r1 r2 传递给内核启动函数
    ulong	        bi_arch_number;	/* unique id for this board , 目标板id */
    ulong	        bi_boot_params;	/* where this board expects params , taglist的地址*/
   
    struct				/* RAM configuration */
    {
	ulong start;
	ulong size;
    } 			bi_dram[CONFIG_NR_DRAM_BANKS];
} bd_t;

在/include /configs/smdk2440.h(embedsky.h) 中定义的 CFG_GBL_DATA_SIZE 为 128 ,  gd_t 和 bd_t 都保存在该区域中.

二 以上为 init_sequence 中的函数, 下边为 start_armboot 中接下来的代码解析:

flash_init

由内部的代码可知这里是norflash的初始化

#define PHYS_FLASH_1    0x00000000
flashbase = PHYS_FLASH_1;   //因为cpu从0地址处开始执行代码, 2440在0地址处只可能有nor-flash和内部4k的sram, 所以这里是nor-flash的初始化无疑

该函数中初始化了nor-flash的各个sector起始地址和大小, 在display_flash_config中打印出了flash的配置信息

显示配置

addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);    //计算 framebuffer内存地址
size = lcd_setmem (addr);                                  //设置 framebuffer尺寸大小
gd->fb_base = addr;                                        //设置 framebuffer起始地址

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);        //将 堆区(malloc) 全部清零

初始化nand

注意要在 /include/configs/smdk2440.h(embedsky.h) 中打开 CONFIG_COMMANDS 和 CFG_CMD_NAND 对 nand 的支持

    nand_init();

初始化环境变量参数

/* initialize environment */
env_relocate ();	//初始化环境变量

设置ip地址

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

从网卡读出mac地址

/* MAC Address */														//从网卡寄存器读出mac地址
	int i;
	ulong reg;
	char *s, *e;
	char tmp[64];

	i = getenv_r ("ethaddr", tmp, sizeof (tmp));	
	s = (i > 0) ? tmp : NULL;

	for (reg = 0; reg < 6; ++reg) {
		gd->bd->bi_enetaddr[reg] = s ? simple_strtoul (s, &e, 16) : 0;//这里问号表达式的优先级高于 =  即:先判断s是否为真, 根据结果再赋值
		if (s)													
			s = (*e) ? e + 1 : e;
	}

开发板上的设备初始化

devices_init();   //初始化 iic/lcd/video/logbuff/system/serial等设备

初始化跳转表

jumptable_init();        //初始化跳转表

初始化控制台

console_init_r();

打开中断

enable_interrupt();

保存"加载地址"

/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL)
{
	load_addr = simple_strtoul (s, NULL, 16);
}

查找环境变量里是否含有 load_addr , 有的话将他的值写入到 load_addr 中, 他是加载地址, 在bootm中还会见到他. 他表示uboot将kernel加载到内存的地址.

保存framebuffer

if ((s = getenv ("bootfile")) != NULL) {
	copy_filename (BootFile, s, sizeof (BootFile));		//保存framebuffer
}

开发板初始化

board_late_init ();	//因为每个板子都不一样, 板子特殊的初始化可以写在这个函数里

网卡信息打印

eth_initialize

main_loop

/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;)
{
	main_loop ();		//进入主循环
}

截止目前还没有讲到uboot如何启动linux, 而这最关键的一部分代码就在main_loop中.

main_loop中主要是完成了 1. 设置启动次数, 有些项目需要检测设备的启动次数, 如果大于某值则不予启动   2. modem功能  3. uboot版本号 4. 命令自动补全功能(类似shell的自动补全命令)  5. 启用倒计时启动功能  6. 进入死循环 执行 read_line(), 读取控制台(一般都是串口)输入的命令. 执行命令 run_command() 如果想要知道如何解析命令可以去跟踪一下 run_command() 函数 7. 启动linux

可见main_loop函数的重要性, 我们在下一篇中继续分析main_loop函数


© 著作权归作者所有

下一篇: arm9 adc及触摸屏
季风_落地窗
粉丝 4
博文 16
码字总数 16829
作品 0
杨浦
程序员
私信 提问
U-boot在S3C2440上的移植详解(三)---支持NAND FLASH启动

5)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上Nand Flash的支持)。 目前u-boot中还没有对2440上Nand Flash的支持,也就是说要想u-boot从Nand Flash上启动得自己去实现了。 首...

宁宁爸
2015/11/26
190
0
u-boot源码整体框架

源码解压以后,我们可以看到以下的文件和文件夹: cpu 与处理器相关的文件。每个子目录中都包括 cpu.c 和 interrupt.c、start.S、u-boot.lds。 cpu.c 初始化 CPU、设置指令 Cache 和数据 Ca...

nothingfinal
2012/04/05
0
0
U-boot在S3C2440上的移植详解(二) --- NOR FLASH 启动u-boot

4)准备进入u-boot的第二阶段(在u-boot中添加对我们开发板上Nor Flash的支持)。 通常,在嵌入式bootloader中,有两种方式来引导启动内核:从Nor Flash启动和从Nand Flash启动。u-boot中默认...

宁宁爸
2015/11/24
138
2
pcDuino的u-boot移植与分析

一、准备工作 1.获取支持pcDuino的u-boot源码 https://github.com/linux-sunxi/u-boot-sunxi 在XP下直接点ZIP即可开始下载 2.获取交叉编译链 http://code.google.com/p/smp-on-qemu/downloa...

pc朵拉
2013/07/02
297
0
uboot U_BOOT_CMD

其中UBOOTCMD命令格式如下:  各个参数的意义如下: UBOOTCMD宏在include/command.h中定义: 与都是预编译操作符,有字符串连接的功能,表示后面紧接着的是一个字符串。 其中Struct_Sectio...

青春无极限
2018/09/06
240
0

没有更多内容

加载失败,请刷新页面

加载更多

Mybatis Plus删除

/** @author beth @data 2019-10-17 00:30 */ @RunWith(SpringRunner.class) @SpringBootTest public class DeleteTest { @Autowired private UserInfoMapper userInfoMapper; /** 根据id删除......

一个yuanbeth
今天
4
0
总结

一、设计模式 简单工厂:一个简单而且比较杂的工厂,可以创建任何对象给你 复杂工厂:先创建一种基础类型的工厂接口,然后各自集成实现这个接口,但是每个工厂都是这个基础类的扩展分类,spr...

BobwithB
今天
4
0
java内存模型

前言 Java作为一种面向对象的,跨平台语言,其对象、内存等一直是比较难的知识点。而且很多概念的名称看起来又那么相似,很多人会傻傻分不清楚。比如本文我们要讨论的JVM内存结构、Java内存模...

ls_cherish
今天
4
0
友元函数强制转换

友元函数强制转换 p522

天王盖地虎626
昨天
5
0
js中实现页面跳转(返回前一页、后一页)

本文转载于:专业的前端网站➸js中实现页面跳转(返回前一页、后一页) 一:JS 重载页面,本地刷新,返回上一页 复制代码代码如下: <a href="javascript:history.go(-1)">返回上一页</a> <a h...

前端老手
昨天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部