视频1 视频21 视频41 视频61 视频文章1 视频文章21 视频文章41 视频文章61 推荐1 推荐3 推荐5 推荐7 推荐9 推荐11 推荐13 推荐15 推荐17 推荐19 推荐21 推荐23 推荐25 推荐27 推荐29 推荐31 推荐33 推荐35 推荐37 推荐39 推荐41 推荐43 推荐45 推荐47 推荐49 关键词1 关键词101 关键词201 关键词301 关键词401 关键词501 关键词601 关键词701 关键词801 关键词901 关键词1001 关键词1101 关键词1201 关键词1301 关键词1401 关键词1501 关键词1601 关键词1701 关键词1801 关键词1901 视频扩展1 视频扩展6 视频扩展11 视频扩展16 文章1 文章201 文章401 文章601 文章801 文章1001 资讯1 资讯501 资讯1001 资讯1501 标签1 标签501 标签1001 关键词1 关键词501 关键词1001 关键词1501 专题2001
mini 2440启动代码2
2025-10-02 19:16:29 责编:小OO
文档
ARM 启动过程详解及启动代码简化版(MINI2440)

ARM, 详解, 代码

基于ARM的芯片多数为复杂的片上系统,这种复杂系统里的多数硬件模块都是可配置的,需要由软件来设置其需要的工作状态。因此在用户的应用程序之前,需要由专门的一段代码来完成对系统的初始化。由于这类代码直接面对处理器内核和硬件控制器进行编程,一般都是用汇编语言。一般通用的内容包括: 

中断向量表 

初始化存储器系统 

初始化堆栈 

初始化有特殊要求的断口,设备 

初始化用户程序执行环境 

改变处理器模式 

呼叫主应用程序 

中断向量表 

ARM要求中断向量表必须放置在从0地址开始,连续8X4字节的空间内。 

每当一个中断发生以后,ARM处理器便强制把PC指针置为向量表中对应中断类型的地址值。因为每个中断只占据向量表中1个字的存储空间,只能放置一条ARM指令,使程序跳转到存储器的其他地方,再执行中断处理。 

中断向量表的程序实现通常如下表示: 

AREA Boot ,CODE, READONLY 

ENTRY 

B  ResetHandler 

B  UndefHandler 

B  SWIHandler 

B  PreAbortHandler 

B  DataAbortHandler 

B  IRQHandler 

B  FIQHandler 

其中关键字ENTRY是指定编译器保留这段代码,因为编译器可能会认为这是一段亢余代码而加以优化。链接的时候要确保这段代码被链接在0地址处,并且作为整个程序的入口。 

初始化存储器系统 

存储器类型和时序配置 

通常Flash和SRAM同属于静态存储器类型,可以合用同一个存储器端口;而DRAM因为有动态刷新和地址线复用等特性,通常配有专用的存储器端口。 

存储器端口的接口时序优化是非常重要的,这会影响到整个系统的性能。因为一般系统运行的速度瓶颈都存在于存储器访问,所以存储器访问时序应尽可能的快;而同时又要考虑到由此带来的稳定性问题。 

存储器地址分布 

一种典型的情况是启动ROM的地址重映射。 

初始化堆栈 

因为ARM有7种执行状态,每一种状态的堆栈指针寄存器(SP)都是的。因此,对程序中需要用到的每一种模式都要给SP定义一个堆栈地址。方法是改变状态寄存器内的状态位,使处理器切换到不同的状态,让后给SP赋值。注意:不要切换到User模式进行User模式的堆栈设置,因为进入User模式后就不能再操作CPSR回到别的模式了,可能会对接下去的程序执行造成影响。 

这是一段堆栈初始化的代码示例,其中只定义了三种模式的SP指针: 

MRS  R0,CPSR 

BIC  R0,R0,#MODEMASK 安全起见,屏蔽模式位以外的其他位 

ORR  R1,R0,#IRQMODE 

MSR  CPSR_cxfs,R1 

LDR  SP,=UndefStack 

ORR  R1,R0,#FIQMODE 

