移植u-boot-2012.04.01到JZ2440(三:修改源碼之實現NOR啓動與NAND啓動)

目錄

3. 修改源碼之實現從NOR啓動與NAND啓動
    3.1 前言

    3.2 實現從NOR啓動
        3.2.1 修改設置時鐘
            3.2.1.1 CLKDIVN寄存器
            3.2.1.2 總線模式
            3.2.1.3 MPLL寄存器
            3.2.1.4 修改源碼
        3.2.2 修改初始化存儲控制器參數
        3.2.3 修改串口波特率相關內容
        3.2.4 解決編譯錯誤
        3.2.5 測試NOR Flash啓動
    3.3 實現從NAND啓動
       
3.3.1 去掉 "-pie"選項
        3.3.2 添加init.c文件
        3.3.3 修改啓動流程
        3.3.4 修改鏈接地址
        3.3.5 修改重定位地址
        3.3.6 修改鏈接腳本
        3.3.7 測試NAND Flash啓動


3. 修改源碼之實現從NOR啓動與NAND啓動

3.1 前言

    通過移植u-boot-2012.04.01到JZ2440(二:分析啓動流程)uboot啓動流程的分析,我們知道源碼做了許多工作,回顧一下流程如下:
    1. 設置異常向量表
    2. 設置SVC管理模式、關看門狗、關中斷、設置時鐘頻率
    3. 禁用Cache和MMU、初始化存儲控制器
    4. 設置棧
    5. 調用第一個C函數board_init_f
    6. 重定位
        6.1 拷貝代碼到SDRAM
        6.2 修改動態鏈接地址數據
    7. 清除bss段
    8. 調用第二個C函數board_init_r(跳到SDRAM中運行)

    修改代碼前,首先了解NOR啓動與NAND啓動的概念:
    NOR啓動:上電後NOR Flash被映射到0x00000000地址(就是nGCS0,不需要片內SRAM輔助,片內SRAM的起始地址爲0x40000000),然後CPU0x00000000開始運行(在NOR Flash上運行)
    NAND啓動:上電後CPU自動將NAND Flash中的前4K代碼拷貝到SRAM中,SRAM被映射爲0x00000000地址,CPU0x00000000開始運行(在SRAM上運行)
    我們只需修改少量單板相關代碼就能將該uboot下載到NOR Flash上啓動,這是沒有問題的,但是想要在NAND Flash上運行,就會涉及一個嚴重問題:ubootNAND Flash啓動,CPU自動將uboot4K代碼拷貝到片內RAM(此時片內RAM的基地址從0開始)中運行,前4步的代碼量很小,完全可以存放進這前4K的代碼裏,但是第5步調用第一個C函數board_init_f時,這部分的代碼基本不在前4K代碼以內,執行到這裏就會出錯NOR Flash上能直接運行代碼,完全不用擔心該問題)
    所以想要在NAND Flash上啓動uboot,就需要在uboot前4K代碼裏將NAND Flashuboot的代碼拷貝到SDRAM中,也就是前4K代碼裏就要完成重定位(將NAND Flash內的uboot拷貝到SDRAM中)並跳轉到SDRAM中去執行剩餘代碼,而源碼重定位的信息又是通過調用第一個C函數board_init_f(暫且不考慮位置相關碼),但是我們又不能先調用board_init_f再重定位爲了讓uboot能同時在NOR Flash也能在NAND Flash上啓動,所以啓動流程的後半部分我們基本自己來實現。

3.2 實現從NOR啓動

    在移植u-boot-2012.04.01到JZ2440(一:創建單板)中我們已經創建好了單板相關文件,接下來按照啓動分析流程進行修改源碼實現從NOR Flash啓動uboot

3.2.1 修改設置時鐘

3.2.1.1 CLKDIVN寄存器

    在start.S文件中,uboot源碼只設置了CLKDIVN寄存器,查看S3C2440A芯片手冊第七章Clock & Power Management
有如下圖說明:
   
    默認FCLK120MHz,則HDIVNPDIVN都不爲0,這裏我們設置CLKDIVN寄存器的值爲5,也就是FCLK:HCLK:PCLK=1:4:8

3.2.1.2 總線模式

    同時手冊中還有如下說明:
   
    表示S3C2440不支持同步總線模式,我們必須改爲異步模式,那麼我們必須在時鐘設置上加上那段代母,修改CPU的總線模式。

