新闻中心

EEPW首页 > 嵌入式系统 > 设计应用 > 基于PowerPC405的Xilkernel内核实现多任务操作

基于PowerPC405的Xilkernel内核实现多任务操作

作者:四川九洲电器集团公司 李涛时间:2008-03-25来源:电子产品世界收藏

  引言

本文引用地址:http://www.eepw.com.cn/article/80623.htm

  集成在Virtex-II Pro器件中的PowerPC405,是一个32位RISC硬核,它支持总线的标准外设集合。使用总线,可以方便地控制多个外设。在EDK集成开发环境下,对于多个外设,每个外设都有对应的任务。PowerPC405默认的嵌入式内核是standalone,在其上开发的多个任务是宏观串行执行的,只有利用参数传递或全局参变量来建立各任务间的关系。在很多情况下,系统需要多个任务系统宏观并行执行,使用standalone显然是不合适的。而通过把嵌入的standalone内核改变为EDK自带的内核,适当地改变软件平台设置的内容,就可以实现多个任务的并行执行。也支持多任务间通讯和。根据各种通讯方式,也可以建立各个任务之间的联系;通过,处理器可以及时响应外设产生的事件。

  硬件系统结构

  如图1所示,PowerPC405使用FPGA外部的存储单元,使用总线和外围接口。CoreConnect总线的标准外设集合可以重复使用,使系统整合变的更加容易。

  CoreConnect总线结构

  PLB总线接口:用于PowerPC405内核与高性能设备的连接。PLB接口包括ISPLB接口和DSPLB接口两种。其中,ISPLB接口用于外设与PowerPC405指令缓冲的连接,DSPLB接口用于外设与PowerPC405数据缓冲的连接。

  OPB总线接口:片上外设总线,内核通过OPB来访问低速和低性能的系统资源。它不是直接连接到处理器内核。处理器内核借助于“PLB to OPB”桥,通过OPB访问从外设;OPB总线控制器的外设可以借助“OPB to PLB”桥,通过PLB访问存储器。

  硬件平台构件

  在EDK集成开发环境中,由用户向导生成MHS文件,用户也可以根据MHS文件的语法添加自定义的外设。MHS文件用于描述硬件体系结构,其主要包括平台的处理器类型、总线结构、外围接口、处理和地址空间。

  EDK工具platGen使用MHS文件作为输入来创建硬件平台,它创建不同形式的网表文件(NGC,EDIF),下游工具的支持文件和顶级HDL包装以允许用户添加其他的组件到硬件平台。

  软件系统结构

  在EDK集成开发环境中,MSS文件用于描述软件体系结构,其主要定义了平台的内核、软件库、驱动程序和文件系统的参数。

  EDK工具libGen使用MSS文件作为输入,定制驱动、库、文件系统和中断处理程序。

  模块结构

  Xilkernel模块结构如图2所示,Xilkernel提供与内核的POSIX接口。但并不是每一个通过POSIX定义的概念和接口都是可用的。取而代之的是一个精细选择的子集,几乎覆盖了所有有用的接口和概念。其支持POSIX线程、POSIX无名、XSI、POSIX互斥锁、中断处理等。

  Xilkernel的软件平台配置

  Xilkernel已经被设计为可以和EDK软件和硬件流紧密共同工作,完全被整合在软件平台配置和自动的库、板级支持包产生机制之中。在软件配置平台,可以对Xilkernel支持的功能进行配置,下面介绍一些主要的配置:

  a、指定系统定时器的频率值和时间片间隔。

  b、指定系统可以运行的线程数量、任务调度方式(这里我们设置为优先级抢占方式,以保证重要的突发事件及时得到处理)和系统中断控制器。

  c、配置系统的通讯方式,可以通过这些开关来确定系统需要的通讯方式,并可以确定各个通讯方式的参数。包括等。

  d、指定系统的静态任务,也就是完全进入内核后执行的第一个任务,可以在这个任务里产生和设置系统需要的其它任务。

  e、一些增强系统功能的设置等等。

  主要任务间的通讯方式和中断

  必需的配置

  首先要生成连接脚本,是通过硬件需要生成的,此脚本反映了Xilkernel需要的不同的段存储器。比如.vectors段被分配于一个有64KB地址边界的存储器的开始,而.boot段在0xFFFFFFFC处。其余的代码和数据存储器可以放在任何地方。

  其次,Xilkernel是作为一个库来架构的。这意味着应用程序源文件仅需要连接Xilkernel,就能够访问Xilkernel的功能。这些需要设置编译器的库连接选项为xilkernel,并在用户代码中包含“xmk.h”文件。应用程序提供main()入口,然后通过调用xilkernel_main()作为内核的入口点。产生库、BSP并编译程序后,Xilkernel将自动作为系统启动、初始化硬件核、中断和软件处理程序的一部分。下面是一个简单的内核入口代码:

  #include "xmk.h"
  /* 定义和声明 */
  int main()
  {
  /* 用户完成预处理,不允许调用内核接口 */
  xilkernel_main ();    /* 开始内核 */
  /* 程序不会执行到这里 */
  }
  /* 系统的静态任务 */
  Void * first_thread ()
  {
  /* 产生一些线程来处理用户需要 */
  }

  线程的创建

  线程的创建及属性的简单设置可以由下面几个函数实现:

  int pthread_attr_init (pthread_attr_t* attr)
  int pthread_attr_setschedparam (pthread_attr_t* attr, struct sched_param *schedpar)
    int pthread_create (pthread_t thread,pthread_attr_t* attr, void* (*start_func)(void*),void* param)
  pthread_attr_init()初始化线程的属性。thread_attr_setschedparam()来设置线程的优先级,attr是线程的属性,schedpar是包含有线程优先级的数据结构。pthread_create()创建一个线程,thread表明线程ID,attr指出线程属性,start_func函数指针是线程创建成功后开始执行的函数,param是这个函数的一个唯一的参数。

  在静态任务中调用这些函数来产生一些有优先级的任务。如下例:
  static pthread_t tid0,tid1;
  static pthread_attr_t attr;
  static struct sched_param prio;
  void * first_thread () {   ......
       pthread_attr_init(&attr);
       prio.sched_priority = 4;
      pthread_attr_setschedparam (&attr,&prio);
       ret = pthread_create (&tid0, &attr, (void*)important_task, NULL);
       pthread_attr_init (&attr);
       prio.sched_priority = 5;
       pthread_attr_setschedparam (&attr,&prio);
       ret = pthread_create (&tid1, &attr, (void*)second_important_task, NULL);
          ......
  }

 这样,系统会发起important_task和second_important_task两个任务,important_task的优先级比second_important_task高,会优先运行。除非important_task任务阻塞或退出,second_important_task才可能得到运行。
