ARM2020/2/81第五章嵌入式系统软件设计•嵌入式系统作为一个完整的、独立的、能够单独运行的系统。与传统的通用计算机系统相比,它有许多独有的特点。这些特点也产生了它与通用计算机系统的区别。这些区别不仅表现在整个系统软件的结构上,也体现在软件的开发方法上。ARM2020/2/825.1嵌入式软件结构及组成用户应用软件高层用户应用软件底层GUI协议设备驱动文件系统操作系统层板级支持包初始化引导代码ARM2020/2/835.2初始化引导代码实例•初始化引导代码也称为引导加载程序,即BootLoader。它是系统上电后运行的第一段程序代码。通过这段代码,可以初始化存储器空间的映射,从而将系统的软硬件环境设置到一个合适的状态,以便为最终调用操作系统内核准备好环境。ARM2020/2/84•PC的启动过程:在系统上电后由BIOS(一段固件程序)进行POST(上电自检),在分配资源过后,将硬盘MBR(主引导记录)中的OSBootLoader如LILO,或者GRUB)载入内存,然后将控制权交给OSBootLoader,将操作系统内核映象加载到内存中,接着就跳转到内核的入口处去执行,即启动操作系统。ARM2020/2/85•而在嵌入式系统中,通常并没有像BIOS那样的固件程序(注,有的嵌入式CPU也会内嵌一段短小的启动程序),因此整个系统的加载启动任务就完全由BootLoader来完成。一般在上电或者复位后,嵌入式系统都会从一个固定的地址处开始执行。这个固定地址一般由CPU生产厂商决定(通常为0x0000_0000),在这个地址处安排的通常就是系统的Boot-Loader程序。ARM2020/2/86•与普通PC的启动相比较,在嵌入式系统中所说的BootLoader的功能与普通PC中的BIOS以及OSBootLoader类似。与Bootloader不同的是BIOS在装载OS系统的同时,还传递一些参数设置(中断端口定义、显存大小等),而Bootloader一般只简单地装载系统。ARM2020/2/87•BootLoader的启动一般分为两个阶段。阶段一主要完成的是与CPU体系结构有关的初始化,如设备的初始化。它一般用汇编语言来实现。阶段二主要完成的是一些常规的初始化,通常用C语言实现。ARM2020/2/88•我们按照U-Boot的启动流程介绍了各个阶段主要完成的工作以及相应的一些示意代码和程序框架。•对ARM9核处理器,U-boot启动流程主要体现在三个文件上,即•cpu/***/start.s•lib_arm/board.c•common/main.c。ARM2020/2/89start.s文件•在start.s文件中通过如下语句来指定这个入口:•.globl_start•_start:•因此,必须通知编译器以使其知道这个入口,该工作可通过修改连接器脚本文件u-boot.lds来完成。u-boot.lds文件用来设置U-boot中各个目标文件的连接地址。•注:链接脚本文件的功能是将输入文件的各节映射到输出文件中;换句话说,它将所有输入对象文件都链接到单一的可执行文件中,将该可执行文件的各节装入到指定地址处。ARM2020/2/810设置异常向量•异常中断向量表是u-boot与操作系统内核发生联系的关键地方之一。即使操作系统内核已经得到处理器的运行控制权,一旦发生中断,处理器还是会自动跳转到从0x0地址开始的异常中断向量表中的某个位置(依据中断类型)处读取指令运行。•ARM9要求异常中断向量表必须设置在从0地址开始,连续8×4字节的空间,分别是复位、未定义指令错误、软件中断、预取指令错误、数据存取错误、一个保留的中断向量以及IRQ和FIQ。ARM2020/2/811•breset•ldrpc,_undefined_instruction;将标号_undefined_instruction的地址加载到pc中。在该例中,_undefined_instruction处又放的是•;undefined_instruction的地址,这样经过一次中间转换,•;当未定义指令错误到来时,实际上是将•;undefined_instruction处的地址加载到pc中,即运行的是•;undefined_instruction处的指令。•ldrpc,_software_interrupt•ldrpc,_prefetch_abort•ldrpc,_data_abort•ldrpc,_not_used•ldrpc,_irq•ldrpc,_fiqARM2020/2/812•_undefined_instruction:.wordundefined_instruction;在存储器中预留一个字的空间大小,并将标号undefined_instruction放到该空间中,并用标号undefined_instruction表示这个预留的空间•_software_interrupt:.wordsoftware_interrupt•_prefetch_abort:.wordprefetch_abort•_data_abort:.worddata_abort•_not_used:.wordnot_used•_irq:.wordirq•_fiq:.wordfiq•……•.align5;为了指令对齐•undefined_instruction:•……•bldo_undefined_instructionARM2020/2/813设置CPU模式•;设置CPU模式为SVC32模式,禁止IRQ和FIQ中断,参考3.4.2节•;CPSR位31302928………..76543210•;11010011•mrsr0,cpsr;将cpsr加载到r0中•bicr0,r0,#0x1f;将cpsr的低5位清0•orrr0,r0,#0xd3;将cpsr的低8位设置为11010011•msrcpsr,r0;将r0加载到cpsr中ARM2020/2/814设置若干寄存器•设置看门狗计时器(关闭),设置中断屏蔽寄存器(屏蔽所有中断,因为为中断提供服务通常是操作系统设备驱动程序的责任,在BootLoader执行过程中可以不必响应任何中断,更重要的是此时系统还没有为中断执行准备必要的堆栈等条件),设置时钟除法控制寄存器(设置FCLK:HCLK:PCLK)。ARM2020/2/815•#definepWTCON0x53000000•#defineINTMSK0x4A000008/*中断控制基址寄存器*/•#defineINTSUBMSK0x4A00001C•#defineCLKDIVN0x4C000014/*时钟除法寄存器*/•/*关闭看门狗计时器*/•ldrr0,=pWTCON/*参考3.4.4节ldr伪指令介绍*/•/*该指令将pWTCON的值加载到r0中,这里编译器会把该条指令编•/*译为两条指令,但对于程序员而言,这是透明的,即在程序员看来,•/*r0中保存的是pWTCON的值*/•movr1,#0x0/*将r0赋值为0*/•strr1,[r0]/*将r1的值0保存到r0所表示的存储器单元,即0x53000000处*/ARM2020/2/816•/*在默认情况下,通过设置INTMSK来屏蔽所有的IRQ中断*/•movr1,#0xffffffff•ldrr0,=INTMSK•strr1,[r0]•ldrr1,=0x3ff•ldrr0,=INTSUBMSK•strr1,[r0]•/*默认情况下,FCLK为120MHz,这里设置FCLK:HCLK:PCLK=1:2:4*/•ldrr0,=CLKDIVN•movr1,#3•strr1,[r0]ARM2020/2/817CPU、协处理器和RAM初始化•CPU、协处理器和RAM初始化ARM2020/2/818程序复制以及栈的初始化•U-Boot代码等0x00FFFFFF0x00FE0000堆(malloc调用)0x00FC00000x00FBFFACBoard信息栈(向下增长)0x00FBFF20应用程序空间0x000000000x00002000异常向量•将ROM中的程序复制到RAM中,进行栈的初始化,为第二阶段准备RAM空间。注意U-Boot没有用MMU单元,而是使用实地址。•U-Boot被初始安装在第一个Bank的前128K的地址空间。在经过引导,DRAM初始化后,代码将它自己搬移到DRAM的高地址空间。在U-Boot代码后,将保留大小为CFG_MALLOC_LEN的空间用于被malloc()调用。之后,初始化堆栈(向下增长),然后是一个包含Board信息的结构。最后,异常向量被放到DRAM的低8K空间。典型的有16M空间的配置如图5-2。ARM2020/2/81916M空间的典型配置U-Boot代码等0x00FFFFFF0x00FE0000堆(malloc调用)0x00FC00000x00FBFFACBoard信息栈(向下增长)0x00FBFF20应用程序空间0x000000000x00002000异常向量ARM2020/2/820•具体程序参见p158ARM2020/2/821跳转到RAM中运行•具体代码如下:•ldrpc,_start_armboot•_start_armboot:.wordstart_armboot/*标号_start_armboot表示一个字大小的空间,而在该空*/•/*间中存放的正是start_armboot的地址*/•其中的start_armboot在下面即将介绍的board.c中定义。ARM2020/2/8221.定义的数据结构•该文件主要定义了下面的数据结构.•typedefint(init_fnc_t)(void);•init_fnc_t*init_sequence[]={•cpu_init,/*basiccpudependentsetup*/•board_init,/*basicboarddependentsetup*/•interrupt_init,/*setupexceptions*/•env_init,/*initializeenvironment*/•init_baudrate,/*initialzebaudratesettings*/•serial_init,/*serialcommunicationssetup*/•console_init_f,/*stage1initofconsole*/•display_banner,/*saythatwearehere*/•#ifdefined(CONFIG_DISPLAY_CPUINFO)•print_cpuinfo,/*displaycpuinfo(andspeed)*/•#endif•#ifdefined(CONFIG_DISPLAY_BOARDINFO)•checkboard,/*displayboardinfo*/•#endif•dram_init,/*configureavailableRAMbanks*/•display_dram_config,•NULL,•};ARM2020/2/823•函数start_armboot(void)中,通过循环来实现对各个部件的初始化。包括cpu_init(CPU相关的设置),board_init(板子相关的设置),interrupt_init(中断设置),env_init(环境变量设置),init_baudrate(串口的波特率设置),serial_init(串口初始化),console_init_f(控制台设置),display_banner(显示标题),dram_init(可用内存配置)。ARM2020/2/824•voidstart_armboot(void)•{•init_fnc_t**init_fnc_ptr;•……•for(init_fnc_ptr=init_sequence;*init_fnc_ptr;++init_fnc_ptr)•{•if((*init_fnc_ptr)()!=0)•{•han