3.2.1.3 MPLL寄存器

    datesheet中還有如下框圖:
   
    我們CPU的啓動是在normal mode中,normal mode說明如下:
   
    MPLL爲鎖相環輸出頻率 ,用來給MCU提供頻率,進而再給CPU的各個模塊供應頻率。具體可以看看datesheet中的Clock Generator Block Diagram框圖。儘管MPLL僅僅只是在一個reset後啓動,但是直到軟件對MPLLCON寄存器寫一個有效的設置之後,MPLL才作爲系統時鐘的輸出。在有效設置之前,從外部晶振源(XTlPll或EXTCLK)獲得的時候將直接用於系統時鐘。哪怕用戶不想要改變MPLLCON寄存器的值,用戶都應該寫一個相同的值進MPLLCON寄存器。
    而我們的源碼中,在時鐘設置後,就直接跳到cpu_init_crit去執行,在其中又跳進lowlevel_init中執行SDRAM的初始化,並且註釋中HCLK的默認頻率爲60MHZ,此時的HCLK是依賴於FCLK也就是MPLL,因此我們必須添加上MPLL的設置。如下圖datesheet中的說明:
   
   
    這裏我們設置MPLL400MHZ,所以MDIV設爲0x5cPDIV設爲1,SDIV設爲1。

3.2.1.4 修改源碼

   將以上3點修改到源碼,同時再啓動ICASHE。下面修改源碼,將以下內容(arch/arm/cpu/arm920t/start.S文件中)
   
    改爲:

    /* 設置時鐘 */
	ldr r0, =0x4c000014
	mov r1, #0x05;			  // FCLK:HCLK:PCLK=1:4:8
	str r1, [r0]

	/* 如果HDIVN非0,CPU的總線模式應該從“fast bus mode”變爲“asynchronous bus mode” */
	mrc	p15, 0, r1, c1, c0, 0		/* 讀出控制寄存器 */ 
	orr	r1, r1, #0xc0000000			/* 設置爲“asynchronous bus mode” */
	mcr	p15, 0, r1, c1, c0, 0		/* 寫入控制寄存器 */

#define S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))

	/* MPLLCON = S3C2440_MPLL_400MHZ,FCLK=PLL=400MHZ */
	ldr r0, =0x4c000004
	ldr r1, =S3C2440_MPLL_400MHZ
	str r1, [r0]
    
        /* 啓動ICACHE */
	mrc p15, 0, r0, c1, c0, 0	@ read control reg
	orr r0, r0, #(1<<12)
	mcr	p15, 0, r0, c1, c0, 0   @ write it back

    由於後面會調用board_init_f()->board_early_init_f()裏面又設置了系統時鐘,這裏已經設置好了,所以屏蔽board_early_init_f()函數裏的時鐘設置,如下(board/samsung/jz2440/jz2440.c文件中)
  

3.2.2 修改初始化存儲控制器參數

    修改內存控制寄存器默認參數,將以下內容(board/samsung/jz2440/lowlevel_init.S文件)

SMRDATA:
    .word (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))
    .word ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC))
    .word ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC))
    .word ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC))
    .word ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC))
    .word ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC))
    .word ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC))
    .word ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN))
    .word ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN))
    .word ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)
    .word 0x32
    .word 0x30
    .word 0x30

    改爲:

SMRDATA:
	.long 0x22011110	 //BWSCON
	.long 0x00000700	 //BANKCON0
	.long 0x00000700	 //BANKCON1
	.long 0x00000700	 //BANKCON2
	.long 0x00000700	 //BANKCON3  
	.long 0x00000740	 //BANKCON4
	.long 0x00000700	 //BANKCON5
	.long 0x00018005	 //BANKCON6
	.long 0x00018005	 //BANKCON7
	.long 0x008C04F4	 // REFRESH
	.long 0x000000B1	 //BANKSIZE
	.long 0x00000030	 //MRSRB6
	.long 0x00000030	 //MRSRB7

3.2.3 修改串口波特率相關內容

    此時如果重新編譯燒寫ubootNOR Flash運行會輸出亂碼,是由於串口設置問題,串口是在調用第一個C函數board_init_f()函數裏面調用init_sequence數組裏的serial_init()函數進行串口設置的,serial_init()函數如下(drivers/serial/serial.c中)
   
    返回serial_init_dev()函數,參數爲UART_NR宏,該宏定義如下(drivers/serial/serial_s3c24x0.c中)
   
    在include/configs/jz2440.h中默認定義了CONFIG_SERIAL1宏,也就是使用2440的串口0,S3C24X0_UART0是一個枚舉變量0,所以傳入參數爲0。serial_init_dev()函數如下(drivers/serial/serial_s3c24x0.c中)

