ARM的嵌入式Linux移植体验之BootLoader.docx
- 文档编号:25921924
- 上传时间:2023-06-16
- 格式:DOCX
- 页数:19
- 大小:79.54KB
ARM的嵌入式Linux移植体验之BootLoader.docx
《ARM的嵌入式Linux移植体验之BootLoader.docx》由会员分享,可在线阅读,更多相关《ARM的嵌入式Linux移植体验之BootLoader.docx(19页珍藏版)》请在冰豆网上搜索。
ARM的嵌入式Linux移植体验之BootLoader
ARM的嵌入式Linux移植体验之BootLoader
2007-04-2523:
03
2006-08-1208:
00作者:
宋宝华出处:
天极开发
BootLoader指系统启动后,在操作系统内核运行之前运行的一段小程序。
通过BootLoader,我们可以初始化硬件设备、建立内存空间的映射图,从而将系统的软硬件环境带到一个合适的状态,以便为最终调用操作系统内核准备好正确的环境。
通常,BootLoader是严重地依赖于硬件而实现的,特别是在嵌入式世界。
因此,在嵌入式世界里建立一个通用的BootLoader几乎是不可能的。
尽管如此,我们仍然可以对BootLoader归纳出一些通用的概念来,以指导用户特定的BootLoader设计与实现。
BootLoader的实现依赖于CPU的体系结构,因此大多数BootLoader都分为stage1和stage2两大部分。
依赖于CPU体系结构的代码,比如设备初始化代码等,通常都放在stage1中,而且通常都用汇编语言来实现,以达到短小精悍的目的。
而stage2则通常用C语言来实现,这样可以实现更复杂的功能,而且代码会具有更好的可读性和可移植性。
BootLoader的stage1通常包括以下步骤:
·硬件设备初始化;
·为加载BootLoader的stage2准备RAM空间;
·拷贝BootLoader的stage2到RAM空间中;
·设置好堆栈;
·跳转到stage2的C入口点。
BootLoader的stage2通常包括以下步骤:
·初始化本阶段要使用到的硬件设备;
·检测系统内存映射(memorymap);
·将kernel映像和根文件系统映像从flash上读到RAM空间中;
·为内核设置启动参数;
·调用内核。
本系统中的BootLoader参照韩国mizi公司的vivi进行修改。
1.开发环境
我们购买了武汉创维特信息技术有限公司开发的具有自主知识产权的应用于嵌入式软件开发的集成软、硬件开发平台ADT(ARMDevelopmentTools)它为基于ARM核的嵌入式应用提供了一整套完备的开发方案,包括程序编辑、工程管理和设置、程序编译、程序调试等。
ADT嵌入式开发环境由ADTEmulatorforARM和ADTIDEforARM组成。
ADTEmulatorforARM通过JTAG实现主机和目标机之间的调试支持功能。
它无需目标存储器,不占用目标系统的任何端口资源。
目标程序直接在目标板上运行,通过ARM芯片的JTAG边界扫描口进行调试,属于完全非插入式调试,其仿真效果接近真实系统。
ADTIDEforARM为用户提供高效明晰的图形化嵌入式应用软件开发环境,包括一整套完备的面向嵌入式系统的开发和调试工具:
源码编辑器、工程管理器、工程编译器(编译器、汇编器和连接器)、集成调试环境、ADTEmulatorforARM调试接口等。
其界面同MicrosoftVisualStudio环境相似,用户可以在ADTIDEforARM集成开发环境中创建工程、打开工程,建立、打开和编辑文件,编译、连接、设置、运行、调试嵌入式应用程序。
ADT嵌入式软件开发环境采用主机-目标机交叉开发模型。
ADTIDEforARM运行于主机端,而ADTEmulatorforARM实现ADTIDEforARM与目标机之间的连接。
开发时,首先由ADTIDEforARM编译连接生成目标代码,然后建立与ADTEmulatorforARM之间的调试通道,调试通道建立成功后,就可以在ADTIDEforARM中通过ADTEmulatorforARM控制目标板实现目标程序的调试,包括将目标代码下载到目标机中,控制程序运行,调试信息观察等等。
2.ARM汇编
ARM本身属于RISC指令系统,指令条数就很少,而其编程又以C等高级语言为主,我们仅需要在Bootloader的第一阶段用到少量汇编指令:
(1)+-运算
ADDr0,r1,r2
――r0:
=r1+r2
SUBr0,r1,r2
――r0:
=r1-r2
其中的第二个操作数可以是一个立即数:
ADDr3,r3,#1
――r3:
=r3+1
第二个操作数还可以是位移操作后的结果:
ADDr3,r2,r1,LSL#3
――r3:
=r2+8.r1
(2)位运算
ANDr0,r1,r2
――r0:
=r1andr2
ORRr0,r1,r2
――r0:
=r1orr2
EORr0,r1,r2
――r0:
=r1xorr2
BICr0,r1,r2
――r0:
=r1andnotr2
(3)寄存器搬移
MOVr0,r2
――r0:
=r2
MVNr0,r2
――r0:
=notr2
(4)比较
CMPr1,r2
――setcconr1-r2
CMNr1,r2
――setcconr1+r2
TSTr1,r2
――setcconr1andr2
TEQr1,r2
――setcconr1orr2
这些指令影响CPSR寄存器中的(N,Z,C,V)位
(5)内存操作
LDRr0,[r1]
――r0:
=mem[r1]
STRr0,[r1]
――mem[r1]:
=r0
LDRr0,[r1,#4]
――r0:
=mem[r1+4]
LDRr0,[r1,#4]!
――r0:
=mem[r1+4]r1:
=r1+4
LDRr0,[r1],#4
――r0:
=mem[r1]r1:
=r1+4
LDRBr0,[r1]
――r0:
=mem8[r1]
LDMIAr1,{r0,r2,r5}
――r0:
=mem[r1]r2:
=mem[r1+4]r5:
=mem[r1+8]
{..}可以包括r0~r15中的所有寄存器,若包括r15(PC)将导致程序的跳转。
(6)控制流
例1:
MOVr0,#0;initializecounter
LOOP:
ADDr0,r0,#1;incrementcounter
CMPr0,#10;comparewithlimit
BNELOOP;repeatifnotequal
例2:
CMPr0,#5
ADDNEr1,r1,r0
SUBNEr1,r1,r2
――
if(r0!
=5){
r1:
=r1+r0-r2
}
3.BootLoader第一阶段
3.1硬件设备初始化
基本的硬件初始化工作包括:
·屏蔽所有的中断;
·设置CPU的速度和时钟频率;
·RAM初始化;
·初始化LED
ARM的中断向量表设置在0地址开始的8个字空间中,如下表:
每当其中的某个异常发生后即将PC值置到相应的中断向量处,每个中断向量处放置一个跳转指令到相应的中断服务程序去进行处理,中断向量表的程序如下:
@0x00:
Reset
bReset
@0x04:
Undefinedinstructionexception
UndefEntryPoint:
bHandleUndef
@0x08:
Softwareinterruptexception
SWIEntryPoint:
bHandleSWI
@0x0c:
PrefetchAbort(InstructionFetchMemoryAbort)
PrefetchAbortEnteryPoint:
bHandlePrefetchAbort
@0x10:
DataAccessMemoryAbort
DataAbortEntryPoint:
bHandleDataAbort
@0x14:
Notused
NotUsedEntryPoint:
bHandleNotUsed
@0x18:
IRQ(InterruptRequest)exception
IRQEntryPoint:
bHandleIRQ
@0x1c:
FIQ(FastInterruptRequest)exception
FIQEntryPoint:
bHandleFIQ
复位时关闭看门狗定时器、屏蔽所有中断:
Reset:
@disablewatchdogtimer
movr1,#0x53000000
movr2,#0x0
strr2,[r1]
@disableallinterrupts
movr1,#INT_CTL_BASE
movr2,#0xffffffff
strr2,[r1,#oINTMSK]
ldrr2,=0x7ff
strr2,[r1,#oINTSUBMSK]
设置系统时钟:
@initclk
@1:
2:
4
movr1,#CLK_CTL_BASE
movr2,#0x3
strr2,[r1,#oCLKDIVN]
mrcp15,0,r1,c1,c0,0@readctrlregister
orrr1,r1,#0xc0000000@Asynchronous
mcrp15,0,r1,c1,c0,0@writectrlregister
@now,CPUclockis200Mhz
movr1,#CLK_CTL_BASE
ldrr2,mpll_200mhz
strr2,[r1,#oMPLLCON]
点亮所有的用户LED:
@AllLEDon
movr1,#GPIO_CTL_BASE
addr1,r1,#oGPIO_F
ldrr2,=0x55aa
strr2,[r1,#oGPIO_CON]
movr2,#0xff
strr2,[r1,#oGPIO_UP]
movr2,#0x00
strr2,[r1,#oGPIO_DAT]
设置(初始化)内存映射:
ENTRY(memsetup)
@initialisethestaticmemory
@setmemorycontrolregisters
movr1,#MEM_CTL_BASE
adrlr2,mem_cfg_val
addr3,r1,#52
1:
ldrr4,[r2],#4
strr4,[r1],#4
cmpr1,r3
bne1b
movpc,lr
设置(初始化)UART:
@setGPIOforUART
movr1,#GPIO_CTL_BASE
addr1,r1,#oGPIO_H
ldrr2,gpio_con_uart
strr2,[r1,#oGPIO_CON]
ldrr2,gpio_up_uart
strr2,[r1,#oGPIO_UP]
blInitUART
@InitializeUART
@
@r0=numberofUARTport
InitUART:
ldrr1,SerBase
movr2,#0x0
strr2,[r1,#oUFCON]
strr2,[r1,#oUMCON]
movr2,#0x3
strr2,[r1,#oULCON]
ldrr2,=0x245
strr2,[r1,#oUCON]
#defineUART_BRD((50000000/(UART_BAUD_RATE*16))-1)
movr2,#UART_BRD
strr2,[r1,#oUBRDIV]
movr3,#100
movr2,#0x0
1:
subr3,r3,#0x1
tstr2,r3
bne1b
#if0
movr2,#'U'
strr2,[r1,#oUTXHL]
1:
ldrr3,[r1,#oUTRSTAT]
andr3,r3,#UTRSTAT_TX_EMPTY
tstr3,#UTRSTAT_TX_EMPTY
bne1b
movr2,#'0'
strr2,[r1,#oUTXHL]
1:
ldrr3,[r1,#oUTRSTAT]
andr3,r3,#UTRSTAT_TX_EMPTY
tstr3,#UTRSTAT_TX_EMPTY
bne1b
#endif
movpc,lr
此外,vivi还提供了几个汇编情况下通过串口打印字符的函数PrintChar、PrintWord和PrintHexWord:
@PrintChar:
printsthecharacterinR0
@r0containsthecharacter
@r1containsbaseofserialport
@writesrowithXXX,modifiesr0,r1,r2
@TODO:
writerowithXXXregtoerrorhandling
PrintChar:
TXBusy:
ldrr2,[r1,#oUTRSTAT]
andr2,r2,#UTRSTAT_TX_EMPTY
tstr2,#UTRSTAT_TX_EMPTY
beqTXBusy
strr0,[r1,#oUTXHL]
movpc,lr
@PrintWord:
printsthe4charactersinR0
@r0containsthebinaryword
@r1containsthebaseoftheserialport
@writesrowithXXX,modifiesr0,r1,r2
@TODO:
writerowithXXXregtoerrorhandling
PrintWord:
movr3,r0
movr4,lr
blPrintChar
movr0,r3,LSR#8/*shiftwordright8bits*/
blPrintChar
movr0,r3,LSR#16/*shiftwordright16bits*/
blPrintChar
movr0,r3,LSR#24/*shiftwordright24bits*/
blPrintChar
movr0,#'\r'
blPrintChar
movr0,#'\n'
blPrintChar
movpc,r4
@PrintHexWord:
printsthe4bytesinR0as8hexasciicharacters
@followedbyanewline
@r0containsthebinaryword
@r1containsthebaseoftheserialport
@writesrowithXXX,modifiesr0,r1,r2
@TODO:
writerowithXXXregtoerrorhandling
PrintHexWord:
movr4,lr
movr3,r0
movr0,r3,LSR#28
blPrintHexNibble
movr0,r3,LSR#24
blPrintHexNibble
movr0,r3,LSR#20
blPrintHexNibble
movr0,r3,LSR#16
blPrintHexNibble
movr0,r3,LSR#12
blPrintHexNibble
movr0,r3,LSR#8
blPrintHexNibble
movr0,r3,LSR#4
blPrintHexNibble
movr0,r3
blPrintHexNibble
movr0,#'\r'
blPrintChar
movr0,#'\n'
blPrintChar
movpc,r4
3.2Bootloader拷贝
配置为从NANDFLASH启动,需要将NANDFLASH中的vivi代码copy到RAM中:
#ifdefCONFIG_S3C2410_NAND_BOOT
blcopy_myself
@jumptoram
ldrr1,=on_the_ram
addpc,r1,#0
nop
nop
1:
b1b@infiniteloop
#ifdefCONFIG_S3C2410_NAND_BOOT
@
@copy_myself:
copyvivitoram
@
copy_myself:
movr10,lr
@resetNAND
movr1,#NAND_CTL_BASE
ldrr2,=0xf830@initialvalue
strr2,[r1,#oNFCONF]
ldrr2,[r1,#oNFCONF]
bicr2,r2,#0x800@enablechip
strr2,[r1,#oNFCONF]
movr2,#0xff@RESETcommand
strbr2,[r1,#oNFCMD]
movr3,#0@wait
1:
addr3,r3,#0x1
cmpr3,#0xa
blt1b
2:
ldrr2,[r1,#oNFSTAT]@waitready
tstr2,#0x1
beq2b
ldrr2,[r1,#oNFCONF]
orrr2,r2,#0x800@disablechip
strr2,[r1,#oNFCONF]
@getreadtocallCfunctions(fornand_read())
ldrsp,DW_STACK_START@setupstackpointer
movfp,#0@nopreviousframe,sofp=0
@copyvivitoRAM
ldrr0,=VIVI_RAM_BASE
movr1,#0x0
movr2,#0x20000
blnand_read_ll
tstr0,#0x0
beqok_nand_read
#ifdefCONFIG_DEBUG_LL
bad_nand_read:
ldrr0,STR_FAIL
ldrr1,SerBase
blPrintWord
1:
b1b@infiniteloop
#endif
ok_nand_read:
#ifdefCONFIG_DEBUG_LL
ldrr0,STR_OK
ldrr1,SerBase
blPrintWord
#endif
@verify
movr0,#0
ldrr1,=0x33f00000
movr2,#0x400@4bytes*1024=4K-bytes
go_next:
ldrr3,[r0],#4
ldrr4,[r1],#4
teqr3,r4
bnenotmatch
subsr2,r2,#4
beqdone_nand_read
bnego_next
notmatch:
#ifdefCONFIG_DEBUG_LL
subr0,r0,#4
ldrr1,SerBase
blPrintHexWord
ldrr0,STR_FAIL
ldrr1,SerBase
blPrintWord
#endif
1:
b1b
done_nand_read:
#ifdefCONFIG_DEBUG_LL
ldrr0,STR_OK
ldrr1,SerBase
blPrintWord
#endif
movpc,r10
@clearmemory
@r0:
startaddress
@r1:
length
mem_clear:
movr2,#0
movr3,r2
movr4,r2
movr5,r2
movr6,r2
movr7,r2
movr8,r2
movr9,r2
clear_loop:
stmiar0!
{r2-r9}
subsr1,r1,#(8*4)
bneclear_loop
movpc,lr
#endif@CONFIG_S3C2410_NAND_BOOT
3.3进入C代码
首先要设置堆栈指针sp,堆栈指针的设置是为了执行C语言代码作好准备。
设置好堆栈后,调用C语言的main函数:
@getreadtocallCfunctions
ldrsp,DW_STACK_START@setupstackpointer
movfp,#0@nopreviousframe,sofp=0
mova2,#0@setargvtoNULL
blmain@callmain
movpc,#FLASH_BASE@otherwise,reboot
4.BootLoader第二阶段
viviBootloader的第二阶段又分成了八个小阶段,在main函数中分别调用这几个小阶段的相关函数:
intmain(intargc,char*argv[])
{
intret;
/*
*Step1:
*/
putstr("\r\n");
putstr(vivi_banner);
reset_handler
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- ARM 嵌入式 Linux 移植 体验 BootLoader