基于S3C2440的bootloader详细分析

本文详细分析了基于S3C2440的Bootloader启动第一阶段代码,涉及汇编语言、SDRAM设置、处理器模式切换、堆栈初始化、中断向量表构建等内容。通过包含头文件、设置SDRAM自刷新、初始化堆栈、代码模式判断、中断处理程序入口地址等步骤,阐述了Bootloader如何启动和管理不同工作模式。
摘要由CSDN通过智能技术生成

s3c2440的Bootloader启动第一阶段代码

 

1、包含头文件

GET option.inc   

GET memcfg.inc

GET 2440addr.inc

/*

注释:

汇编程序指令不能顶格写

*.inc代表头文件,汇编语言专用

GET 伪指令用于将一个源文件包含到当前的源文件中,并将被包含的源文件在当前位置进行汇编处理。可以使用 INCLUDE代替 GET

汇编程序中常用的方法是在某源文件中定义一些宏指令,用 EQU 定义常量的符号名称,用 MAP 和FIELD 定义结构化的数据类型,然后用 GET 伪指令将这个源文件包含到其他的源文件中。GET 伪指令只能用于包含源文件,包含目标文件需要使用 INCBIN 伪指令

*/

2、设置SDRAM自刷新

BIT_SELFREFRESH   EQU  (1<<22)

/*这句话的意思是:“BIT_SELFREFRESH”就是“将刷新控制寄存器的第22位置1”的意思。SDRAM:同步动态随机存储器,相当于c里面 #define BIT_SELFREFRESH  (1<<22);就是第22位置1的意思。

动态存储器(Dynamic RAM)都存在刷新问题。这里主要采用自动刷新方式。每隔一段时间向SDRAM发一条刷新命令。

EQU伪指令的作用类似于C语言中的#define.用于为一个常量定义名称

*/

/*

1ARM 种模式,用户模式,快速中断模式,中断模式,管理模式,中止模式,未定义模式和系统模式(咱们这里只用了6种);

2)系统堆栈的初始化主要是给各个处理器模式分配堆栈空间。堆栈是为中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进行恢复,以保证原来的程序正确运行

*/

/*CPSR的后8位进行设置*/

;Pre-defined constants   //预定义6中工作模式

USERMODE    EQU     0x10              //用户模式 00010000

FIQMODE      EQU  0x11              //快速中断模式 00010001

IRQMODE     EQU     0x12              //中断模式 00010010

SVCMODE     EQU     0x13              //监管模式 00010011

ABORTMODE   EQU  0x17              //异常中断模式 00010111

UNDEFMODE   EQU  0x1b              //未定义模式 000011011

MODEMASK    EQU 0x1f               // 模式掩码,多出来的 000011111

NOINT         EQU     0xc0         //取消中断 11000000

 

;The location of stacks        //设置6中工作模式的堆栈的起始地址

/*定义各种模式下使用的堆栈起始地址,_STACK_BASEADDRESS是由option.inc定义的,在option.inc中定义了_STACK_BASEADDRESS  EQU  0x33ff8000*/

//堆栈是为中断或程序跳转服务的,当发生中断或程序跳转时,需要将当前处理器的状态及一些参数保持在堆栈中,当中断处理完毕以后或程序执行完后返回时,再将堆栈保存的现场数据进行恢复,以保证原来的程序正确运行

//堆栈初始化及ARM工作模式切换

UserStack EQU (_STACK_BASEADDRESS-0x3800) ;0x33ff4800 ~

SVCStack EQU (_STACK_BASEADDRESS-0x2800) ;0x33ff5800 ~

UndefStack EQU (_STACK_BASEADDRESS-0x2400) ;0x33ff5c00 ~

AbortStack EQU (_STACK_BASEADDRESS-0x2000) ;0x33ff6000 ~

IRQStack EQU (_STACK_BASEADDRESS-0x1000) ;0x33ff7000 ~

FIQStack EQU (_STACK_BASEADDRESS-0x0) ;0x33ff8000 ~

 

 

/*下面一段要遇到的问题提前注释如下:

1tasm.exe是什么程序,有什么作用???

   就是THUMB汇编编译文件

2CONFIG是在哪里定义了???

   CONFIG为汇编器的内置变量,内置变量有很多

   如果要汇编成ARM指令则值为32,如果要汇编成THUMB指令则值为16

3[]| 的用法怎样??? 

    "["表示"if","|"表示"else","]"表示"endif"

ARM板上电时处于ARM状态,故无论指令为ARM集还是THUMB集,都先强制成ARM集,待init.s初始化完成后,再转换;下面这一段是为了统一目前的处理工作状态和软件编译方式。

*/

//检查在tasm.exe里是否设置了采用THUMB16位)代码,判断是不是thumb指令

;Check if tasm.exe(armasm -16 ...@ADS 1.0) is used.

GBLL    THUMBCODE        

/*定义THUMBCODE全局变量(逻辑型),注意,其中,GBLL为伪指令,定义全局逻辑型变量*/

[ {CONFIG} = 16                

//如果发现是用16位代码的话,表示目前使用的是tasm.exe编译,thumb 编译方式

THUMBCODE  SETL  {TRUE}       

//THUMBCODE设置为TURESETL也为伪指令,用于给一个逻辑变量赋值

    CODE32     //CODE32;指示汇编编译器后面的指令为32位的ARM指令, 

伪指令本身并不进行程序状态的切换.要进行状态切换,可以使用BX指令操作(暂且把处理器设置成为ARM模式,以方便初始化)

  |            //否则是ARM模式

THUMBCODE SETL  {FALSE}

]

