原创 ARM Bootloader 的实现-------C 和ASM 混合编程

2007-5-15 08:13 3132 6 6 分类: MCU/ 嵌入式

ARM Bootloader 的实现-------C 和ASM 混合编程 


Cirrus Logic的clps7111~Ep9312 系列ARM core的CPU内置128 字节的boot 程 
序。这个boot程序为把操作系统下载到裸机提供了极大的方便。这样再焊接电路 
板之前不用把操作系统预先写入Flash,而且日后升级操作系统也非常方便。 

这个boot程序的功能是:

1. 设置串行口的参数为:9600, 8N1,No FlowControl。

2. 然后送出一个< 字符

3. 开始接收2K 字节程序(Bootloader)

4. 送出一个> 字符

5. 跳转去执行这2K 的程序。

烧写操作系统的过程是:

1. 连接ARM target的产性口和PC的串行口

ARM PC

RX ------------------- TX

TX ------------------- RX

GND ---------------- GND

2. 从BOOT程序引导ARM target

3. 在Windows NT4.0 的console中, 设置串行口的参数9600 8N1

C:>mode COM2: baud=9600 data=8 parity=n stop=1

4. 在console中把bootloader送到串行口。/b表示以二进制方式

C:>copy /b bootldr.bin COM1:

5. 在console中, 根据bootloader的设置来调整串行口的参数115200 8N1

C:>mode COM2: baud=115200 data=8 parity=n stop=1

6. 在console中把vxworks image送到串行口。/b表示以二进制方式

C:>copy /b vxworks COM1:

7. Power off ARM target,设置其从Flash启动。

8. reboot,进入VxWorks

这2K字节的程序就是我们说的ARM Bootloader,它的任务一般是:

1. 必要的硬件初始化

2. 从串行口接收VxWorks的二进制文件,并写入Flash

3. 在这过程中,显示一些提示信息。

像Bootloader 这样底层的程序一般认为是要用纯汇编来写的。但是用汇编写的程序

可读性肯定没有用C写的程序好。汇编程序不宜维护,没办法向其它类型的CPU

去移植。这些方面C的程序是没有问题的!_

 

那么Bootloader能否用纯C语言去写呢?不可能。因为有些操作特殊寄存器的指

令也是特殊指令,用C是实现不了的。有些功能用C也是不能实现的。用C不能

作的有:

1. 操作CP15寄存器的指令

2. 中断使能

3. 堆栈地址的设定

所以,只要知道这几条汇编指令可以了,不必学习所有的汇编指令。是不是上手很

快呀。下面来看看我们在Bootloader中所用到的汇编部分:

asm ("_my_start:

mov r14, #0x70

mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */

mrs r14, cpsr

bic r14, r14, #0x1f /* Mask */

orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */

msr cpsr, r14

ldr r13, =0xc0020000 /* Setup Stack */

");

简单吧,比看几十K的汇编程序感觉好得多吧。

也许你会问:硬件的初始化怎么办?那是要操作寄存器的。

我说:看看这段C的代码:

*((DWORD*)(dwHardwareBase + HW1_SYSCON1)) = SYSCON1_VALUE;

明白了吧,ARM中把寄存器映射在内存中了,就跟读写内存没有区别。

现在编写程序的问题已经全部解决了,但是否就没有问题了呢?不是。你的程序应

该写成什么样呢?怎么编译生成二进制文件呢?

让我们先写一个程序试一下吧:

#define DWORD unsigned int

int main(void)

{

register DWORD dwHardwareBase;

asm ("_my_start:

mov r14, #0x70

mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */

mrs r14, cpsr

bic r14, r14, #0x1f /* Mask */

orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */

msr cpsr, r14

ldr r13, =0xc0020000 /* Setup Stack */

");

dwHardwareBase = (DWORD)0x80000000;

return 0;

}

 

编译一下:

C:>ccarm -c -O2 -mcpu=arm710 -mlittle-endian -nostdlib -fvolatile -nostdinc –static

sam1.c

C:>ldarm -o sam1.out -Ttext 10000000 sam1.o

ldarm: warning: cannot find entry symbol _start; defaulting to 10000000

sam1.o(.text+0xc):fake: undefined reference to `__gccmain‘

sam1.o(.text+0xc):fake: relocation truncated to fit: ARM_26 __gccmain

我们发现应该把main定义成_start

#define DWORD unsigned int

void start(void) //gcc需要把它定义成_start,vxworks的egcs要把它定义成start。

{

register DWORD dwHardwareBase;

asm ("_my_start:

mov r14, #0x70

mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */

mrs r14, cpsr

bic r14, r14, #0x1f /* Mask */

orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */

msr cpsr, r14

ldr r13, =0xc0020000 /* Setup Stack */

");

dwHardwareBase = (DWORD)0x80000000;

}

编译一下:

C:>ccarm -c -O2 -mcpu=arm710 -mlittle-endian -nostdlib -fvolatile -nostdinc –static

sam1.c

C:>ldarm -o sam1.out -Ttext 10000000 sam1.o

C:>objdumparm -d sam1.out > sam1.asm

现在来看看汇编的源代码:

sam1.out: file format coff-arm-little

Disassembly of section .text:

10000000 <_start>:

10000000: e1a0c00d mov ip, sp

10000004: e92dd800 stmdb sp!, {fp, ip, lr, pc}

10000008: e24cb004 sub fp, ip, #4

1000000c <_my_start>:

1000000c: e3a0e070 mov lr, #70

10000010: ee01ef10 mcr 15, 0, lr, cr1, cr0, {0}

10000014: e10fe000 mrs lr, cpsr

10000018: e3cee01f bic lr, lr, #1f

1000001c: e38ee0d3 orr lr, lr, #d3

 

10000020: e129f00e msr cpsr, lr

10000024: e59fd000 ldr sp, 1000002c <$$lit__1>

10000028: e91ba800 ldmdb fp, {fp, sp, pc}

1000002c <$$lit__1>:

1000002c: c0020000 andgt r0, r2, r0

10000030 <__CTOR_LIST__>:

10000030: ffffffff swinv 0x00ffffff

10000034: 00000000 andeq r0, r0, r0

10000038 <__DTOR_LIST__>:

10000038: ffffffff swinv 0x00ffffff

1000003c: 00000000 andeq r0, r0, r0

哈哈!在<_start>和<_my_start>之间的代码在干什么?是在保存现场吧,还用到了

堆栈。而这时堆栈还没初始化,向堆栈里写东西那不乱套了!应该屏蔽这段代码。

那就在<_start>之前放一个跳转指令跳到<_my_start>吧。

#define DWORD unsigned int

asm ("

.text

.global _start

_start:

bl _my_start /* Omit the entry code for function c_start() */

");

void c_start(void)

{

register DWORD dwHardwareBase;

asm ("_my_start:

mov r14, #0x70

mcr p15, 0, r14, c1, c0, 0 /* MMU disabled, 32 Bit mode, Little endian */

mrs r14, cpsr

bic r14, r14, #0x1f /* Mask */

orr r14, r14, #0xc0 + 0x13 /* Diasble IRQ and FIQ, SVC32 Mode */

msr cpsr, r14

ldr r13, =0xc0020000 /* Setup Stack */

");

dwHardwareBase = (DWORD)0x80000000;

}
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
6
关闭 站长推荐上一条 /3 下一条