MSR  CPSR_cxsf,R1 

LDR  SP,=FIQStack 

ORR  R1,R0,#SVCMODE 

MSR  CPSR_cxsf,R1 

LDR  SP,=SVCStack  

初始化有特殊要求的端口,设备 

初始化应用程序执行环境 。一个ARM映像文件由RO,RW和ZI三个段组成,其中RO为代码段,RW是已初始化的全局变量,ZI是未初始化的全局变量。 映像一开始总是存储在ROM/Flash里面的,其RO部分即可以在ROM/Flash里面执行,也可以转移到速度更快的RAM中执行;而RW和ZI这两部分是必须转移到可写的RAM里去。所谓应用程序执行环境的初始化,就是完成必要的从ROM到RAM的数据传输和内容清零。 

下面是在ADS下,一种常用存储器模型的直接实现: 

编译器使用下列符号来记录各段的起始和结束地址: 

|Image$$RO$$Base| :RO段起始地址 

|Image$$RO$$Limit| :RO段结束地址加1 

|Image$$RW$$Base| :RW段起始地址 

|Image$$RW$$Limit| :ZI段结束地址加1 

|Image$$ZI$$Base| :ZI段起始地址 

|Image$$ZI$$Limit| :ZI段结束地址加1 

这些标号的值是根据链接器中设置的中ro-base和rw-base的设置来计算的。 

初始化用户执行环境主要是把RO、RW、ZI三段拷贝到指定的位置。 

调用主应用程序 

当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是: 

IMPORT main 

B? main

LDR  r0,=|Image$$RO$$Limit| 得到RW数据源的起始地址 

LDR  r1,=|Image$$RW$$Base| RW区在RAM里的执行区起始地址 

LDR  r2,=|Image$$ZI$$Base| ZI区在RAM里面的起始地址 

CMP  r0,r1         比较它们是否相等 

   BEQ  %F1 

0   CMP  r1,r3 

   LDRCC r2,[r0],#4

STRCC r2,[r1],#4 

   BCC  %B0 

1   LDR  r1,=|Image$$ZI$$Limit| 

   MOV  r2,#0 

2   CMP  r3,r1 

   STRCC r2,[r3],#4 

   BCC  %B2 

程序实现了RW数据的拷贝和ZI区域的清零功能。其中引用到的4个符号是由链接器第一输出的。 

|Image$$RO$$Limit|:表示RO区末地址后面的地址,即RW数据源的起始地址 

|Image$$RW$$Base|:RW区在RAM里的执行区起始地址,也就是编译器选项RW_Base指定的地址 

|Image$$ZI$$Base|:ZI区在RAM里面的起始地址 

|Image$$ZI$$Limit|:ZI区在RAM里面的结束地址后面的一个地址 

程序先把ROM里|Image$$RO$$Limt|开始的RW初始数据拷贝到RAM里面|Image$$RW$$Base|开始的地址,当RAM这边的目标地址到达|Image$$ZI$$Base|后就表示RW区的结束和ZI区的开始,接下去就对这片ZI区进行清零操作,直到遇到结束地址|Image$$ZI$$Limit| 

改变处理器模式 

因为在初始化过程中,许多操作需要在模式下才能进行(比如对CPSR的修改),所以要特别注意不能过早的进入用户模式。 

内核级的中断使能也可以考虑在这一步进行。如果系统中另外存在一个专门的中断控制器,这么做总是安全的。 

呼叫主应用程序 

当所有的系统初始化工作完成之后,就需要把程序流程转入主应用程序。最简单的一种情况是: 

IMPORT main 

B   main 

直接从启动代码跳转到应用程序的主函数入口,当然主函数名字可以由用户随便定义。 

在ARM ADS环境中,还另外提供了一套系统级的呼叫机制。 

IMPORT __main 

B   __main 

__main()是编译系统提供的一个函数,负责完成库函数的初始化和初始化应用程序执行环境,最后自动跳转到main()函数。

理解启动代码(ADS) 

