mini2451 BootLoader

手上的板子是友善之臂的mini2451,最近刚开始学BootLoader这块的内容,想具体了解下如何实现的,加上友善之臂的Superboot没有开源,所以想自己写一个,在此记录下遇到的问题。

按照我的理解BootLoader主要就是做了三件事

1.初始化一些简单的外设

2.从nand flash 复制 kernel

3.启动kernel(跳转到kernel)。


我现在用的板子是mini2451,cpu是S3C2451 ,S3C2451支持两种启动方式,一是 OneNAND 启动,二是 IROM 启动。 Mini2451 并没有 OneNAND,所以它使用的是第二种启动方式。 S3C2451 含有一个 64K 的 IROM 和 8K 的 SRAM。

系统刚启动时,会自动运行 IROM 中的固化代码,进行一些通用的初始化,具体步骤包括:
1)关闭看门狗;
2) 初始化拷贝函数
3) 初始化栈;
4) 初始化时钟;
5) 初始化 icache;
6) 初始化堆;
7) 如果是 NAND Flash 作为启动设备的话,将 NAND Flash 从 0 地址处开始的 8K 代码拷贝到 SRAM的 0x40000000 处,

uboot源码一般都是大于8K的所以需要分为BootLoader1和BootLoader2,BootLoader1主要是初始化硬件,并且把完整的BootLoader代码复制到RAM。BootLoader2主要是一些指令的处理以及跳转到kernel。


我的代码也是参考uboot的启动步骤

1.设置svc32模式

2.关闭看门狗

3.关闭中断

4.关闭mmu

5.初始化SDRAM

6.设置栈

7.设置时钟 (C语言)

8.初始化NAND Falsh(C语言)

9.从NAND Falsh复制kernel 到 SDRAM

10.设置kernel启动参数

11.跳转到kernel启动地址

因为我这个只有一个启动kernel的功能,所以整体代码小于8KB,不需要像uboot那样分为两个BootLoader,直接使用2451芯片在IROM固化的代码就可以把整个BootLoader完整拷贝到SRAM。


我按照上面的步骤编写代码,首先编写了前4个步骤,并且增加了一个led的初始化,这样可以让我调试代码,知道运行到什么位置了。汇编代码如下


.text
.global _start
_start:
	b reset
	ldr pc,=_undefined_instructions
	ldr pc,=_software_interrupt
	ldr pc,=_prefetch_abort
	ldr pc,=_data_abort
	ldr pc,=_nop_opeartion
	ldr pc,=_irq
	ldr pc,=_fiq

_undefined_instructions: .word undefined_instructions
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_nop_opeartion: .word nop_opeartion
_irq: .word irq
_fiq: .word fiq

undefined_instructions:
	nop

software_interrupt:
	nop

prefetch_abort:
	nop

data_abort:
	nop

nop_opeartion:
	nop

irq:
	nop

fiq:
	nop

reset:
	bl set_svc_mode //设置svc32模式
    	bl disable_watchdog  //关闭看门狗
	bl disable_interrupt	//关闭中断
	bl disable_mmu	//关闭mmu
	bl init_led	//初始化led
	bl delay
led:	bl bright_led
	bl delay
	bl dark_led
	bl delay
	b led

set_svc_mode:
	mrs r0,cpsr
	bic r0,r0,#0x1f
	orr r0,r0,#0xdf
	msr cpsr,r0
	mov pc, lr

#define WTCON 0x53000000
disable_watchdog:
	ldr r0,=WTCON
    	mov r1,#0x00
	str r1,[r0]
	mov pc, lr
	
#define INTMSK_g1 0x4a000008
#define INTMSK_g2 0x4a000048
disable_interrupt:
	mvn r1,#0x00
	ldr r0,=INTMSK_g1
	str r1,[r0]
	ldr r0,=INTMSK_g2
	str r1,[r0]
	mov pc, lr

disable_mmu:
	mcr p15,0,r0,c7,c7,0  //失效 ID cache
	mrc p15,0,r0,c1,c0,0	//读取CP15  register 1
	bic r0, r0, #0x00000005 //   101   Dcache  MMU
	mcr p15,0,r0,c1,c0,0 //写入cp15  register1
	mov pc, lr

#define GPCON 			0x56000000
#define GPDAT 			0x56000004
#define GPIO_OUT(GPIO)    	(0x01<<(GPIO))
#define LED_1_GPIO  		GPIO_OUT(25)
#define LED_2_GPIO  		GPIO_OUT(26)
init_led:
	ldr r0,=GPCON
	ldr r1,[r0]
	bic r1,r1,#LED_1_GPIO
	bic r1,r1,#LED_2_GPIO
	str r1,[r0]
	mov pc,lr

bright_led:
	ldr r0,=GPDAT	
	ldr r1,[r0]
	bic r1,r1,#LED_1_GPIO
	bic r1,r1,#LED_2_GPIO
	str r1,[r0]
	mov pc,lr