/*

这段代码的意思:说白了整个预编译段就是为了说明要ARM的当前状态和编译器的状态统一而已。这里他只是给了个标准的例子,实际大多数情况不需要啦。但是上面的config==16只说明你编译器当前使用的是TASM.EXE,但现在由于 Thumb-capable ARM processors start in ARM state(注意是MCU的状态和编译器无关),所以要使两者一致编译出来的代码才能被运行,使用CODE32 使编译器也处于ARMASM.EXE状态,CODE16/32只会影响编译器,只有BX会改变processors state,只要两者一致就好。这段代码根据是否使用了TASM.EXE设置了全局变量THUMBCODE 的值,但是由于ARM PROCESSOR处于ARM state,所以后面都要用CODE32标识指令。

*/

// 宏定义MOV_PC_LR,作用:子程序返回

// THUMB模式和ARM模式的子程序返回的方法是不同的,所以这里判断是不//THUMB模式。

MACRO            //宏定义

MOV_PC_LR           //宏名

  [ THUMBCODE       //判断THUMBCODE是否为真;

    bx  lr    

//lr最低位为1,则转入THUMB指令;lr最低位为0,则转入ARM指令。//参考资料“常用汇编指令及汇编P27

/*如果目标地址是THUMB指令,在ARM模式中,要用BX指令转THUMB,是跳到THUMB指令,并转换模式。lr为链接寄存器,也可以使用其他寄存器,例如RM*/

|

    mov pc,lr   

/*否则,就是目标地址是ARM模式,就直接把函数返回地址赋给PCPC为程序计数器,作用是存放下一条要执行的指令的首地址;*/

  ]

MEND  //宏定义结束

 

//宏定义MOVEQ_PC_LR,作用:带相等条件判断的子程序返回。与宏定义MOV_PC_LR类似

/*注意:这里多出来的“EQ”是指CPSR程序状态寄存器的Z是否等于1,如果等于1则执行bxmov等指令,作用于上个宏类似。*/

MACRO             //宏定义

MOVEQ_PC_LR         //宏名

  [ THUMBCODE      

        bxeq lr

  |

    moveq pc,lr

  ]

MEND

//宏定义结束

//下面就是开始建立中断向量表的过程

下面这个宏是用于第一次查表过程的实现中断向量的重定向,你会发现在_ISR_STARTADDRESS=0x33FF_FF00里定义的第一级中断向量表是采用型如Handle***的方式的。而在程序的ENTRY(程序开始处)采用的是b Handler***的方式.在这里Handler***就是通过HANDLER这个宏和Handle***进立联系的这种方式的优点就是真正定义的向量数据在内存空间里,而不是在ENTRY处的ROM(FLASH)空间里这样,我们就可以在程序里灵活的改动向量的数据了.其中HANDLER是一个宏,用于查找中断处理程序的入口地址.这些地址存放在由HandleXXX指向的表项中,该表定位在RAM高端,基地址为_ISR_STARTADDRESS.假如_ISR_STARTADDRESS为 0x800000000,IRQ中断时,根据b HandlerFIQ,先跳转,再根据^ _ISR_STARTADDRESS基地址+HandleIRQ 的偏移地址(4*6)得到的中断地址 ;0x80000000+0x00000024=0x80000024

这个宏是用于第一次查表过程的实现中断向量的重定向。那么这样做有什么优点呢?

   这种方式的优点就是真正定义的向量数据放到内存空间RAM,而不是在ENTRY(地址0x0)处的ROM(FLASH)空间里这样,我们就可以在程序里灵活的改动中断向量的数据了(因为ROM是只读的,而RAM为可读可写的)。

在这里HANDLER是一个宏,就负责查找中断处理程序的入口地址。这些中断入口地址存放在由HandleXXX指向的表项中,该表定位在RAM高端,它的基地址为_ISR_STARTADDRESS

  例如:_ISR_STARTADDRESS0x800000000,IRQ中断时,根据b HandlerFIQ,先跳转再根据_ISR_STARTADDRESS基地址+HandleIRQ的偏移地址(4*6)得到的中断地址0x80000000+0x00000024=0x80000024

/*大致的作用就是把宏的第一个参数$HandlerLabel 转换成一个标号,然后让程序跳转到第二个参数$HandleLabel (为一个地址)对应的值的地址去*/

//这样做的目的是为了后面多种中断不需要每一个都写这么多代码

//该代码主要实现跳转功能,把异常处理程序地址HandleXXX送到PC中。例如//产生IRQ中断时,PC会被强制设置为0x18,执行指令:b  HandlerIRQ

MACRO

$HandlerLabel   HANDLER   $HandleLabel

/*注解:$HandlerLabel 为标号,字符串型;HANDLER 为宏名;$HandleLabel 为参数,指针型。*/

/*ARM体系中,每个指针变量占4个字节,这就解释了这里为什么每个标号分配了四个字节的空间,里面放的就是函数指针*/

$HandlerLabel        //定义标号

sub sp,sp,#4 ;     //把栈顶指针减4,留出一个字的空间(用于保存跳转地址的值)sp=sp-4

stmfd sp!,{r0}

//首先把sp4然后把将要使用的r0寄存器入栈 ,堆栈寻址方式,要加{}

ldr   r0, =$HandleLabel

//给寄存器r0赋值,将HandleXXX的址址放入r0,加载指定地址上的数据(字),放入Rd中 

ldr   r0, [r0]  

//给寄存器r0赋值

//HandleXXX所指向的内容(也就是中断程序的入口)放入r0

//[]表示r0寄存器里的地址所对应的值,不加[]表示r0寄存器里的地址

str     r0,[sp,#4]      

//;store the contents(ISR) of HandleXXX to stack把中断服

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值