static int serial_init_dev(const int dev_index)
{
        /* 得到ULCON0控制寄存器基地址0x50000000 */
	struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);    

	/* 使能FIFO, 清空Tx/Rx的FIFO緩衝區 */
	writel(0x07, &uart->ufcon);
	writel(0x0, &uart->umcon);

	/* Normal,No parity,1 stop,8 bit */
	writel(0x3, &uart->ulcon);
	/*
	 * tx=level,rx=edge,disable timeout int.,enable rx error int.,
	 * normal,interrupt or polling
	 */
	writel(0x245, &uart->ucon);

#ifdef CONFIG_HWFLOW
	writel(0x1, &uart->umcon);	/* rts up */
#endif

	/* FIXME: This is sooooooooooooooooooo ugly */
#if defined(CONFIG_ARCH_GTA02_v1) || defined(CONFIG_ARCH_GTA02_v2)
	/* we need auto hw flow control on the gsm and gps port */
	if (dev_index == 0 || dev_index == 1)
		writel(0x10, &uart->umcon);
#endif
	_serial_setbrg(dev_index);    /*設置波特率*/

	return (0);
}

    該函數首先得到得到ULCON0控制寄存器基地址0x50000000,然後設置相關寄存器(配置FIFO、停止位、校驗位等),最後調用_serial_setbrg()函數,代碼如下(drivers/serial/serial_s3c24x0.c中)

void _serial_setbrg(const int dev_index)
{
	struct s3c24x0_uart *uart = s3c24x0_get_base_uart(dev_index);
	unsigned int reg = 0;
	int i;

	/* value is calculated so : (int)(PCLK/16./baudrate) -1 */
	reg = get_PCLK() / (16 * gd->baudrate) - 1;

	writel(reg, &uart->ubrdiv);
	for (i = 0; i < 100; i++)
		/* Delay */ ;
}

    由於串口時鐘使用的是PLCK,其中通過get_PCLK()函數獲得PLCK時鐘,get_PCLK()函數如下:

   
    由於之前設置的CLKDIVN寄存器爲5,所以get_PCLK()函數返回get_HCLK()/2,按照前面的CLKDIVN寄存器設置,get_HCLK()返回的應該是get_FCLK()/4,但是CONFIG_S3C2440宏卻沒有定義,執行的是else的代碼,所以需要我們添加CONFIG_S3C2440宏。
    將以下內容(include/configs/jz2440.h文件中)
   
改爲:
   

3.2.4 解決編譯錯誤

    此時重新編譯uboot會有如下錯誤:
   
    可以找到drivers/mtd/nand/s3c2410_nand.c文件中多處使用s3c2410_nand這個結構體,之前將CONFIG_S3C2410宏改爲CONFIG_S3C2440後,就沒有定義s3c2410_nand這個結構體了,如下(include/configs/jz2440.h文件中)
   
    所以暫時讓uboot不編譯s3c2410_nand.c文件,進入該文件的目錄下查看Makefile,如下:
   
    CONFIG_NAND_S3C2410宏定義也在jz2440.h,如下:
   
    直接來屏蔽CONFIG_CMD_NAND宏即可,因爲該宏下都是與2410相關的,所以修改如下(include/configs/jz2440.h文件中)
   
    此時重新編譯make distclean;make jz2440_config;make,還有如下錯誤:
   
    同樣是s3c2410_nand這個結構體被調用了,查看fs/yaffs2/Makefile,有如下代碼:
   
    屏蔽CONFIG_YAFFS2宏即可,修改如下(include/configs/jz2440.h文件中)
   
    此時重新編譯make distclean;make jz2440_config;make成功。

3.2.5 測試NOR Flash啓動

    燒寫試驗(使用韋東山老師的舊uboot進行燒寫快一點)
      1. 使用韋東山老師的舊u-boot.bin,下載到norflash開發板撥置nor啓動,輸入q進入命令行;
      2. 輸入 usb 1 30000000
使用usb下載到SDRAM上,1表示一直下載,直到完成
      3. 使用dnw軟件下載編譯好的u-boot.bin;
傳輸新的uboot.bin給usb
      4. 輸入 protect off all
關閉nor的寫保護
      5. 輸入 erase 0 7ffff
擦除nor上的 0~7FFFF地址內容,擦除長度7FFF==512kb,要大於uboot.bin才行
      6. 輸入 cp.b 30000000 0 80000
(將SDRAM上的新的uboot.bin,拷貝到nor flash 0地址上)
    重啓,此時串口輸出正確的報錯信息,如下所示:
   
PS:此時還不能修改jz2410.h中的指定鏈接地址的宏(CONFIG_SYS_TEXT_BASE,默認爲0),因爲調用第一個C函數board_init_f時調用init_sequence全局數組裏的函數,這是全局變量,裏面保存的是鏈接地址,而此時代碼又沒有拷貝到對應鏈接地址去(也就是重定位),跳到鏈接地址就會導致錯誤此時宏CONFIG_SYS_TEXT_BASE=0時鏈接地址與運行地址才相同)。

3.3 實現從NAND啓動

3.3.1 去掉 "-pie"選項

    uboot源碼在編譯執行arm-linux-ld時加了"-pie"選項,使得u-boot.bin裏多了"*(.rel*)", "*(.dynsym)",導致文件非常大;並且如果我們啓用了pie功能,那麼程序中的變量就會被抽出來放到代碼段後面的一些特殊段,而這些變量的地址肯定超過了4K,無法再訪問了。所以我們要去除這個pie選項,執行“grep  "\-pie" * -nR”查找如下:
   
    修改arch/arm/config.mk文件去掉pie選項如下:
   

3.3.2 添加init.c文件

    添加init.c文件到board/samsung/jz2440目錄下,代碼如下:

/* NAND FLASH控制器 */
#define NFCONF (*((volatile unsigned long *)0x4E000000))
#define NFCONT (*((volatile unsigned long *)0x4E000004))
#define NFCMMD (*((volatile unsigned char *)0x4E000008))
#define NFADDR (*((volatile unsigned char *)0x4E00000C))
#define NFDATA (*((volatile unsigned char *)0x4E000010))
#define NFSTAT (*((volatile unsigned char *)0x4E000020))

void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len);
void nand_read0(unsigned int addr, unsigned char *buf, unsigned int len);

int isBootFromNorFlash(void)
{
	volatile int *p = (volatile int *)0;
	int val;

	val = *p;
	*p = 0x12345678;
	if (*p == 0x12345678)
	{
		/* 寫成功, 是nand啓動 */
		*p = val;
		return 0;
	}
	else
	{
		/* NOR不能像內存一樣寫 */
		return 1;
	}
}

void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{	
	int i = 0;
	
	/* 如果是NOR啓動 */
	if (isBootFromNorFlash())
	{
		while (i < len)
		{
			dest[i] = src[i];
			i++;
		}
	}
	else
	{
		nand_read_ll((unsigned int)src, dest, len);
	}
}

/* 清bss段 */
void clear_bss(void)
{
	extern int __bss_start, __bss_end__;    //bss段的開始地址與結束地址
	int *p = &__bss_start;
	
	for (; p < &__bss_end__; p++)    /* 循環清0 */
		*p = 0;
}

/* 初始化NAND Flash */
void nand_init_ll(void)
{
#define TACLS   0
#define TWRPH0  1
#define TWRPH1  0
	/* 設置時序 */
	NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
	/* 使能NAND Flash控制器, 初始化ECC, 禁止片選 */
	NFCONT = (1<<4)|(1<<1)|(1<<0);	
}

static void nand_select(void)
{
	NFCONT &= ~(1<<1);	
}

static void nand_deselect(void)
{
	NFCONT |= (1<<1);	
}

static void nand_cmd(unsigned char cmd)
{
	volatile int i;
	NFCMMD = cmd;
	for (i = 0; i < 10; i++);
}

static void nand_addr(unsigned int addr)
{
	unsigned int col  = addr % 2048;
	unsigned int page = addr / 2048;
	volatile int i;

	NFADDR = col & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR = (col >> 8) & 0xff;
	for (i = 0; i < 10; i++);
	
	NFADDR  = page & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR  = (page >> 8) & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR  = (page >> 16) & 0xff;
	for (i = 0; i < 10; i++);	
}

static void nand_page(unsigned int page)
{
	volatile int i;
	
	NFADDR  = page & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR  = (page >> 8) & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR  = (page >> 16) & 0xff;
	for (i = 0; i < 10; i++);	
}

static void nand_col(unsigned int col)
{
	volatile int i;

	NFADDR = col & 0xff;
	for (i = 0; i < 10; i++);
	NFADDR = (col >> 8) & 0xff;
	for (i = 0; i < 10; i++);
}


static void nand_wait_ready(void)
{
	while (!(NFSTAT & 1));
}

static unsigned char nand_data(void)
{
	return NFDATA;
}

static int nand_bad(unsigned int addr)
{
	unsigned int col  = 2048;
	unsigned int page = addr / 2048;
	unsigned char val;

	/* 1. 選中 */
	nand_select();
	
	/* 2. 發出讀命令00h */
	nand_cmd(0x00);
	
	/* 3. 發出地址(分5步發出) */
	nand_col(col);
	nand_page(page);
	
	/* 4. 發出讀命令30h */
	nand_cmd(0x30);
	
	/* 5. 判斷狀態 */
	nand_wait_ready();

	/* 6. 讀數據 */
	val = nand_data();
	
	/* 7. 取消選中 */		
	nand_deselect();

	if (val != 0xff)
		return 1;  /* bad blcok */
	else
		return 0;
}

void nand_read_ll(unsigned int addr, unsigned char *buf, unsigned int len)
{
	int col = addr % 2048;
	int i = 0;
		
	while (i < len)
	{
		if (!(addr & 0x1FFFF) && nand_bad(addr)) /* 一個block只判斷一次 */
		{
			addr += (128*1024);  /* 跳過當前block */
			continue;
		}

		/* 1. 選中 */
		nand_select();
		
		/* 2. 發出讀命令00h */
		nand_cmd(0x00);

		/* 3. 發出地址(分5步發出) */
		nand_addr(addr);

		/* 4. 發出讀命令30h */
		nand_cmd(0x30);

		/* 5. 判斷狀態 */
		nand_wait_ready();

		/* 6. 讀數據 */
		for (; (col < 2048) && (i < len); col++)
		{
			buf[i] = nand_data();
			i++;
			addr++;
		}
		col = 0;
		/* 7. 取消選中 */		
		nand_deselect();
	}
}

    該文件函數說明如下:

1. isBootFromNorFlash()函數
    1.1 對於從NAND Flash啓動的情況,其開始4KB的代碼會被自動複製到CPU內部4K RAM中,這4K RAM被映射爲0地址,此時讀寫0地址就是讀寫RAM,因此可以直接讀寫0地址。
    1.2 對於從NOR Flash啓動的情況,NOR Flash的開始地址即爲0,此時讀寫0地址就是讀寫NOR Flash,因此必須通過一定的命令序列才能讀寫0地址。
    所以可以根據這兩點來分辨是從NAND Flash還是NOR Flash啓動:向地址0寫入一個數據,然後讀出來,如果發現寫入失敗的就是NOR啓動,否則就是NAND啓動
2. copy_code_to_sdram()函數
     不使用源碼的重定位,使用該函數進行重定位,不需要進行修改動態鏈接地址,比較簡單,首先判斷boot啓動方式,然後將對應代碼拷貝到指定地址去(後面會通過指定相同的鏈接地址與重定位地址,然後調用該函數將u-boot代碼拷貝到重定位地址)。
3. clear_bss()函數
    首先定義指針指向bss段的開始地址(鏈接地址)__bss_start,將該地址到bss段的結束地址(鏈接地址)__bss_end__全部清0。__bss_start__bss_end__是在鏈接文件u-boot.lds中的標號。
4. NAND Flash相關函數
    包含NAND Flash的設置、選中函數、發命令函數、發地址函數、判斷狀態函數、讀函數等。

    再將該文件添加進board/samsung/jz2440目錄下的Makefile,修改如下:
   

3.3.3 修改啓動流程

    在3.1 前言裏提到,不能按照源碼的啓動方式,在調用第一個C函數board_init_f之前要先重定位,所以修改start.S文件,將源碼調用board_init_f函數、重定位、清bss段、調用board_init_r函數代碼去掉或註釋,如下:

   
    ... ...
   

    將該段代碼(上圖中203行~354行)替換成如下代碼:

	ldr	sp, =(CONFIG_SYS_INIT_SP_ADDR)
	bic	sp, sp, #7 /* 8-byte alignment for ABI compliance */
	bl nand_init_ll    /* 初始化NAND Flash */

	mov r0, #0    
	ldr r1,_TEXT_BASE    /* _TEXT_BASE存在前4k空間裏,若直接使用CONFIG_SYS_TEXT_BASE編譯
                             器可能會將這個宏定義放在SDRAM上,此時SDRAM上還沒有代碼 */
	ldr r2, _bss_start_ofs
	
        /* 重定位,參數1爲重定位前代碼存放的起始地址=0,參數2爲重定位地址,參數3爲代碼重定位的結束地址 */
	bl copy_code_to_sdram    
	bl clear_bss    /* 清bss段 */
	ldr pc,=call_board_init_f    /* 跳到SDRAM中執行call_board_init_f */

call_board_init_f:
	ldr	r0,=0x00000000
	bl	board_init_f    /* 調用board_init_f */

.globl	call_board_init_r       /* 定義爲全局標號,在board_init_f函數中調用 */
call_board_init_r:              /* 從board_init_f中調用,得到參數r0=gd_t結構體指針,r1=重定位地址,r2=新的棧地址 */
	mov sp, r2		/* 重新設置棧 */
	bl board_init_r		/* 調用board_init_r,參數爲r0,r1 */

    對該段代碼說明如下:

1. 設置棧
    調用C函數nand_init_ll之前需要先設置棧,這時的sp=0x30000F80
2. 調用nand_init_ll函數
    調用之前添加的init.c文件中的nand_init_ll函數,重定位之前調用該函數,因爲uboot可能存放在NAND Flash中。
3. 重定位
    調用之前添加的init.c文件中的copy_code_to_sdram函數拷貝代碼到SDRAM中,傳入的參數1爲重定位前代碼存放的起始地址=0,參數2爲重定位地址,參數3爲代碼重定位的結束地址。
4. 清bss段
    調用之前添加的init.c文件中的clear_bss函數。
5. 跳到SDRAM中執行uboot程序
6. 調用board_init_f函數
7. 重新設置棧

    在board_init_f函數中得到新的棧地址r3,這裏設置新棧地址。
8. 調用board_init_r函數
   
board_init_f函數中得到參數r0=gd_t結構體指針,r1=重定位地址。

3.3.4 修改鏈接地址

    由於沒有使用源碼的動態修改鏈接地址代碼,所以在編譯U-Boot前修改鏈接地址,鏈接地址在start.S中定義如下:
       

    所以修改宏CONFIG_SYS_TEXT_BASE即可,修改如下(include/configs/jz2440.h文件中):
   

3.3.5 修改重定位地址

    源碼是通過在board_init_f函數中計算得到的重定位地址,我們這裏重定位地址必須與鏈接地址一樣,所以修改board_init_f()函數,將重定位地址設爲鏈接地址CONFIG_SYS_TEXT_BASE,修改如下(arch/arm/lib/board.c文件中)
   
    同時去掉源碼board_init_f函數裏的重定位函數,替換爲調用call_board_init_r,修改如下(arch/arm/lib/board.c文件中)
   

3.3.6 修改鏈接腳本

    把start.S、init.c(實現重定位、清bss等)、lowlevel_init.S(實現初始化SDRAM)等文件編譯時放在u-boot.bin的最前面,編譯時會在board/samsung/jz2440目錄下生成一個libsmdk2440.o文件,該文件是將jz2440單板目錄下的所有*.c、*S文件編譯後,連接成一個庫文件,只需要將該庫文件放在其他文件的代碼段之前即可,添加代碼“board/samsung/jz2440/libjz2440.o (.text)”到arch/arm/cpu/u-boot.lds文件(編譯後頂層目錄的u-boot.lds也會更新),修改如下:
   

    此時重新編譯make distclean;make jz2440_config;make成功。

3.3.7 測試NAND Flash啓動

    燒寫試驗(使用韋東山老師的舊uboot進行燒寫快一點)
      1. 使用韋東山老師的舊u-boot.bin,下載到norflash開發板撥置nor啓動,輸入q進入命令行;
      2. 輸入 usb 1 30000000
使用usb下載到SDRAM的30000000上,1表示一直下載,直到完成
      3. 使用dnw軟件下載編譯好的u-boot.bin;
傳輸新的uboot.bin給usb
      4. 輸入 nand erase 0 80000
擦除NAND Flash的0~80000地址
      5. 輸入 nand write 30000000 0 80000
將SDRAM上的新的uboot.bin,拷貝到NAND Flash 0地址上
    開發板撥置NAND啓動,此時串口輸出正確的報錯信息,如下所示:
   

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章