BSP

 

1       BSP概述

BSPBoard Support Package,板级支持包。它来源于嵌入式操作系统与硬件无关的设计思想,操作系统被设计为运行在虚拟的硬件平台上。对于具体的硬件平台,与硬件相关的代码都被封装在BSP中,由BSP向上提供虚拟的硬件平台,BSP与操作系统通过定义好的接口进行交互。BSP是所有与硬件相关的代码体的集合

一个成熟的商用操作系统,其被广泛应用的必要条件之一就是能够支持众多的硬件平台,并实现应用程序的硬件无关性。一般来说,这种无关性都是由操作系统实现的

 

但对于嵌入式系统来说,它没有像PC机那样具有广泛使用的各种工业标准、统一的硬件结构。各种嵌入式系统各不同的应用需求就决定了它一般都选用各自定制的硬件环境,每种嵌入式系统从核心的处理器到外部芯片在硬件结构上都有很大的不同。这种诸多变化的硬件环境就决定了无法完全由操作系统来实现上层软件与底层硬件之间的无关性。

 

因此各种商用实时操作系统,都采用了分层设计的方法,它将系统中与硬件直接相关的一层软件独立出来,称之为Board Support Package,简称为BSP。顾名思义,BSP是针对某个特定的单板而设计的。如果没有单板支持软件包,则操作系统就不能在单板上运行。并且它对于用户(指开发者)也是开放的,用户可以根据不同的硬件需求对其作改动或二次开发。 BSP在嵌入式系统中的角色,很相似于在PC系统中的BIOS和驱动程序的地位

BSP的具体结构和组成根据不同的嵌入式操作系统而不同。BSP的开发要求设计人员具备软硬件方面的综合知识。

BSP软件与其他软件的最大区别在于BSP软件有一整套模板和格式,开发人员必须严格遵守,不允许任意发挥。在BSP软件中,绝大部分文件的文件名和所要完成的功能都是固定的。所以,BSP软件的开发一般来说都是在一个基本成型的BSP软件上进行修改,以适应不同单板的需求。 针对某类CPU的硬件单板,嵌入式操作系统(vxWorks)通常提供有其DEMO板的BSP, 这些程序位于指定的目录之下。也就是我们所说的最小系统BSP。一般来说,我们在硬件系统设计好之后,都会先找到一个与自己系统相近的DEMOBSP(最起码是使用相同的CPU)。并以此为基础,开发自己单板的BSP

Ø定义

BSP就是为软件操作系统正常运行提供最基本、最原始的硬件操作的软件模块,它和操作系统息息相关,但又不属于操作系统的一部分。BSP可以分为三大部分:

1:系统上电时的硬件初始化。

2:为操作系统访问硬件驱动程序提供支持。

3:集成的硬件相关和硬件无关的操作系统所需的软件模块。

ØBSP的表现形式

BSP主要以两种形式来表现:

1:源代码(C代码、汇编代码)、系统编译连接依靠文件。

2:二进制的目标代码和目标代码库。

 

ØBSP在软件系统中的位置

      BSP在软件系统中的位置可以用下图来表示,BSP为操作系统和硬件设备的互操作建了一个桥梁,操作系统通过BSP来完成对指定硬件的配置和管理。

 

BSP向上层提供的接口有

l         与操作系统内核的接口(如报告DRAM大小、修改中断屏蔽级别等)

l         与操作系统的I/O系统的接口

l          与应用程序的接口

 

ØCPU最小系统BSP的定义

  

广义上讲,单板中所有需要CPU控制的硬件的程序,都属于单板BSP,但是,为了调试方便和软件的模块化,我们通常就将与此单板最小系统相关的程序简称为BSP而将其他程序称为驱动程序。

   对于嵌入式系统来说,所谓最小系统就是一个包含:CPU, Bootrom, RAM,系统时钟,网口,串口的计算机运行环境。

   这样,最小系统BSP就包含了CPU系统的初始化程序以及网口,串口,系统时钟等设备的驱动程序。

 

 

ØBSP的主要功能

     BSP的主要功能在于配置系统硬件使其工作于正常的状态,完成硬件与软件之间的数据交互,为OS及上层应用程序提供一个与硬件无关的软件平台。因此从执行角度来说,其可以分为两大部分:

    1)目标板启动时的硬件初始化及多任务环境的初始化

    2)目标板上控制各个硬件设备正常运行的设备驱动程序,由它来完成硬件与软件之间的信息交互

        通常我们认为BSPOS服务的, 但实际上, BSP软件包中的部分程序对OS也并不是必须的,从这个角度,又可以将BSP划分为两部分:

    1)最小系统BSP,即我们通常所称的BSP

    2) 设备驱动程序

 

Ø开发BSP需要的条件

l         目标硬件:硬件调测完毕,经过必要的软件测试

l         必要的硬件设计文档:如地址空间的分布,CPU和其他芯片的工作模式等。

l         操作系统

l         交叉开发工具:编译器、汇编器、链接器等

l         下载机制:bootrom或仿真器等

 

 

2       BSP实际开发的主要过程

1.     掌握开发中使用的操作系统,和在这种操作系统下开发BSP的要求。

2.     研读所选CPU的资料。

3.     研读硬件设计文挡。

4.     研读电路板中器件的资料。

5.     找一个BSP模板,熟悉它并在此基础上开发自己的BSP。从头研制BSP工作量极大,也没有必要。

6.     利用仿真器进行调试,开发最小BSP系统。

7.     在最小BSP的基础上,利用Tornado集成开发环境,进一步调试外围设备,配置、完善系统。

8.     调试单板上的设备驱动程序

3       BSP的调试方法(最小系统的调试和设备驱动程序的调试)

3.1  仿真器调试方式:

在串口和网口初始化及发挥功能以前,用仿真器调试是一种相对来讲很方便的手段。

BSP软件的调试通常需要利用仿真器来进行。目前市场上的大多数仿真器都能支持JTAG接口。典型的仿真器调试环境如下图所示:

调试计算机通过RS232接口与仿真器相连,完成对仿真器的初始化配置工作,通常这项工作只在第一次使用仿真器时进行,配置结果一般会被仿真器存储起来。仿真器通过以太网口与调试计算机相连,通过JTAG接口与目标板相连,利用这条通路,仿真器就可将计算机上的程序下载到目标板上进行调试。

目标板上的串口和以太网口为被调试的对象,和调试计算机相连,主要是可以通过调试计算机检验目标板上的接口是否工作正常。

在仿真器环境下,既可以调试vxWorks映象,也可以调试bootrom映象。当调试BootRom映象时,需要修改CONFIG.HMAKEFILE文件将这段代码定位到RAM中,然后通过仿真器下载到目标板上的RAM中进行调试。

 

目前常用的有两类仿真器,一是JTAG仿真器,二是全功能在线仿真器。前者是利用处理器中的调试模块的功能,通过其JTAG边界扫描口来与仿真器连接。这种方式的仿真器比较便宜,连接比较方便。但由于仅通过十几条线来调试,因而功能有局限。对于全功能在线仿真器来说,由于其仿真头完全取代目标板上的CPU,因而功能非常强大。这类仿真器为了能够全速仿真时钟速度高于100MHz的处理器,通常必须采用极其复杂的设计和工艺,因而其价格比较昂贵。

3.2  “黑”调

在没有仿真器的情况下一般使用“黑”调,具体的方法是加“指示灯”、用示波器测量硬件信号等,目的是打通串口,达到宿主机与目标机的通信。这种调试方法无法跟踪软件的运行这种调试方法要求所使用的BSP模板与自己的单板基本一致。

“黑”调的工程步骤:

                  BSP完成


3.3  使用集成开发环境

在进行VxWorksBSP开发时,如果最小系统的BSP已经能够正常运行,则可以使用VxWorks的集成开发环境Tornado Tornado II工具提供一个高度可视化和自动化的开发环境,加快了基于VxWorks的应用开发。这样,不论对于初次使用还是有经验的开发者,使用Tornado II开发其应用是快速而方便的。

 


4       中断处理

4.1  采用中断处理方式的原因:

保证处理的实时性、减少CPU的消耗。

4.2  中断的处理流程

中断处理程序首先切换到中断堆栈,保存程序计数器和寄存器等中断现场状态,然后对中断进行处理,中断处理过程中必须要及时清除中断源,最后要恢复中断前的程序计数器和寄存器等现场状态,由中断处理程序返回。

 

Interrupt Service Code

实时系统中的中断处理非常重要,系统通常通过中断获取外部事件。为了尽可能块的响应中断,VxWorks中的ISRs运行在特定的上下文(非任务上下文),中断处理无需任务的上下文切换。

我们可以使用除了VxWorks系统使用的之外的系统硬件中断,VxWorks提供了例程intConnect( )用于将C程序与任何中断相连接。VxWorksISRs运行在特定的上下文(x86中断使用当前被中断掉的任务的堆栈,PPC有单独的全局中断堆栈)因而中断处理没有任务的上下文切换。

 

 

4.3  中断的堆栈

大部分系统规定(如PowerPC):所有的中断使用同一个专用堆栈,这个堆栈在系统启动时根据特定的配置参数由系统来分配和初始化。要求堆栈足够大来处理最坏的中断嵌套。

然而有一些系统不允许有单独的中断堆栈(如x86),在这种情况下,中断使用当前被中断掉的任务的堆栈。如果使用这种结构,必须给每个任务开足够大的任务堆栈来处理最坏的中断嵌套和调用嵌套。

可以在开发中使用 checkStack()来查看在栈空间中任务和中断的堆栈是如何分布的。

4.4  ISR的一些限制

1、  ISR要尽量的短,能在任务中完成的工作就不要放在ISR中。

2、  ISR不能调用将会导致阻塞的子程序。

3、  ISR不能take 信号量,但是ISR可以give信号量。

4、  由于子程序malloc()、free()使用了信号量,ISR不能调用它们。

5、  ISR不能通过VxWorks的驱动执行I/O

6、  ISR不能调用使用了浮点协处理器的子程序。

ISR中不能调用的函数列表参见:《VxWorks Programmer Guide》中2.5.3 Special Limitations of ISRs

 

4.5  中断服务程序与任务的通信

由于中断事件通常涉及到任务级代码,因此必须提供中断服务程序和一般任务的通信机制。VxWorks提供的中断服务程序和一般任务的通讯机制有:

Ø共享存储区和环形缓冲

Ø信号量: 中断服务程序能够释放信号量(不包括互斥信号量和VxMP共享信号量),任务能够等待该信号量。

Ø消息队列,中断服务程序能够向消息队列发送消息,任务能够从消息队列里接收消息。

Ø管道:中断服务程序可以向管道写数据,任务可以从管道读取数据。

Ø信号灯:中断服务程序能够通过发信号通知任务,触发相应的信号处理程序的异步调度。

 

 

 

5       常用总线协议:

ØHDLC

High-level data link control (HDLC) is one of the most common protocols in the data link

layer, layer 2 of the OSI model.

ØUART

      universal asynchronous receiver transmitter (UART) protocol is commonly used to

send low-speed data between devices.

ØEthernet/IEEE 802.3

ØATM

ØPCI

ØI2C

ØSPI(motorola : Serial Peripheral Interface)

exchange data between  cpu and peripheral devices (such as EEPROMs, real-time clocks, A/D)converters, and ISDN devices.

 

5.1 PCI总线简介

PCI:周边器件互联(Peripheral Component Interconnect)。

目前最新版本2.2,但很多器件还只支持2.1

PCI与器件的基本关系。

Ø         Host/PCI 北桥:连接主处理器总线到PCI总线

Ø         PCI/ISA 南桥:连接PCI总线到ISA(或EISA)总线,南桥通常含IDE控制器、中断

