写在前面:

我的博客已迁移至自建服务器:博客传送门,CSDN博客暂时停止,如有机器学习方面的兴趣,欢迎来看一看。

此外目前我在gitHub上准备一些李航的《统计学习方法》的实现算法,目标将书内算法全部手打实现,欢迎参观并打星。GitHib传送门

正文

boot是为了启动内核,本质上也就是一个裸板程序,就是为了引导内核的启动。所以打算自己写一个boot,功能只有引导内核启动。

首先是汇编的代码段,是为了关闭看门狗,设置时钟以及代码的重定位,这些都是在main函数之前执行的。之前学习单片机的时候,我们只看到main函数,实际上是main之前的执行步骤都被包起来了。

整个汇编文件的开头要写上

.text                @这是为了表示这是一个代码段
.global _start
_start:

第一步:关闭看门狗,2440是默认关门狗打开的,如果不关闭看门狗,三秒钟后板子会自动重启

/* 关看门狗 */ldr    r0,     = 0x53000000       /* 看门狗的寄存器地址,通过芯片手册可以查看 */mov r1,    #0              /* 把0放入r1*/str  r1,  [r0]               /* 把r1中的0赋给看门狗,即关闭看门狗 */

第二步:设置时钟,2440板子的晶振是12M(也可以使用16M或其他的),如果不设置时钟去倍频,板子是以12M的速度跑的,这里是设置分频系数以及设置板子的频率为400M
//经过实测,boot在200M和400M的情况下,都需要6秒才能启动内核,速度有点慢,所以使用了ICACHE提高速度,使用ICACHE以后,启动内核只需要2秒,可以接受

#define  S3C2440_MPLL_400MHZ     ((0x5c<<12)|(0x01<<4)|(0x01))
/* 设置时钟 */ldr   r0,     = 0x4c000014mov r1,    #0x05           //FCLK:HCLK:PCLK=1:4:8str  r1,     [r0]ldr     r0,     =0x4c000004ldr     r1,     =S3C2440_MPLL_400MHZstr    r1,     [r0]/******以下为启动ICACHE*******/
/* 启动ICACHE */mrc    p15, 0, r0, c1, c0, 0orr   r0, r0, #(1<<12)mcr    p15, 0, r0, c1, c0, 0
/******* 以上为启动ICACHE *****/

启动Icahe的原理是这样:每次CPU去RAM读代码,很费力,ICACHE就是把这段代码copy到片子里的一个区域,CPU直接去读这块区域就行了,而且这块区域的读速度比RAM快多了。就好像每天都有一份快递,自己每天去取费时费力,直接让快递员送门口,就省很多事。

第三步:初始化SDRAM,代码需要在SDRAM中执行,所以需要初始化

/* 初始化SDRAM */ldr    r0,     =MEM_CTL_BASE      //设置SDRAM寄存器的首地址adr     r1,     sdram_config            /*SDRAM每个寄存器的配置值 */add  r3,     r0, #(13 * 4)           //SDRAM寄存器的尾地址1:ldr r2,     [r1], #4                //把寄存器的配置值写入r2,然后r1地址加4字节,定位到sdram_config的下一个配置值str   r2,     [r0], #4                //r2里的值写入到r0地址,也就是SDRAM的第一个寄存器,然后寄存器地址加四字节,指
//向下一个寄存器cmp r0,    r3                  //当前寄存器地址和寄存器尾地址比较,如果不一致说明还没配置完,跳转到1继续循环bne   1b/*放在文件末尾,这是关于SDRAM的每个寄存器的配置值*/
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

第四步:代码重定位,因为代码是存在NOR FLASH 或者 NAND FLASH里面的,CPU要将代码移入SDRAM中才可以使用

