摘要: 本文讨论了嵌入式实时操作系统VxWorks的I/O系统及驱动程序实现的原理, 并根据PMC FPGA转接卡的开发, 详细分析了VxWorks下PCI9030设备驱动程序的实现过程及相关代码。同时就该驱动程序对系统性能及实时性的提高作了必要的分析和讨论。

关键词: VxWorks, I/O系统, PMC-FPGA, PCI9030

随着通信技术、计算机技术的飞速发展, 嵌入式实时操作系统越来越广泛地应用到无线通信、交通、工业控制、军事、航空航天、卫星通信等各个领域。由于这些领域对实时性、可靠性要求很高, 从而使得实时操作系统迅速发展起来。其中VxWorks 是目前公认的最出色的一种实时操作系统。VxWorks 具有可裁减的微内核; 高效的任务管理; 优先级抢占和时间片轮转调度; 准确的上下文切换; 快速灵活的任务间通信等优点。

它已成为实际的嵌入式实时操作系统的工业标准和军用标准[1]。嵌入式系统特别强调“量身定做”的原则, 基于某一种特殊用途, 可以针对这项用途开发出截然不同的一项系统,这就是所谓的客制化[2]。通常需要根据系统的功能、成本、尺寸及用户需求等方面来定制合适的硬件系统, 这就要求用户开发自己的硬件驱动程序。驱动程序的开发是系统开发的重要组成部分, 其性能、实时性、可靠性、指令的简练性关系着应用系统的性能和可靠性, 所以驱动程序的开发显得至关重要。

本文针对以PCI9030 为接口控制芯片的PMC - FPGA(PCI Mezzanine Card)转接卡的开发, 论述了VxWorks下设备驱动程序结构及PMC- FPGA 板卡驱动程序的实现。

1 VxWorks下I/O系统及驱动程序

1.1 I/O系统简介

VxWorks下I/O系统为各种设备提供一个简单、统一、独立的设备接口, VxWorks下I/O系统的独特设计使其比其他I/O系统更快、更灵活。这是实时系统的一个重要特征。[3]I/O系统的功能是将用户的I/O请求路由到合适的驱动程序中的对应函数。它通过一个文件描述符表来实现这一功能。VxWorks设备驱动程序基本上通过I/O系统进行访问, 设备驱动程序被作为内核过程来实现, 进一步提高了系统的实时性。I/O系统把设备作为特殊文件进行处理, 提供了统一的管理、统一的界面和统一的使用方法, 并把设备、文件、网络通信组织成为一致的层次抽象。图1描述了I/O系统与驱动程序间的层次关系。

VxWorks IO System

VxWorks下I/O系统中三个主要的元素是: 驱动程序、设备和文件[3]。文件是用户访问设备的统一接口; 驱动程序是实现I/O 系统所需的七个具体基本函数; 设备是实际物理设备的抽象定义。

1.2 驱动程序的执行逻辑VxWorks下I/O系统提供七个基本的I/O函数:

1、问题的提出

通用串行总线(USB)作为一种中高速的数据方式,已经很普遍地应用于外设与主机的通信中。VxWorks是当今十分流行的实时操作系统,在通信、国防、工业控制、医疗设备等嵌入式实时应用领域。很多VxWorks系统中都有USB设备,可是关于USB的驱动实现并没有相关的资料可以参考,给实际工作带来了难题。本文通过详细地分析VxWorks下的USB驱动栈,具体提出了其实际应用的方法,为USB在应用VxWorks的嵌入式系统的开发扫清了技术障碍。

2、VxWorks简介

VxWorks是WindRiver公司开发的具有工业领导地位的高性能实时操作系统(Real Tim Operation System,RTOS)内核,具有先进的网络功能。VxWorks的开放式结构和对工业标准的支持,使得开发人员易于设计高效的嵌入式系统,并可以很小的工作量移植到其它不同的处理器上。

作为一种先进的实时操作系统,它具有以下特点:

  • 可裁剪微内核结构
  • 高效的任务管理
  1. 多任务,具有256个优先级
  2. 具有优先级排队和循环调度
  3. 快速的、确定性的上下文切换
  • 灵活的任务间通讯
  1. 三种信号灯:二进制、计数、有优先级继承特性的互斥信号灯
  2. 消息队列
  3. 套接字(Socket)
  4. 共享内存
  5. 信号(Signals)
  • 微秒级的中断处理
  • 支持POSIX 1003.1b实时扩展标准
  • 支持多种物理介质及标准的、完整的TCPIP网络协议
  • 灵活的引导方式。支持从ROM、flash、本地盘(软盘或硬盘)或网络引导
  • 支持多处理器并行处理
  • 快速灵活的l/O系统
  • 支持MS-DOS和RT-11文件系统
  • 支持本地盘,flash,CD-ROM 的使用
  • 完全符合ANSI C标准
  • 多个系统调用