控制器、USB主控制器、DMA控制器。

Ø         PCI/PCI     连接PCI总线PCI总线

 

设备和功能:

支持256PCI总线。

每条PCI总线上最多可以有32个设备(最多56个比较合适)。

每个设备可以有18个功能。

性能:

33M32bit  传输速率132MB

33M64bit  传输速率264MB

66M32bit  传输速率264MB

66M64bit  传输速率528MB

 

 

6       单板的硬件组成

BSP与单板密切相关,要开发BSP就要了解单板的硬件组成,单板一般由CPU最小系统和一些外围硬件设备构成。

CPU最小系统:

CPU、内存、内存控制器、调试串口、调试网口、系统时钟、桥片、外围芯片(包括)、实时时钟、定时器、FPGA、部分嵌入式系统也包括软硬盘控制器、显卡、键盘。

不同单板使用不同的专用设备芯片:

DMA控制器、E1传输芯片、光接口芯片、时隙交换、FLASHhost/pci桥片 pci/pci桥片、以太网口芯片(如intel82559realtek8139)、以太网口交换芯片(BCM5616)、CSM5000CSM5500、看门狗、专用FPGA逻辑等等。

典型的单板组成示意图:

ØIS95/1X中的SVICM单板组成示意图


Ø3G系统中的CPU最小系统结构示意


Ø3G系统中的CCM单板结构示意

7       处理器: 

我们目前经常使用的处理器:POWERPCARMMIPSx86等系列处理器

  ØPOWERPC Mpc860Mpc8260Mpc850Mpc8250Mpc755Mpc8245Mpc7450IBM750

ØARM     IXP1200IXC1100

ØX86     PIII处理器)

各处理器的特点(见硬件相关文档):嵌入式系统使用的CPU一般具有低功耗、体积小、集成度高等特点,而且一般内部集成了内存控制器、串口控制器、以太网口控制器等芯片。

例如:MPC8260 POWERQUICC是一个功能强大的嵌入式通讯处理器,它集成了一个64-bit高性能的PowerPC系列的RISC微处理器和一个32-bit RISC通讯处理器。同MPC860一样,MPC8260由三个主要功能块组成,但它具有更强大的功能:

¨         一个64-bit 的内核,是PowerPC MPC603e微处理器的变种。它的处理速度可达100~200MHz,并支持L2 cache

¨         一个系统接口单元(SIU)。它具有一个更加灵活的存储器控制器,可以与几乎所有类型的存储器接口。并支持JTAG控制器 IEEE 1149.1 测试端口(TAP)。

¨         一个通信处理模块。它不仅包含了MPC860上的所用通信外围控制器,还增加了三个高性能通信通道(FCC)支持新的高速协议,两个多通道控制器(MCC)可支持128个串行全双工通道。

 

 

 

 

 


8       BSP提供的vxWorks下的设备驱动

BSP的另一项主要任务是为操作系统访问硬件设备驱动程序提供支持。所谓设备是指独立于处理器内核之外的,能够接收CPU的输入数据和/或向CPU提供数据输出的硬件单元。

8.1   网口、串口的设备驱动

对于一些通用的设备,如网口、串口,由于包含在BSP最小系统内,vxWorks 都会提供基本的驱动程序。常用的串口设备驱动程序参见目录/tornado/target/src/drv/sio,网口设备驱动程序参见目录/tornado/target/src/drv/end。这些驱动程序仅与设备本身的硬件特性密切相关,如配置一些必须的设备控制寄存器,中断寄存器,初始化设备驱动结构等。具体的这些寄存器,驱动结构的值是什么,这些与用户的实际应用密切相关的信息放在各单板BSP目录下的sysXXX.c文件中,如串口配置源文件sysSerial.c(定义了用作串口的UART设备的基地址,寄存器数据位数,使用的缺省波特率,晶振频率等),网口配置源文件sysFei82557End.c(定义了82559网口芯片的PCI基地址,EEPROM中的MAC地址等)。大多数应用只需要修改sysXXX.c中的配置参数就可以了。

VxWorks已经为网口和串口设备驱动提供了标准的库函数,用于将与硬件相关的设备驱动加载到 硬件无关的I/O层,使得应用层不必关心具体的硬件驱动实现,直接调用统一的Socket接口函数或I/O接口函数就可以正常的收发数据,执行查询、控制操作。

vxWorks或设备供应商提供的网口,串口驱动已经包含了较为完备的设备驱动接口,完成设备的初始化、数据收发,中断服务程序连接与中断使能。实际应用时,做相应的修改就可以了。详细步骤可以参见相关的最小系统详细设计文档。

8.2  一般硬件的设备驱动

一般硬件是指除BSP最小系统之外的各应用使用的硬件设备。这些设备的供应商可能不会提供专用的设备驱动程序,或是提供的设备驱动程序无法满足实际应用的需要。因此,必须由BSP开发人员编写相应的设备驱动程序,为操作系统访问硬件设备驱动程序提供支持。

BSP提供的设备驱动接口大致有以下几类:

8.2.1          设备初始化接口

所有的设备驱动程序都应该提供设备初始化接口。该接口实现主要包含以下内容:

1、保存应用程序传入的正常回调函数指针。该回调函数在设备正常的收发中断中调用,用于向操作系统的驱动任务传递收发事件消息,触发驱动任务开始收发数据,使得系统尽快退出中断,真正的数据收发都在驱动任务中完成,接受OSS的调度,提高CPU的利用率。对于没有收发事件的设备,如Plx9030Bcm5616 ,就无需这个函数指针。

2、保存应用程序传入的异常回调函数指针。设备出现异常状况通常也会引起中断,在异常中断中调用异常回调函数,将设备出错信息传给OSS的实时控制进程,由该进程对设备的异常情况进行处理,有必要的话,可以报告给后台告警。通常一个实时控制进程负责监控单板上所有设备的异常状况,因此应对给设备的异常状况统一编码,实时控制进程根据不同的告警码来判断何种设备出现了异常。

