(嵌入式開發)自己寫bootloader之編寫第一階段
阿新 • • 發佈:2019-01-28
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段結束地址
}