所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初始化处理器模式,设置堆栈,初始化变量等等.由于以上的操作均与处理器体系结构和系统配置密切相关,所以一般由汇编来编写. 

具体到S,启动代码分成两部分,一是与ARM7TDMI内核相关的部分,包括处理器各异常向量的配置,各处理器模式的堆栈设置,如有必要,复制向量到RAM,以便remap之后处理器正确处理异常,初始化数据(包括RW与ZI),最后跳转到Main.二是与处理器外部设备相关的部分,这和厂商的联系比较大.虽然都采用了ARM7TDMI的内核,但是不同的厂家整合了不同的片上外设,需要不同的初始化,其中比较重要的是初始化WDT,初始化各子系统时钟,有必要的话,进行remap.这一部分与一般控制器的初始化类似,因此,本文不作重点描述. 

在进行分析之前,请确认如下相关概念: 

S片上FLASH起始于0x100000,共kB,片上RAM起始于0x200000,共16kB. 

S复位之后,程序会从0开始执行,此时FLASH被映射到0地址,因此,S可以取得指令并执行.显然,此时还是驻留在0x100000地址.如果使用remap命令,将会把RAM映射到0地址,同样的这时0地址的内容也只是RAM的镜像. 

S的FLASH可以保证在最差情况时以30MHz进行单周期访问,而RAM可以保证在最大速度时的单周期访问. 

OK,以下开始分析启动代码. 

一,处理器异常 

S将异常向量至于0地址开始的几个直接,这些是必需要处理的.由于复位向量位于0,也需要一条跳转指令.具体代码如下: 

RESET 

B SYSINIT ; Reset 

B UDFHANDLER ; UNDEFINED 

B SWIHANDLER ; SWI 

B PABTHANDLER ; PREFETCH ABORT 

B DABTHANDLER ; DATA ABORT 

B . ; RESERVED 

B VECTORED_IRQ_HANDLER 

B . ; ADD FIQ CODE HERE 

UDFHANDLER 

B . 

SWIHANDLER 

B . 

PABTHANDLER 

B . 

DABTHANDLER 

B . 

请注意,B指令经汇编后会替换为当前PC值加上一个修正值(+/-),所以这条指令是代码位置无关的,也就是不管这条指令是在0地址还是在0x100000执行,都能跳转到指定的位置,而LDR PC,=?将向PC直接装载一个标号的值,请注意,标号在编译过后将被替换为一个与RO相对应的值,也就是说,这样的指令无论在哪里执行,都只会跳转到一个指定的位置.下面举一个具体的例子来说明两者的区别: 

假定有如下程序: 

RESET 

B INIT 或者 LDR PC,=INIT 

… 

INIT 

… 

其中RESET为起始时的代码,也就是这条代码的偏移为0,设INIT的偏移量为offset.如果将这段程序按照RO=0x1000000编译, 那么B INIT可理解为ADD PC, PC, #offset,而LDR PC,=INIT可被理解为 MOV PC,#(RO+offset) .显然当系统复位时,程序从0开始运行,而0地址有FLASH的副本,执行B INIT将把PC指向位于0地址处的镜像代码位置,也即INIT;如果执行LDR PC,=INIT将会将PC直接指向位于FLASH中的原始代码.因此以上两者都能正确运行.下面将RO设置为0x200000,编译后生成代码,还是得烧写到FLASH中,也就是还是0x100000,系统复位后从0地址执行,还是FLASH的副本,此时执行B INIT,将跳到副本中的INIT位置执行,此处有对应的代码;但是如果执行LDR PC,=INIT,将向PC加载0x200000+offset,这将使得PC跳到RAM中,而此时由于代码没有复制,RAM中的指定位置并没有代码,程序无法运行. 

二,处理器模式 

ARM的处理器可工作于多种模式,不同模式有不同的堆栈 ,以下设置各模式及其堆栈. 

预定义一些参数: 

MODUSR EQU 0x10 

MODSYS EQU 0x1F 

MODSVC EQU 0x13 

MODABT EQU 0x17 

MODUDF EQU 0x1B 

MODIRQ EQU 0x12 

