WinCE案例解说开发实现OEM函数.docx
《WinCE案例解说开发实现OEM函数.docx》由会员分享,可在线阅读,更多相关《WinCE案例解说开发实现OEM函数.docx(43页珍藏版)》请在冰豆网上搜索。
WinCE案例解说开发实现OEM函数
第3章
案例解说开发实现OEM函数
3.1 DeviceEmulator虚拟平台的硬件设计
尽管三星的S3C2410作为典型的SoC(SystemonChip)芯片片内已经集成了开发消费类嵌入式产品所需的大多数端口外设控制器,与本书相关的典型的外设端口有UART串口、内存控制器、LCD控制器等,但是仍然需要一些外接的端口控制器,被DeviceEmulator的虚拟硬件平台使用的主要是以太网接口和PCMCIA总线的控制器。
虽然DeviceEmulator是一个虚拟的基于S3C2410的开发板,但是虚拟的逼真度相当高,微软为之开发的BSP的各组成部分是基于一个统一的硬件平台的,构成了一个完整的嵌入式系统。
实际上,DeviceEmulator的前身是在存在于WindowsCE5.0及以前版本中的一套基于一块真实的三星S3C2410开发板的BSP,可以直接在实际硬件平台上运行,当时的Emulator调试工具是基于x86平台的。
为了后文讲述DeviceEmulatorBSP的BootLoader的OEM函数开发实例做准备,这里有必要先探讨一下DeviceEmulator这个虚拟的嵌入式平台的硬件设计,重点在于搞清楚与软件开发密切相关的系统物理地址的使用分配布局,分析的依据是%_WINCEROOT%\PLATFORM\DEVICEEMULATOR\SRC\INC\oemaddrtab_cfg.inc文件中的g_oalAddressTable数组:
g_oalAddressTable
DCD0x80000000,0x30000000,64;64MBDRAMBANK6
DCD0x84000000,0x10000000,32;nGCS2:
PCMCIA/PCCARD
DCD0x86000000,0x18000000,32;32MBSROM(SRAM/ROM)BANK3(CS8900netcard)
DCD0x88000000,0x00000000,96;96mbNORflash
DCD0x90800000,0x48000000,1;Memorycontrolregister
DCD0x90900000,0x49000000,1;USBHostregister
DCD0x90A00000,0x4A000000,1;InterruptControlregister
DCD0x90B00000,0x4B000000,1;DMAcontrolregister
DCD0x90C00000,0x4C000000,1;Clock&Powerregister
DCD0x90D00000,0x4D000000,1;LCDcontrolregister
DCD0x90E00000,0x4E000000,1;NANDflashcontrolregister
DCD0x91000000,0x50000000,1;UARTcontrolregister
DCD0x91100000,0x51000000,1;PWMtimerregister
DCD0x91200000,0x52000000,1;USBdeviceregister
DCD0x91300000,0x53000000,1;WatchdogTimerregister
DCD0x91400000,0x54000000,1;IICcontrolregister
DCD0x91500000,0x55000000,1;IIScontrolregister
DCD0x91600000,0x56000000,1;I/OPortregister
DCD0x91700000,0x57000000,1;RTCcontrolregister
DCD0x91800000,0x58000000,1;A/Dconvertregister
DCD0x91900000,0x59000000,1;SPIregister
DCD0x91A00000,0x5A000000,1;SDInterfaceregister
;0x92000000,0x00000000,32;originallocationof32MBofNORflash
DCD0x94000000,0x34000000,192;192MBbank6&7-ExtendedRAM
DCD0x00000000,0x00000000,0;endoftable
按照S3C2410芯片内置的内存控制器的设计,从0x48000000至0x5FFFFFFF范围内的系统物理地址空间被命名为SFR(SpecialFunctionRegister)的片内外设寄存器(包括控制寄存器、状态寄存器以及数据缓冲区等)占用,无论各种S3C2410芯片内置外设是否在DeviceEmulator中起作用,这部分物理地址空间都不可再用作其他用途。
顺便说一下,SFR本来占用了0x48000000~x5FFFFFFF范围的共384MB的物理地址空间,但是其对应的虚拟内存则只占用0x90800000~0x91AFFFFF范围的19MB的虚拟地址空间,从这里也可以看出使用虚拟存储的好处。
尽管对于目前的嵌入式系统来说,4GB的地址空间还是绰绰有余的,但是要为嵌入式硬件的迅速发展留有余地。
物理地址0x30000000~0x3FFFFFFF这一块长达256MB的区域被物理RAM使用,这实际是用满了S3C2410内存控制器的Bank6和Bank7。
S3C2410的内存控制器只有Bank6和Bank7可以支持RAM存储类型,DeviceEmulator的硬件设计是使用了S3C2410芯片能够支持的最大数量的RAM存储容量。
实际的嵌入式产品是不可能这样不计成本的大量使用RAM内存的,但是现在由于是虚拟的硬件,而且要为OEM用户在开发过程中进行软件仿真提供足够的使用的方便和灵活性,这样做是完全必要的。
尽管RAM的物理地址是连续的,虚拟内存地址却不连续,分隔在0x80000000~0x83FFFFFF和0x94000000~0x9FFFFFFF这两个区域。
图3-1是S3C2410芯片内置的内存控制器在系统上电或复位以后对系统物理地址的默认分配使用示意图。
可供CPU芯片外接的外设控制器或者存储设备使用的物理地址范围是0x00000000~0x3FFFFFFF这1GB的地址空间,一共被分成8个Bank,每个Bank享有128MB的物理地址空间,其中可接RAM存储器的物理地址空间只有被称作Bank6的0x30000000~0x37FFFFFF和Bank7的0x38000000~0x3FFFFFFF。
用在Bank6和Bank7的RAM内存的容量大小可以是2MB/4MB/8MB/16MB/32MB/64MB/128MB,但是这两个Bank的存储容量大小必须相等。
图3-1 S3C2410芯片内在控制器物理地址分配
物理地址0x00000000~0x05FFFFFF区域被NorFlash存储占用,其总容量为96MB,占用的虚拟内存地址范围是0x88000000~0x8DFFFFFF。
NorFlash在DeviceEmulator中扮演非可易失的ROM存储器的角色,用于保存BootLoader和WindowsCE操作系统的镜像以及其他需要掉电保存的数据。
与RAM类似,实际的嵌入式产品不可能有如此大容量的NorFlash可用,但是作为软件仿真工具这是可以理解而且是必要的。
虚拟的硬件使用的NorFlash存储器芯片型号为AM29LV800。
虚拟的开发板在系统上电后从NorFlash启动,所以由NorFlash存储器占据起始地址为0x0的Bank0物理存储空间。
显然DeviceEmulator的虚拟硬件设计使用的是图3-1左侧的“不使用NANDflash作为启动ROM”的启动方式,而右侧的“使用NANDflash作为启动ROM”的启动方式就是前文提到过的采用“阶石”技术的混合模式启动技术。
存储控制器的Bank2被一块32MB的存储类型PC卡外设占据,准确地说是被一块CPU芯片外置的型号为PD6710的PCMCIA接口控制器芯片占用,其物理地址为0x10000000~0x11FFFFFF,对应的虚拟内存地址范围是0x84000000~0x85FFFFFF。
存储控制器的Bank3被外置的CS8900型以太网控制器芯片占用,占用0x18000000~0x19FFFFFF共32MB的物理地址空间和0x86000000~0x87FFFFFF范围内的虚拟内存地址空间。
3.2 开天辟地的StartUp函数
基于WindowsCE的嵌入式系统上电或者复位以后运行的第一条指令是StartUp函数的二进制代码。
虽然微软并没有强制要求这第一个WindowsCE执行函数的函数名必须是StartUp,但这确实是约定俗成的惯例,实际上OEM用户可以通过修改BootLoader或者OAL的工程文件来改变这一点。
以下是从DeviceEmulator的BootLoader的工程文件%_WINCEROOT%\PLATFORM\DEVICEEMULATOR\SRC\BOOTLOADER\EBOOT\sources中摘取的部分内容:
TARGETNAME=eboot
TARGETTYPE=PROGRAM
RELEASETYPE=PLATFORM
EXEENTRY=StartUp
用户完全可以通过修改其中的EXEENTRY项的取值而不接受StartUp的默认函数名,但是这样做通常既没有必要也没有好处。
上述其他选项的取值内容表示DeviceEmulator的BootLoader的可执行二进制文件的文件名是eboot.exe,并且产生这个二进制文件后它的存放位置在BSP各自所属的PLATFORM子目录下。
StartUp函数是用汇编语言编写的,DEVICEEMULATOR的该函数其源代码位于%_WINCEROOT%\PLATFORM\DEVICEEMULATOR\SRC\BOOTLOADER\EBOOT\startup.s文件中。
这个函数的首要功能是对目标系统的嵌入式CPU执行最基本的初始化,主要是为CPU准备一个合适的运行环境,比如:
清空TLB和cache、关中断、配置PLL、设置内存控制器等。
除StartUp函数外,为WindowsCE的BootLoader执行硬件初始化任务的还有OEMPlatform-Init函数。
微软为这两个初始化函数的功能分界是,前者执行最基本的嵌入式CPU芯片级的初始化,后者执行目标平台板级的初始化。
实际上用户只需把握一个原则,那就是StartUp函数应该做尽可能少的初始化工作,只要CPU能正常运行起来即可,而把大量的复杂的初始化任务留给OEMPlatformInit函数。
原因很简单,StartUp函数是用汇编语言编写的,OEMPlatformInit函数是用高级语言编写的,在BootLoader的源代码中过多地、不必要地使用汇编语言会给软件开发和后续的维护工作制造麻烦。
基于不同CPU架构和硬件平台的BSP的BootLoader,其StartUp函数的初始化工作具体步骤各不相同。
好在这部分跟板级硬件关系不大,而只和嵌入式CPU有关,通常知名的SoC芯片厂商的产品只要宣称支持WindowsCE操作系统都会给出这部分初始化功能代码。
但是这些各自不同的初始化代码中,有一点处理步骤是相同的,那就是关中断。
以下是DeviceEmulator的相应处理代码:
ldrr0,=INTMSK
ldrr1,=0xffffffff;disableallinterrupts
strr1,[r0]
在整个BootLoader运行的过程中,所有外设的中断请求功能都是关闭的,BootLoader使用轮询的方式对外设进行访问。
关闭了外设中断请求的BootLoader其运行过程是一个线条型的顺序执行流,相当于一个不太复杂的单片机程序,这大大简化了WindowsCEBootLoader的开发和理解难度。
尽管轮询访问外设的方式效率不及中断,但是BootLoader的功能仅是引导和加载系统,不会用到很多种类型及数量的外设,在BootLoader中启用中断反而会使对问题的处理复杂化。
除对CPU芯片执行最基本的初始化以外,StartUp函数的第二件带有普遍意义的工作是将自身复制到RAM。
BootLoader的镜像在系统不上电时保存在非可易失的ROM存储设备中,在DeviceEmulator中是Nor型的Flash存储器,系统上电或者复位之后BootLoader以XIP(ExecuteInPlace)的方式在ROM型存储中开始启动运行。
所以,BootLoader将自身复制到RAM中这一步骤也不一定是必须的,它完全可以在ROM型的存储设备中继续以XIP的方式运行下去。
但是如果考虑到BootLoader运行速度的因素,毕竟ROM类型存储器的读取速度都远不及RAM,以正确有效的方法实现这一功能是完全必要的。
DeviceEmulator的StartUp函数的代码首先要检查自身是否已经在RAM存储中开始运行:
andsr9,pc,#0xFF000000;seeifweareinflashorinram
bne%f20;goaheadifwearealreadyinram
要做这一甄别处理的原因是StartUp函数在将BootLoader的镜像复制到RAM中以后会跳转到位于RAM中的BootLoader的起始执行位置StartUp函数处重新开始运行,此后还会回到当前讨论的代码位置并且试图再次复制BootLoader自身。
判断当前执行指令是否在RAM中的依据是程序计数器PC的取值是否大于0x00FFFFFF,因为供BootLoader做XIP运行的NorFlash的起始物理地址是0x00000000,而RAM的起始物理地址是0x30000000,这可以从当前BSP的g_oalAddressTable数组中看得很清楚。
接下来DeviceEmulator的BootLoader进入具体的复制动作:
;Thisistheloopthatperformcopying.
ldrr0,=0x21000;offsetintotheRAM
addr0,r0,#PHYBASE;addphysicalbase
movr1,r0;(r1)copydestination
ldrr2,=0x0;(r2)flashstartedatphysicaladdress0
ldrr3,=0x10000;counter(0x40000/4)
10ldrr4,[r2],#4
strr4,[r1],#4
subsr3,r3,#1
bne%b10
设定好关键的3个数据:
复制操作的NorFlash源起始地址、复制操作的RAM目的起始地址和以4字节为单位的待复制数据量,然后依次将BootLoader的镜像数据从NorFlash中读出并且存入RAM中。
复制操作的源地址起始值是NorFlash的起始物理地址0x00000000,存放在寄存器r2中。
复制的起始目的地址是RAM起始物理地址加上0x21000,其数值存放在寄存器r1中,RAM内存的最开始的132KB(0x21000B)存储空间被DeviceEmulator用于其他用途。
r3寄存器充当复制计数器,取值为0x10000,因为DeviceEmulator的BootLoader镜像占用256KB(0x40000Byte)存储空间,而每次复制搬移4个Byte(因为32位的CPU其内部寄存器是32Bit)的数据。
复制操作完成以后,随后的汇编程序代码将程序计数器PC赋值为BootLoader在RAM中的起始指令所在的物理地址:
;RestartfromtheRAMpositionaftercopying.
movpc,r0
这意味着BootLoader又要从头开始运行,但是这下一次在RAM中的运行过程就不再执行复制自身的操作,而是直接进入下一步骤——设置页表并且启用MMU。
对嵌入式的CPU启用MMU硬件单元就意味着对嵌入式系统使用虚拟内存地址而不再是先前的系统物理地址。
对包括嵌入式系统在内的计算机系统使用虚拟内存有许多好处,本书会在在第二篇详细介绍。
客观地说,对WindowsCE的BootLoader这么一个规模不大的嵌入式软件采用虚拟内存并不能充分地体现虚拟内存的好处。
但是微软对WindowsCE的BootLoader所建议实现的功能与特性之一就是BootLoader的硬件初始化代码应该尽最大限度地和OAL的初始化代码保持共享,而OAL作为WindowsCE操作系统运行的开始是需要启用虚拟内存并且为MMU的正确工作设置所需的页表的。
除此之外,在BootLoader中启用虚拟内存还有一个更为重要的理由。
指导WindowsCE的编译系统产生可执行二进制文件的.bib工程文件中使用的内存地址都是虚拟内存地址,而这些虚拟地址是编译系统对二进制代码文件进行地址重定位的依据。
以下是从DeviceEmulatorBSP的BootLoader的工程文件D:
\WINCE600\PLATFORM\DEVICEEMULATOR\SRC\BOOT-LOADER\EBOOT\boot.bib中摘取的部分内容:
MEMORY
;NameStartSizeType
;---------------------------
PTS8000000000020000RESERVED
ARGS8002000000000800RESERVED
SLEEPSTATE8002080000000800RESERVED
EBOOT8002100000040000RAMIMAGE
STACK8006100000004000RESERVED
RAM8006500000006000RAM
命名为EBOOT的起始地址0x80021000的RAM区域用来存放DeviceEmulator的BootLoader镜像,这个镜像的数据量大小正好是256KB(0x40000B)。
0x80021000却是一个虚拟内存地址,虽然不难根据g_oalAddressTable数组得知它对应的物理内存地址是0x30021000,但是WindowsCE的编译系统会依据上表中EBOOT一行的数据内容而将整个BootLoader的源代码中所有的变量名字和函数名都映射为0x80021000~0x80060FFF之间的虚拟内存地址数值。
只有在BootLoader中启用虚拟内存,它的执行代码才可能依靠MMU将这些虚拟地址转换成物理内存地址,否则CPU会把0x80021000~0x80060FFF范围的地址当作系统物理地址使用,而这是不被S3C2410的内存控制器硬件接受的。
计算机系统的MMU硬件单元的功能是将CPU发出的虚拟内存地址转换成可访问实际物理存储的系统物理地址,MMU实现地址转换的依据是一个地址转换表,这个地址转换表就是前文提到的页表。
页表的存放位置在RAM内存中,MMU只需知道它的存放起始位置。
供DeviceEmulator的BootLoader使用的页表存放位置就是前文摘录的boot.bib工程文件的PTS一行所指示的从0x80000000开始的128KB(0x20000B)的RAM内存区域。
构造页表并且启用MMU可以看作是对MMU硬件单元的初始化,是前面提到的硬件初始化工作的继续。
相对于WindowsCE操作系统而言,BootLoader的虚拟内存设计并不复杂,它不涉及较为复杂的二级页表,只使用以1MB为单位的一级页表。
一级页表就是供MMU使用的地址转换表中的每一个表项对应着1MB的虚拟内存地址空间和相同大小的物理存储空间。
WindowsCE也称这1MB的地址空间为“段”(Section),但这只是借用了一个概念而已,因为以ARM为代表的嵌入式CPU大多采用的是页式的存储管理技术,内部并没有类似x86架构CPU中的段寄存器。
BootLoader构造一级页表的依据就是g_oalAddressTable数组。
每个标准的WindowsCE的BSP其中都会定义这样一个命名为g_oalAddressTable的数组,这个数组不仅BootLoader会用到,而且它实际上主要是供WindowsCE的OAL模块使用的。
在BSP的高级语言程序代码中,这个数组的元素类型定义为OAL_ADDRESS_TABLE,这是一个在头文件%_WINCEROOT%\PLATFORM\COMMON\SRC\INC\oal_memory.h中定义的结构体:
typedefstruct{
UINT32CA;//cachedvirtualaddress
UINT32PA;//physicaladdress
UINT32size;//size,inMBbytes
}OAL_ADDRESS_TABLE,*POAL_ADDRESS_TABLE;
CA和PA成员分别是一个g_oalAddressTable数组元素所指向的内存段的虚拟内存起始地址和物理存储起始地址,size成员是一个数组元素所包含的内存段的以MB为单位的容量大小。
细心的读者也许已经注意到了,DeviceEmulator的g_oalAddressTable数组其第一列的所有虚拟内存地址、即所有的数组元素的OAL_ADDRESS_TABLE结构体数据的CA成员取值都限制在0x80000000~0x9FFFFFFF的共512MB地址范围内。
这不是偶然的,而是任何一个WindowsCEBSP开发者定义它的g_oalAddressTable数组时必须遵守的规则,在后文OAL一篇里读者会详细地了解到,这一块虚拟内存地址被称作WindowsCE的cachable静态映射虚拟内存。
什么是cachable、什么是静态映射以及二级页表的原理都在后面讲述OAL开发的一篇里会有详尽的解释,现在读者需要知道的是,任何一个基于WindowsCE的嵌入式系统,它的所有物理存储都必须映射到这块虚拟内存区域,这也就是为什么WindowsCE系统的物理存储总容量不得超过512MB的缘由。
注意这里的物理存储不是仅指物理RAM内存,也包括其他的CPU可直接寻址访问的物理存储单元,比如Flash存储器及CPU芯片内置外设的SFR寄存器等。
MMU硬件单元使用一级页表将虚拟内存地址转换成物理存储地址的原理是这样的。
一个32位嵌入式系统的任何地址值都是32位的,其中的低20位是段内偏移,高12位是段索引。
从内存中的页表起始存放物理地址(记做TableStartP)开始,每4个字节存放一个页表项,一个页表项的起始存放位置的物理地址记做EntryP,其中的4字节数据表示为*(EntryP),则MMU将任一虚拟内存地址VA转换为对应的物理地址PA的计算方法可用下面的等式表达:
PA=*((VA/0x100000)*4+TableStar