自己写bootloader笔记2---start.S分析

1、框架

(1)关看门狗//对2440来说,看门狗一上电是打开的,不关掉过3秒会复位整个开发板

(2)设置时钟//2440一上电时运行频率是12M,所以要让它能运行更快点

(3)初始化SDRAM (重定位时用到)

(4)重定位(bootloader比较小时,在nor flash上运行就可以了。如果bootloader比较大,要把它重定位到SDRAM)

执行main(其它复杂功能的代码用C函数实现)


2、程序


#define S3C2440_MPLL_200MHZ     ((0x5c<<12)|(0x01<<4)|(0x02))


#define MEM_CTL_BASE    0x48000000

//.text表示一个代码段

.text

//.global表示标号_start是全局标号

.global _start
_start:


/* 1. 关看门狗 */对2440来说,看门狗一上电是打开的,不关掉过3秒会复位整个开发板

//0x53000000是看门狗定时控制寄存器的地址,有等于号的ldr指令是 伪汇编指令,把0x53000000这个地址值赋给r0寄存器。

ldr r0, =0x53000000//直接赋值

//把立即数0赋给r1寄存器

mov r1, #0

//把数据r1保存到地址为r0的内存单元

str r1, [r0]


/* 2. 设置时钟 */2440一上电时系统运行频率是12M,所以要让它能运行更快点

//0x4c000014是时钟分频控制寄存器的地址

ldr r0, =0x4c000014
mov r1, #0x03;            //分频系数  FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1
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 */

//0x4c000004是MPLL配置寄存器的地址

对应的反汇编33f80024:e59f0084 ldrr0, [pc, #132]; 33f800b0 <sdram_config+0x38>

如果对于ldr伪汇编指令,0x4c000004这个数比较复杂,不能用mov指令表示,会吧这个数存在地址(pc + 132)处,也就是33f800b0处然后从(pc + 132)的存储器地址处取出数据,加载到寄存器r0中。

ldr r0, =0x4c000004


ldr r1, =S3C2440_MPLL_200MHZ
str r1, [r0]


/* 3. 初始化SDRAM */

//吧设置的SDRAM控制器的值赋给SDRAM的控制器里面去,MEM_CTL_BASE是控制器的基址

//MEM_CTL_BASE是控制器的基址,也就是 r0是控制器的基址

ldr r0, =MEM_CTL_BASE

//adr是伪汇编指令是读取相对地址值,也就是r1是 sdram_config标号的当前地址 

adr r1, sdram_config     /* sdram_config的当前地址 */

//由于用作SDRAM控制器的寄存器有13个,且每个地址的值占4个字节,也就是r3是最后一个寄存器的地址的内容的尾部字节地址。

add r3, r0, #(13*4)  //r3=r0+13*4

//这里1是局部标签,用于跳转

1:

//将地址为r1的内存单元数据读到r2中,然后r1+4

ldr r2, [r1], #4   

//将r2的数据保存到地址为r0的内存单元中,然后r0+4

str r2, [r0], #4

//比较r0和r3的地址值

cmp r0, r3

/*

1: ;A
cmp r0, #0
beq 1f ; r0==0那么向前跳转到B处执行
bne 1b ; 否则向后跳转到A处执行
1: ;B

*/

bne 1b


/* 4. 重定位 : 把bootloader本身的代码从flash复制到它的链接地址去 */

用C语言写代码要先设置栈,让栈指针指向一块空的内存(内存大小64M,基地址是0x30000000,让其指向最高内存),让其指向SDRAM的最高内存,栈是往下增长的

ldr sp, =0x34000000
:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长
//先调用nand flash的初始化函数,因为无论是nor flash启动还是nand flash启动,都会从nand flash读取内核到SDRAM
bl nand_init     //bl是相对跳转,根据当前指令找到nand_init函数的偏差值然后跳过去执行

传给C函数的参数r0 ,r1,r2

r0对应C函数的第一个参数src,是0地址(从nand flash的0地址或nor flash的0地址复制到SDRAM的链接地址)

mov r0, #0

r1对应dest,也就是链接地址,第一条指令_start的地址

ldr r1, =_start

r2对应len,也就是要拷贝的程序的长度,看链接脚本,_bss_start是bss端的起始地址,二进制文件的大小应为bss端的起始地址减去代码段的起始地址(链接地址)的值

ldr r2, =__bss_start

相当于r2=r2-r1

sub r2, r2, r1
把代码从nand flash 拷贝到SDRAM 

bl copy_code_to_sdram   (copy_code_to_sdram 函数参数来自于上面的r0,r1,r2

//把bss段占用的内存清零

bl clear_bss

/* 5. 执行main */ 其他代码使用C函数(初始化nand flash,从nand flash把内核读到内存)

bl main是相对跳转,若从nand flash的0地址开始执行,跳转的时候不能调到SDRAM里面去,因为是根据当前指令地址PC,PC值实际的值是A+8(跳转指令处的地址是A,以当前指令bl的bit[23:0]找到main函数的偏差值,然后跳转到(PC值+偏差值)的地址。

连接寄存器r14(LR),用于保存返回地址,用于main函数执行完后返回

ldr lr, =halt

该指令是从内存中的某个位置(main)读出数据并赋给

PC,同样依

赖当前PC的值,但是偏移量是那个位置(main)的连接地址(运行时的地址),所以

以用它实现从Flash到RAM的程序跳转

把main函数的地址赋给PC ,跳转到main函数执行

ldr pc, =main //可以从flash跳到SDRAM里面执行

//进入死循环,防止程序跑飞

halt:
b halt

//sdram_config标号下是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







  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值