MODFIQ EQU 0x11 

IRQBIT EQU 0x80 

FIQBIT EQU 0x40 

RAMEND EQU 0x00204000 ; S : 16KB RAM 

VECTSIZE EQU 0x100 ; 

UsrStkSz EQU 8 ; size of USR stack 

SysStkSz EQU 128 ; size of SYS stack 

SvcStkSz EQU 8 ; size of SVC stack 

UdfStkSz EQU 8 ; size of UDF stack 

AbtStkSz EQU 8 ; size of ABT stack 

IrqStkSz EQU 128 ; size of IRQ stack 

FiqStkSz EQU 16 ; size of FIQ stack 

修改这些值即可修改相应模式堆栈的尺寸. 

以下为各模式代码: 

SYSINIT 

MRS R0,CPSR 

BIC R0,R0,#0x1F 

MOV R2,#RAMEND 

ORR R1,R0,#(MODSVC :OR: IRQBIT :OR: FIQBIT) 

MSR cpsr_cxsf,R1 ; ENTER SVC MODE 

MOV sp,R2 

SUB R2,R2,#SvcStkSz 

ORR R1,R0,#(MODFIQ :OR: IRQBIT :OR: FIQBIT) 

MSR CPSR_cxsf,R1 ; ENTER FIQ MODE 

MOV sp,R2 

SUB R2,R2,#FiqStkSz 

ORR R1,R0,#(MODIRQ :OR: IRQBIT :OR: FIQBIT) 

MSR CPSR_cxsf,R1 ; ENTER IRQ MODE 

MOV sp,R2 

SUB R2,R2,#IrqStkSz 

ORR R1,R0,#(MODUDF :OR: IRQBIT :OR: FIQBIT) 

MSR CPSR_cxsf,R1 ; ENTER UDF MODE 

MOV sp,R2 

SUB R2,R2,#UdfStkSz 

ORR R1,R0,#(MODABT :OR: IRQBIT :OR: FIQBIT) 

MSR CPSR_cxsf,R1 ; ENTER ABT MODE 

MOV sp,R2 

SUB R2,R2,#AbtStkSz 

;ORR R1,R0,#(MODUSR :OR: IRQBIT :OR: FIQBIT) 

;MSR CPSR_cxsf,R1 ; ENTER USR MODE 

;MOV sp,R2 

;SUB R2,R2,#UsrStkSz 

ORR R1,R0,#(MODSYS :OR: IRQBIT :OR: FIQBIT) 

MSR CPSR_cxsf,R1 ; ENTER SYS MODE 

MOV sp,R2 ; 

三,初始化变量 

编译完成之后,连接器会生成三个基本的段,分别是RO,RW,ZI,并会在image中顺序摆放.显然,RW,ZI在运行开始时并不位于指定的RW位置,因此必须初始化 

LDR R0,=|Image$$RO$$Limit| 

LDR R1,=|Image$$RW$$Base| 

LDR R2,=|Image$$ZI$$Base| 

CMP R1,R2 

LDRLO R3,[R0],#4 

STRLO R3,[R1],#4 

BLO %B1 

MOV R3,#0 

LDR R1,=|Image$$ZI$$Limit| 

CMP R2,R1 

STRLO R3,[R2],#4 

BLO %B2 

四,复制异常向量 

由于代码于RAM运行时,有明显的速度优势,而且变量可以动态配置,因此可以通过remap将RAM映射到0,使得出现异常时ARM从RAM中取得向量. 

IMPORT |Image$$RO$$Base| 

IMPORT |Image$$RO$$Limit| 

IMPORT |Image$$RW$$Base| 

IMPORT |Image$$RW$$Limit| 

IMPORT |Image$$ZI$$Base| 

IMPORT |Image$$ZI$$Limit| 

COPY_VECT_TO_RAM 

LDR R0,=|Image$$RO$$Base| 

LDR R1,=SYSINIT 

LDR R2,=0x200000 ; RAM START 

CMP R0,R1 

LDRLO R3,[R0],#4 

STRLO R3,[R2],#4 