3、进行必要的硬件初始化。

4、使用系统函数intConnect()将设备中断服务程序连接到中断向量表中。

1、 使用系统函数intEnable()使能设备连接的CPU中断。

 

对于具有数据收发功能的设备,如UARTHDLC,应提供数据收、发接口。

8.2.2          设备发送数据接口

设备发送数据接口用于将上层应用的数据输出到设备,再由设备输出。该接口的入参应包括待输出用户数据缓存的指针,数据长度。

8.2.3          设备接收数据接口

设备接收数据接口用于将设备接收到的数据传递给上层应用。该接口的入参应包括用于接收数据的用户缓存的指针,用户缓存的长度。输出参数包括实际接受到的数据长度。

8.2.4          设备控制接口

上层应用调用设备控制接口完成对设备的设置和查询。如可以调用UART的控制接口查询或重新设置波特率,查询发送、接收缓存是否为空等。

该接口的入参应包括查询码,用于区分不同的操作。对于查询操作,输出参数为查询的结果,对于控制操作,入参还应包括相应的设置参数。

8.2.5          中断服务程序

中断服务程序属于BSP的内部接口,在设备初始化时被连接到中断向量表中。当CPU检查该中断时,就会根据中断向量表中填写的中断服务程序指针调用该函数,完成相应的操作。

在实时应用中,中断服务程序应当尽可能的简短。其主要任务就是检查产生中断的原因,根据不同的中断原因调用相应的回调函数,通知相应的任务去处理中断事件。

 

 

8.3  BSP同其他软件的关系

BSP同硬件和上层软件系统的关系,以及BSP内部模块之间的关系图示如下:

 

 

 

 

9       以太网口驱动

以太网口驱动采用标准的END接口。

vxWorks里如何使用END network

vxWorks里使用两种类型的网络设备驱动,一种是BSD4.3 network drivers,它和IP协议结合紧密,使用不灵活;另一种就是SENS(scalable enhanced network stack), 它在硬件driver和协议driver之间插入了MUX这一层功能实体,如图:

 这样,高层协议和底层驱动间就可灵活配置,MUX的作用如下图:

既然MUX介于这二层之间,它就需要和这二层打交道,它提供了一个结构体来作这方面的工作: XX_DRV_CTRL,它又包含了几个结构。

下面我们看这几个结构:

END_OBJEND对象结构,它是XX_DRV_CTRL结构的基类

DEV_OBJ:包含了设备的具体信息;

NET_FUNCS:包含设备驱动函数的指针

NET_PROTOCOL:设备同上层协议的接口;

 

这些结构的关系如图所示:

 

利用MUX层提供的函数来创建这些结构,(vxworks里使用END,必须执行下面的三个函数)叙述如下:

muxDevLoad() :  loading a Device into the MUX; 它实际是调用了底层driverxxload(),它返回END_OBJ, 并填充了DEV_OBJNET_FUNCS;

muxDevStart() : starting a driver; 它调用了NET_FUNCS(*start)(…); 主要是连接ISRxxInt)和启动设备;

muxBind()    将上层的protocol driver绑定到MUX上;创建NET_PROTOCOL结构;此函数最关键的是指明了MUX层在收到网络包时,调用上层协议的哪个函数。

(函数的使用参见ref手册)

我们看三层协议间的函数调用关系:

 

在接收数据包时,整个过程如图:

   

 设备产生中断,执行xxInt(), 中断服务程序调用netjobAdd()xxReceive挂到os tNetTask队列中执行,xxReceive()调用muxReceive(),后者再调用protocol里的接收函数,象上面提到的EpStackRcv.

 

 

发送包时如图:

 

 

 

 

VxWorks BSP的目录结构:

BSP文件主要位于Tornado安装之后的Target目录下,具体目录结构如下:

 

 

 

 

config目录:用于vxWorks核心的配置及编译的文件。其中又包含多个子目录。

all目录:     通用的配置文件

          comps目录:   用于Tornado工程管理工具配置的源文件

          bspname目录: 每个单板都有一个相应目录,命名可根据单板而定,其中包含有关此单板BSP的代码文件,目标文件及编译文件等,这也就是我们通常所说的单板BSP

 

h目录:其中包含所有vxWorks操作系统的头文件及通用编译文件。其中的各个子目录表示了vxWorks中不同模块的头文件。

 lib目录:由vxWorks为不同平台提供的库文件及目标文件。

 

src目录:vxWorks提供的部分硬件芯片驱动程序的源代码文件,其又包含多个子目录:

            config目录:有关vxWorks操作系统的一些通用的源代码文件。

drv目录:vxWorks中一些硬件芯片的通用驱动程序源代码文件,其根据不同功能的芯片分为若干个子目录,例如end 表示网口驱动,sio表示串口驱动等

 

unsupported目录:其中包含一些较古老的硬件BSP代码。

 

 

3       三种不同的VxWorks映象比较

理解VxWorks映象是开发VxWorks BSP的基础。

VxWorks映象通常由三个部分组成:BSP、操作系统内核和上层应用。从文件的组织形式上可以将映象分为三段:BSS段、Data段和Text段。其中,BSS段存放的是未初始化的全局变量或静态变量,Data段存放的是已经初始化的全局变量或静态变量,Text段存放的是可执行的代码。

根据应用场合的不同,VxWorks映象可分为三类:

Loadable images

Rom-based images

Rom-resident images

其中,Loadable images通过烧写在ROM中的Bootrom装载到RAM中来启动;Rom-based images使用自身带的启动代码把自己装载到RAM中来启动;而Rom-resident imagesROM中启动,只是把需要动态修改的Data段和BSS段装载到RAM中去。

9.1  3.1  可加载的VxWorks映象

这是一种运行于RAMVxWorks映象。它不包含搬移程序,需要借助于一些外部的程序如bootRom才能加载到RAM的低端RAM_LOW_ADRS地址处。这是缺省的开发映象。

