溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

u-boot第二階段啟動(dòng)流程是什么

發(fā)布時(shí)間:2021-12-20 10:41:40 來源:億速云 閱讀:188 作者:iii 欄目:互聯(lián)網(wǎng)科技

這篇文章主要介紹“u-boot第二階段啟動(dòng)流程是什么”,在日常操作中,相信很多人在u-boot第二階段啟動(dòng)流程是什么問題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”u-boot第二階段啟動(dòng)流程是什么”的疑惑有所幫助!接下來,請(qǐng)跟著小編一起來學(xué)習(xí)吧!

tq2440的uboot目錄有點(diǎn)繁瑣. 用nor中的uboot下載uboot到nand中可以使用tftp方式. 1.連接開發(fā)板與pc 2.選擇tftp下載

u-boot第二階段啟動(dòng)流程是什么

 3. 設(shè)置pc與開發(fā)板同一個(gè)網(wǎng)段

u-boot第二階段啟動(dòng)流程是什么

之后在pc上安裝tftp server軟件. 然后在uboot的tftp下載模式下輸入 1. 因?yàn)閠q2440的uboot源碼寫死了通過tftp下載的uboot文件名稱為 u-boot.bin, 所以將天嵌提供的uboot改名為 u-boot.bin 放到 tftp工具的同級(jí)目錄里即可自動(dòng)完成下載, 下載前最好先格式化nand.

個(gè)人感覺uboot第一階段就是為了芯片能夠運(yùn)行起來保證最最低的運(yùn)行要求, 第二階段才是真正干活的代碼.

第二階段總體概覽

uboot啟動(dòng)的第二階段流程大致如圖所示:

u-boot第二階段啟動(dòng)流程是什么

進(jìn)入 start_armboot 之后首先為 gd , gd->bd 安排存儲(chǔ)空間, 然后將該空間清0. 

一 init_sequence中的函數(shù)

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;        //機(jī)器ID和ram中taglist的基地址在do_boot_linux中作為參數(shù)傳遞給kernel, [theKernel(0, machid, bd->bi_boot_params)]
        
        /* 開啟 指令/數(shù)據(jù) 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 , 自動(dòng)重裝*/
	timers->TCON = (timers->TCON & ~0x0700000) | 0x500000;
	timestamp = 0;

	return (0);
}

u-boot第二階段啟動(dòng)流程是什么

(1) 預(yù)分頻值計(jì)算

這里使用沒有輸出引腳的定時(shí)器 TIM4, 定時(shí) 10ms, 自動(dòng)重裝. 

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, 預(yù)分頻值Prescaler 1 = 1/16;

TCFG1=0x0000, 分頻值  1/2 (默認(rèn)值0000為2分頻)

(2) 定時(shí)器初值計(jì)算
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的值計(jì)算得出FCLK,然后根據(jù)CLKDIVN的分頻率比得出HCLK 和 PCLK. 要做一個(gè)10ms的定時(shí)器,所以這里計(jì)算初值。上面prescaler1 = 16, 且此處沒有設(shè)置TCFG1,所以TCFG1為默認(rèn)值0, 則divider選擇了1/2; 1s = 10ms * 100;

于是,需要的三個(gè)參數(shù)全都出來了, 分頻后頻率為 (clk /(2 * 16)),即每秒要做(clk/(2 * 16))次減計(jì)數(shù),那么10ms就要做 clk / (2 * 16 * 100)次減計(jì)數(shù)。

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

將定時(shí)器計(jì)數(shù)初值保存在 TCNTB4 中, 在減計(jì)數(shù)完成時(shí)自動(dòng)將 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 此時(shí),真正開始定時(shí)器4

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

3. env_init

環(huán)境變量應(yīng)該是如果在flash里有環(huán)境變量那就使用flash里的, 如果沒有就使用默認(rèn)的環(huán)境變量. 

這個(gè)函數(shù)在多個(gè)文件中定義, 但是每個(gè)文件開頭都有一個(gè)宏定義來將整個(gè)文件包含起來, 如在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 就表示參數(shù)保存在nand中.

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

將默認(rèn)的環(huán)境變量數(shù)組地址賦值給gd->env_addr;

default_environment[]定義在 common/env_common.c

4. init_baudrate

 getenv_r ("baudrate", tmp, sizeof (tmp)); 這里是對(duì)應(yīng)上一步的 env_init, 如果設(shè)置了環(huán)境變量就依據(jù) env_ptr 去尋找 "baudrate">

5. serial_init 
/*
 * 使用給定的 波特率 初始化串口. 始終設(shè)置為 8個(gè)數(shù)據(jù)位  無校驗(yàn) 1個(gè)停止位 無開始位
 */
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 , 這里是為了計(jì)算波特率寄存器的值: 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++);
}

使用上一步獲取到的波特率來初始化串口, 串口默認(rèn)設(shè)置為 8 N 1 無. 

6. console_init_f 

gd->have_console = 1; 等于設(shè)置了一個(gè)標(biāo)志位, 表示uboot使用命令終端. 

7. display_banner & print_cpuinfo

