最简单的bootloader的编写步骤:

1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH

2. 如果bootloader比较大,要把它重定位到SDRAM

3. 把内核从NAND FLASH读到SDRAM

4. 设置"要传给内核的参数"

5. 跳转执行内核





改进:

1. 提高CPU频率, 200MHZ ==> 400MHZ

2. 启动ICACHE

重定位
分为nor启动和nand启动

int isBootFromNorFlash(void)

{

volatile int *p = (volatile int *)0;

int val;





val = *p;

*p = 0x12345678;

if (*p == 0x12345678)

{       

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

//nand启动时,0地址对应内存,内存是可以写的

*p = val;//回复原来的值

return 0;

}

else

{

/* NOR不能像内存一样写 */

return 1;

}

}



TACLS、TWRPH0 、TWRPH1的设置

nand_read
读页数据读到页寄存器
nand结构







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段结束地址
}


(嵌入式开发)自己写bootloader之编写第一阶段的更多相关文章

  1. (嵌入式开发)自己写bootloader之编写第二阶段

    内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M.     ...

  2. 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)

    作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42462795 转载请著名出处 相关资源下载 :  -- u-boo ...

  3. 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410

    作者 : 万境绝尘 转载请著名出处 终于拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...

  4. 谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写

    本文来自 网易云社区 . 一.本文讲的是什么问题? 在开发 iOS 应用时,基本都会遇到个人中心.设置.详情信息等页面,这里截取了某应用的详情编辑页面和个人中心页面,如下: 我们以页面结构的角度考虑这 ...

  5. 嵌入式系统烧写uboot/bootloader/kernel的一般方法

    嵌入式系统烧写uboot/bootloader/kernel的一般方法   本文介绍了在嵌入式系统中烧写uboot/bootloader/kernel 的一般方法,以及如果uboot或者内核出现错误, ...

  6. 用arduino的uno开发板为nano板子烧写bootloader

    这篇文章,是为了记录下某宝上淘到的一个没有bootloader的nano开发板的历程(比较坑),自己搜索资料而记录的. 如果没有bootloader,板子就不能接收上传的程序,什么也干不了. 烧写bo ...

  7. 应聘linux/ARM嵌入式开发岗位

    **************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...

  8. 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )

    [嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )     一. 内存 ...

  9. 【4412嵌入式开发板学习笔记】认识uboot

    转自迅为讨论群:http://www.topeetboard.com 重要说明:这份笔记不是4412开发配套的,是我在网上看视频的时候下载上课老师的笔记后修改的.所以我试了一下笔记上的uboot命令, ...

随机推荐

  1. InnoDB引擎索引大观

    InnoDB是mysql处理OLTP(online transcation process)类型业务的存储引擎.为了加快数据查询速度.InnoDB引擎提供了丰富的索引实现. 1. 索引的分类 索引能够 ...

  2. Razor小案例

    Model using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace ...

  3. amaze ui使用简介

    amaze ui使用简介 amaze UI 类似于bootstrap 不过比bootstrap更加轻量级 可以用来开发响应式网站,并且是移动优先的,针对移动设备开发的网站可以考虑使用这个框架 css中 ...

  4. Android引入library失败的可能原因

    eclipse环境,引入library一直失败 谷歌到的原因是 Windows下 目标工程必须和引用工程在同一磁盘盘符下 然后我将要引入的library项目移动和现在项目同一磁盘下,引入成功 至于为什 ...

  5. Kinect 开发 —— 显示骨骼用户插件

    public partial class SkeletonViewer : UserControl { private readonly Brush[] _SkeletonBrushes = new ...

  6. call(),apply()和bind()的详解使用:

    obj.call(thisObj, arg1, arg2, ...); obj.apply(thisObj, [arg1, arg2, ...]); 两者作用一致,都是把obj(即this)绑定到th ...

  7. 洛谷 P1256 显示图像

    P1256 显示图像 题目描述 古老的显示屏是由N×M个像素(Pixel)点组成的.一个像素点的位置是根据所在行数和列数决定的.例如P(2,1)表示第2行第1列的像素点.那时候,屏幕只能显示黑与白两种 ...

  8. OpenCASCADE Job - 深圳鞋博士

    鞋博士 鞋博士经过8年沉淀,在鞋类工业4.0全流程平台上积累了相当的技术实力,获投资商亲睐. 新的一年,在投资商协助下,将踏上新的征途,因此诚邀您加盟顶层技术合伙人. 如果您具备以下实力,我们期待您的 ...

  9. android-Preference 风格调整

    <CheckBoxPreference android:defaultValue="false" android:layout="?android:attr/pre ...

  10. GestureDetector- 滑屏手势方式实现

    今天做的项目中,需要使用滑屏来调出一个界面,经过自己的尝试,结合网上的方法,成功实现了. 代码如下 package com.example.text; import android.app.Activ ...