自己写一个bootloader基本步骤如下:
1.上电关看门狗避免复位
-
.text
-
.global _start
-
_start:
-
/* 1.关看门狗 */
-
ldr r0, =0x53000000 //WDTCON
-
mov r1, #0
-
str r1, [r0]
2.设置时钟,单板上电复位后几毫秒即可设置时钟
-
/* 2.设置时钟 到时候可以改变一下时钟试一试, pclk:hclk:fclk=1:2:4*/
-
ldr r0, =0x4c000014
-
mov r1, #3
-
str r1, [r0]
-
/* 3.因为HDIVN不是0,所以要改变cpu模式从fast bus to asynchronous */
-
mrc p15, 0, r1, c1, c0, 0
-
orr r1, r1, #0xc0000000
-
mcr p15, 0, r1, c1, c0, 0
-
/* 4.设置MPLLCON时钟,便可确定FCLK HCLK PCLK */
-
ldr r0, =0x4c000004
-
ldr r1, =MPLLCON_SET_200MHZ
-
str r1, [r0]
3.初始化sdram,之后我们需要把代码从flash中读取到sdram中去运行
-
/* 5.初始化sdram */
-
ldr r0, =MEM_CTL_BASE
-
adr r1, sdram_config
-
add r3, r0, #52
-
0:
-
ldr r2, [r1], #4
-
str r2, [r0], #4
-
cmp r0, r3
-
bne 0b
-
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
4.设置好堆栈,之后都是运行C了,将堆栈指针sp指向一块不用的内存即可
ldr sp, =0x34000000
5.初始化串口,可以打印一些有用的信息
-
void uart_init(void)
-
{
-
GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0
-
GPHUP = 0x0c; // GPH2,GPH3内部上拉
-
-
ULCON0 = 3;//115200 1 stopbit 8bit no parity
-
UCON0 = 5;//查询方式 clock pclk
-
UFCON0 = 0;//不用fifo
-
UMCON0 = 0;//不用流控
-
UBRDIV0 = UART_BRD;
-
}
6.初始化nandflash
-
void nand_init(void)
-
{
-
#define TACLS 0
-
#define TWRPH0 1
-
#define TWRPH1 0
-
-
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
-
/* 初始化ECC 禁止片选 */
-
NFCONT = (1<<4)|(1<<1)|(1<<0);
-
}
-
void nand_read(unsigned char* dest, unsigned long addr, unsigned long len)
-
{
-
unsigned long i = 0;
-
unsigned short col = addr%2048;
-
-
nand_select();
-
-
while (i < len)
-
{
-
nand_cmd(0x00);
-
nand_addr(addr);
-
nand_cmd(0x30);
-
wait_for_ready();
-
while (col < 2048)
-
{
-
dest[i++] = nand_data();
-
col++;
-
addr++;
-
}
-
col = 0;
-
}
-
-
nand_diselect();
-
}
7.拷贝代码从flash到sdram去运行
-
mov r0, #0
-
ldr r1, =_start
-
ldr r2, =__bss_start
-
sub r2, r2, r1
-
-
bl copy_code_2_sram
8.清bss段
-
void clear_bss(void)
-
{
-
extern int __bss_start, __bss_end;
-
int *p = &__bss_start;
-
-
for (; p < &__bss_end; p++)
-
{
-
*p = 0;
-
}
-
}
9.main中代码负责:
1.将内核从flash读入sdram固定位置(0x30008000)
2.设置启动参数,memory commandline,启动参数存放位置(0x30000100)
3.跳转到内核特定位置去执行
-
puts("in main func\r\n");
-
void (*thekernel)(int zero, int arch, unsigned long params);
-
/* 将内核从flash中读入sdram */
-
puts("copy kernel to sdram\r\n");
-
nand_read((unsigned char *)0x30008000 ,0x60000 + 64, 0x200000);
-
/* 设置启动参数 */
-
setup_start_tag();
-
setup_memory_tag();
-
setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");
-
setup_end_tag();
-
/* 跳转去执行 */
-
puts("boots kernel\r\n");
-
thekernel = (void(*)(int, int, unsigned long))0x30008000;
-
thekernel(0, 362, 0x30000100)
编写的过程中主要被以下几个地方给难住了
1:copy code to sdram 起始地址目的地址给搞混了,起始地址应该是flash中的0地址,
拷贝的目的地址应该是_start 长度为 _bss_start-_start
2: clear bss还没有完全搞明白
3:几个地址搞明白,目的地址_start 0x30008000是代码应该允许的位置,0x60000是flash中内核所在的位置(带头部),
0x30000100是启动参数存放
地址,所以应该把内核搬运到0x30008000-64的位置去。
阅读(1613) | 评论(0) | 转发(0) |