linux arm9 clkout0,ARM9启动代码(裸机版)

ARM9启动代码(裸机版)

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

在一般32位ARM应用系统中,软件大多数采用c语言进行编程,并且以嵌入式操作系统为开发平台,这样就大大提高了开发效率和软件性能。为了能够进行系统初始化,通常采用一个汇编文件作启动代码。它可以实现向量表定义、堆栈初始化、系统变量初始化、中断系统初始化、I/O初始化、地址重映射等操作。

启动代码是芯片复位后进入c语言的main函数前执行的一段代码,主要是为运行c语言程序提供基本运行环境。

在实际应用中,为提高系统的实时性,加快代码的执行速度,系统启动后程序往往要被搬移到RAM中,因为RAM的存取速度要比ROM快得多,这样大大提升系统的性能。启动程序要完成的任务包括:硬件初始化,系统存储系统的配置,复制二级中断向量表。

启动程序大概过程如下:

(1)系统硬件初始化

系统上电或复位后,程序从位于地址0x0的Reset Exception Vector处开始执行,因此需要在这里放置bootloader的第一条指令:b Reset,跳转到标号为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-defined constants

.equ USERMODE, 0x10

.equ FIQMODE,0x11

.equIRQMODE,0x12

.equSVCMODE,0x13

.equABORTMODE,0x17

.equUNDEFMODE,0x1b

.equMODEMASK,0x1f

.equNOINT,0xc0

@各个异常模式的堆栈

@The location of stacks

.equ_STACK_BASEADDRESS,0x33ff8000

.equ_ISR_STARTADDRESS,0x31ffff00

.equUserStack,(_STACK_BASEADDRESS-0x3800)@0x33ff4800 ~

.equSVCStack,(_STACK_BASEADDRESS-0x2800)@0x33ff5800 ~

.equUndefStack,(_STACK_BASEADDRESS-0x2400)@0x33ff5c00 ~

.equAbortStack,(_STACK_BASEADDRESS-0x2000)@0x33ff6000 ~

.equIRQStack,(_STACK_BASEADDRESS-0x1000)@0x33ff7000 ~

.equFIQStack,(_STACK_BASEADDRESS-0x0)@0x33ff8000 ~

.equINTMSK,0x4a000008@Interrupt mask control

.equINTSUBMSK,0x4a00001c@Interrupt sub mask

.equINTOFFSET,0x4a000014@Interruot request source offset

.macro HANDLER HandleLabel

subsp,sp,#4@decrement sp(to store jump address)

stmfdsp!,{r0}@PUSH the work register to stack(lr does not push because it return to original address)

ldrr0,=\HandleLabel@load the address of HandleXXX to r0

ldrr0,[r0]@load the contents(service routine start address) of HandleXXX

