您的位置 首页 被动

自己写bootloader

启动汇编文件:defineS3C2440_MPLL_200MHZ((0x5c12)

发动汇编文件

#define S3C2440_MPLL_200MHZ ((0x5c<<12)|(0x01<<4)|(0x02))
#define S3C2440_MPLL_400MHZ ((0x5c<<12)|(0x01<<4)|(0x01))
#define MEM_CTL_BASE 0x48000000
.text
.global _start
_start:
// 1、关看门狗 //
ldr r0, =0x53000000
mov r1, #0
str r1, [r0]
// 2、设置体系时钟 //
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 // 写入操控寄存器 //
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
// 3、初始化SDARM //
ldr r0, =MEM_CTL_BASE
adr r1, sdarm_config
add r3, r0,#(13*4)
1:
ldr r2, [r1],#4
str r2, [r0],#4
cmp r0, r3
bne 1b
// 4. 重定位 : 把bootloader自身的代码从flash复制到它的链接地址去 //
ldr sp, =0x34000000 //让SP指向最高的内存,栈是往下增加的
bl nand_init //即使是nor发动也要初始化nand flash,由于内核是存在nandflash上面的,还 //要去nandflash上面把内核读出来。
mov r0, #0
ldr r1, =_start //链接地址在链接脚本中注明,即程序运转时应该在的当地,0x33f80000
ldr r2, =__bss_start
sub r2, r2, r1
bl copy_code_to_sdram
bl clear_bss
// 5、履行main函数 //
ldr lr, =halt
ldr pc, =main //跳到main函数中运转,不必bl指令是由于该指令会跳到sdarm中履行
halt:
halt
sdarm_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 00x008C04F4 //REFRESH
.long 00x000000B1 //BANKSIZE
.long 00×00000030 //MRSRB6
.long 00×00000030 //MRSRB7
==============================================================
nand flash初始化文件:
// 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_init(void)
{
#define TACLS 0
#define TWRPH0 3
#define TWRPH1 0
// 设置时序 //
NFCONF = (TACLS<<12)|(TWRPH0<<8)|(TWRPH1<<4);
// 使能NAND Flash操控器, 初始化ECC, 制止片选 //
NFCONT = (1<<4)|(1<<1)|(1<<0);
}
void nand_select(void)
{
NFCONT &= ~(1<<1);
}
void del_select(void)
{
NFCONT |= (1<<1);
}
void nand_cmd(unsigned char cmd)
{
volatile int i=0;
NFCMMD = cmd;
for(i=0; i<10; i++);
}
void nand_wait_ready(void)
{
while (!(NFSTAT & 1));
}
unsigned char nand_data(void)
{
return NFDATA;
}
void nand_addr(unsigned int addr)
{
unsigned int col = addr % 2048;
unsigned int page = addr / 2048;
unsigned int i=0;
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++);
}
//addr—>0,下面的nand_read函数从nand flash的0地址读数据放在buf当地
//buf—->_start 即链接地址在链接脚本中注明,程序运转时应该在的当地,0x33f80000,把程序复制曩昔
//len—–>__bss_start-_start
void nand_read(unsigned int addr, unsigned char *buf, unsigned int len)
{
int col = addr 48;
int i = 0;
// 选中片选 //
nand_select();
while(i
{
// 发读指令 //
nand_cmd(0x00);
// 发地址 //
nand_addr(addr);
// 发读指令 //
nand_cmd(0x30);
// 判别状况 //
nand_wait_ready();
// 读数据 //
for(; (col<2048) && (i
{
buf[i] = nand_data();
i++;
addr++;
}
col = 0;
}
// 撤销片选 //
del_select();
}
//
为什么该函数能够判别成功,由于程序运转到此刻假如是nand发动,则上电后nand flash的前4K会被复制到片内SARM中运转,往地址0写入数据相当于往片内内存0地址写数据能够成功;可是假如是nor发动,上电后直接在nor的0地址运转,程序仍然在nor flash中,nor flash能够像内存相同读,可是无法像内存相同写,故直接向0地址赋值会失利。
//
int isBootFromNorFlash(void)
{
volatile int *p =(volatile int *)0;
int val=0;
val = *p;
*p = 0x1234ab;
if(*p == 0x1234ab)
{
//写成功,表明是nand flash发动
*p=val;
return 0;
}
else
{
//写失利,阐明是nor flash发动
return 1;
}
}
void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)
{
unsigned int i=0;
if(isBootFromNorFlash())//nor发动
{
while(i
{
dest[i] = src[i];
i++;
}
}
else //nand发动
{
nand_read(src,dest,len);
}
}
void clear_bss(void)
{
extern int __bss_start, __bss_end;
int *p = &__bss_start;
for(; p < &__bss_end; p++)
*p = 0;
}
#define PCLK 50000000 // init.c中的clock_init函数设置PCLK为50MHz
#define UART_CLK PCLK // UART0的时钟源设为PCLK
#define UART_BAUD_RATE 115200 // 波特率
#define UART_BRD ((UART_CLK / (UART_BAUD_RATE * 16)) – 1)
//
* 初始化UART0
* 115200,8N1,无流控
//
void uart0_init(void)
{
GPHCON |= 0xa0; // GPH2,GPH3用作TXD0,RXD0
GPHUP = 0x0c; // GPH2,GPH3内部上拉
ULCON0 = 0x03; // 8N1(8个数据位,无较验,1个中止位)
UCON0 = 0x05; // 查询方法,UART时钟源为PCLK
UFCON0 = 0x00; // 不运用FIFO
UMCON0 = 0x00; // 不运用流控
UBRDIV0 = UART_BRD; // 波特率为115200
}
//
* 发送一个字符
//
void putc(unsigned char c)
{
// 等候,直到发送缓冲区中的数据现已悉数发送出去 //
while (!(UTRSTAT0 & TXD0READY));
// 向UTXH0寄存器中写入数据,UART即主动将它发送出去 //
UTXH0 = c;
}
void puts(char *str)
{
int i = 0;
while (str[i])
{
putc(str[i]);
i++;
}
}
void puthex(unsigned int val)
{
// 0x1234abcd //
int i;
int j;
puts(“0x”);
for (i = 0; i < 8; i++)
{
j = (val >> ((7-i)*4)) & 0xf;
if ((j >= 0) && (j <= 9))
putc(0 + j);
else
putc(A + j – 0xa);
}
}
=================================================================
boot.c
#include “setup.h”
extern void uart0_init(void);
extern void nand_read(unsigned int addr, unsigned char *buf, unsigned int len);
extern void puts(char *str);
extern void puthex(unsigned int val);
static struct tag *params;
void setup_start_tag(void)
{
params = (struct tag *)0x30000100;
params->hdr.tag = ATAG_CORE;
params->hdr.size = tag_size (tag_core);
params->u.core.flags = 0;
params->u.core.pagesize = 0;
params->u.core.rootdev = 0;
params = tag_next (params);
}
void setup_memory_tags(void)
{
params->hdr.tag = ATAG_MEM;
params->hdr.size = tag_size (tag_mem32);
params->u.mem.start = 0x30000000;
params->u.mem.size = 64*1024*1024;
params = tag_next (params);
}
int strlen(char *str)
{
int i = 0;
while (str[i])
{
i++;
}
return i;
}
void strcpy(char *dest, char *src)
{
while ((*dest++ = *src++) != \0);
}
void setup_commandline_tag(char *cmdline)
{
int len = strlen(cmdline) + 1;
params->hdr.tag = ATAG_CMDLINE;
params->hdr.size = (sizeof (struct tag_header) + len + 3) >> 2;
strcpy (params->u.cmdline.cmdline, cmdline);
params = tag_next (params);
}
void setup_end_tag(void)
{
params->hdr.tag = ATAG_NONE;
params->hdr.size = 0;
}
int main(void)
{
void (*theKernel)(int zero, int arch, unsigned int params);
// 0. 帮内核设置串口: 内核发动的开端部分会从串口打印一些信息,可是内核一开端没有初始化串口 //
uart0_init();
// 1. 从NAND FLASH里把内核读入内存 //
nand_read(0x60000+64,(unsigned char *)0x30008000,0x200000);
//在u-boot顶用mtd指令能够看到u-boot的分区,uImage文件放在NAND FLASH的0x60000地址,可是 //uImage文件包含文件头+内核,文件头长64,故实践内核放在0x60000+64的当地,所以要从 //0x60000+64的当地读取内核到0x30008000,在SDRAM中内核存在哪里能够看内核的发动信息,一般 //0x30008000。
// 2. 设置参数 //
setup_start_tag();
setup_memory_tags();
setup_commandline_tag(“noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0”);
setup_end_tag();
// 3. 跳转履行 //
puts(“Boot kernel\n\r”);
theKernel = (void (*)(int, int, unsigned int))0x30008000;
theKernel(0, 362, 0x30000100); //第一个参数是0;第二个参数是机器ID,在内核里边用号码表明单板归于 //哪一类,内核里边会保存若干单板的ID;第三个参数表明参数的方位 //0x30000100,把参数的方位作为参数传给内核setup_start_tag函 //数中现已写死。
留意:第二个参数机器ID能够在u-boot函数:boot_jump_linux中unsigned long machid = gd->bd->bi_arch_number;中查找“bi_arch_number”找到:board/samsung/smdk2440/smdk2410.c中界说:
gd->bd->bi_arch_number = MACH_TYPE_SMDK2410;依据MACH_TYPE_SMDK2410的界说找到:
#define MACH_TYPE_S3C2440 362
//
* mov r0, #0
* ldr r1, =362
* ldr r2, =0x30000100
* mov pc, #0x30008000
//
// 假如一切正常, 不会履行到这儿 //
return -1;
}
===============================================================
链接文件:
SECTIONS {
. = 0x33f80000;
.text : { *(.text) }
. = ALIGN(4);
.rodata : {*(.rodata*)}
. = ALIGN(4);
.data : { *(.data) }
. = ALIGN(4);
__bss_start = .;
.bss : { *(.bss) *(COMMON) }
__bss_end = .;
}
===============================================================
注:
最简略的bootloader的编写过程:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 假如bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置”要传给内核的参数”
5. 跳转履行内核
改善:
1. 进步CPU频率, 200MHZ ==> 400MHZ
2. 发动ICACHE
存储地址解析:
u-boot文件存在nand flash/nor flash的0地址处,但正式在SDRAM中运转时的链接地址在链接脚本中现已注明,程序运转时应该在的当地:0x33f80000,因而从地址0x33f80000~0x34000000共512K用于寄存u-boot。
在外部flash中,u-boot(bootloader)、params、kernel、root等存储地址能够在u-boot发动后用mtd指令能够看到u-boot的分区,uImage文件放在NAND FLASH的0x60000地址,可是uImage文件包含文件头+内核,文件头长64,故实践内核放在0x60000+64的当地,所以要从0x60000+64的当地读取内核到0x30008000,在SDRAM中内核存在哪里能够看内核的发动信息,一般是0x30008000。
栈是往下增加的,即从高地址向低地址增加。
留意:nor flash发动的u-boot既能够烧写nor flash自身也能够烧写nand flash,可是假如是nand flash发动的话就没办法操作nor flash,由于nand flash发动的时分0地址对应片内内存,无法访问nor flash。

声明:本文内容来自网络转载或用户投稿,文章版权归原作者和原出处所有。文中观点,不代表本站立场。若有侵权请联系本站删除(kf@86ic.com)https://www.86ic.net/ziliao/beidong/263431.html

为您推荐

联系我们

联系我们

在线咨询: QQ交谈

邮箱: kf@86ic.com

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

返回顶部