汇编启动

使用汇编代码启动S5PV210开发板。


使用汇编进行启动,需要经过下面几个基本步骤:

  • 关看门狗
  • 设置栈
  • 初始化iCache

看门狗

看门狗的概念

看门狗,watchDog Timer,其实是一个定时器,这个定时器每隔固定时间会发出一次让CPU复位的指令,以防止CPU跑飞或者出现其他异常,之后可以复位重置。

为什么要喂狗

在启动阶段如果不去喂狗,则看门狗就会发出复位指令,所以我们为了省事,一般在启动的前阶段关闭看门狗,当系统起来之后再决定是否打开看门狗,一旦打开就需要定时喂狗,否则就会复位。
在S5PV210的iROM代码BL0中其实已经关闭看门狗了,我们在这里再关一遍只是为了例行公事,因为在其他开发板或者Soc中还是有很多没有BL0的,所以它不会给你关看门狗。

看门狗的关闭位置

查看用户手册得到,看门狗寄存器有这么几个:

RegisterAddressR/WDescriptionReset Value
WTCON0xE270_0000R/WWatchdog Timer Control Register0x00008021
WTDAT0xE270_0004R/WWatchdog Timer Data Register0x00008000
WTCNT0xE270_0008R/WWatchdog Timer Count Register0x00008000
WTCLRINT0xE270_000CWWatchdog Timer Interrupt Clear Register-

关键操作的就是WTCON寄存器,第5位表示看门狗的开关,1代表开,0代表关。
汇编代码如下:

#define WTCON 0xE2700000
// 关闭看门狗
ldr r0,=0<<5
ldr r1,=WTCON
str r0,[r1]

关门狗代码应该放在程序最前边,越早关闭看门狗,就越早不会受到打扰。

设置栈和C语言调用

C语言运行环境

C语言的运行需要一定的环境和条件,这些环境和条件由汇编提供,只有当运行环境完备之后,C语言才能正常的运行起来,这也是在内核中起始代码只能为汇编的原因,C语言的运行环境最重要的元素就是栈。

栈的概念

栈是一种数据类型的表示,这种数据类型具有先进后出(FILO)的特性,在C语言中,所有的局部变量都存在于栈中.
栈的寄存器是SP,在每种模式下都有自己的独立SP寄存器,这样每个工作模式的栈互不影响,如果我们要设置栈,不可能也没有权限设置每种模式下的栈,只能设置当前当前程序下的栈,所以需要知道自己当前程序的栈。
通过查阅用户手册可知,系统上电/复位之后是默认进入SVC模式的,所以我们需要访问SVC模式下的栈寄存器,则需要这样的步骤:

  • 设置当前模式为SVC,
  • 设置SP寄存器

系统复位之后默认是SVC模式,所以可以直接设置SP寄存器,由于当前CPU刚复位,则外部DRAM尚未被初始化,可用的内存只有内部的SRAM,而且SRAM不需要初始化就可以使用,所以我们的栈只能设置在SRAM中。
我们查阅iROM文档得到SRAM的分配图,则得到SVC Stack区域的范围,内存地址为0xD0037780~0xD0037D80的1.5K的区域,
我们已经知道栈的进出形式,有增减,满增和满减几种形式,我们的ARM的ATPCS中要求使用满减栈,根据满减栈的规则,则我们的栈设置的位置就在0xD0037D80这个起始地址上,以后的压栈和出栈都会基于这个地址下的1.5K内存区域进行,汇编代码为:

#define SVC_STACK 0xD0037D80
// 设置SVC栈
ldr sp,=SVC_STACK   // 从这里开始可以愉快的调用C程序了

我们可以将原来的led.S汇编代码改造成调用C语言来实现,则汇编代码为:

#define WTCON 0xE2700000

#define SVC_STACK 0xD0037D80

.global _start          // 把_start声明为外部可访问,以便汇编程序定位到这里开始运行
_start:
// 关闭看门狗
ldr r0,=0<<5
ldr r1,=WTCON
str r0,[r1]

// 设置SVC栈
ldr sp,=SVC_STACK   // 从这里开始可以愉快的调用C程序了

bl led_blink        // 调用C语言函数led_blink

b.          // 死循环不能丢

调用语言进行LED闪烁的代码为:

#define GPJ0CON 0xE0200240
#define GPD0CON 0xE02000A0

#define GPJ0DAT 0xE0200244
#define GPD0DAT 0xE02000A4

#define rGPJ0CON *((int *)GPJ0CON)
#define rGPD0CON *((int *)GPD0CON)

#define rGPJ0DAT *((int *)GPJ0DAT)
#define rGPD0DAT *((int *)GPD0DAT)

void sleep();

void led_blink() {
    // 初始化LED寄存器,设置寄存器模式为输出
    rGPJ0CON = 1<<12 | 1<<16 | 1<<20;
    rGPD0CON = 1<<4;
    while (1)
    {
        // led3亮
        rGPJ0DAT = 0<<3 | 1<<4 | 1<<5;
        rGPD0DAT = 1<<1;
        // 延时
        sleep();
        // led4亮
        rGPJ0DAT = 1<<3 | 0<<4 | 1<<5;
        rGPD0DAT = 1<<1;
        // 延时
        sleep();
        // led5亮
        rGPJ0DAT = 1<<3 | 1<<4 | 0<<5;
        rGPD0DAT = 1<<1;
        // 延时
        sleep();
        // led6亮
        rGPJ0DAT = 1<<3 | 1<<4 | 1<<5;
        rGPD0DAT = 0<<1;
        // 延时
        sleep();
    }
}

void sleep() {
    volatile int i = 999999;    // volatile 禁止编译器优化该句代码,就按照我们写的执行
    while(i--);                 // 这样才能真正的延时
}

指令Cache(iCache)

Cache是一种高速缓存,iCache是寄存器和DDR之间的一块高速内存,用于平衡DDR和寄存器之间的速度差异,S5PV210内部有32K的I/D Cache,ICache是指令缓存,DCache是数据缓存。
iCache的一切动作都是自动的,不需要认为干预,我们所需要做的就是打开或者关闭iCache,S5PV210在iROM中的BL0已经打开了iCache。
iCache在CP15协处理器中进行操作,CP15中的C1引脚中bit12就是iCache的开关,0为关闭,1为开启,汇编代码为:

// 开关icache
mrc p15,0,r0,c1,c0,0;   // 读取CP15协处理器的c1寄存器到r0中
bic r0,r0,#(1<<12)  // bit12置为0,关闭iCache
// orr r0,r0,#(1<<12)   // bit12置为1,开启iCache
mcr p15,0,r0,c1,c0,0;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值