在开发的初期阶段,用户可以根据需要添加或删除一些VxWorks组件,生成自己的可加载的VxWorks映象,存放在开发主机的某个目录下。目标板上电后,由烧结在BOOT中的起始引导程序(BootStrap Programs)BOOT中的ROM引导程序(ROM Boot Programs)拷贝到RAM的高端地址RAM_HIGH_ADRS处,并跳转至该地址执行ROM引导程序,配置好所选的加载方式(缺省为网络方式),将指定的主机目录下的可加载的VxWorks映象下载到目标板的RAM地址RAM_LOW_ADRS处,并跳转到此处执行。如图1所示。

1、可加载的VxWorks映象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


这种映象的优点是生成的VxWorks映象可以存放在开发主机PC机上,不用烧到BOOT中,节省了BOOT容量,也便于随时修改不同的VxWorks映象,适用于调试的初期阶段。不足之处是需要在主机上维护一个正确的VxWorks映象,对于调试硬件无关的上层应用程序显得不是很方便。

    Tornado工作台的Build窗口中,选择Rules属性页中的VxWorks即可生成可加载的VxWorks映象。

9.2  3.2  基于ROMVxWorks映象

这是一种运行于RAM中,但起初存放于ROM中的VxWorks映象。即该映象需要和搬移程序一起固化在BOOT中。目标板上电后,首先运行BOOT中的引导搬移程序,将整个VxWorks映象拷贝到RAM地址RAM_LOW_ADRS处,并跳转到此处执行。如图2所示。

2 基于ROMVxWorks映象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


该映象根据是否被压缩又可分为:

l        基于ROM的未压缩的VxWorks映象,可直接从ROM拷贝到RAM

l        基于ROM的压缩的VxWorks映象,这种映象主要是为了节约BOOT空间,在从ROM拷贝到RAM的过程中需要解压缩,因此与上述未压缩的映象相比,它的引导过程相对较慢,但两者在RAM中的运行速度是一样的。

9.3  3.3  驻留ROMVxWorks映象

这种映象起初也和搬移程序一起固化在BOOT中。目标板上电后,首先运行BOOT中的引导搬移程序,但仅将VxWorks映象的数据段和BSS段拷贝到RAM地址RAM_LOW_ADRS处,映象的代码段仍旧留在ROM中,从ROM中开始执行。如图3所示。

这种映象的优点是具有最快的引导速度,占用最少的RAM空间,适用于RAM空间有限的目标板。但是由于该映象在ROM中运行,运行速度在三种映象中是最慢的。

 

 

3 驻留ROMVxWorks映象

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


几种不同的BOOTROM的比较

针对上述三种不同的VxWorks映象,可以生成以下几种不同的BOOTROM,主要体现在执行搬移程序romStart( )( 位于bootInit.c文件中)时不同:

9.4  4.1  用于可加载VxWorks映象的BOOTROM

由图1所示可知,用于可加载VxWorks映象的BOOTROM包含两部分:起始引导程序(BootStrap Programs)ROM引导程序(ROM Boot Programs)。

起始引导程序驻留在ROM中,主要包含:

l         汇编级的硬件初始化程序romInit.s,用于系统的基本初始化,设置一些重要寄存器的初始值,进行存储器的映射

l         搬移程序bootInit.c,将ROM引导程序拷贝至RAM的高端地址RAM_HIGH_ADRS,然后跳转到此处执行ROM引导程序。

ROM引导程序起初存放在ROM中,初始化时被拷贝到RAM中,主要用于系统的进一步初始化,并配置加载方式,将VxWorks映象加载至RAM。可分为三种不同的类型:

l        压缩的ROM引导程序,在拷贝的过程中需要解压缩,在RAM中执行

l        未压缩的ROM引导程序,可直接拷贝,在RAM中执行

l        驻留ROMROM引导程序,仅拷贝ROM引导程序的数据段,代码段仍旧在ROM中执行

Tornado开发环境中,通过在主窗口点击Build|Build Boot ROM…可以选择生成以上

三种BOOTROM,分别为:bootrom_uncmp.hex(未压缩的BOOTROM)bootrom.hex(压缩的BOOTROM)bootrom_res.hex(驻留的BOOTROM)。

静态连接到可加载的VxWorks映象的系统初始化代码执行并完成整个初始化过程。

引导过程成功以后,RAMROM引导程序占用的空间(从RAM_HIGH_ADRS开始)可以重新被系统利用。

1中所示的各地址含义为:

l        LOCAL_MEM_LOCAL_ADRSRAM的起始地址

l        RAM_LOW_ADRSVxWorks的加载点,也是VxWorks代码段的起始位置

l        FREE_RAM_ADRSVxWorks映象的结束点。通常也是系统内存池和目标服务器内存池的起始地址

l        RAM_HIGH_ADRSROM引导程序的加载点。它也是ROM引导程序(除驻留ROM引导程序之外)的代码段的起始位置,或驻留ROM引导程序数据段的起始位置。

9.5   用于基于ROMVxWorks映象的BOOTROM

由图2所示可知,用于该映象的BOOTROM包含两部分:起始引导程序(BootStrap Programs)和基于ROMVxWorks映象。搬移程序bootInit.c负责将VxWorks映象的文本段和数据段搬移到用户定义的低端内存地址RAM_LOW_ADRS,如果需要进行必要的解压缩,然后直接启动VxWorks映像。

因此BOOTROM的容量相对于1节中描述的BOOTROM要大一些,但无需在主机目录下维护一个可用的VxWorks映象。

基于ROMVxWorks BOOTROM有压缩和未压缩之分。在Tornado工作台的Build窗口中,选择VxWorks映象Rules属性页中的VxWorks_rom即可生成基于ROM的未压缩的VxWorks BOOTROM,选中VxWorks_romCompress即可生成基于ROM的压缩的VxWorks BOOTROM

9.6  4.3  用于驻留ROMVxWorks映象的BOOTROM

由图3所示可知,用于该映象的BOOTROM包含两部分:起始引导程序(BootStrap Programs)和驻留ROMVxWorks映象,VxWorks系统文本段驻留在ROM,搬移程序bootInit.c负责将数据段和bss段搬移到用户定义的低端内存地址RAM_LOW_ADRS,直接启动VxWorks映像(含符号表)。此时,RAM_LOW_ADRSVxWorks映象的加载点,它也是VxWorks数据段的起始点。

Tornado工作台的Build窗口中,选择VxWorks映象Rules属性页中的VxWorks_romResident即可生成驻留ROMVxWorks BOOTROM

 

 

BSP的执行流程

下面以“BootRom + vxWorks”的启动形式说明BSP的执行流程:

 

 

 

 

 

9.7  5.1  BOOTROM的启动过程

2、   目标板加电之后,程序指针指向RESET中断程序入口处,开始执行初始化程序romInit.s,设置机器状态字及其它硬件相关寄存器,关闭中断,禁止程序和数据CACHE,初始化内存,并设置堆栈指针,保存启动类型,调用romStart( )

3、 程序跳到第一个C程序bootInit.c的函数romStart( )入口地址,根据堆栈中的参数决定是否清零内存RAM(如是冷启动(cold start)则清零),根据不同的bootrom文件,把ROM中数据段和文本段拷贝到RAM(如果ROM代码是压缩的,还要解压);

4、 程序跳到RAM入口地址(文件bootConfig.c中函数usrInit( ) ),根据用户配置来设置cache的工作模式,清零bss段,初始化异常处理程序,进行板级硬件初始化sysHwInit( )

5、 启动多任务内核KernelInit( ),执行usrRoot任务。在该任务中初始化串口,创建console终端设备。创建bootCmdLoop任务,根据单板设计选择不同方式加载VxWorks映像文件,如通过串口、网口、硬盘等方式。

9.8  5.2  VxWorks映象的启动过程

VxWorks进入点sysInit()

启动VxWorks系统的第一步就是将系统映象加载到主内存。这通常是在VxWorks boot Rom 的控制下,从开发主机上下载。接着,boot Rom将控制权交给VxWorks的起始进入点:sysInit()。在makefile config.h文件里,已将这个进入点设置成位于地址RAM_LOW_ADRS

函数sysInit()位于系统特定的汇编语言模块sysALib.s中。它可以锁住中断,关闭cache(如果使用了话),初始化处理器的寄存器(包括C堆栈指针)至缺省值。它还会关闭跟踪,清除所有未决的中断,并调用一个位于usrConfig.c 模块的C语言子程序:usrInit() 。对于某些目标板,sysInit()还执行一些必要的与系统有关的硬件初始化,以便在usrInit()中执行完剩余的初始化内容。仅供usrInit()使用的初始堆栈指针,被设置成位于系统映象(RAM_LOW_ADRS)以下,向量表以上的位置。

初始化代码usrInit()

函数usrInit()(位于usrConfig.c中),储存有关引导类型的信息,处理在内核启动之前必须执行的初始化,而后启动内核执行。它是运行于VxWorks内的第一个C函数。此时,所有的中断都已被锁住。

许多VxWorks工具在usrInit( )中都不能使用。这是因为此时还没有任务的上下文(没有TCB和任务堆栈),那些需要任务上下文的工具无法被调用。函数usrInit( )仅做一些创建初始化任务usrRoot( )所必须的工作。然后由usrRoot( )完成启动过程。

usrInit( ) 中的初始化过程如下所述:

Cache初始化

usrInit( ) 的起始代码初始化cache,设置cache 模式,并将cache放置在一个安全的位置。在usrInit( )结束时,缺省情况下,指令cache和数据cache被使能。

对系统的BSS段清零

C C++语言规定所有未初始化的变量缺省的初始值为零。这些未初始化的变量被放置在一个称为bss的段内。由于usrInit( )是系统执行的第一个C代码,在它的一开始对包含bss段的内存清零。VxWorksboot ROM 也会清内存,但VxWorks映象假设没有采用boot ROM,仍然执行清内存的操作。

初始化中断向量

异常向量必须在使能中断和启动内核之前建立。首先,调用intVecBaseSet( ) 建立向量表基地址。而后,调用excVecInit( ) 初始化所有的异常向量至缺省句柄,以便安全地捕获和报告由程序错误或意外的硬件中断导致的异常。

初始化硬件至静止状态

通过调用系统相关函数sysHwInit( )初始化系统硬件。该函数复位并关闭那些在中断使能(内核启动时)以后可能产生中断的硬件设备。这一点很重要,因为VxWorks ISRs(用于I/O设备,系统时钟等)直到在任务usrRoot( )中完成系统初始化以后,才被连接到它们的中断向量上。不要在sysHwInit( ) 调用中试图为一个中断连接一个中断句柄(也就是不能使用intConnect( )),因为此时内存池还没有初始化。

初始化内核

函数usrInit( )结束时调用了两个内核初始化函数:

usrKernelInit( )(usrKernel.c中定义)为每个指定的可选内核组件调用合适的初始化代码。

kernelInit( ) (kernelLib.c的一部分)初始化多任务环境,不用返回。函数参数包括:

l         用以产生作为根任务的应用程序,典型的为usrRoot( )

l         使用的堆栈大小

l         可用的起始内存地址,一般位于VxWorks映象的代码段,数据段和bss 段之后,如果包含可选的主机内存池,则还要加上WDB_POOL_SIZE

l         sysMemTop( )定义的内存顶部

l         中断堆栈的大小

l         中断封锁级别

kernelInit( ) 调用intLockLevelSet( ),关闭循环模式,创建一个中断堆栈(如果结构支

持的话)。然后从内存池的顶部创建一个根堆栈和TCB,创建一个根任务,usrRoot,并终止usrInit( )线程的执行。此时使能中断,所有的中断源已被关闭,未决中断已被清除。

    初始化内存池