/* 重定位 */ldr sp, =0x34000000        //因为nand初始化比较复杂,使用C语言实现,如果要使用C语言,需要设置堆栈指针bl nand_init              //初始化nand,其实如果使用的是nor,完全可以不初始化nand,这里是考虑到不知道使用的是nor还是nand,所以把nand初始化了,之后会判断使用的是什么flash。之所以不用初始化nor,是因为CPU可以直接读normov r0,  #0              //设置copy_code_to_sdram的参数,r0是该函数第一个参数,r1是第二个,r2是第三个,很好理解ldr     r1, =_startldr     r2, = __bss_startsub   r2, r2, r1bl copy_code_to_sdram     //调用C函数,实现将代码copy至SDRAMbl clear_bss              //清除BSS段,未初始化或初始化为0的变量都存在这里,所以需要清零

bss段挺好玩的,这样的,如果你程序里面设置了很多变量,初始值都为0,那么程序一开始一个一个去赋0效率太低了,所以所有初始值为0的变量,都保存在bss代码段里,程序启动前将整个代码段清零就行了,就很方便

第五步:执行main函数

/* 执行main */ldr  lr, =halt      //设置main函数的返回地址,其实boot启动内核以后直接就死掉了,根本不会返回,这里还是写一下返回地址,main返回以后去执行下面的halt段,不断地死循环,防止出问题ldr pc, =main      //指针定位到main函数halt:b halt

/* 重定位过程中使用的C函数 挑了几个稍微重要点的,别的都不写了,没什么意思*/
/* nand初始化,没什么意思,就不多写了 */

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

/* 复制代码段到SDRAM*/
//这里就是判断是NOR还是NAND,因为它们的特性不同,导致nand可读可写,NOR只能读,所以测试的时候就随便找个地址,往里写一个数,再往外读,如果值被修改了,说明是nand,反之是nor

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_read((unsigned int)src, dest, len);}
}
/* main函数 */
int main (void)
{void (*theKernel)(int zero, int arch, unsigned int params);        //申明内核的指针/*0:  设置串口,内核启动的开始部分会从串口打印一些信息,但是内核一开始并没有初始化串口,提前初始化好,免得内核没有串口用 */uart0_init();/* 1.从NAND FLASH 里把内核读入内存 */puts("Copy kernel from nand\n\r");nand_read(0x60000 + 64, (unsigned char *)0x30008000, 0x200000);    /*读出地址是根据板子看的,mtd命令可以看板子的分区,找到kernel分区就行了*//* 存入地址在内核启动的时候会显示, 大小也是,板子用的内核是1.8M ,这里给了2M 空间 *//* 2. 设置参数 */
//这里设置各种传给内核的参数,因为启动内核以后boot就死了,没法和内核面对面交流,boot将一些命令放在一块区域里,内核启动以后去这个地址读就行了puts("Set boot params\n\r");setup_start_tag();setup_memory_tag();setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");setup_end_tag();/* 3. 跳转执行 */puts("Boot kernel\n\r");theKernel = ((void (*)(int, int, uint))0x30008000);theKernel(0, 362, 0x30000100);        //第一个参数写的就是0,我就直接写0了,第二个参数是ID,2440的ID是这个,第三个参数是参数存放的地址,内核直接去这个地址读就可以了/* 如果一切正常,不会执行到这里,也就是内核启动以后不会再回到boot */puts("Error!\n\r");return -1;
}/* 给内核传入的各种命令 */
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_tag (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);
}
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;
}