這兩個(gè)函數(shù)都是調(diào)試使用的, 他們打印出調(diào)試信息, 這里要注意的是打印函數(shù), printf ;  在/common/console.c中它調(diào)用puts來實(shí)現(xiàn)輸出 

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

最后總結(jié)一下最重要的兩個(gè)結(jié)構(gòu)體:

(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;
    //下面兩個(gè)參數(shù)在進(jìn)入內(nèi)核時(shí)候會(huì)作為參數(shù) r1 r2 傳遞給內(nèi)核啟動(dòng)函數(shù)
    ulong	        bi_arch_number;	/* unique id for this board , 目標(biāo)板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 都保存在該區(qū)域中.

二 以上為 init_sequence 中的函數(shù), 下邊為 start_armboot 中接下來的代碼解析:

flash_init

由內(nèi)部的代碼可知這里是norflash的初始化

#define PHYS_FLASH_1    0x00000000
flashbase = PHYS_FLASH_1;   //因?yàn)閏pu從0地址處開始執(zhí)行代碼, 2440在0地址處只可能有nor-flash和內(nèi)部4k的sram, 所以這里是nor-flash的初始化無疑

該函數(shù)中初始化了nor-flash的各個(gè)sector起始地址和大小, 在display_flash_config中打印出了flash的配置信息

顯示配置
addr = (_bss_end + (PAGE_SIZE - 1)) & ~(PAGE_SIZE - 1);    //計(jì)算 framebuffer內(nèi)存地址
size = lcd_setmem (addr);                                  //設(shè)置 framebuffer尺寸大小
gd->fb_base = addr;                                        //設(shè)置 framebuffer起始地址

mem_malloc_init (_armboot_start - CFG_MALLOC_LEN);        //將 堆區(qū)(malloc) 全部清零
初始化nand

注意要在 /include/configs/smdk2440.h(embedsky.h) 中打開 CONFIG_COMMANDS 和 CFG_CMD_NAND 對(duì) nand 的支持

    nand_init();
初始化環(huán)境變量參數(shù)
/* initialize environment */
env_relocate ();	//初始化環(huán)境變量
設(shè)置ip地址
gd->bd->bi_ip_addr = getenv_IPaddr ("ipaddr");
從網(wǎng)卡讀出mac地址
/* MAC Address */														//從網(wǎng)卡寄存器讀出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;//這里問號(hào)表達(dá)式的優(yōu)先級(jí)高于 =  即:先判斷s是否為真, 根據(jù)結(jié)果再賦值
		if (s)													
			s = (*e) ? e + 1 : e;
	}
開發(fā)板上的設(shè)備初始化
devices_init();   //初始化 iic/lcd/video/logbuff/system/serial等設(shè)備
初始化跳轉(zhuǎn)表

jumptable_init();        //初始化跳轉(zhuǎn)表

初始化控制臺(tái)

console_init_r();

打開中斷

enable_interrupt();

保存"加載地址"
/* Initialize from environment */
if ((s = getenv ("loadaddr")) != NULL)
{
	load_addr = simple_strtoul (s, NULL, 16);
}

查找環(huán)境變量里是否含有 load_addr , 有的話將他的值寫入到 load_addr 中, 他是加載地址, 在bootm中還會(huì)見到他. 他表示uboot將kernel加載到內(nèi)存的地址.

保存framebuffer
if ((s = getenv ("bootfile")) != NULL) {
	copy_filename (BootFile, s, sizeof (BootFile));		//保存framebuffer
}
開發(fā)板初始化
board_late_init ();	//因?yàn)槊總€(gè)板子都不一樣, 板子特殊的初始化可以寫在這個(gè)函數(shù)里
網(wǎng)卡信息打印

eth_initialize

main_loop
/* main_loop() can return to retry autoboot, if so just run it again. */
for (;;)
{
	main_loop ();		//進(jìn)入主循環(huán)
}

截止目前還沒有講到uboot如何啟動(dòng)linux, 而這最關(guān)鍵的一部分代碼就在main_loop中.

main_loop中主要是完成了 1. 設(shè)置啟動(dòng)次數(shù), 有些項(xiàng)目需要檢測(cè)設(shè)備的啟動(dòng)次數(shù), 如果大于某值則不予啟動(dòng)   2. modem功能  3. uboot版本號(hào) 4. 命令自動(dòng)補(bǔ)全功能(類似shell的自動(dòng)補(bǔ)全命令)  5. 啟用倒計(jì)時(shí)啟動(dòng)功能  6. 進(jìn)入死循環(huán) 執(zhí)行 read_line(), 讀取控制臺(tái)(一般都是串口)輸入的命令. 執(zhí)行命令 run_command() 如果想要知道如何解析命令可以去跟蹤一下 run_command() 函數(shù) 7. 啟動(dòng)linux

可見main_loop函數(shù)的重要性, 我們?cè)谙乱黄欣^續(xù)分析main_loop函數(shù)

到此,關(guān)于“u-boot第二階段啟動(dòng)流程是什么”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)砀鄬?shí)用的文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI