1. 程式人生 > >(嵌入式開發)自己寫bootloader之編寫第一階段

(嵌入式開發)自己寫bootloader之編寫第一階段

bootloader的最終目的是啟動核心,而在啟動核心前要進行一系列的初始化:
關閉看門狗、改變系統時鐘、初始化儲存控制器、重定位程式碼(將更多的程式碼複製到記憶體中去)等,
然後將核心從nand flash讀到SDRAM中,為核心傳遞啟動引數,跳到相應的地址啟動核心。
<pre name="code" class="objc" style="widows: 1;">#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))
#define MEM_CTL_BASE    0x48000000
<pre name="code" class="objc" style="widows: 1;">.text                 //指定了後續編譯出來的內容放在程式碼段【可執行】;
.global _start  //告訴編譯器後續跟的是一個全域性可見的名字【可能是變數,也可以是函式名】;
_start:            /*_start是一個函式的起始地址,也是編譯、連結後程序的起始地址。由於程式是通過載入器來載入的,
                       必須要找到 _start名字的函式,因此_start必須定義成全域性的,以便存在於編譯後的全域性符合表中,
                       供其它程式【如載入器】尋找到。*/
1. 關閉看門狗
向WTCON暫存器WTCON中寫入零
彙編程式碼:
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
C程式碼:(呼叫C程式碼之前必須先設定棧,即sp指標,指令mov sp, #4096)
#define WTCON (*(volatile unsigned long *)0x53000000)
void disable_watch_dog(void)
{
WTCON = 0; // 關閉WATCHDOG很簡單,往這個暫存器寫0即可
}
2. 設定系統時鐘
彙編程式碼:
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))

ldr r0, =0x4c000014
// mov r1, #0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
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 /* 寫入控制暫存器 */
/* MPLLCON = S3C2440_MPLL_200MHZ */
ldr r0, =0x4c000004
ldr r1, =S3C2440_MPLL_400MHZ
str r1, [r0]

C程式碼:
void clock_init(void)
{
// LOCKTIME = 0x00ffffff; // 使用預設值即可
CLKDIVN = 0x03; // FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
// 潛入彙編的寫法,語法上的要求。
/* 如果HDIVN非0,CPU的匯流排模式應該從“fast bus mode”變為“asynchronous bus mode” */
__asm__(
"mrc p15, 0, r1, c1, c0, 0\n" /* 讀出控制暫存器 */ 
"orr r1, r1, #0xc0000000\n" /* 設定為“asynchronous bus mode” */
"mcr p15, 0, r1, c1, c0, 0\n" /* 寫入控制暫存器 */
);
/*******************************************************************
 *                 時鐘初始化函式
 * 對於MPLLCON暫存器,[19:12]為MDIV,[9:4]為PDIV[1:0]為SDIV
 * 計算公式如下:
 * S3C2410 : MPLL(FCLK)=(m*fin)/(p*2^s)
 * S3C2440 : MPLL(FCLK)=(2*m*Fin)/(p*2^s)
 * 其中:m=MDIV+8;    p=PDIV+2; s=SDIV
 * 設定CLKDIVN,令分頻比為:FCLK:HCLK:PCLK=1:2:4
 * 由於開發板的輸入時鐘為12MHz,而且設定MDIV PDIV SDIV分別為
 * S3C2410 : MDIV=0x5C      PDIV=0x04    SDIV=0x00
 * S3C2440 :MDIV=0x12   PDIV=0x01    SDIV=0x02
 * 則有:FCLK=200MHz      HCLK=100MHz   PCLK=50MHz
*******************************************************************/

MPLLCON = S3C2440_MPLL_200MHZ; /* 現在,FCLK=200MHz,HCLK=100MHz,PCLK=50MHz */

}
//3. 初始化SDRAM
彙編程式碼:
ldr r0, =MEM_CTL_BASE
adr r1, sdram_config /* sdram_config的當前地址 */
add r3, r0, #(13*4)
1:
ldr r2, [r1], #4//將r1地址中的內容存到r2中,同時r1=r1+4
str r2, [r0], #4//將r2中的值存到r0所指定的地址中, 同時r0=r0+4
cmp r0, r3 // 比較r0和r1的值
bne 1b // bne 表示如果不相同跳轉的標號為1的地方,後面跟一個b表示跳轉到前面的1標號,如果跳轉到後面去將b改為f即可