3、USB驱动程序的结构概述

图1提供了一个USB主驱动栈结构的简单概括

异常和中断处理过程是每个体系结构和OS都要面对的重要问题,本文从硬件角度以及软件角度来分析在PowerPC处理器上的这个过程。

PowerPC定义了十几种异常,其中常见的如DataTLB miss,Instruction TLB miss,external input....每一个都有固定的地址(ivorn),从物理地址0x100开始,0x500是中断入口,0xe00是DTLBmiss,0xf00是itlbmiss入口。首先整体了解一下中断发生时硬件/软件处理流程,硬件会做什么,OS软件要做什么。

PowerPC 中断的硬件处理流程:

1、把正在执行的指令序列下一条指令地址保存到中断寄存器SRR0(Save/RestoreRegiser 0)。

2、把当前 MSR 的内容保存到 SRR1。

3、把 MSR 某些比特置为 0,如MSR[SPE,WE,EE,PR,FP,FE0,FE1,IS,DS]are 0 by all interrupts。

[注]:

PowerPC有两种执行模式,分别为用户模式(UserMode) 和特权模式(SupervisorMode),MSR[PR]=0 表示特权模式。Linux内核运行在特权模式,而普通程序运行在用户模式。VxWorks全部运行在特权模式。

1、在新的 MSR 状态下,从中断向量偏移处开始指令读取和执行。

2、外部中断处理结束时,必须通过 rfi 指令返回。

3、rfi 的执行,会把 SRR1 的内容恢复到 MSR, 并从 SRR0 所保存的地址处继续执行。

PowerPC Linux (软件)中断处理程序

异常向量ExternalInput处的处理程序主要分为以下几个步骤:

1、NORMAL_EXCEPTION_PROLOG宏

建立用户中断处理程序的栈帧,并把一些寄存器的值保存在栈帧中,它们在栈帧中布局由structpt_regs 定义。

2、执行 do_IRQ

在irq_enter()函数中更新preempt_count。 读MPIC的IACK寄存器,通知MPIC开始处理该中断,同时获取MPIC中断号,映射为软件中断号,找到并运行注册在该软件中断号上的用户中断处理程序,最后写MPIC的EOI寄存器,通知MPIC中断处理结束。

在irq_exit()中更新preempt_count,调用invoke_softirq()处理pending的软中断。

3、ret_from_except

某些条件满足时,进行进程调度和信号处理,最后调用rfi指令返回。

本文描述了如何在Virtual Box下运行VxWorks,希望对想利用虚拟机来运行VxWorks的读者有一些参考和帮助。

先来看一张效果图:

VxWorks on Virtual Box

在tornado端运行shell访问VxWorks

VxWorks on shell

成功运行第一个程序,输出了visualbox can run the vxworks !

确实比在vmware下的速度快多了!

不过telnet组件添加后,有时登录不了,还是上一张登录成功的图吧

用 i 命令输出当前的任务:

VxWorks Cmd i

下面看看我们的配置是怎么样的:

visualbox的网卡设置VirtualBox Host-Only Network:

VxWorks network config

bsp用的是C:\Tornado2.2\target\config\pcPentium

congfig.h配置如下

SJA1000 是一种独立控制器,用于移动目标和一般工业环境中的区域网络控制(CAN),它是PHILIPS半导体PCA82C200 CAN 控制器BasicCAN 的替代产品而且它增加了一种新的工作模式PeliCAN, 这种模式支持具有很多新特性的CAN 2.0B 协议.

系统采用两路SJA1000。分别对应at91rm9200的IRQ0和IRQ1脚中断。中断时采用回调的方式调用上层的处理程序,同时缓冲区也在上层应用程序中。

上层应用程序的使用例子

/*******************************************************************************************/  
  
#include "vxWorks.h"
#include "errnoLib.h"
#include "taskLib.h"
#include "intLib.h"  
#include "netLib.h"
#include "iv.h"
#include "sysLib.h"  
#include "logLib.h"
  
#include "CAN_Drv.h"  

uchar chRecvData[11]={'0','1','2','3','4','5','6','7','8'};  
uchar chSendData[8];  
  
STATUS hook_Display(uchar* pDataBuf){  
uchar DLC = 0,i;  
DLC = (uchar)*pDataBuf&0xf;  
  
for(i=0;i<DLC;i++)  
    logMsg("Hook: %d - %3d/n",i+1,*((pDataBuf++)+3),0,0,0,0);  
}

void main(){
    InstallHook(hook_Display,chRecvData);  
    Open_Can(1);  
    Open_Can(2);  
}  
  
void Send(){  
    uchar i = 0;  
    for(i=0;i<8;i++)  
    chSendData[i] = 4*i+1;  
    Send_Can(1,0,0,i,0,5,0,0,0,chSendData);  
}  

SJA1000底层驱动部分