自己写一个最简单的bootloader_jz2440相关推荐

  1. Java入门知识,写一个最简单java程序

    本文目录 一.Java语言的简介 二.写一个最简单Java程序 1.Notepad配置 2.最简单的Java程序 3.代码分析 4.编译与编译常见错误 5.执行java程序 一.Java语言的简介 0 ...

  2. 用JAVA写一个最简单的飞翔的小鸟

    如果你想写一个最简单的飞翔的小鸟的 Java 程序,可以先了解 Java 的图形绘制功能.Java 提供了一个叫做 Graphics 的图形绘制类,可以用来绘制图形.填充颜色.画线等. 你可以通过创建 ...

  3. 利用taichi写一个最简单的SPH(光滑粒子动力学)

    简介 参考doyub Kim那本<Fluid Engine Development>写一个最简单的弱可压SPH. 目前有BUG, 粒子太散了 效果展示 CSDN有图片大小限制,大概就这样 ...

  4. python写一个很简单的Atm机系统,使用pickle模块和json模块来保存数据

    我做的是一个很简单的Atm系统,很多都没约束条件 目的: 用面向对象思维来处理 运用文件的读写 文件的读写用pickle模块和json模块来处理 pickle模块: pickle模块处理数据功能很强大 ...

  5. Unity自学第一天, 新手小白进!写一个最简单的脚本让方块旋转起来

    Unity自学入坑第一天 今天开始自学unity,如何让场景中的基本方块开始旋转起来,一个非常简单的小sample,作为入门,本人纯纯新手小白,之后会继续更新所有自学记录 在场景中添加一个方块,左边空 ...

  6. 如何用初级的JavaWeb知识写一个较简单的网站(一)

    本人JavaWeb初学者,这是学校一门课程的期末作业,之前已经写了不少了,但是由于一点误操作,导致项目有点崩盘,加上心态不是很好,所以把整个项目全部删除重做,这里记录一下我写整个项目的全过程. 环境是 ...

  7. 安装汇编环境,写一个最简单的窗口程序

    用汇编写一个窗口程序 1.安装汇编所需的环境 1.下载masm32 下载地址 可以安装到D:\masm32目录下 2.配置环境变量 include = D:\masm32\include lib = ...

  8. cmd写java程序_用cmd写一个最简单的Java程序

    一,准备: 1.确保电脑中装有eclipse软件并且确保配置好环境变量 (1)环境变量配置方法: 特别提示:jdk和eclipse保存的路径不能有中文字符 1.打开我的电脑--属性--高级--环境变量 ...

  9. 用Python写一个最简单的计时秒表

    在Windows上是自带计时秒表的,这玩意一般用不上.但对于做视频相关操作的工程师有一个巨大的作用,就是用摄像头拍屏幕上的秒表然后把摄像头得到的图像经过压缩->传输->解析->重新显 ...

最新文章

  1. 在java中的ascii_在Java中绘制ASCII艺术
  2. phpcms避免字段值重复
  3. Blazor University (1)介绍 - 什么是 Blazor?
  4. 前端学习(1355) 子模板
  5. TypeScript基本数据类型
  6. 鸿蒙手表怎么安装,万物皆可鸿蒙,这次鸿蒙系统将应用到手表上了
  7. Java--Socket客户端,服务端通信
  8. pta l2-1紧急救援(Dijkstra)
  9. (转)DeepMind 的下一场博弈:用机器学习颠覆资产管理?
  10. NFine.Framework框架去除cookie后门
  11. 在css中arial,Helvetica或Arial作为CSS中的基本字体?
  12. R语言入门-常用的向量运算
  13. PT8023W(单触控双输出 LED 调光 IC)
  14. 怎么做浪漫表白二维码?给喜欢的人制作一个二维码
  15. iphone xr xs_如何关闭iPhone X,XS和XR
  16. ES Module 和 Commonjs 的区别
  17. linux网卡slave状态,生产环境中linux bonding 主备模式slave网卡切换的方法
  18. NCUT 数据库基础 铁路购票系统
  19. Android 优质精准的用户行为统计和日志打捞方案
  20. 孙向晖-《领域驱动设计》读书心得交流会-UMLChina讲座-实录

热门文章

  1. LeetCode ZigZag Conversion
  2. LeetCode K-diff Pairs in an Array
  3. UVa10672 - Marbles on a tree(BFS)
  4. 如何实现electron多页面间通信
  5. setDAta 字符串拼接
  6. python将dict中的unicode打印成中文
  7. 20162313苑洪铭 第二周作业
  8. 需要在Emulator上模拟来电 效果
  9. (转载)文件系统与数据库系统的区别
  10. android之多媒体篇(二)