内存池的初始化是由kernelInit( )来完成的。kernelInit( ) 的参数指定了初始内存池的起始和终止地址。在缺省的usrInit( )中,将内存池设置在紧接于引导的系统映象之后,并包含所有剩余的可用内存。

可用内存的大小由sysMemTop( )决定。如果你的系统有其它的不连续的内存片,你可以在usrRoot( )任务中通过调用memAddToPool( )将它们包含进通用的内存池。

    VxWorks包含了一个位于memPartLib模块中的内存分配工具,它管理一个可用内存池。用户可以调用malloc( )函数从内存池中获得可变大小的内存块。VxWorks也利用malloc( )函数来动态分配内存。许多VxWorks工具在初始化过程中需要分配数据结构。因此,内存池必须在任何其他的VxWorks工具初始化之前初始化。

    Tornado目标服务器也管理一部分目标内存以支持目标模块的下载和其他开发功能。VxWorks使用malloc( )函数为已下载的模块分配空间,为已产生的任务分配堆栈,在初始化时分配数据结构。用户也可以使用malloc( )函数为自己的应用程序分配所需的内存空间。因此,推荐将所有的未用内存分配给VxWorks内存池,除非必须为一个特殊的应用保留一片固定的绝对内存。

初始任务usrRoot( )

当多任务内核启动执行以后,所有的VxWorks多任务工具就可以用了。控制权被传送至usrRoot( )任务,并完成初始化系统。

usrRoot( ) 执行以下操作:

l         初始化系统时钟

l         初始化I/O系统和驱动

l         创建控制台设备

l         设置标准输入和标准输出

l         安装异常处理和登陆

l         初始化管道驱动器

l         初始化标准I/O

l         创建文件系统设备并安装磁盘驱动器

l         初始化浮点支持

l         初始化性能监视工具

l         初始化网络

l         初始化可选的工具

l         初始化WindView

l         初始化目标代理

l         执行一个用户提供的启动脚本

l         初始化VxWorks Shell

下面对各个步骤进行详尽的描述:

初始化系统时钟

usrRoot( ) 任务执行的第一个操作就是初始化VxWorks时钟。通过调用sysClkConnect( )

将系统时钟的中断向量连接到usrClock( )函数上。调用sysClkRateSet( )将系统时钟率设置为60Hz

   sysClkConnect( ) 函数调用sysHwInit2( )。风河的BSP采用sysHwInit2( )执行在sysHwInit( )中未完成的进一步的板级初始化。例如,可以利用intConnect( )连接ISR,因为此时已经分配了内存,系统处于多任务环境。

初始化I/O系统

如果在configAll.h中定义了INCLUDE_IO_SYSTEM,就可以调用iosInit( )函数初始化VxWorksI/O系统。该函数的参数指定了可被顺序安装的最大驱动器的数目,可以在系统中同时打开的最大文件数目,和VxWorksI/O系统包含的“空”设备的名字。

包含或去除INCLUDE_IO_SYSTEM还会影响是否创建控制台设备,是否设置标准的输入、输出和标准的出错信息。

创建控制台设备

如果包含了板上串口驱动器(定义了INCLUDE_TTY_DEV),就可以通过调用驱动器的初始化函数(典型的是ttyDrv( ))将它安装进I/O系统。实际的设备是通过调用驱动器的设备创建函数(典型的是ttyDevCreate())来创建和命名的。这个函数的参数包括设备名称,一个串行I/O通道描述字(从BSP获得),和输入输出缓存大小。

NUM_TTY定义了tty口的数量(缺省是2)。宏CONSOLE_TTY指定了哪个口作为控制台口(缺省是0),宏CONSOLE_BAUD_RATE指定了其比特率(缺省是9600 bps)。这些宏都在configAll.h中定义,但对于那些具有非标口数的单板可以在config.h中对它们进行重新定义。

设置标准输入、标准输出和标准出错信息

系统级的标准输入、标准输出和标准错误信息的配置是通过打开控制台设备并调用ioGlobalStdSet( )来建立的。这些配置作为VxWorks的缺省设备用于与应用开发人员通讯。为了使控制台设备成为一个交互式的终端,调用ioctl( )将设备选项设为OPT_TERMINAL

安装异常处理和登录

    初始化VxWorks的异常处理工具(由excLib模块提供)和登录工具(logLib库提供)。这些工具检查在根任务内部或者初始化各种工具时产生的程序错误。

当定义了宏INCLUDE_EXC_HANDLINGINCLUDE_EXC_TASK后,调用excInit( )初始化异常处理工具。excInit( ) 函数产生一个异常支持任务excTask( )。初始化以后,可以安全地捕获和报告导致硬件异常的程序错误,报告并解除没有初始化向量的中断。当定义了INCLUDE_SIGNALS后,调用sigInit( )初始化VxWorks的信号工具,该工具用于任务的异常处理。

当定义了INCLUDE_LOGGING宏以后,调用logInit( )初始化登录工具。其参数定义了显示登录信息的设备的文件描述字,和分配的登录信息缓存数。登录初始化还创建了一个登录任务logTask( )

初始化管道驱动

如果需要所谓的管道,在configAll.h中定义INCLUDE_PIPE,就会自动地调用pipeDrv( )初始化管道。而后任务就可以利用管道通过标准的I/O接口互相通讯了。管道必须由pipeDevCreate( ) 函数创建。

初始化标准I/O

当定义了宏INCLUDE_STDIO以后,VxWorks 就会包含一个可选的标准I/O包。

创建文件系统设备并初始化设备驱动

许多VxWorks配置至少包含一个磁盘驱动器,或带有dosFs/rt11Fs/rawFs文件系统的RAM磁盘。首先,通过调用驱动器的初始化代码安装一个磁盘驱动器。而后,驱动器的设备创建代码会定义一个设备。这个调用会返回一个指向描述设备的BLK_DEV结构的指针。

