您的位置:首页 > 其它

自己写bootloader笔记3---init.c分析

2016-08-24 19:07 393 查看
启动流程

(1)nor flash启动

把bootloader烧写到norflash的0地址,重定位时,norflash可以像读内存一样读,把0地址后的bootloader代码复制到SDRAM的链接地址(运行地址)上去

(2)nand flash启动

一上电,前面4K代码会自动复制到2440内部的0地址开始的4K RAM,然后从0地址开始执行 ,CPU看到的0地址是片内4K RAM的0地址,这4K代码用于把bootloader从flash复制到SDRAM的链接地址。

/* 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))

/* GPIO */

#define GPHCON              (*(volatile unsigned long *)0x56000070)

#define GPHUP               (*(volatile unsigned long *)0x56000078)

/* UART registers*/

#define ULCON0              (*(volatile unsigned long *)0x50000000)

#define UCON0               (*(volatile unsigned long *)0x50000004)

#define UFCON0              (*(volatile unsigned long *)0x50000008)

#define UMCON0              (*(volatile unsigned long *)0x5000000c)

#define UTRSTAT0            (*(volatile unsigned long *)0x50000010)

#define UTXH0               (*(volatile unsigned char *)0x50000020)

#define URXH0               (*(volatile unsigned char *)0x50000024)

#define UBRDIV0             (*(volatile unsigned long *)0x50000028)

#define TXD0READY   (1<<2)

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

//nor flash启动和nand flash启动的判断:利用nor flash的特点,nor flash可以像内存一样读,但不可以像内存一样写.nand flash启动,0地址对应片内内存,可读可写。因此可从是否能写0地址来判断

int isBootFromNorFlash(void)

{

//p指针指向0地址

volatileint *p = (volatile int *)0;
int val;
//先读取0地址的值

val = *p;

//修改0地址的值

*p = 0x12345678;

//如果修改成功,证明是nand flash启动,否则是nor flash启动

if (*p == 0x12345678)
{

/* 写成功, 是nand启动 */

恢复修改前的0地址的值

*p = val;
return 0;
}
else
{
/* NOR不能像内存一样写 */
return 1;
}

}

//从flash拷贝代码到SDRAM , src是源地址,dest是目的地址,lens是要拷贝的长度

src, dest, len这三个参数是从汇编程序中传过来的(看start.S)

void copy_code_to_sdram(unsigned char *src, unsigned char *dest, unsigned int len)

{
int i = 0;

/* 如果是NOR启动 */
if (isBootFromNorFlash())

{

循环拷贝代码,其中len是以字节为单位的,因而每次拷贝一字节,每拷贝完一次i值累加

while (i < len)
{
dest[i] = src[i];
i++;
}
}
else
{

//nand_init();

从源地址src读到目的地址dest, 读取的长度为len

nand_read((unsigned int)src, dest, len);
}

}

//清除bss段

void clear_bss(void)

{

把__bss_start, __bss_end定义为外部的整型变量

extern int __bss_start, __bss_end;

定义指针指向bss段的首地址

int *p = &__bss_start;
循环把bss段中变量赋为0,直到bss段的结尾
for (; p < &__bss_end; p++)
*p = 0;

}

//因为无论是nor flash启动还是nand flash启动,都会从nand flash读取内核到SDRAM 

nand flash的初始化函数参考http://blog.csdn.net/qingkongyeyue/article/details/52132868

nand_init函数

void nand_init(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);

}

//nand flash的片选、取消片选、发命令、发地址、空闲等待,读数据函数参考http://blog.csdn.net/qingkongyeyue/article/details/52132195

//片选

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;//1页的哪1个地址
unsigned int page = addr / 2048;//哪1页
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;

}

//nand flash读函数参考http://blog.csdn.net/qingkongyeyue/article/details/52086786

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();

}

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

}

//输出16进制数

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

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: