历史上的今天
返回首页

历史上的今天

今天是:2025年04月17日(星期四)

正在发生

2020年04月17日 | 自己写一个最简单的bootloader_jz2440

2020-04-17 来源:eefocus

写在前面:

我的博客已迁移至自建服务器:博客传送门,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, = 0x4c000014

mov r1, #0x05 //FCLK:HCLK:PCLK=1:4:8

str r1, [r0]


ldr r0, =0x4c000004

ldr r1, =S3C2440_MPLL_400MHZ

str r1, [r0]


/******以下为启动ICACHE*******/

/* 启动ICACHE */

mrc p15, 0, r0, c1, c0, 0

orr 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可以直接读nor


mov r0, #0 //设置copy_code_to_sdram的参数,r0是该函数第一个参数,r1是第二个,r2是第三个,很好理解

ldr r1, =_start

ldr r2, = __bss_start

sub r2, r2, r1

bl copy_code_to_sdram //调用C函数,实现将代码copy至SDRAM

bl 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 nandnr");

nand_read(0x60000 + 64, (unsigned char *)0x30008000, 0x200000);  /*读出地址是根据板子看的,mtd命令可以看板子的分区,找到kernel分区就行了*/

/* 存入地址在内核启动的时候会显示, 大小也是,板子用的内核是1.8M ,这里给了2M 空间 */

/* 2. 设置参数 */

//这里设置各种传给内核的参数,因为启动内核以后boot就死了,没法和内核面对面交流,boot将一些命令放在一块区域里,内核启动以后去这个地址读就行了

puts("Set boot paramsnr");

setup_start_tag();

setup_memory_tag();

setup_commandline_tag("noinitrd root=/dev/mtdblock3 init=/linuxrc console=ttySAC0");

setup_end_tag();


/* 3. 跳转执行 */

puts("Boot kernelnr");

theKernel = ((void (*)(int, int, uint))0x30008000);

theKernel(0, 362, 0x30000100); //第一个参数写的就是0,我就直接写0了,第二个参数是ID,2440的ID是这个,第三个参数是参数存放的地址,内核直接去这个地址读就可以了


/* 如果一切正常,不会执行到这里,也就是内核启动以后不会再回到boot */

puts("Error!nr");


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;

}

推荐阅读

史海拾趣

DMC Tools公司的发展小趣事

DMC Tools公司高度重视人才培养和引进。公司定期举办各种培训活动,提高员工的技能水平和综合素质。同时,公司还积极引进高端人才,为公司的技术创新和产品研发提供有力支持。这些人才为公司的发展注入了新的活力,推动了公司的持续进步。

GAPTEC Electronic GmbH & Co. KG公司的发展小趣事

背景:在闪存市场取得成功后,Galaxy Microelectronics开始探索多元化发展道路。

发展:公司决定进入DRAM市场,并投入大量资源进行技术研发。经过几年的努力,Galaxy Microelectronics成功推出了多款高性能DRAM产品,进一步丰富了其产品线。此外,公司还开始涉足SSD固态硬盘领域,推出了多款具有竞争力的产品,进一步巩固了其在存储市场的地位。

Denyo Europa Gmbh公司的发展小趣事

Denyo Europa Gmbh公司在电子行业中崭露头角,始于一次技术创新的突破。公司研发团队经过数年的努力,成功开发出一种新型高效能电池技术,这种电池不仅容量大,而且充电速度快,安全性能高。这一技术的推出,迅速赢得了市场的认可,也为公司带来了可观的收益。公司因此逐渐在电子行业中建立了自己的技术领先地位。

Elite Semiconductor Products Inc公司的发展小趣事

随着全球对环保和可持续发展的关注度不断提高,Elite也积极响应这一趋势。公司注重环保理念在生产过程中的贯彻实施,采用环保材料和节能技术,减少生产过程中的污染排放。同时,Elite还积极参与环保公益活动,推动环保理念的普及和实践。这些努力不仅提升了公司的社会形象,也为公司的可持续发展奠定了坚实基础。

Frequency Electronics Inc公司的发展小趣事

高频电子在精确时间和频率生成技术方面一直处于世界领先地位。公司不断投入研发,推出了一系列高精度、高性能的定时和频率控制产品,如铷蒸气原子振荡器、温度稳定的石英晶体振荡器等。这些产品被广泛应用于商业、政府、军事等多个领域,包括卫星通信、指挥控制系统、电子战系统等。高频电子的技术创新不仅提升了自身竞争力,还推动了整个行业的发展。

捷茂微(GATEMODE)公司的发展小趣事

高频电子深知技术创新是企业持续发展的核心动力。因此,公司一直保持着对研发的持续投入,不断推出新产品、新技术和新服务。同时,高频电子还积极与高校、科研机构等合作,共同开展前沿技术的研发和应用。这些努力使得高频电子在技术创新方面始终保持着领先地位,为公司的长期发展奠定了坚实的基础。

综上所述,高频电子通过战略转型、技术创新、全球化布局、多元化业务布局以及持续投入研发等举措,实现了从一家小型国防合同制造商到全球领先的高科技供应商的华丽蜕变。这些故事不仅展现了高频电子的发展历程和成就,也为我们揭示了企业成功背后的关键因素。

问答坊 | AI 解惑

发两个常在我U盘中的小软件

第一个是电阻衰减网络计算器,射频工程师调试电路时用起来很方便。 第二个是我自己编的计算反射参数S11与驻波比关系的小软件…

查看全部问答>

力传感器怎么跟ARM9连接

请问一下FSS系列的力传感器怎么跟ARM9开发板连接?要不要放大电路的?如果要的话,放大电路要怎样设计?…

查看全部问答>

wince c# 画图问题

系统:wince 5.0 开发工具: vs C# 2008 SDK :.net compact framework 3.5 问题描述:              开始在windows xp 执行这段代码没有问题,能画图:          &nb ...…

查看全部问答>

PIC16单片机的C编译起哪儿下载?如何与MAPLAB一起使用?

找了很长时间,网上都没有PIC16单片机得C编译器(很多都是DEMO)。还有,如果有了这个单片机,如何在MAPLAB中使用这个编译器?…

查看全部问答>

Vxworks下USB盘识别问题

我用的是Tornado2.2,Vxworks版本是5.5,包含USB模块, 新建立工程后,工程中加入USB支持,选中下列组件: Hardware->Buses->USB Hosts->USB Host Stack Hardware->Buses->USB Hosts->OHCI Hardware->Buses->USB Hosts->UHCI Hardware->Buses- ...…

查看全部问答>

有关51单片机低频频率计的问题

我想问一下低频频率计用C语言编写,一秒种是如何采集3次信号的 用测周期法测量.…

查看全部问答>

【请教贴】关于system C

近段时间接触到了System C,也是用作Soc的设计语言,但是设计效率据说比HDL高很多倍,不知道咱们坛子上有没有人在用这个,想了解下System C目前的应用情况,希望用到的朋友们能谈一谈感受。…

查看全部问答>

ActiveSync连接电脑

小弟刚开始接触arm,遇到了两个奇怪的问题,一个是我在电脑上用ActiveSync4.5(这个软件是在360上下的啊)连接开发板时说什么也连不上啊,我使用usb连接的,开发板是飞凌的2440,开发板跑的是wince5.0,电脑上是XP系统(用DNW连接开发板时一切都好 ...…

查看全部问答>

LM3S811库函数使用

IntEnable()里面的参数是什么 怎么用的   手册上好像没说…

查看全部问答>