POSIX无名

  信号量提供高速的任务间同步和互斥机制。对于互斥,信号量可以上锁共享资源,使得该共享资源在同一时刻只有一个线程所拥有。关于此信号量的一些常用函数如下:

  int sem_init(sem_t *sem,int pshared,unsigned int value);
  int sem_wait(sem_t *sem);
  int sem_post(sem_t *sem);
  sem_init()创建一个信号量,并初始化信号量的值为value;sem_wait()调用将阻塞进程,直到信号量的值大于0,此函数返回时信号量的值减1;sem_post()是将信号量的值加1,并发出信号唤醒等待的进程。

  信号量用于同步,一般要初始化为0,等待要同步的任务阻塞在sem_wait()调用上。任务调用sem_post来解锁该信号量,来达到同步。下面一个例子是用信号量实现同步操作的:

  static sem_t    protect;
  void * first_thread (){   ......
   sem_init(&protect, 1, 0);
          ......
  }
   void* thread_func1 (){ ......
       while(1){
    sem_wait(&protect);
     ......
       }
  }
  void* thread_func2 (){ ......
       while(1){......
    if(某种条件成立)sem_post(&protect);
       }
  }

  当信号量用于互斥时,一般要初始化为一个大于0的值,就可以让资源可用。如果信号量的初始值为1,第一个上锁该信号量的线程会立即执行,后继的线程将会阻塞,直到下次信号量解锁才会执行。

  XSI

  消息队列允许长度可变、数目可变的消息排队。任何任务或中断服务程序可以发送消息到消息队列。任何任务可从消息队列接收消息。关于此消息队列的一些常用函数如下:

   int msgget(key_t key, int msgflg)
  int msgsnd (int msqid, const void *msgp, size_t msgsz, int msgflg)
  ssize_t msgrcv (int msqid, void *msgp, size_t nbytes, long msgtyp, int msgflg)
   msgget()来创建一个消息队列,key是消息队列的标识符,msgflag目前有两个选项,IPC_CREAT和IPC_EXCL。msgsnd()函数往队列发送一条消息,msgp是消息缓冲指向的指针,msgsz表示消息的字节数。msgrcv ()函数作用是从消息队列中读取消息,把接收到的消息拷贝到msgp指针指向的缓冲区,nbytes表示缓冲支持的消息字节数。发送和接收消息中的msqid是消息队列描述符,用来标识相关的消息队列。下面是消息队列单向通信的简单代码:

  struct _msg {
    short type;
    char  first;
     char  last;
  };
  static struct _msg msg_p;
  static struct _msg msg_c;
  static int msgid;
  void * first_thread (){   ......
   msgid = msgget(5,IPC_CREAT | IPC_EXCL);
          ......
  }
  void* consumer ()
  {
   while(1) {
    msgrcv( msgid, &msg_c, 4, 0,0 );
    ......
   }
  }
  void* producer ()
  {
   while(1) {......
    msgsnd (msgid, &msg_p, 4, 0);
   }
  }

  在例子开始,建立消息的数据结构。在producer()中操作消息的各项数据,通过msgsnd()发送此消息。在consumer()中,如果消息队列里没有消息,则msgsnd()阻塞此线程,直到消息队列非空时,msgsnd()才把消息复制到msg_p指向的数据结构中,此时此线程开始执行,并可以对接收到的消息进行处理。

  中断

  Xilkernel已经被设计为可以和多个中断设备共同工作,用户用opb_intc IP核作为中断控制器来处理硬件中断。Xilkernel仅支持一个中断控制器来连接PPC405的外部中断引脚,而且不支持中断控制器连接临界的中断。对于中断程序设计,Xilkernel继承了standalone的中断处理方法。

  在xilkernel_main()中已经完成了初始化PowerPC405的中断表,并能使了中断控制器连接在处理器上的非临界的中断。下面是摘抄xilkernel_main()内部执行相关代码:

   XExc_Init();     /*初始化PowerPC405的中断表*/
   XExc_mEnableExceptions (XEXC_NON_CRITICAL);    /*能使非临界中断*/
  下面是开发应用程序要做的一些工作。首先,使中断控制器开始接收中断;其次,把必需的非临界中断添加到中断控制器上;再就是注册此非临界中断;最后能使此中断。下面是一个串口中断接收的简单代码:

   void * first_thread (){......
  XIntc_mMasterEnable(XPAR_ MYINTC_BASEADDR);
   XIntc_mEnableIntr(XPAR_ MYINTC_BASEADDR,  XPAR_MYUART_INTERRUPT_MASK);
  XIntc_RegisterHandler(XPAR_ MYINTC_BASEADDR,  XPAR_MYUART_INTERRUPT_INTR,
  (XInterruptHandler)uart_int_ handler,(void *)XPAR_MYINTC_BASEADDR);
   XUartLite_mEnableIntr (XPAR_MYUART _BASEADDR);
  ......
  }
  void uart_int_handler(void *baseaddr_p) {/* 中断处理程序 */ while(!XUartLite_mIsReceiveEmpty (XPAR_MYUART_BASEADDR)) {
     ch = XUartLite_RecvByte( XPAR_MYUART_BASEADDR);
     ......
    }
  }

  一个中断事件和中断处理程序相连接。而中断处理程序应该尽量短,如果中断处理程序不能完全处理此事件,可以由信号量同步发起一个任务来处理本事件。

  结语

  Xilinx公司的Vritex-II Pro实现了“微处理器+可编程逻辑”的可配置设计平台,其出众的性能受到高端应用的青睐。在此平台上利用  Xilkernel嵌入式操作系统,为嵌入式应用开发提供了极大的系统结构灵活性。本文仅介绍了几种常用的基于Xilkernel的嵌入式应用程序设计方法,读者还可以利用互斥、软件定时器等实现其它的功能。读者也可以根据嵌入式开发的经验和Xilkernel的强大功能,构建复杂的FPGA嵌入式系统。■

  参考文献:

  1.  Xilinx. Embedded System Tools Guide. EDK (v6.3i). 2004。

  2.  Xilinx. EDK OS and Libraries Reference Manual. EDK (v6.3i).2004。

  3.  任晓东,文博. CPLD/FPGA高级应用开发指南.电子工业出版社. 2003。

  4.  王磊.32 位软处理器MicroBlaze的体系结构及其应用.今日电子. 2004。



评论


相关推荐

技术专区

关闭