C语言环境的初始化是为了完成另外一些外设的初始化,如网口、串口等。毕竟在某些方面使用汇编没有C方便。
C语言环境的初始化主要完成以下几个方面的内容:
栈的初始化,BSS段的清除。
查找了一些网络资料,栈的主要作用是:保护现场/上下文,传递参数。更详细的解释,请参考uboot start.s 分析
因此在接下来要使用C语言,必须要对栈进行相应的初始化。
栈是一种具有先进后出性质的数据组织方式,也就是说后存放的先取出,先存放的
后取出。栈底是第一个进栈的数据所处的位置,栈顶是最后一个进栈的数据所处的
位置。
根据SP指针指向的位置,栈可以分为满栈和空栈。
1. 满栈:当堆栈指针SP总是指向最后压入堆栈的数据
2. 空栈:当堆栈指针SP总是指向下一个将要放入数据的空位置
ARM采用满栈!
根据SP指针移动的方向,栈可以分为升栈和降栈。
1. 升栈:随着数据的入栈,SP指针从低地址->高地址移动
2. 降栈:随着数据的入栈,SP指针从高地址->低地址移动
ARM采用降栈!
根据以前的知识可以得知,内存的其实地址为0x50000000 ,并且ARM是采用满降栈的方式,因此可以将栈的起始位置设置在0x50000000 + 64MB = 0x54000000,64MB这个值可以随意设置,只要不超过内存的0x57ffffff,这个地址即可。
init_stack: ldr sp,=0x54000000 mov pc,lr
BSS段:存放未初始化全局变量,对BSS段全部清零,防止数据出错
clean_bss:
ldr r0, =bss_start
ldr r1,=bss_end
cmp r0,r1 @将r0与r1进行比较,如果相等,说明没有数据,则不需要进行清除
moveq pc ,lr @如果相等,则返回
以下是全部代码:
@****************************
@file name: start.S
@author : stone
@date : 2016.6.30
@function :
@ 异常向量表
@ 设置SVC模式
@ 关闭看门狗
@ 关闭中断
@ 关闭MMU
@ 外设基地址初始化
@ 点亮LED
@ 时钟初始化
@ 内存初始化
@ 代码搬移
@ 初始化栈
@ 清除BSS段
@****************************
.text
.global _start @将_start声明为全局变量
_start:
b reset
ldr pc, _undefined_instruction
ldr pc, _software_interrupt
ldr pc, _prefetch_abort
ldr pc, _data_abort
ldr pc, _not_used
ldr pc, _irq
ldr pc, _fiq
_undefined_instruction: .word undefined_instruction
_software_interrupt: .word software_interrupt
_prefetch_abort: .word prefetch_abort
_data_abort: .word data_abort
_not_used: .word not_used
_irq: .word irq
_fiq: .word fiq
undefined_instruction: @处理未定义指令异常
nop
software_interrupt: @软中断
nop
prefetch_abort: @预取指令异常
nop
data_abort: @数据访问异常
nop
not_used: @空位
nop
irq: @中断
nop
fiq: @快速中断
nop
reset: @reset
bl set_svc @设置为SVC模式
bl set_peri_port @外设基地址初始化
bl disable_watchdog @关闭看门狗
bl disable_interrupt @关闭中断
bl disable_mmu @关闭mmu
bl init_clock @时钟初始化
bl mem_init @内存初始化
bl copy_to_ram @代码搬移
bl init_stack @初始化栈
bl clean_bss @初始化bss段
bl light_led @点亮LED
set_svc:
mrs r0, cpsr @将值取出cpsr寄存器
bic r0, r0, #0x1f @将后5位 即M[4:0]清零
orr r0, r0, #0xd3 @0b10011 转化为16进制为0x13 同时为了屏蔽irq和fiq,可以将其设置为0b11010011即0xd3
msr cpsr, r0 @将值送回cpsr寄存器
mov pc, lr @返回
set_peri_port:
ldr r0, =0x70000000 @基地址
orr r0, r0, #0x13 @256MB
mcr p15,0,r0,c15,c2,4 @写入cp15
mov pc, lr
#define pwTCON 0x7E004000 @WTCON寄存器
disable_watchdog:
ldr r0, =pwTCON @把地址装载到R0
mov r1, #0x0 @置0,关闭看门狗
str r1,[r0]
mov pc,lr
disable_interrupt:
mvn r1,#0x0 @0x0 取反,给r1
ldr r0,=0x71200014 @VIC0
str r1,[r0]
ldr r0,=0x71300014 @VIC1
str r1,[r0]
mov pc,lr
disable_mmu:
mcr p15,0,r0,c7,c7,0 @使ICACHE 和DCACHE 无效
mrc p15,0,r0,c1,c0,0 @read control register
bic r0,r0,#0x00000007 @mmu 和 dcache置零
mcr p15,0,r0,c1,c0,0 @write control register
mov pc,lr
#define CLK_DIV0 0x7e00f020
#define CLK_SRC 0x7e00f01c
#define OTHERS 0x7e00f900
#define MPLL_CON 0X7E00F010
#define APLL_CON 0X7E00F00c
#define PLL_VAL ((1<<31)|(266<<16)|(3<<8)|(1<<0))
#define DIV_VAL ((0X0<<0)|(0X1<<9)|(0X1<<8)|(0X3<<12))
init_clock:
ldr r0,=CLK_DIV0 @设置分频系数
ldr r1,=DIV_VAL
str r1,[r0]
ldr r0,=OTHERS @设置异步工作模式 第7位为0 第6位为0(时钟选择器)
ldr r1,[r0]
bic r1,r1,#0xc0
str r1,[r0]
ldr r0,=APLL_CON @APLL设置为533Mhz
ldr r1,=PLL_VAL
str r1,[r0]
ldr r0,=MPLL_CON @MPLL设置为533Mhz
ldr r1,=PLL_VAL
str r1,[r0]
ldr r0, =CLK_SRC @选择时钟源为APLL MPLL还是外部
mov r1, #0x3 @APLL MPLL
str r1, [r0]
mov pc,lr
copy_to_ram:
ldr r0, =0x0c000000 @起始地址
ldr r1, =0x50008000 @搬移地址
add r3, r0, #1024*4 @复制4KB数据
copy_loop:
ldr r2, [r0], #4 @从r0中读取数据
str r2, [r1], #4 @写入r1中
cmp r0, r3 @比较r0是否已经移动到了末尾
bne copy_loop
mov pc, lr
init_stack:
ldr sp,=0x54000000 @50000000 + 64
mov pc,lr
clean_bss:
ldr r0,=bss_start
ldr r1,=bss_end
cmp r0,r1 @将r0与r1进行比较,如果相等,说明没有数据,则不需要进行清除
moveq pc,lr @如果相等,则返回
clean_loop:
mov r2,#0
str r2,[r0],#4
cmp r0,r1
bne clean_loop
mov pc,lr
#define GPMCON 0x7F008820 @控制寄存器
#define GPMDAT 0x7F008824 @数据寄存器
light_led:
ldr r0,=GPMCON
ldr r1,=0x1111 @输出模式
str r1,[r0]
ldr r0,=GPMDAT
ldr r1,=0x00 @低电平点亮
str r1,[r0]
mov pc,lr
菜鸟一枚,如有错误,多多指教。。。