dark_led:
	ldr r0,=GPDAT
        ldr r1,[r0]
        orr r1,r1,#LED_1_GPIO
        orr r1,r1,#LED_2_GPIO
        str r1,[r0]
        mov pc,lr

delay:
	mov r0, #0x100000
delay_loop:
	cmp r0, #0
	sub r0, r0, #1
	bne delay_loop
	mov pc, lr


在linux下编译成功后下载到板子,发现led不会闪烁,把除了led初始化以及闪烁的代码之外都注释掉,编译下载后发现可以闪烁,定位错误应该在前面初始化有问题,于是一行行开始解注释,找到设置svc32模式后会出现程序跑飞问题,可以吧cpsr寄存器的数据读出来,只要一写进去

msr cpsr,r0

就会跑飞,这个问题暂时没找到是什么原因,所以后面的代码我是直接把这个注释掉的。有知道原因的可以告诉我下

注释掉  svc32模式 ,led可以闪烁,说明到这里的代码暂时是没问题的。

后面添加 

初始化SDRAM

设置栈

设置时钟 (C语言)

初始化NAND Falsh(C语言)

并且添加了串口初始化调试代码。


把串口代码烧写到板子后,发现在电脑端收到的数据有问题,我初始化串口是115200波特率 8位数据位 1位停止位  无奇偶校验 ,串口助手也没配置错,但是数据都是错的,我以为是时钟设置有问题,检测了下代码是按照数据手册来配置的,时钟这块出问题应该不大,还是去检查串口初始化的代码

void uart_init(void)
{
	// 配置引脚  
	GPHCON = (GPHCON & ~0xffff ) | 0xaaaa;
		
	// 设置数据格式等  
	ULCON0 = 0x3;  					// 数据位:8, 无校验, 停止位: 1, 8n1 
	UCON0  = 0x5;  					// 时钟:PCLK,禁止中断,使能UART发送、接收 
	UFCON0 = 0x01; 					// FIFO ENABLE
	UMCON0 = 0;						// 无流控
	
	// 设置波特率  
	// DIV_VAL = (PCLK / (bps x 16 ) ) - 1 = (66500000/(115200x16))-1 = 35.08
	// DIV_VAL = 35.08 = UBRDIVn + (num of 1’s in UDIVSLOTn)/16 
	UBRDIV0   = 35;
	UDIVSLOT0 = 0x1;
}
对比数据手册发现了一个可能的问题 



按照串口的公式算出来 

DIV_VAL = (PCLK / (bps x 16 ) ) - 1 = (66500000/(115200x16))-1 = 35.08
UBRDIV0   = 35;
UDIVSLOT0 = 0x1;
35.08 

整数给UBRDIV0

小数按照手册数据对照后写入UDIVSLOTO

按照手册来看 0.08应该是0x01没错,但是这个是要求时钟是准确的66.5MHz,有偏差的话小数就有可能不是0.08,加上我以前写单片机程序时也有遇到这种问题, 于是我把UDIVSLOTO值修改为0x02,编译烧写后,串口数据打印正常。


到这里前面的初始化基本就完成了,剩下从NAND Falsh复制kernel到SDRAM中以及跳转到kernel。

从NAND Falsh中赋值kernel首先要知道kernel在NAND Falsh中的烧写地址,因为SuperBoot不是开源的所以我也不太清楚友善之臂把kernel下载在什么位置,幸好kernel启动过程中会打印一些调试信息,其中就有kernel在nand flash中的地址。


于是按照这里给出的nand falsh中的地址 以及kernel所占空间的大小,把整个kernel从nand falsh中复制到SDRAM中设置的地址,我设置的地址为0x30008000 ,SDRAM1的地址是从0x30000000到0x38000000 ,为什么不直接复制到0x30000000,是因为kernel的参数我要放在前面的空间,所以直接让kernel从0x30008000开始。


跳转到kernel需要传入三个参数

具体内容可以去看看这篇博客      http://blog.csdn.net/u013686019/article/details/22417165

第一个规定是0

第二参数需要传入和kernel约定的id,我在调试信息中找了半天终于找到一个疑似ID的东西


暂且先把这个作为ID传进去

第三个参数就是传给kernel的命令行,kernel的参数也是通过调试信息找到的 



到这里代码基本是写完了,于是编译烧写到板子运行 ,提示我ID有错



我发现他提示这里也给了我一个16进制的ID 0x00000695,我把这个ID直接传入就可以了

ID错了有两种方式解决,可以百度下。


编译烧写,成功启动kernel


中间还遇到了一些其它问题,不过很多都是其它原因导致了,有时候代码下载进去,需要把整块板子断电才可以成功运行,不然一直会打印错误信息

关于链接器地址以及实际地址这块也纠结了一段时间,百度后大致知道是什么东西了,因为整个BootLoader除了最后跳转到kernel用的是绝对跳转,其它地方都是相对跳转,所以链接器地址在这里不用太多的关注。

代码已经上传到github,下面是地址 

https://github.com/ypckyin/BootLoader_2451

git@github.com:ypckyin/BootLoader_2451.git



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值