然后就可以调用文件系统的设备初始化代码-dosFsDevInit( ), rt11FsDevInit( ), or rawFsDevInit( )(如果定义了宏INCLUDE_DOSFS, INCLUDE_RT11FSINCLUDE_RAWFS)初始化和命名设备。在初始化一个设备之前,必须用dosFsInit( ), rt11FsInit( ) rawFsInit( )初始化文件系统模块。文件系统的设备初始化函数的参数取决于特定的文件系统,但典型的包括设备名称,由驱动器的设备创建代码产生的一个指向BLK_DEV结构的指针,可能还有一些文件系统特定的配置参数。

初始化浮点支持

如果在configAll.h中包含了INCLUDE_FLOATING_POINT宏定义,则调用floatInit( )函数初始化浮点I/O支持。当定义了INCLUDE_HW_FP,调用mathHardInit( )初始化对浮点协处理器的支持。当定义了INCLUDE_SW_FP,调用mathSoftInit( )初始化对软件浮点仿真的支持。

包含性能仿真

VxWorks具有两个内嵌的性能监视工具。一个由spyLib提供的任务活动综述,一个由timexLib提供的子程序执行定时器。如果在configAll.h中定义了宏INCLUDE_SPY INCLUDE_TIMEX,就会包含这些工具。

初始化网络

如果配置头文件中定义了INCLUDE_NET_INITusrRoot( )就会调用usrNetInit( )函数初始化网络(usrNetInit( )的源代码位于installDir/target/src/config/usrNetwork.c)。usrNetInit( )函数使用了一个配置字符串作为它的参数。这个配置字符串通常是一条“引导行”,用于VxWorksboot ROM引导系统。根据这个字符串,usrNetInit( )函数执行以下操作:

l         调用netLibInit( )初始化网络子系统

l         连接并配置合适的网络驱动器

l         添加网关路由

l         初始化远程文件存取驱动器netDrv,并添加一个远程文件存取设备

l         初始化远程登录工具

l         可选地初始化远端程序调用(RPC

l         可选地初始化网络文件系统(NFS)工具

如前所述,是否包含这些网络工具由configAll.h中的宏定义决定。

初始化可选产品和其它组件

可选产品VxMP可提供共享内存目标。如果定义了宏INCLUDE_SM_OBJusrRoot( )就会调用usrSmObjInit( )函数(源代码位于installDir/target/src/config/usrSmObj.c),初始化共享内存目标。共享内存目标库需要VxWorks引导行中的域值。这些函数包含在usrNetwork.c文件中。如果不包含网络服务,usrNetwork.c就不会被包含,共享内存初始化就会失败。工程工具计算所有的依存关系,但如果使用手工配置,可以将INCLUDE_NETWORK添加进configAll.h,或是从usrNetwork.c文件中将引导行代码提取出来放置到其他地方。

如果定义了INCLUDE_MMU_BASIC,就可以提供基本的MMU支持。如果定义了INCLUDE_MMU_FULL,可选产品VxVMI就可以提供代码保护,向量表保护和一个虚拟内存接口。MMU由函数usrMmuInit( )初始化,该函数位于installDir/target/src/config/usrMmuInit.c文件中。如果还定义了宏INCLUDE_PROTECT_TEXT INCLUDE_PROTECT_VEC_TABLE,就会初始化代码保护和向量表保护。

初始化WindView

可选产品WindView可提供内核测试工具。如果在configAll.h中定义了宏INCLUDE_WINDVIEW,就可以在usrRoot( )中调用windviewConfig ( )初始化WindView。其它的WindView常量控制特定的初始化步骤。

初始化目标代理

如果定义了INCLUDE_WDB,调用函数wdbConfig( )(位于installDir/target/src/config/usrWdb.c)。这个函数初始化通讯接口,然后启动代理。

执行一个启动脚本

如果VxWorks配置了目标驻留的shell,定义了INCLUDE_STARTUP_SCRIPT,并且在boot引导过程中在启动脚本参数中输入了脚本文件的名称,usrRoot( )函数就可以执行一个用户提供的启动脚本。如果在引导过程中忽略了启动脚本参数,就不会执行启动脚本。

可加载VxWorks映象的初始化过程

  

   数 功 能

所 在 文 件

sysInit()

(a)锁住中断;(b)禁用缓冲;

(c)用缺省值初始化系统中断表(仅i960);

(d)用缺省值初始化系统错误表(仅i960);

(e)初始化处理器寄存器到一缺省值;

(f)使回溯失效;(g)清除所有悬置中断;

(h)激活usrInit(),指明启动类型。

sysALib.s

usrInit()

a)bss清零;

(b)保存bootTypesysStartType;

(c)调用excVecInit(),初始化所有系统和缺省中断向量;

(d)依次调用sysHwInit(),  usrKernelInit(),kernelInit().

usrConfig.c

usrKernelInit()

依次调用classLibInit(),taskLibInit(),taskHookInit(),semBLibInit(),semMLibInit(),

semCLibInit(),semOLibInit(),wdLibInit()

msgQLibInit(),qInit(),workQInit()

usrKernel.c

kernelInit()

初始化并启动内核。

(a)激活intLockLevelSet();

(b)从内存池顶部创建根堆栈和TCB(c)调用taskInit(), taskActivate(),用于usrRoot();

(d)调用usrRoot().

kernelLib.h

usrRoot()

初始化I/O系统,驱动器,设备(在configAll.hconfig.h中指定)

(a)调用sysClkConnect(),sysClkRateSet(),

iosInit(),[ttyDrv()];

(b)初始化excInit(),logInit(),sigInit().

(c)初始化管道,pipeDrv();

(d)stdioInit(),mathSoftInit()mathHardInit()

(e)wdbConfig():配置并初始化目标代理机

usrConfig.c

 

 

你可能感兴趣的:(ARM,include,任务,工具,嵌入式操作系统,嵌入式,cache)