sdram_config:
.long 0x22011110 //BWSCON
.long 0x00000700 //BANKCON0
.long 0x00000700 //BANKCON1
.long 0x00000700 //BANKCON2
.long 0x00000700 //BANKCON3 
.long 0x00000700 //BANKCON4
.long 0x00000700 //BANKCON5
.long 0x00018005 //BANKCON6
.long 0x00018005 //BANKCON7
.long 0x008C04F4 // REFRESH
.long 0x000000B1 //BANKSIZE
.long 0x00000030 //MRSRB6
.long 0x00000030 //MRSRB7
C程式碼:

void memsetup(void)
{
volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

/* 這個函式之所以這樣賦值,而不是像前面的實驗(比如mmu實驗)那樣將配置值
* 寫在陣列中,是因為要生成”位置無關的程式碼”,使得這個函式可以在被複制到
* SDRAM之前就可以在steppingstone中執行
*/
/* 儲存控制器13個暫存器的值 */
p[0] = 0x22011110; //BWSCON
p[1] = 0x00000700; //BANKCON0
p[2] = 0x00000700; //BANKCON1
p[3] = 0x00000700; //BANKCON2
p[4] = 0x00000700; //BANKCON3 
p[5] = 0x00000700; //BANKCON4
p[6] = 0x00000700; //BANKCON5
p[7] = 0x00018005; //BANKCON6
p[8] = 0x00018005; //BANKCON7

/* REFRESH,
* HCLK=12MHz: 0x008C07A3,
* HCLK=100MHz: 0x008C04F4
*/ 
p[9] = 0x008C04F4;
p[10] = 0x000000B1; //BANKSIZE
p[11] = 0x00000030; //MRSRB6
p[12] = 0x00000030; //MRSRB7
}
/*
* 初始化SDRAM後,必須重新設定棧,且將sp指標記憶體的指向最高,因為棧是重高地址向低地址向下增長的,
* 即使用命令ldr sp, =0x34000000 (將0x34000000賦值給sp指標,ldr是一條偽指令,當0x34000000數字很大的時候不能轉換為一個立即數的時候,會通過幾條彙編指令來完成) 
*/
4. 初始化nand控制器
bl nand_init // 彙編呼叫C函式

/* 初始化NAND Flash */
void nand_init(void)
{
// 這三個值結合S3C2440手冊和nand flash手冊設定時序
#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);
}

//5. 重定位程式碼
// 彙編中呼叫C函式時,r1傳遞函式的第一個引數,r2傳遞函式的第二個引數,r3傳遞函式的第三個引數
mov r0, #0//從0地址開始複製
ldr r1, =_start // 來自彙編程式碼的第一行
// .text
// .global _start
// _start:
ldr r2, =__bss_start // __bss_start 來自連結指令碼
sub r2, r2, r1

bl copy_code_to_sdram

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_init();
nand_read((unsigned int)src, dest, len);
}
}

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

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

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

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

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

unsigned char nand_data(void)
{
return NFDATA;
}

void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int col = addr % 2048;
int i = 0;

/* 1. 選中 */
nand_select();

while (i < len)
{
/* 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();
}

連結指令碼為:
SECTIONS {
. = 0x33f80000; // 起始連結地址
.text : { *(.text) } // 程式碼段
. = ALIGN(4); // 四位元組對齊

.rodata : {*(.rodata*)} // 只讀資料段
. = ALIGN(4);

.data : { *(.data) } // 資料段
. = ALIGN(4);

__bss_start = .; //bss段開始地址
.bss ALIGN(4) : { *(.bss) *(COMMON) }
__bss_end = .; //bss段結束地址
}