找回密码
 立即注册

QQ登录

只需一步,快速开始

搜索
查看: 13695|回复: 1
打印 上一主题 下一主题
收起左侧

几乎是每个arm程序程序必备的启动代码

[复制链接]
跳转到指定楼层
楼主
ID:76190 发表于 2015-4-5 17:56 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
启动代码是几乎是每个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 64MB顶部
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 ;计算结束地址

;下面是用于向内存控制器
;装入配置信息的循环
0
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


我在写一个arm920T的微型OS,主要是想借着写OS的过程学习ARM的底层编程,然后跳到Linux。启动代码是固件的一部分,最经学校要搞个设计,不知OS什么时候能写好,反正搞定后立即发帖。

下边是完整的工程

运行平台:mini2440 51heiarm5845.rar (61.48 KB, 下载次数: 22)



大家好像都对地址,装载很感兴趣,我来唠唠:)

我认为之所以要指定镜像的各段的起始位置是因为程序中的标号,函数,变量的位置是在编译时根据镜像运行时(注意)计算出来的,所以镜像的各段的位置设置在哪里就一定要把镜像的各段放在那里,否者程序就会由于地址对不上而跑飞。

但是究竟位置应该设定在哪呢 ?

主要有两种情况:

1。 程序放在Nor里

程序的代码段可知放在Nor里运行,为什么?想想看,你的程序应该不会在运行时动态改变程序的指令吧,也就意味着只会读Nor,cpu(在有内存控制器的时候)读Nor和读RAM除了速度慢其他的没什么区别。
但是对于RW ZI 不只有读还有写,应为没法向写RAM一样的写Nor,所以RW ZI 一定要放到 RAM里(不管是SRAM还是DRAM),只有在那里程序才能写RW ZI

   如何装载?
   编译好的镜像处于一种“压缩”的状态。这么压缩的? 比如镜像运行时 RO从0x0 - 0x10  RW从Ox3000_0000 - 0x3000_0004  ZI 0x3000_0008 - 0x3000_000C 如果直接原样镜像,镜像文件肯定会有很大空隙,且ZI全是零完全没必要镜像下来,只要记住起始 结束地址就行了。所以镜像文件在运行前RO RW ZI 是连载一起的,且RO在最前边

   所以就以上分析装载分两大步
   step1:由bootloader完成
          (1).bootloader直接把整个镜像copy到RAM里,从哪读镜像无所谓,ROM,uart,usb,SD卡,以太,甚至是无线都可以,但是目的位置一定是RO$$Base
          (2).然后PC = Ro$$Base
   step2:由镜像自己干
           上边过后由于Ro段在镜像的最前边且RO的起始位置正好就在Ro$$base所以镜像Ro顺利运行,但是RW ZI还不一定在正确位置上,所以有了启动代码的第五步


2.  程序在Nand上
    这是的区别就是与nor相比想直接读nand都难了,所以这是要想让程序顺利运行就必须把Ro段也搬到RAM里,这时要有两个东西,一个小程序 《4k 在NAND最前边,负责装载,就是bootloader。把正真想完成任务的程序放在后边,上电时小程序最先被自动copy到sram,sram是定位在0x0的,所以bootloader 的 Ro起始地址必须设置在0x0,然后bootloader就和上边的没什么区别了,先把nand里的大程序整个copy到DRAM,然后大程序执行把自己的RW ZI copy 到正确位置,所以大程序的RO$$Base可以是DRAM的地址。







“这个B后面要是跳到MAIN话,ADS会根据
|Image$$RO$$Limit|
|Image$$RW$$Base|  
算出你程序的代码段地址(也就是程序的运行段)和读写段地址(也就是已初始的变量段) "

我觉得bootloader完全没必要分析镜像,只要直接copy就行了,只要镜像自己保证自己的Ro在最前边就行了,这是编译器的事,后边的RW zi 镜像自己就搞定了。


”说白一点,如果你想把程序在FLASH里面运行,这时要把|Image$$RO$$Limit|设为0X00000000(FLASH地址),
                                                 |Image$$RW$$Base| 设为0X0C000000(RAM地址) “


这个|Image$$RO$$Limit| =0x0 那Base在哪?没看见ADS可以设置Limit啊.


"想用UBOOT引导后在RAM运行,这时要把|Image$$RO$$Limit|设为0X0C000000(RAM地址),
                                                  |Image$$RW$$Base| 设为0X0C000000(RAM地址)”

在ads里如果只设置 ro$$base 那后边的段是接起来的

  请看



我想自己设置肯定可以,如果开MMU的话具体放在哪应该还是要仔细考虑的。




分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏3 分享淘帖 顶 踩
回复

使用道具 举报

沙发
ID:87961 发表于 2015-8-9 06:55 | 只看该作者
作为入门者,还是值得学习的
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|51黑电子论坛 |51黑电子论坛6群 QQ 管理员QQ:125739409;技术交流QQ群281945664

Powered by 单片机教程网

快速回复 返回顶部 返回列表