BLO %B0 

这段程序将SYSINIT之前的代码,也就是异常处理函数,全部复制到RAM中, 这就意味着不能将RW设置为0x200000,这样会使得向量被冲掉. 

四,在RAM中运行 

如果有必要,且代码足够小,可以将代码置于RAM中运行,由于RAM中本身没有代码,就需要将代码复制到RAM中: 

COPY_BEGIN 

LDR R0,=0x200000 

LDR R1,=RESET ; =|Image$$RO$$Base| 

CMP R1,R0 ; 

BLO COPY_END ; 

ADR R0,RESET 

ADR R2,COPY_END 

SUB R0,R2,R0 

ADD R1,R1,R0 

LDR R3,=|Image$$RO$$Limit| 

CMP R1,R3 

LDRLO R4,[R2],#4 

STRLO R4,[R1],#4 

BLO %B3 

LDR PC,=COPY_END 

COPY_END 

程序首先取得RESET的连接地址,判断程序是否时是在RAM中运行,方法是与RAM起始地址比较,如果小于,那么就跳过代码复制. 

在复制代码的时候需要注意,在这段程序结束之前的代码没有必要复制,因为这些代码都已经执行过了,所以,先取得COPY_END,作为复制起始地址,然后计算其相对RESET的偏移,然后以RO的值加上这个偏移,就是复制目的地的起始地址,然后开始复制. 

五,开始主程序 

以上步骤完成,就可以跳转到main运行 

IMPORT Main 

LDR PC,=Main 

B . 

六,器件初始化 

主程序首先要进行器件的初始化,对S而言,应该先初始化WDT,因为默认情况下,WDT是打开的,然后是各设备的时钟分配,最后应该remap

如果是驱动led,那么用1K左右的就行了。如果希望亮度大一些,电阻可减小,最小不要小于200欧姆,否则电流太大;如果希望亮度小一些,电阻可增大,增加到多少呢,主要看亮度情况,以亮度合适为准,一般来说超过3K以上时,亮度就很弱了,但是对于超高亮度的LED,有时候电阻为10K时觉得亮度还能够用。通常就用1k的。

对于驱动光耦合器,如果是高电位有效,即耦合器输入端接端口和地之间,那么和LED的情况是一样的;如果是低电位有效,即耦合器输入端接端口和VCC之间,那么除了要串接一个1——4.7k之间的电阻以外,同时上拉电阻的阻值就可以用的特别大,用100k——500K之间的都行,当然用10K的也可以,但是考虑到省电问题,没有必要用那么小的。

对于驱动晶体管,又分为PNP和NPN管两种情况:对于NPN,毫无疑问NPN管是高电平有效的,因此上拉电阻的阻值用2K——20K之间的,具体的大小还要看晶体管的集电极接的是什么负载,对于LED类负载,由于发管电流很小,因此上拉电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此上拉电阻的阻值最好不要大于4.7K,有时候甚至用2K的。对于PNP管,毫无疑问PNP管是低电平有效的,因此上拉电阻的阻值用100K以上的就行了,且管子的基极必须串接一个1——10K的电阻,阻值的大小要看管子集电极的负载是什么,对于LED类负载,由于发光电流很小,因此基极串接的电阻的阻值可以用20k的,但是对于管子的集电极为继电器负载时,由于集电极电流大,因此基极电阻的阻值最好不要大于4.7K。

对于驱动TTL集成电路,上拉电阻的阻值要用1——10K之间的,有时候电阻太大的话是拉不起来的,因此用的阻值较小。但是对于CMOS集成电路,上拉电阻的阻值就可以用的很大,一般不小于20K,我通常用100K的,实际上对于CMOS电路,上拉电阻的阻值用1M的也是可以的,但是要注意上拉电阻的阻值太大的时候,容易产生干扰,尤其是线路板的线条很长的时候,这种干扰更严重,这种情况下上拉电阻不宜过大,一般要小于100K,有时候甚至小于10K。

根据以上分析,上拉电阻的阻值的选取是有很多讲究的,不能乱用。

 ARM启动.pdf (390.06 KB) 下载次数: 35