strr0,[sp,#4]@store the contents(ISR) of HandleXXX to stack

ldmfdsp!,{r0,pc}@POP the work register and pc(jump to ISR)

.endm

.externmain

.text

.global _start

_start:

@********************************************************************

@中断向量

@********************************************************************

bReset

@ 0x04:未定义指令中止模式的向量地址

bHandlerUndef

@ 0x08:管理模式的向量地址,通过SWI指令进入此模式

bHandlerSWI

@ 0x0c:指令预取终止导致的异常的向量地址

bHandlerPrefetchAbort

@ 0x10:数据访问终止导致的异常的向量地址

bHandlerDataAbort

@ 0x14:保留

bHandlerNotUsed

@ 0x18:中断模式的向量地址

bHandlerIRQ

@ 0x1c:快中断模式的向量地址

bHandlerFIQ

Reset:

ldr sp, =4096@设置栈指针,以下都是C函数,调用前需要设好栈

bldisable_watch_dog@关闭WATCHDOG,否则CPU会不断重启

ldrr0,=INTMSK

ldrr1,=0xffffffff@all interrupt disable

strr1,[r0]

ldrr0,=INTSUBMSK

ldrr1,=0x7fff@all sub interrupt disable

strr1,[r0]

blclock_init@设置MPLL,改变FCLK、HCLK、PCLK

blmemsetup@设置存储控制器以使用SDRAM

bls3c2440_nand_init

ldr r0, =0x30000000 @ 1.目标地址= 0x30000000,这是SDRAM的起始地址

mov r1, #4096@ 2.源地址= 4096,运行地址在SDRAM中的代码保存在NAND Flash 4096地址开始处

mov r2, #180*1024 @ 3.复制长度= 16K,对于本实验,这是足够了

blCopyCode2SDRAM@调用C函数CopyCode2SDRAM

blclean_bss@清除bss段,未初始化或初值为0的全局/静态变量保存在bss段

blInitStacks@inital the stack

@ Setup IRQ handler

ldrr0,=HandleIRQ@This routine is needed

ldrr1,=IsrIRQ@if there is not 'subs pc,lr,#4' at 0x18, 0x1c

strr1,[r0]@这三条语句很明显就是说明了,HandleIRQ这个中断向量

@的存储单元被赋上了IsrIRQ标号的地址,这样发生IRQ中

@断后就会直接去到二级表,去确认具体发生哪个中断。

ldr pc, =on_sdram@跳到SDRAM中继续执行

on_sdram:

msr cpsr_c, #0x5f@设置I-bit=0,开IRQ中断

ldr sp, =0x34000000@设置栈指针,

ldr lr, =halt_loop@设置返回地址

ldr pc, =main@调用main函数

halt_loop:

bhalt_loop

HandlerFIQ:

HANDLER HandleFIQ

HandlerIRQ:

HANDLER HandleIRQ

HandlerUndef:

HANDLER HandleUndef

HandlerSWI:

HANDLER HandleSWI

HandlerDataAbort:

HANDLER HandleDabort

HandlerPrefetchAbort:

HANDLER HandlePabort

HandlerNotUsed:

b.

InitStacks:

@Do not use DRAM,such as stmfd,ldmfd......

@SVCstack is initialized before

@Under toolkit ver 2.5, 'msr cpsr,r1' can be used instead of 'msr cpsr_cxsf,r1'

mrs r0,cpsr

bic r0,r0,#MODEMASK

orr r1,r0,#UNDEFMODE|NOINT

msr cpsr_c,r1@UndefMode

ldr sp,=UndefStack@ UndefStack=0x33FF_5C00

orr r1,r0,#ABORTMODE|NOINT

msr cpsr_c,r1@AbortMode

ldr sp,=AbortStack@ AbortStack=0x33FF_6000

orr r1,r0,#IRQMODE|NOINT

msr cpsr_c,r1@IRQMode

ldr sp,=IRQStack@ IRQStack=0x33FF_7000

orr r1,r0,#FIQMODE|NOINT

msr cpsr_c,r1@FIQMode

ldr sp,=FIQStack@ FIQStack=0x33FF_8000

bic r0,r0,#MODEMASK|NOINT

orr r1,r0,#SVCMODE

msr cpsr_c,r1@SVCMode

ldr sp,=SVCStack@ SVCStack=0x33FF_5800

@USER mode has not be initialized.

mov pc,lr

@the LR register will not be valid if the current mode is not SVC mode.

IsrIRQ:

sub lr, lr, #4@计算返回地址

stmfdsp!,{ r0-r12,lr }@保存使用到的寄存器

sub sp,sp,#4@reserved for PC;保留pc寄存器的值

stmfdsp!,{r8-r9}@把r8 r9按入堆栈

ldr lr, =int_return@设置调用ISR即EINT_Handle函数后的返回地址

ldr r9,=INTOFFSET@把中断偏移INTOFFSET的地址装入r9里面

ldr r9,[r9] @取出INTOFFSET单元里面的值给r9

ldr r8,=HandleEINT0@向量表的入口地址赋给r8

add r8,r8,r9,lsl #2@求出具体中断向量的地址

ldr r8,[r8] @中断向量里面存储的中断服务程序的入口地址赋给r8

str r8,[sp,#8]@按入堆栈

ldmfdsp!,{r8-r9,pc}@堆栈弹出,跳转到相应的中断服务程序

int_return:

ldmfdsp!,{ r0-r12,pc }^@中断返回, ^表示将spsr的值复制到cpsr

.align 4

.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)

@Do not use the label 'IntVectorTable',

@The value of IntVectorTable is different with the address you think it may be.

@IntVectorTable

@0x33FF_FF20

.equHandleEINT0, (_ISR_STARTADDRESS+0x20)

.equHandleEINT1,(_ISR_STARTADDRESS+0x24)

.equHandleEINT2,(_ISR_STARTADDRESS+0x28)

.equHandleEINT3,(_ISR_STARTADDRESS+0x2c)

.equHandleEINT4_7,(_ISR_STARTADDRESS+0x30)

.equHandleEINT8_23,(_ISR_STARTADDRESS+0x34)

.equHandleCAM,(_ISR_STARTADDRESS+0x38)@ Added for 2440.

.equHandleBATFLT,(_ISR_STARTADDRESS+0x3c)

.equHandleTICK,(_ISR_STARTADDRESS+0x40)

.equHandleWDT,(_ISR_STARTADDRESS+0x44)

.equHandleTIMER0,(_ISR_STARTADDRESS+0x48)

.equHandleTIMER1,(_ISR_STARTADDRESS+0x4c)

.equHandleTIMER2,(_ISR_STARTADDRESS+0x50)

.equHandleTIMER3,(_ISR_STARTADDRESS+0x54)

.equHandleTIMER4,(_ISR_STARTADDRESS+0x58)

.equHandleUART2,(_ISR_STARTADDRESS+0x5c)

@0x33FF_FF60

.equHandleLCD,(_ISR_STARTADDRESS+0x60)

.equHandleDMA0,(_ISR_STARTADDRESS+0x64)

.equHandleDMA1,(_ISR_STARTADDRESS+0x68)

.equHandleDMA2,(_ISR_STARTADDRESS+0x6c)

.equHandleDMA3,(_ISR_STARTADDRESS+0x70)

.equHandleMMC,(_ISR_STARTADDRESS+0x74)

.equHandleSPI0,(_ISR_STARTADDRESS+0x78)

.equHandleUART1,(_ISR_STARTADDRESS+0x7c)

.equHandleNFCON, (_ISR_STARTADDRESS+0x80)@ Added for 2440.

.equHandleUSBD,(_ISR_STARTADDRESS+0x84)

.equHandleUSBH,(_ISR_STARTADDRESS+0x88)

.equHandleIIC,(_ISR_STARTADDRESS+0x8c)

.equHandleUART0, (_ISR_STARTADDRESS+0x90)

.equHandleSPI1,(_ISR_STARTADDRESS+0x94)

.equHandleRTC,(_ISR_STARTADDRESS+0x98)

.equHandleADC,(_ISR_STARTADDRESS+0x9c)

以下部分是我们bootloader中调用到的c程序部分,主要是完成:关看门狗、初始化存储器、初始化时钟、从Nandflash将代码拷贝到SDRAM、初始化数据段。

/*

*关闭WATCHDOG,否则CPU会不断重启

*/

void disable_watch_dog(void)

{

WTCON = 0;//关闭WATCHDOG很简单,往这个寄存器写0即可

}

#define S3C2410_MPLL_200MHZ((0x5c<<12)|(0x04<<4)|(0x00))

#define S3C2440_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

*/

void clock_init(void)

{

// LOCKTIME = 0x00ffffff;//使用默认值即可

CLKDIVN= 0x03;// FCLK:HCLK:PCLK=1:2:4, HDIVN=1,PDIVN=1

/*如果HDIVN非0,CPU的总线模式应该从“fast bus mode”变为“asynchronous bus mode”*/

__asm__(

"mrcp15, 0, r1, c1, c0, 0\n"/*读出控制寄存器*/

"orrr1, r1, #0xc0000000\n"/*设置为“asynchronous bus mode”*/

"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

void memsetup(void)

{

volatile unsigned long *p = (volatile unsigned long *)MEM_CTL_BASE;

/*这个函数之所以这样赋值,而不是像前面的实验(比如mmu实验)那样将配置值

*写在数组中,是因为要生成”位置无关的代码”,使得这个函数可以在被复制到

* SDRAM之前就可以在steppingstone中运行

*/

/*存储控制器13个寄存器的值*/

p[0] = 0x22011110;//BWSCON

p[1] = 0x00000700;//BANKCON0

p[2] = 0x00000700;//BANKCON1

p[3] = 0x00000700;//BANKCON2

p[4] = 0x00000700;//BANKCON3

p[5] = 0x00000700;//BANKCON4

p[6] = 0x00000700;//BANKCON5

p[7] = 0x00018005;//BANKCON6

p[8] = 0x00018005;//BANKCON7

/* REFRESH,

* HCLK=12MHz:0x008C07A3,

* HCLK=100MHz: 0x008C04F4

*/

p[9]= 0x008C04F4;

p[10] = 0x000000B1;//BANKSIZE

p[11] = 0x00000030;//MRSRB6

p[12] = 0x00000030;//MRSRB7

}

/*

void copy_steppingstone_to_sdram(void)

{

unsigned int *pdwSrc= (unsigned int *)0;

unsigned int *pdwDest = (unsigned int *)0x30000000;

while (pdwSrc < (unsigned int *)4096)//copy 4K bytes

{

*pdwDest = *pdwSrc;

pdwDest++;

pdwSrc++;

}

}*/

int CopyCode2SDRAM(unsigned char *buf, unsigned long start_addr, int size)

{

extern void s3c2440_nand_read(unsigned char *buf, unsigned long start_addr, int size);

s3c2440_nand_read(buf, start_addr, size);

return 0;

}

void clean_bss(void)

{

extern int __bss_start, __bss_end;

int *p = &__bss_start;

for (; p < &__bss_end; p++)

*p = 0;

}

__bss_start, __bss_end在连接脚本中有定义,连接脚本(其书写方式在其它的文档中已有作介绍)如下:

SECTIONS {

. = 0x00000000;

.init : AT(0){ /home/txgcwm/weidongshan/arm_sources/code/obj/2440start.o /home/txgcwm/weidongshan/arm_sources/code/obj/init.o /home/txgcwm/weidongshan/arm_sources/code/obj/nand.o}

. = 0x30000000;

.text : AT(4096) { *(.text) }

.rodata ALIGN(4) : AT((LOADADDR(.text)+SIZEOF(.text)+3)&~(0x03)) {*(.rodata*)}

.data ALIGN(4): AT((LOADADDR(.rodata)+SIZEOF(.rodata)+3)&~(0x03)) { *(.data) }

__bss_start = .;

.bss ALIGN(4): { *(.bss)*(COMMON) }

__bss_end = .;

}

以上就是最基本的bootloader的书写(仅供参考),其中还有很大的不足,有兴趣的朋友可作补充修改。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值