基于VxWorks的bootrom代码改进
扫描二维码
随时随地手机看文章
关键词:VxWorks bootrom BSP makefile
1 问题的提出
VxWorks是Wind River公司开发的一种嵌入式操作系统,具有强大的功能和友好的用户界面。VxWorks软件应用非常广泛,不仅应用在网络设备、家用电器当中,而且还应用在航天、导弹等高端领域。在网络设备应用领域中,通常利用VxWorks来开发bootrom代码、BSP(Boadr Support Packet)代码以及上层的网络协议代码。
通常,Bootrom软件有以下功能:①通过串口下载操作系统映像;②通过串口升级自身映像;③通过串口下载系统配置文件、系统信息文件;④加载操作系统映像,使其正常启动;⑤其它的辅助功能,如地址内容查看功能、址内容修改功能和bootrom菜单显示信息控制的功能等。对于通常的bootrom代码编译,大多数用户选择使用GNU make的DOS命令来进行。因为如果bootrom不提供网口功能的话,单纯的CPU驱动部分代码量是不大的;但如果提供网口功能的话,代码量相对较大,文件之间的内在关系比较复杂,因此,系统使用GNU make的DOS命令来编译生成bootrom就显得有点麻烦。Makefile文件主要是由“目标”(target)、“依赖性”(dependencies)和“动作”组成的一系列规则,而VxWorks提供的GNU make工具就是根据Makefile指定的规则来编译和链接程序的。Makefile基本结构虽然简单,但正确、灵活地运用这些规则并不是那么轻松的事情。即使根据GNU Makefile规则书写出适当的Makefile文件,但随着代码的改变,Makefile还需要经常修改,这就增加了BSP开发人员的负担。而且,对于交换产品而言,其成本控制是非常严格的。对于存放bootrom软件的Flash来说,通常要求大小为512KB,这样就需要考虑到容量的限制。
因此,必须使用新的方法来简化bootrom代码编译,而且要在保证提供网口功能的情况下,系统提供的bootrom软件小于512KB。
2 实现方法
2.1 通过建立工程生成bootrom映像
采用建立工程的方式可以有效地解决这个问题。首先依据bootrom中的BSP代码来新建一个工程,然后将网中驱动代码加入到建好的工程中。这样就可以通过编译工程实现带有网口下载功能的bootrom软件。但对于VxWorks嵌入式系统而言,它有自己一套规范,求bootrom软件在加电后必须从romInit()函数开始运行,而建立工程所得的入口函数为sysInit()。因此需要增加一个系统壳代码,这个壳代码所要完成的任务就是加载bootrom工程的映像,并从romInit()开始执行bootrom代码。通过建立工程来调试bootrom代码有以下优点。
(1)责任分明
开发产品是一个复杂的工程,需要多人合作完成。对于CPU子系统和网口驱动系统这两块内容来说,更需要责任明确。通过建立工程来调试代码,可以准确地定位错误文件和函数的位置。是BSP代码出现问题,还是网口驱动代码出现问题,可以一目了然,做到责任明确。
(2)提高效率
通过建立工程可以节省书写Makefile文件时间,并可以让VxWorks软件的友好图形界面这优点得到更好的体现。另外,建立工程也可以减少文件编译排错的难度,方便工程文件的调试以及功能的验证,从而大大提高工作效率,加快工作进度。
2.2 编写壳代码并实现加载
建立工程生成映像文件的入口函数为sysInit(),而VxWorks规范中要求系统加电后要从romInit()函数开始运行。因此,需要设计一个方案来加载编译工程得到的映像,而且要具备初始化CPU和SDRAM存储空间的功能。
首先,系统从romInit()函数中开始运行,完成CPU和SDRAM初始化,跳转到函数romStart()运行。然后,加载第二份bootrom代码到系统的低地址处,运行壳代码中的sysInit()函数,并调用自己构造的解压函数usrInit(),将压缩后的工程映像文件解压缩到系统高地址处,之后系统继续运行解压后的bootrom代码。此时,系统开始运行sysInit()函数,调用VxWorks系统中的usrInit()函数,开始初始化系统硬件、内核以及其它外围设备。
壳函数usrInit()代码如下:
void usrInit(void){
volatile FUNCPTR absEntry;
if(inflate((UCHAR*)ROM_OFFSET(_binArrayStart),
(UCHAR *)(RAM_LOW_ADRS),&_binArrayEndbinArrayStart)!=OK)
return;
absEntry=(FUNCPTR)(RAM_LOW_ADRS);
(absEntry)();
}
其中RAM_LOW_ADRS为系统低地址,是操作系统运行的起始地址,但bootrom可以利用低地址来实现在系统高地址的运行。宏ROM_OFFSET用于准确定位函数地址,因为bootrom中运行的代码要求以相对地址方式来寻址,而不能以绝对地址方式寻址。_binArrayStart为压缩后二进制代码的开始符,_binArrayEnd为压缩后二进制代码的结束符。Inflate()是VxWorks软件的解压缩函数,用于解压缩由deflate()压缩函数压缩的二进制文件映像。同时,需要将下述代码添加到编译规则文件rules.vxWorks中的相应部分:
$(CC)-c $(CFLAGS)$(BSP_DIR)/unzip.c-o$(BSP_DIR)/unzip.o
$(LD)$(LDFLAGS)-e sysInit -Ttext $(RAM_LOW_ADRS)o
unzip_obj.o sysALib.o $(BSP_DIR)/unzip.o $(LIBS)flex.z.o
其中,unzip.c中包含构造的壳函数usrInit()。SysInit()为解压软件入口函数。上述语句的功能:第一行完成壳文件的编译,第二、三行完成壳目标代码与第二份bootrom代码的链接。这样,一个具有解压功能的壳函数就被链接到第二份bootrom映像中了。
图1、图2是修改前的系统运行方式与修改后系统运行方式比较。
通过这两种方式的比较可以看出,修改前系统运行式与修改后的运行方式有下面两点差异;①第一份bootrom启动后,前者存在解压缩自射映像的操作,而后者没有;②对于第二份bootrom,前者没有壳代码,而后者有。
2.3 缩减文件长度
通常第一份bootrom代码只有两个文件,一个是包含CPU和SDRAM初始化文件romInit.s,另外就是包含romStart()函数的bootInit.c文件。另外,根据需要还可以添加提供串口轮询显示功能的文件。对于第一份bootrom代码,通常只有10KB左右(这是针对系统修改后的方式),而对于包含壳函数代码的,通过建立工程并编译而生成的第二份bootrom比较大,通常为570KB左右。(注意:这几个数值是通过特定的产品来得出的结论,并不应用于所有产品,但遇到类似的情况可以借鉴处理。)而其后面的一部分完全是0,可以考虑去掉这些0,但不能影响软件的功能。经过测试得出结论:去掉后面的0对系统功能和性能没有任何影响。
通过文件的操作来实现两份bootrom合并,合并后的大小要求小于或等于512KB。如果不采用任何措施,直接将两个文件合并起来要在580KB左右,大于512KB,这是很多系统不能满足的。第二份bootrom映像的后面部分的内容类似表1所列的信息。
表1 bootrom映像部分二进制内容
000 | 210h | EB | E2 | BD | 95 | BD | 15 | 87 | AE | 3C | 74 | FD | 5C | 5F | 6A | FD | 8B |
000 | 220h | D6 | BD | 3A | EB | FF | 6F | CF | 2A | D2 | 69 | 95 | E9 | 34 | AE | E7 | EF |
000 | 230h | 86 | 94 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
000 | 240h | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
000 | 250h | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 | 00 |
从表1可以看出,在地址0x00078233处就是0值了。这样可以通过对文件内容的操作将0x00078240后面的内容全部剔除,从而可将合并后的bootrom代码控制在512KB以内。当然,我们通常会选择一个整数值进行操作,即将0x0007824后的所有值去掉即可。
这样处理,可以减少维护和开发的工作量。如果按照以往的做法,bootrom软件对外将有第一份和第二份的区别,无论是生产、上层软件调试还是开发,都需要分别对待,这样维护量和开发量将会加大。而经过修改后,可把区别只控制在开发阶段,在线升级时,可以按照一个软件来通过串口或网口来进行升级。通过对bootrom最后生成文件大小的控制,可以简化生产流程,加快生产进度。
3 小结
在嵌入式操作系统中进行程序开发,需要经常开辟新的思路,以一些简单的实现方式代替复杂易错的方式。在本次产品开发过程中,将bootrom映像生成方式由惯用的GNU make命令行实现,修改为按照新建工程的方式来实现,是一个相对好的方法,对整个产品的后续批量生产、用户维护和后续开发都奠定了一个良好的基础。