2010-9-22 13:11

启动代码是几乎是每个arm程序程序必备的,刚开始看的时候看别人的启动代码时感觉云里雾里,所以懒惰的想法浮现脑中:别人都写好了我还写什么,直接拿来用不就行了,对在我懂得情况下,我一定会拿来就用,但是现在我还不懂,一切就要从头开始,经过几天的努力,现在的感觉是启动代码不过如此  ,呵呵。 ;--------------------------------------------------------------------- 

;startup.s 

;系统启动代码 

;起始时间 : 2009.5.7 ----->2009.5.11

;--------------------------------------------------------------------- 

;--------------------------------------------------------------------- 

GET ./Include/s3c2440.inc ;寄存器地址信息 

GET ./Include/memcfg.inc ;内存控制器配置信息 

;处理器模式 

USERMODE EQU  0x10 

FIQMODE EQU  0x11 

IRQMODE EQU  0x12 

SVCMODE EQU  0x13 

ABORTMODE EQU  0x17 

UNDEFMODE EQU  0x1b 

SYSMODE EQU 0x1f 

;相关掩码 

MODEMASK     EQU  0x1f 

NOINT EQU  0xc0 

;各个处理器模式下堆栈设置 

_STACK_BASEADDRESS EQU 0x33ff8000 ;BANK6 MB顶部 

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 ~ 

;导入操作系统入口函数 

IMPORT OSEntry 

;导入外部C语言编写的异常与中断处理函数 

IMPORT vectorUNDEF 

IMPORT vectorSWI 

IMPORT vectorPABT 

IMPORT vectorDABT 

IMPORT vectorIRQ 

IMPORT vectorFIQ 

;导入镜像装载域段起始地址 

IMPORT  |Image$$RO$$Limit|  ; End of ROM code (=start of ROM data) 

IMPORT  |Image$$RW$$Base|   ; Base of RAM to initialise 

IMPORT  |Image$$ZI$$Base|   ; Base and limit of area 

IMPORT  |Image$$ZI$$Limit|  ; to zero initialise 

;-------------------------------------------------------------------- 

;------------------------------------------------------ 

AREA startup, CODE, READONLY 

ENTRY 

;系统向量表 

b vectorRESET ;复位向量 

b vectorUNDEF ;未定义指令 

b vectorSWI ;软中断 

b vectorPABT ;预取指终止 

b vectorDABT ;数据终止 

b . ;系统保留 

b vectorIRQ ;外部中断 

b vectorFIQ ;快速中断 

;------------------------------------------------------- 

;-------------------------------------------------------------------------- 

;复位向量 

;复位向量是ARM处理器上电后第一个被执行的异常 

;此时系统处理管理(SVC)模式 

vectorRESET 

;复位向量有以下六件事要做 

;第一步 : 关闭看门狗定时器屏蔽所有中断 

;第二步 : 配置系统时钟 

;第三步 : 配置内存控制器 

;第四步 : 配置每种处理器模式下堆栈指针 

;第五步 : 初始化镜像运行域 

;第六步 : 跳转到操作系统入口 

;------------------------------------------ 

;第一步 : 关闭看门狗定时器 

;具体内容请参看s3c2440a数据手册的第18章 

ldr r0, =WTCON 

ldr  r1, =0x0 

