ARM9启动代码裸机版所谓启动代码就是处理器在启动的时候执行.docx
- 文档编号:28882238
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:28
- 大小:22.46KB
ARM9启动代码裸机版所谓启动代码就是处理器在启动的时候执行.docx
《ARM9启动代码裸机版所谓启动代码就是处理器在启动的时候执行.docx》由会员分享,可在线阅读,更多相关《ARM9启动代码裸机版所谓启动代码就是处理器在启动的时候执行.docx(28页珍藏版)》请在冰豆网上搜索。
ARM9启动代码裸机版所谓启动代码就是处理器在启动的时候执行
ARM9启动代码(裸机版)
所谓启动代码,就是处理器在启动的时候执行的一段代码,主要任务是初
始化处理器模式,设置堆栈,初始化变量等等。
由于以上的操作均与处理器体系
结构和系统配置密切相关,所以一般由汇编来编写。
在一般32位ARM应用系统中,软件大多数采用c语言进行编程,并且以嵌入式操作系统为开发平台,这样就大大提高了开发效率和软件性能。
为了能够进行系统初始化,通常采用一个汇编文件作启动代码。
它可以实现向量表定义、堆栈初始化、系统变量初始化、中断系统初始化、I/O初始化、地址重映射等操作。
启动代码是芯片复位后进入c语言的main函数前执行的一段代码,主要是为运行c语言程序提供基本运行环境。
在实际应用中,为提高系统的实时性,加快代码的执行速度,系统启动后
程序往往要被搬移到RAM中,因为RAM的存取速度要比ROM快得多,这样大大提升系统的性能。
启动程序要完成的任务包括:
硬件初始化,系统存储系统的配置,
复制二级中断向量表。
启动程序大概过程如下:
(1)系统硬件初始化
系统上电或复位后,程序从位于地址0x0的ResetExceptionVector处开
始执行,因此需要在这里放置bootloader的第一条指令:
bReset,跳转到标号为Reset处进行第一阶段的硬件初始化,主要内容为:
关看门狗定时器,关中断,初始化PLL和时钟,初始化存储器系统。
执行完以上程序后,系统进行堆栈和存
储器的初始化。
系统堆栈初始化取决于用户使用了哪些中断,以及系统需要处理哪些错误类型。
一般情况下,管理者堆栈必须设置,如果使用了IRQ中断,则IRQ堆栈也必须设置。
如果系统使用了外设,则需要设置相关的寄存器,以确定
其刷新频率、总线宽度等信息。
(2)代码复制到RAM中运行
因为嵌入式系统的代码通常都是固化在ROM或Flash中,上电后开始运行。
由于ROM和Flash的读取速度相对较慢,这样无疑会降低代码的执行速度和系统的运行效率。
因此,需要把系统的代码复制到RAM中运行。
(3)建立二级中断向量表
在ARM系统中,中断向量表位于0x0开始的地址处,意味着无论运行什么样的上层软件,一旦发生中断,程序就得到Flash存储器的中断向量表里去,降低系统的运行效率。
因此在RAM中建立自己的二级中断向量表,当中断发生后,程序直接从RAM中取中断向量进入中断子程序。
(4)MMU的应用
MMU是存储器管理单元的缩写,是用来管理虚拟内存系统的器件。
MMU完成
的两个主要功能是:
将虚地址转换成物理地址,控制存储器存取允许。
MMU关掉时,虚地址直接输出到物理地址总线。
由于跑的是裸机,没有用到操作系统,以下启动代码不包含MMU的应用。
@下面是对arm处理器模式寄存器对应的常数进行赋值,arm处理器有一个CPSR
寄存器,
@它的后五位决定了处理器处于哪个模式下。
可以看出常数的定义都不会超过后
5位的。
*/
@Pre-definedconstants
.equ
USERMODE,
0x10
.equ
FIQMODE,
0x11
.equ
IRQMODE,
0x12
.equ
SVCMODE,
0x13
.equ
ABORTMODE,
0x17
.equ
UNDEFMODE,
0x1b
.equ
MODEMASK,
0x1f
.equ
NOINT,
0xc0
@各个异常模式的堆栈
@Thelocationofstacks
.equ
_STACK_BASEADDRESS,0x33ff8000
.equ
_ISR_STARTADDRESS,0x31ffff00
.equ
UserStack,
(_STACK_BASEADDRESS-0x3800)@0x33ff4800~
.equ
SVCStack,
(_STACK_BASEADDRESS-0x2800)@0x33ff5800~
.equ
UndefStack,
(_STACK_BASEADDRESS-0x2400)@0x33ff5c00~
.equ
AbortStack,
(_STACK_BASEADDRESS-0x2000)@0x33ff6000~
.equ
IRQStack,
(_STACK_BASEADDRESS-0x1000)@0x33ff7000~
.equ
FIQStack,
(_STACK_BASEADDRESS-0x0)@0x33ff8000~
.equ
INTMSK,
0x4a000008@Interruptmaskcontrol
.equ
INTSUBMSK,0x4a00001c
@Interruptsubmask
.equ
INTOFFSET,0x4a000014
@Interruotrequestsourceoffset
.macro
HANDLERHandleLabel
subsp,sp,#4@decrementsp(tostorejumpaddress)
stmfdsp!
{r0}@PUSHtheworkregistertostack(lrdoes
notpushbecauseitreturntooriginaladdress)
ldrr0,=\HandleLabel@loadtheaddressofHandleXXXtor0
ldrr0,[r0]
@load
the
contents(service
routine
startaddress)ofHandleXXX
strr0,[sp,#4]
@storethecontents(ISR)
ofHandleXXXto
stack
ldmfdsp!
{r0,pc}
@POPtheworkregisterandpc(jumpto
ISR)
.endm
.externmain
.text
.global_start
_start:
@********************************************************************
@中断向量
@********************************************************************
bReset
@0x04:
未定义指令中止模式的向量地址bHandlerUndef
@0x08:
管理模式的向量地址,通过SWI指令进入此模式bHandlerSWI
@0x0c:
指令预取终止导致的异常的向量地址
bHandlerPrefetchAbort
@0x10:
数据访问终止导致的异常的向量地址
cHandlerDataAbort
@0x14:
保留
dHandlerNotUsed
@0x18:
中断模式的向量地址
eHandlerIRQ
@0x1c:
快中断模式的向量地址
fHandlerFIQ
Reset:
ldrsp,=4096
@设置栈指针,以下都是
C函数,调用前需
要设好栈
bldisable_watch_dog
@关闭
WATCHDOG,否则
CPU会不断重启
ldrr0,=INTMSK
ldrr1,=0xffffffff
strr1,[r0]
@allinterruptdisable
ldrr0,=INTSUBMSK
ldrr1,=0x7fff
strr1,[r0]
@allsubinterruptdisable
blclock_init
blmemsetup
@设置MPLL,改变FCLK、HCLK、PCLK
@设置存储控制器以使用SDRAM
bls3c2440_nand_init
ldrr0,=0x30000000
@1.
目标地址
=0x30000000,这是
SDRAM的起
始地址
movr1,#4096
@2.
源地址
=4096
,运行地址在
SDRAM中
的代码保存在NANDFlash4096地址开始处
movr2,#180*1024@3.复制长度
=16K
,对于本实验,这是
足够了
blCopyCode2SDRAM
@调用
C函数
CopyCode2SDRAM
blclean_bss
@清除bss段,未初始化或初值为
0的全局/静
态变量保存在bss段
bl
InitStacks
@initalthestack
@SetupIRQhandler
ldrr0,=HandleIRQ
ldrr1,=IsrIRQ
0x1c
strr1,[r0]@
@Thisroutineisneeded
@ifthereisnot'subspc,lr,#4'at0x18,
这三条语句很明显就是说明了,HandleIRQ
这
个中断向量
@的存储单元被赋上了
IsrIRQ
标号的地址,这样
发生
IRQ中
@断后就会直接去到二级表,去确认具体发生哪
个中断。
ldrpc,=on_sdram
@跳到
SDRAM中继续执行
on_sdram:
msrcpsr_c,#0x5f
ldrsp,=0x34000000
ldrlr,=halt_loop
ldrpc,=main
@设置I-bit=0,开
@设置栈指针,
@设置返回地址
@调用main函数
IRQ中断
halt_loop:
bhalt_loop
HandlerFIQ:
HANDLER
HandleFIQ
HandlerIRQ:
HANDLER
HandleIRQ
HandlerUndef:
HANDLER
HandleUndef
HandlerSWI:
HANDLERHandleSWI
HandlerDataAbort:
HANDLERHandleDabort
HandlerPrefetchAbort:
HANDLERHandlePabort
HandlerNotUsed:
b.
InitStacks:
@DonotuseDRAM,suchasstmfd,ldmfd......
@SVCstackisinitializedbefore
@Undertoolkitver2.5,'msrcpsr,r1'canbeusedinsteadof'msrcpsr_cxsf,r1'
mrsr0,cpsr
bicr0,r0,#MODEMASK
orrr1,r0,#UNDEFMODE|NOINT
msrcpsr_c,r1@UndefMode
ldrsp,=UndefStack@UndefStack=0x33FF_5C00
orrr1,r0,#ABORTMODE|NOINT
msrcpsr_c,r1@AbortMode
ldrsp,=AbortStack@AbortStack=0x33FF_6000
orrr1,r0,#IRQMODE|NOINT
msrcpsr_c,r1@IRQMode
ldrsp,=IRQStack@IRQStack=0x33FF_7000
orrr1,r0,#FIQMODE|NOINT
msrcpsr_c,r1@FIQMode
ldrsp,=FIQStack@FIQStack=0x33FF_8000
bicr0,r0,#MODEMASK|NOINT
orrr1,r0,#SVCMODE
msrcpsr_c,r1@SVCMode
ldrsp,=SVCStack@SVCStack=0x33FF_5800
@USERmodehasnotbeinitialized.
movpc,lr
@theLRregisterwillnotbevalidifthecurrentmodeisnotSVCmode.
IsrIRQ:
sublr,lr,#4@
stmfdsp!
{r0-r12,lr}@
计算返回地址
保存使用到的寄存器
subsp,sp,#4
@reservedforPC;
保留
pc寄存器的值
stmfdsp!
{r8-r9}
@把r8r9
按入堆栈
ldrlr,=int_return
@
设置调用
ISR即
EINT_Handle函数后
的返回地址
ldrr9,=INTOFFSET
ldrr9,[r9]
ldrr8,=HandleEINT0
@把中断偏移INTOFFSET的地址装入
@取出INTOFFSET单元里面的值给r9
@向量表的入口地址赋给r8
r9
里面
addr8,r8,r9,lsl#2
@求出具体中断向量的地址
ldrr8,[r8]
@中断向量里面存储的中断服务程序的入口地址
赋给
r8
strr8,[sp,#8]
@按入堆栈
ldmfdsp!
{r8-r9,pc}
@堆栈弹出,跳转到相应的中断服务程序
int_return:
ldmfdsp!
{
r0-r12,pc
}^@
中断返回,^表示将
spsr
的值复制到
cpsr
.align4
.section.data
.equHandleReset,(_ISR_STARTADDRESS+0x0)
.equHandleUndef,(_ISR_STARTADDRESS+0x4)
.equHandleSWI,(_ISR_STARTADDRESS+0x8)
.equHandlePabort,(_ISR_STARTADDRESS+0xc)
.equHandleDabort,(_ISR_STARTADDRESS+0x10)
.equHandleReserved,(_ISR_STARTADDRESS+0x14)
.equHandleIRQ,(_ISR_STARTADDRESS+0x18)
.equHandleFIQ,(_ISR_STARTADDRESS+0x1c)
@Donotusethelabel'IntVectorTable',
@ThevalueofIntVectorTableisdifferentwiththeaddressyouthinkitmaybe.
@IntVectorTable
@0x33FF_FF20
.equHandleEINT0,(_ISR_STARTADDRESS+0x20)
.equHandleEINT1,(_ISR_STARTADDRESS+0x24)
.equHandleEINT2,(_ISR_STARTADDRESS+0x28)
.equHandleEINT3,(_ISR_STARTADDRESS+0x2c)
.equ
HandleEINT4_7,
(_ISR_STARTADDRESS+0x30)
.equ
HandleEINT8_23,(_ISR_STARTADDRESS+0x34)
.equ
HandleCAM,
(_ISR_STARTADDRESS+0x38)@Addedfor2440.
.equHandleBATFLT,(_ISR_STARTADDRESS+0x3c)
.equ
HandleTICK,
(_ISR_STARTADDRESS+0x40)
.equ
HandleWDT,(_ISR_STARTADDRESS+0x44)
.equ
HandleTIMER0,(_ISR_STARTADDRESS+0x48)
.equHandleTIMER1,(_ISR_STARTADDRESS+0x4c)
.equHandleTIMER2,(_ISR_STARTADDRESS+0x50)
.equHandleTIMER3,(_ISR_STARTADDRESS+0x54)
.equHandleTIMER4,(_ISR_STARTADDRESS+0x58)
.equ
HandleUART2,(_ISR_STARTADDRESS+0x5c)
@0x33FF_FF60
.equ
HandleLCD,
(_ISR_STARTADDRESS+0x60)
.equ
HandleDMA0,
(_ISR_STARTADDRESS+0x64)
.equ
HandleDMA1,
(_ISR_STARTADDRESS+0x68)
.equ
HandleDMA2,
(_ISR_STARTADDRESS+0x6c)
.equ
HandleDMA3,
(_ISR_STARTADDRESS+0x70)
.equ
HandleMMC,(_ISR_STARTADDRESS+0x74)
.equ
HandleSPI0,
(_ISR_STARTADDRESS+0x78)
.equ
HandleUART1,(_ISR_STARTADDRESS+0x7c)
.equ
HandleNFCON,(_ISR_STARTADDRESS+0x80)@Addedfor2440.
.equ
HandleUSBD,
(_ISR_STARTADDRESS+0x84)
.equ
HandleUSBH,
(_ISR_STARTADDRESS+0x88)
.equ
HandleIIC,
(_ISR_STARTADDRESS+0x8c)
.equHandleUART0,(_ISR_STARTADDRESS+0x90)
.equ
HandleSPI1,
(_ISR_STARTADDRESS+0x94)
.equ
HandleRTC,
(_ISR_STARTADDRESS+0x98)
.equ
HandleADC,
(_ISR_STARTADDRESS+0x9c)
以下部分是我们bootloader中调用到的c程序部分,主要是完成:
关看门狗、初始化存储器、初始化时钟、从Nandflash将代码拷贝到SDRAM、初始化数据段。
/*
*关闭WATCHDOG,否则CPU会不断重启
*/
voiddisable_watch_dog(void)
{
WTCON=0;//关闭WATCHDOG很简单,往这个寄存器写0即可
}
#defineS3C2410_MPLL_200MHZ((0x5c<<12)|(0x04<<4)|(0x00))
#defineS3C2440_MPLL_200MHZ((0x5c<<12)|(0x01<<4)|(0x02))
/*
*对于MPLLCON寄存器,[19:
12]为MDIV,[9:
4]为PDIV,[1:
0]为SDIV
*有如下计算公式:
*S3C2410:
MPLL(FCLK)=(m*Fin)/(p*2^s)
*S3C2410:
MPLL(FCLK)=(2*m*Fin)/(p*2^s)
*其中:
m=MDIV+8,p=PDIV+2,s=SDIV
*对于本开发板,Fin=12MHz
*设置CLKDIVN,令分频比为:
FCLK:
HCLK:
PCLK=1:
2:
4,
*FCLK=200MHz,HCLK=100MHz,PCLK=50MHz
*/
voidclock_init(void)
{
//LOCKTIME=0x00ffffff;//使用默认值即可
CLKDIVN=0x03;//FCLK:
HCLK:
PCLK=1:
2:
4,
HDIVN=1,PDIVN=1
/*如果HDIVN非0,CPU的总线模式应该从“fastbusmode”变为
“asynchronousbusmode”*/
__asm__(
"mrcp15,0,r1,c1,c0,0\n"/*读出控制寄存器*/
"orrr1,r1,#0xc0000000\n"/*设置为“asynchronous
busmode”*/
"mcrp15,0,r1,c1,c0,0\n"/*写入控制寄存器*/
);
/*判断是S3C2410还是S3C2440*/
if((GSTATUS1==0x32410000)||(GSTATUS1==0x32410002))
{
MPLLCON=S3C2410_MPLL_200MHZ;/*现在,
FCLK=200MHz,HCLK=100MHz,PCLK=50MHz*/
}
else
{
MPLLCON=S3C2440_MPLL_200MHZ;/*现在,
FCLK=200MHz,HCLK=100MHz,PCLK=50MHz*/
}
}
//设置存储控制器以使用SDRAM
#defineMEM_CTL_BASE0x48000000//存储器控制器寄存器的起始地
址
#defineSDRAM_BASE0x30000000
voidmemsetup(void)
{
volatileunsignedlong*p=(volatileunsignedlong*)MEM_CTL_BASE;
/*
这个函数之所以这样赋值,而不是像前面的实验
(比如
mmu实验)那样将
配置值
*写在数组中,是因为要生成
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ARM9 启动 代码 裸机 所谓 就是 处理器 时候 执行