(嵌入式开发)自己写bootloader之编写第一阶段
最简单的bootloader的编写步骤:
1. 初始化硬件:关看门狗、设置时钟、设置SDRAM、初始化NAND FLASH
2. 如果bootloader比较大,要把它重定位到SDRAM
3. 把内核从NAND FLASH读到SDRAM
4. 设置"要传给内核的参数"
5. 跳转执行内核
改进:
1. 提高CPU频率, 200MHZ ==> 400MHZ
2. 启动ICACHE
{
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;
}
}
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之编写第一阶段的更多相关文章
- (嵌入式开发)自己写bootloader之编写第二阶段
内核编译(make)之后会生成两个文件,一个Image,一个zImage,其中Image为内核映像文件,而zImage为内核的一种映像压缩文件,Image大约为4M,而zImage不到2M. ...
- 【嵌入式开发】 Bootloader 详解 ( 代码环境 | ARM 启动流程 | uboot 工作流程 | 架构设计)
作者 : 韩曙亮 博客地址 : http://blog.csdn.net/shulianghan/article/details/42462795 转载请著名出处 相关资源下载 : -- u-boo ...
- 【嵌入式开发】向开发板中烧写Linux系统-型号S3C6410
作者 : 万境绝尘 转载请著名出处 终于拿到板子了, 嵌入式开发正式开启. 板子型号 : 三星 S3C6410 基于ARM11, 指令集基于arm6指令集; 为毛不是 Cortext A9的板子; 烧 ...
- 谈谈iOS开发如何写个人中心这类页面--静态tableView页面的编写
本文来自 网易云社区 . 一.本文讲的是什么问题? 在开发 iOS 应用时,基本都会遇到个人中心.设置.详情信息等页面,这里截取了某应用的详情编辑页面和个人中心页面,如下: 我们以页面结构的角度考虑这 ...
- 嵌入式系统烧写uboot/bootloader/kernel的一般方法
嵌入式系统烧写uboot/bootloader/kernel的一般方法 本文介绍了在嵌入式系统中烧写uboot/bootloader/kernel 的一般方法,以及如果uboot或者内核出现错误, ...
- 用arduino的uno开发板为nano板子烧写bootloader
这篇文章,是为了记录下某宝上淘到的一个没有bootloader的nano开发板的历程(比较坑),自己搜索资料而记录的. 如果没有bootloader,板子就不能接收上传的程序,什么也干不了. 烧写bo ...
- 应聘linux/ARM嵌入式开发岗位
**************************************************************** 因为发在中华英才和智联招聘没有人采我所以我 在这里发布我的个人简历希望 ...
- 【嵌入式开发】裸机引导操作系统和ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 )
[嵌入式开发]ARM 内存操作 ( DRAM SRAM 类型 简介 | Logical Bank | 内存地址空间介绍 | 内存芯片连接方式 | 内存初始化 | 汇编代码示例 ) 一. 内存 ...
- 【4412嵌入式开发板学习笔记】认识uboot
转自迅为讨论群:http://www.topeetboard.com 重要说明:这份笔记不是4412开发配套的,是我在网上看视频的时候下载上课老师的笔记后修改的.所以我试了一下笔记上的uboot命令, ...
随机推荐
- js---04 属性 this
var oUl = document.getElementsByTagName('ul')[0]; var aLi = oUl.getElementsByTagName('li'); window.o ...
- Android全局退出的两种方法
第一种方法参考<第一行代码>78页 建立一个ActivityCollector类,提供静态方法addActivity,fininshAll(以list为容器) 然后我们建立的Activit ...
- windows 常见环境变量(%AppData%、%TEMP%、%TMP%)
set 命令查看全部环境变量: %AppData%(应用程序数据).%TEMP%(临时文件夹).%TMP%(临时文件夹) .%LocalAppData%(应用程序本地数据)三个环境变量: C:\Use ...
- 扩展: 简介pyinstaller: py文件压缩成exe文件
写在开始: 后缀 .py 的文件, 必须在装有python解释器, 和相对应的模块下才能运行. 对于装13来说,比较不方便. 如果人家电脑上也装上了python解释器, 说不好一眼就看穿了 自己写的 ...
- 分享一下10个常用jquery片段
1. 图片预加载 (function($) { var cache = []; // Arguments are image paths relative to the current page. ...
- django第三方库
1. django_celery_beat 作用:网页端配置定时任务 注意:1,需要迁移表格 2.需要注册app python3 manage.py makemigrations python3 ma ...
- 洛谷 P1957 口算练习题
洛谷 P1957 口算练习题 题目描述 王老师正在教简单算术运算.细心的王老师收集了i道学生经常做错的口算题,并且想整理编写成一份练习. 编排这些题目是一件繁琐的事情,为此他想用计算机程序来提高工作效 ...
- 03006_DOS操作数据乱码解决
1.我们在dos命令行操作中文时,会报错 insert into sort(sid,sname) values(2,"电视机"); ERROR 1366 (HY000): Inco ...
- Android-Volley网络通信框架(二次封装数据请求和图片请求(包含处理请求队列和图片缓存))
1.回想 上篇 使用 Volley 的 JsonObjectRequest 和 ImageLoader 写了 电影列表的样例 2.重点 (1)封装Volley 内部 请求 类(请求队列,数据请求,图片 ...
- 【吴节操点评】中国企业SaaS应用深谙未来者寥寥数几 两极分化将加剧
数年前,在国外企业级应用如火如荼的时候.国内却是一片空白.而现在企业SaaS应用市场,包含用友.金蝶.东软在内的三巨头.已经有数十家之多.相比美国3000亿美元的企业应用三巨头来说,中国企业应用前三甲 ...