str  r1, [r0, #0x0] 

;屏蔽所有中断 

ldr r0, =INTMSK 

ldr r1, =0xffffffff 

str r1, [r0] 

;------------------------------------------ 

;------------------------------------------ 

;第二步 : 配置系统时钟 

;具体内容请看手册第7章 

;先减少锁相环锁定时间,s3c2440a要求PLL 

;锁定时间>300us,在上电时s3c2440a预设值 

;mpll为晶体频率,我用的晶体频率为12MHz 

;300us*12M = 3600设置LOCKTIME = 0xfff 

;足够了 

ldr r0, =LOCKTIME 

ldr r1, =0xfff0fff0 ;高16为对应UPLL 

;低16为对应MPLL 

str r1, [r0, #0x0] 

;根据器件手册我们还有以下几个事要做 

;step1.配置UPLL 

;step2.配置MPLL 

;注:手册要求先配置UPLL后MPLL 

; 且之间要间隔7NOP 

; 详请看手册第7-21. 

;step3.配置分频系数 

;step1: 

ldr r0, =UPLLCON 

ldr r1, =((56<<12) + (2<<4) + 2)

ldr r1, [r0] 

;按手册要求插入7个NOP 

nop 

nop 

nop 

nop 

nop 

nop 

nop 

;step2: 

ldr r0, =MPLLCON 

ldr r1, =((127<<12) + (2<<4) + 1)

ldr r1, [r0] 

;step3: 

ldr r0, =CLKDIVN 

ldr r1, =((0<<3) + (2 << 2) + 1)

ldr r1, [r0] 

;------------------------------------------ 

;------------------------------------------ 

;第三步 : 配置内存控制器 

;内存控制内的寄存器器地址是连续分布的 

;从0x4800_0000 -- 0x4800_0030,所以可以 

;通过一个循环依次填入各个寄存器的内容 

ldr r0, =SMRDATA ;装入配置值的地址 

ldr r1, =BWSCON ;装入起始寄存器地址 

add r2, r0, #0x34 ;计算结束地址 

;下面是用于向内存控制器 

;装入配置信息的循环 

ldr r3, [r0], #4 ;装入配置值到r3,后变址 

str r3, [r1], #4  ;把r3内包含的配置值写入 

;内存控制器的寄存器 

cmp r2, r0 ;结束否? 

bne %B0 ;没结束则继续 

;------------------------------------------ 

;------------------------------------------ 

;第四步 : 配置每种处理器模式下堆栈指针 

;方法与原则: 

;1: 通过CPSR寄存器切换处理器模式 

;2: 对CPSR的操作方式为 读-修改-写回 

;3: 绝对不要跳到用户模式,跳过去容易 

; 回来就难了 

;4: 切到新处理器模式后要屏蔽IRQ和FIQ 

; 防止在未设置好堆栈前进入中断处理 

; 程序,但是在启动代码的最先我们已 

; 经屏蔽了所有的32个中断源,所以感 

; 觉是否屏蔽都可以 

;step1: 先把程序状态寄存器读到r0 

mrs r0, cpsr 

;step2: 清除处理器模式位(最前面5位) 

bic r0, r0, #MODEMASK 

;step3: 设置未定义状态下的堆栈指针 

orr r1, r0, #UNDEFMODE|NOINT 

msr cpsr_cxsf, r1 ;UndefMode 

ldr sp, =UndefStack ;UndefStack=0x33FF_5C00 

;step4: 设置终止状态下的堆栈指针 

orr r1, r0, #ABORTMODE|NOINT 

msr cpsr_cxsf, r1 ;AbortMode 

ldr sp, =AbortStack ;AbortStack=0x33FF_6000 

;step5: 设置中断模式下的堆栈指针 

orr r1, r0, #IRQMODE|NOINT 

msr cpsr_cxsf, r1 ;IRQMode 

ldr sp, =IRQStack ;IRQStack=0x33FF_7000 

;step6: 设置快速中断模式下的堆栈指针 

orr r1, r0, #FIQMODE|NOINT 

msr cpsr_cxsf, r1 ;FIQMode 

ldr sp, =FIQStack ;FIQStack=0x33FF_8000 

;step7: 设置管理模式下的堆栈指针 

orr r1, r0, #SVCMODE|NOINT 

msr cpsr_cxsf, r1 ;SVCMode 

ldr sp, =SVCStack ;SVCStack=0x33FF_5800 

;step8: 因为管理模式与用户模式共用 

; 堆栈指针,所以借着系统模式 

; 来设置用户模式的堆栈指针 

orr r1, r0, #SYSMODE|NOINT 

msr cpsr_cxsf, r1 ;SYSMode 

ldr sp, =UserStack ;SVCStack & USERMode=0x33ff4800 

;现在处理器处于系统模式 

;------------------------------------------ 

    

    

    

;------------------------------------------ 

;第五步 : 初始化镜像运行域 

;复制RW段和ZI段到SDRAM指定地址 

LDR     r0, =|Image$$RO$$Limit| ; 装入RO段结束地址 

LDR     r1, =|Image$$RW$$Base|  ; 装入RW段起始地址 

LDR     r3, =|Image$$ZI$$Base|  ; 装入ZI段起始地址 

;|Image$$RO$$Limit| == |Image$$RW$$Base| ? 跳过RW段复制 : 复制RW段 

CMP     r0, r1      

BEQ     %F2 

;复制RW段 

1        

CMP     r1, r3   

LDRCC   r2, [r0], #4      

STRCC   r2, [r1], #4 

BCC     %B1 

2        

LDR     r1, =|Image$$ZI$$Limit|  

MOV     r2, #0 

;构造ZI段 

3        

CMP     r3, r1      

STRCC   r2, [r3], #4 

BCC     %B3 

;------------------------------------------    

;------------------------------------------ 

;第六步 : 跳转到操作系统入口 

b OSEntry ;不要使用main,因为如果使用main 

;ads还会调用_main()初始化RW和ZI 

;段,但是那里的数据和本程序不同 

b . 

;------------------------------------------ 

;--------------------------------------------------------------------------- 

SMRDATA DATA 

;这里是内存控制器的配置数据 

;配置数据需要根据你使用的存储器修改 

;在第三步时会将以下数据写入 

;内存控制器的相关寄存器中 

;共13个寄存器的配置值 

DCD (0+(B1_BWSCON<<4)+(B2_BWSCON<<8)+(B3_BWSCON<<12)+(B4_BWSCON<<16)+(B5_BWSCON<<20)+(B6_BWSCON<<24)+(B7_BWSCON<<28))

DCD ((B0_Tacs<<13)+(B0_Tcos<<11)+(B0_Tacc<<8)+(B0_Tcoh<<6)+(B0_Tah<<4)+(B0_Tacp<<2)+(B0_PMC)) ;GCS0

DCD ((B1_Tacs<<13)+(B1_Tcos<<11)+(B1_Tacc<<8)+(B1_Tcoh<<6)+(B1_Tah<<4)+(B1_Tacp<<2)+(B1_PMC)) ;GCS1

DCD ((B2_Tacs<<13)+(B2_Tcos<<11)+(B2_Tacc<<8)+(B2_Tcoh<<6)+(B2_Tah<<4)+(B2_Tacp<<2)+(B2_PMC)) ;GCS2

DCD ((B3_Tacs<<13)+(B3_Tcos<<11)+(B3_Tacc<<8)+(B3_Tcoh<<6)+(B3_Tah<<4)+(B3_Tacp<<2)+(B3_PMC)) ;GCS3

DCD ((B4_Tacs<<13)+(B4_Tcos<<11)+(B4_Tacc<<8)+(B4_Tcoh<<6)+(B4_Tah<<4)+(B4_Tacp<<2)+(B4_PMC)) ;GCS4

DCD ((B5_Tacs<<13)+(B5_Tcos<<11)+(B5_Tacc<<8)+(B5_Tcoh<<6)+(B5_Tah<<4)+(B5_Tacp<<2)+(B5_PMC)) ;GCS5

DCD ((B6_MT<<15)+(B6_Trcd<<2)+(B6_SCAN)) ;GCS6

DCD ((B7_MT<<15)+(B7_Trcd<<2)+(B7_SCAN)) ;GCS7

DCD ((REFEN<<23)+(TREFMD<<22)+(Trp<<20)+(Trc<<18)+(Tchr<<16)+REFCNT)

DCD 0x32     ;SCLK power saving mode, BANKSIZE 128M/128M 

DCD 0x30     ;MRSR6 CL=3clk 

DCD 0x30     ;MRSR7 CL=3clk 

ALIGN ;数据边界对齐 

END 下载本文

显示全文
专题