最为详细的vivi代码分析Word文档下载推荐.docx
- 文档编号:4595255
- 上传时间:2023-05-03
- 格式:DOCX
- 页数:59
- 大小:62.39KB
最为详细的vivi代码分析Word文档下载推荐.docx
《最为详细的vivi代码分析Word文档下载推荐.docx》由会员分享,可在线阅读,更多相关《最为详细的vivi代码分析Word文档下载推荐.docx(59页珍藏版)》请在冰点文库上搜索。
三、bootloaderstage1:
【arch/s3c2410/head.S】
首先解决一个问题,就是为什么使用head.S而不是用head.s?
有了GNUAS和GNUGCC的基础,不难理解主要原因就是为了使用C预处理器的宏替换和文件包含功能(GNUAS的预处理无法完成此项功能)。
可以参考前面的总结部分。
这样的好处就是可以使用C预处理器的功能来提高ARM汇编的程序设计环境,更加方便。
但是因为ARM汇编和C在宏替换的细节上有所不同,为了区分,引入了__ASSEMBLY__这个变量,这是通过Makefile中AFLAGS来引入的(一般在顶层Makefile中定义),具体如下:
AFLAGS:
=-D__ASSEMBLY__$(CPPFLAGS)
在后面的头文件中,会看到很多#ifdef__ASSEMBLY__等的操作,就是用来区分这个细节的。
在编译汇编文件时,加入AFLAGS选项,所以__ASSEMBLY__传入,也就是定义了__ASSEMBLY__;
在编译C文件时,没有用AFLAGS选项,自然也就没有定义__ASSEMBLY__。
由此相应的问题就比较清晰了。
这个小技巧也是值得学习和借鉴的。
1首先关注一下开始的三个头文件。
#include"
config.h"
linkage.h"
machine.h"
(1)利用sourceinsight来查看【include/config.h】。
#ifndef_CONFIG_H_
#define_CONFIG_H_
autoconf.h"
#endif/*_CONFIG_H_*/
可见,config.h只是包含一个autoconf.h。
而关于autoconf.h的生成,在vivi配置文件分析的时候也解释的很清楚了,在这里就不用再细分析了。
需要解释的一点是,如果写一个专用的bootloader,不采用vivi的配置机制,那么配置部分就没有这么复杂了,只需要在include文件夹中包含一个配置头文件即可。
现在bootloader的设计有两种趋势,一种是针对特定应用,有特殊要求,也就是“专用”。
那么设计时,不需要过多的配置,只需要简单的完成引导内核的功能就可以了。
二是普通应用,一般是对基本“通用”的bootloader,比如uboot等,然后根据相应的模版进行移植。
这就需要了解uboot等的架构,可以进行定制和功能的增加。
uboot完成的不仅仅是一个bootloader的功能,还可以提供调试等功能,所以其角色还包含驻留程序这个功能,也就是uboot真正的角色是monitor。
当然,可以不加区分,统称为bootloader。
而分析vivi源代码的实现,对这两个方向都有帮助。
(2)
【include/linkage.h】就是实现了ENTRY宏的封装。
其实这个头文件也仅仅为head.S提供了服务,实际上没有必要写的这么复杂,可以简化一些。
比如,我修改了这个头文件,如下:
[armlinux@lqminclude]$catlinkage.h
#ifndef_VIVI_LINKAGE_H
#define_VIVI_LINKAGE_H
#defineSYMBOL_NAME(X)X
#ifdef__STDC__
#defineSYMBOL_NAME_LABEL(X)X##:
#else
#defineSYMBOL_NAME_LABEL(X)X/**/:
#endif
#ifdef__ASSEMBLY__
#defineALIGN.align0
#defineENTRY(name)/
.globlSYMBOL_NAME(name);
/
ALIGN;
SYMBOL_NAME_LABEL(name)
在这里,要加强一下C语言宏的设计和分析能力。
下面就几个点简单的分析一下,后面专门就C宏部分做个总结。
关于__STDC__这个宏,是编译器自动添加的,含义就是支持标准C。
如果支持标准C,那么##的作用就是“连接”,所以SYMBOL_NAME_LABEL(_start)宏展开为_start:
(依我的意思理解##也就相当于分号,表示后面可以继续连接其他的一些定义),如果不支持标准C,则利用了C预处理器对注释的处理方式,就是把/**/替换为一个空格(也就是相当于展开的字符后带个空格),可以测试一下。
另外,关于ENTRY宏的封装,利用了GNUAS在ARM上的相关特点。
首先,利用了分号作为三条语句的连接符(包括最后一个由##展开来的分号),而分号是GNUAS汇编注释符的一种(另外一种是@)。
另外,关于ALIGN为什么用.align0。
这可以参考GNUAS手册,上面讲解的比较清晰,主要是为了兼容ARM本身的编译器。
理解了这个也就不难得出ENTRY(_start)宏展开后的形式了。
有一个技巧就是可以通过下面的命令来检测宏展开后的结果,比如:
[root@lqmvivi_myboard]#gcc-E-D__ASSEMBLY__-I./includearch/s3c2410/head.S>
aaa
可以查看aaa文件的显示结果,做了一些注释:
#1"
arch/s3c2410/head.S"
<
built-in>
"
commandline>
#35"
include/config.h"
1
#14"
include/autoconf.h"
#15"
2
#36"
include/linkage.h"
#37"
include/machine.h"
#19"
include/platform/smdk2410.h"
include/s3c2410.h"
#22"
include/hardware.h"
#23"
include/bitfield.h"
#24"
#3"
include/sizes.h"
#8"
#74"
include/architecture.h"
#75"
#20"
#38"
@Startofexecutablecode
宏定义展开
.globl_start;
.align0;
_start:
.globlResetEntryPoint;
ResetEntryPoint:
下面是装载中断向量表,ARM规定,在起始必须有8条跳转指令,你可以用b标号也可以用ldrpc标号。
这样的8条规则的标志被arm定义为bootloader的识别标志,检测到这样的标志后,就可以从该位置启动。
这样的做法是因为开始的时候不一定有bootloader,必须有一种识别机制,如果识别到bootloader,那么就从bootloader启动。
@
@Exceptionvectortable(physicaladdress=0x00000000)
@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
下面是固定位置存放环境变量
@VIVImagics
@0x20:
magicnumbersowecanverifythatweonlyput
.long0
@0x24:
@0x28:
wherethisviviwaslinked,sowecanputitinmemoryintherightplace
.long_start
//_start用来指定链接后的起始装载地址装载到内存中的地址
@0x2C:
thiscontainstheplatform,cpuandmachineid
.long((1<
24)|(6<
16)|193)
@0x30:
vivicapabilities
@0x34:
bSleepRamProc
@StartVIVIhead
Reset:
//上电后cpu会从0x0地址读取指令执行,也就是bReset
@disablewatchdogtimer
movr1,#0x53000000
movr2,#0x0
strr2,[r1]
#121"
@disableallinterrupts
movr1,#0x4A000000
movr2,#0xffffffff
strr2,[r1,#0x08]
//0x4A000008为中断屏蔽寄存器,将里面赋全1,表示屏蔽这32个中断源
ldrr2,=0x7ff
strr2,[r1,#0x1C]
//0x4A00001C为中断子屏蔽寄存器,里面低11位也用来表示屏蔽掉这11个中断源
@initialisesystemclocks
movr1,#0x4C000000
mvnr2,#0xff000000//MVN指令可完成从另一个寄存器、被移位的寄存器、或将一个立即数加载到目的寄存器。
与MOV指令不同之处是在传送之前按位被取反了,即把一个被取反的值传送到目的寄存器中。
也就是r2=0x00ffffff
strr2,[r1,#0x00]
//我们可以在程序开头启动MPLL,在设置MPLL的几个寄存器后,需要等待一段时间(LockTime),
MPLL的输出才稳定。
在这段时间(LockTime)内,FCLK停振,CPU停止工作。
LockTime的长短由寄存器LOCKTIME设定,LockTime之后,MPLL输出正常,CPU工作在新的FCLK下,前面说过,MPLL启动后需要等待一段时间(LockTime),使得其输出稳定。
位[23:
12]用于UPLL,位[11:
0]用于MPLL。
本实验使用确省值0x00ffffff。
@ldrr2,mpll_50mhz
@strr2,[r1,#0x04]
@1:
2:
4
movr2,#0x3
strr2,[r1,#0x14]
//0x4C000014为分频寄存器,用于设置FCLK、HCLK、PCLK三者的比例
bit[2]——HDIVN1,若为1,则bit[1:
0]必须设为0b00,此时FCLK:
HCLK:
PCLK=1:
1/4:
1/4;
若为0,三者比例由bit[1:
0]确定bit[1]——HDIVN,0:
HCLK=FCLK;
1:
HCLK=FCLK/2bit[0]——PDIVN,0:
PCLK=HCLK;
PCLK=HCLK/2
本实验设为0x03,则FCLK:
1/2:
1/4
mrcp15,0,r1,c1,c0,0@readctrlregister
orrr1,r1,#0xc0000000@Asynchronous
mcrp15,0,r1,c1,c0,0@writectrlregister
上面这三句代码的意思是切换模式:
IfHDIVN=1,theCPUbusmodehastobechangedfromthefastbus
modetotheasynchronousbusmodeusingfollowinginstructions:
MMU_SetAsyncBusMode
mrc
p15,0,r0,c1,c0,0
orr
r0,r0,#R1_nF:
OR:
R1_iA
mcr
p15,0,r0,c1,c0,0
其中的“R1_nF:
R1_iA”等于0xc0000000。
意思就是说,当HDIVN=1时,CPUbusmode需要从原来的“fastbusmode”改为“asynchronousbusmode”。
@now,CPUclockis200Mhz
ldrr2,mpll_200mhz
strr2,[r1,#0x04]//0x4C000004为MPLLCON寄存器,对于MPLLCON寄存器,[19:
12]为MDIV,[9:
4]为PDIV,[1:
0]为SDIV。
有如下计算公式:
MPLL(FCLK)=(m*Fin)/(p*2^s)
其中:
m=MDIV+8,p=PDIV+2对于本开发板,Fin=
12MHz,MPLLCON设为0x5c0040,可以计算出FCLK=200MHz,再由CLKDIVN的设置可知:
HCLK=100MHz,PCLK=50MHz。
#164"
blmemsetup
@Checkifthisisawake-upfromsleep
ldrr1,PMST_ADDR
//0x560000B4为GSTATUS2寄存器,里面[0:
2]三位有效
ldrr0,[r1]//将该寄存器中的值取出来保存到r0中
tstr0,#((1<
1))
//测试r0的第一位。
这位是Power_OFFreset.TheresetafterthewakeupfromPower_OFFmode.Thesettingisclearedbywriting"
1"
tothisbit.TST指令用于把一个寄存器的内容和另一个寄存器的内容或立即数进行按位的与运算,并根据运算结果更新CPSR中条件标志位的值。
bneWakeupStart
@AllLEDon
这里就需要对GPIO口进行控制
movr1,#0x56000000
addr1,r1,#0x50//0x56000050是GPFCON用来配置portF端口
ldrr2,=0x55aa
strr2,[r1,#0x0]
//设置为010*********因为每两位用来控制一个引脚,也就是将GPF4-GPF7设置为输出,将GPF0-GPF3设置为中断
movr2,#0xff
strr2,[r1,#0x8]
//0x56000058为GPFUP为portF的上拉寄存器,全设置为1表示禁止上拉功能
movr2,#0x00
strr2,[r1,#0x4]
//0x56000054是GPFDAT,总共8位有效,每位控制一个引脚,主要是将GPF4-GPF7数据位全设置为0而这四个引脚是用来控制板子上4个LED,置0则表示亮。
#230"
@setGPIOforUART
movr1,#0x56000000
addr1,r1,#0x70//0x56000070为GPHCON用来配置portH而portH主要就是来控制UART的各个引脚如:
UART中接收和发送端口RXDn和TXDn,当然还有自动流控制模式的nRTS0和nCTS0端口。
ldrr2,gpio_con_uart
//我们可以看到gpio_con_uart:
.long
vGPHCON
gpio_up_uart:
.long
vGPHUP而在platform中的smdk2410.h中定义了这两个值#definevGPHCON
0x0016faaa表示GPHCON控制11个引脚,如GPH2若设置为10则表示TXD0.类似,具体的查看数据手册
#definevGPHUP
0x000007ff//同样将这11位的引脚上拉禁止
strr2,[r1,#0x0]
ldrr2,gpio_up_uart
strr2,[r1,#0x8]//上面也是来配置串口所用到的GPIO口,因为串口的输入输出口都是利用到GPIO
blInitUART
#259"
blcopy_myself
@jumptoram
ldrr1,=on_the_ram
addpc,r1,#0
nop
1:
b1b@infiniteloop
on_the_ram:
#279"
@getreadtocallCfunctions开始调用C函数之前就需要将一些参数准备好,如堆栈要准备好函数调用时需要进出栈
ldrsp,DW_STACK_START@setupstackpointer
movfp,#0@nopreviousframe,sofp=0
mova2,#0@setargvtoNULL
blmain@callm
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 最为 详细 vivi 代码 分析