Real-Time Executive (REX)使用手册

Real-Time Executive (REX)使用手册

 

 

第一章   序言

1.1      目的

本文描述了基于ARM平台的Real-Time Executive(REX)操作系统特性及使用方法。

REX是专为小型嵌入式系统设计的简单高效的抢占式多任务实时操作系统(RTOS)。该系统最初是为Intel80186处理器设计的,随后被移植到ARM处理器。本文提供了REX的指南,第7章为编程参考。

1.2      适用范围

本手册适用于需要在REX系统上编写应用程序的读者。

1.3      内容组织

本文按以下结构编写:

n  第2章               REX概述

n  第3-6章            REX的详细叙述及内部工作机制

n  第7章               编程指南

1.4      字形习惯

函数声明,函数名,类型声明,示例代码使用特殊的字体,例如:#include

程序变量用尖括号包围,例如<number>

1.5      版本历史

 

版本

日期

说明

 

1999年1月

初始版本

Rev.A

1999年3月

内容更新

Rev.B

2001年6月

删除了与80186有关的部分,更新了REX的API,更新了中断处理的章节

Rev.C

2001年9月

删除了成型信息

 

1.6      参考资料

 

1

REX Portation Guide

80-24880-1 X1

1999年3月

2

REX++ REX Extensions Users Guide

80-V3083-1 X1

2001年4月

 

1.7      技术支持

如需要求了解本文未详细说明的信息可通过高通的CDMA技术支持邮箱联系:

[email protected]

1.8      术语及习惯用语

本文使用以下术语及习惯用语

APCS                          ARM调用规范。为了支持不同厂家编译系统及汇编语言混合编程,ARM规定了程序调用的入口、出口处理及寄存器的使用规范。

API                             应用程序接口。

ARM                           Advanced RISC Machines Limited,ARM系列处理器制造商,通常也指ARM系列微处理器。

ARM7TDMI                 ARM7系列处理器中的一款。

CPSR                           当前程序状态寄存器,该寄存器保存了旗语,中断控制位和处理器当前操作模式。

critical section               临界区,代码中访问共享资源的部分。

FIQ                             快速中断,由ARM支持的中断之一,FIQ中断服务程序正在进行时也可用来标志处理器工作模式。

IRQ                             普通中断,由ARM支持的中断之一,IRQ中断服务程序正在进行时也可用来标志处理器工作模式。

ISR                              中断服务程序,也称为interrupt trampoline function。

Idle Task                      操作系统中的优先级最低的任务,空闲任务的优先级为0,为一个空等待循环,只能由中断抢夺其CPU控制权。

PC                               程序当前指令寄存器

PSR                             程序状态寄存器

REX                            高通的实时操作系统

RTOS                           实时操作系统

SPSR                           受保护的程序状态寄存器,每一个处理器模式提供一个SPSR用以保存处理器切换到其它模式前的CPSR。

TCB                             任务控制块,REX内部的数据结构,用于存放任务的信息。

timer block                   定时器的别名。

trampoline function       REX之外的中断服务函数,同ISR。

                                  


第二章   REX概述

REX是一个抢先的多任务实时操作系统,为任务的控制,同步,互斥,定时和中断控制提供了相应的API,所有的函数在调用它们的任务提供的设备环境下运行。

2.1  任务

REX将每个任务视为独立的实体句柄,有独享的堆栈和优先级并共同组成任务的设备环境,每个任务有一组数据组成的结构,称为任务控制块(TCB),REX通过任务控制块记录任务运行的设备环境。

REX运行期间允许在任何时候动态创建任意数量的任务,尽管更多的任务造成了遍历任务链表的时间延长,但新增任务对REX性能造成的损失是微乎其微的。不过仍要注意尽量使任务的数量保持最小。

REX所能负担的任务最终取决于处理器种类,时钟速率以及特定应用下的中断响应指标。

2.2  堆栈

前一节提到过每个任务有独立的堆栈空间,任务处于运行状态时,该堆栈被激活,当任务挂起时,任务的设备环境被保存于堆栈顶部,堆栈指针则存放于该任务的TCB中。任务可能由于阻塞,等待信号量,等待中断服务而挂起。

如果任务被重新激活到运行状态,调度程序从TCB中恢复堆栈指针,任务的设备环境就可从堆栈中弹出,任务即可恢复运行。任务调度处理对于任务来讲是透明的。

2.3  优先级和调度

每一个任务都有一个优先级,优先级存放于任务的TCB中,优先级可以是任何的非零的32位数,数值越小则优先级越低,REX占用了优先级0用于空闲任务,旧版本的REX要求每个任务独占优先级,新的版本已经没有这个限制了。

REX的调度策略为放行优先级最高的就绪态任务,即激活不等待任何事件的最高优先级任务。如果满足条件的同优先级任务不只一个,则REX会任选其中的一个任务。被激活的任务会一直运行到主动挂起或中断程序使另一个更高优先级的任务恢复运行。

一个任务等待的事件到达后,该任务进入就绪态,如果所有的任务都处于挂起状态,则空闲任务被激活。

REX支持任务动态修改优先级,一个任务可以提升或降低自己或其他任务的优先级。

2.4  中断

REX为抢占式内核,当中断返回时,控制会交给优先级最高的就绪任务而不是被中断的任务。

2.5  互斥

当两个任务需要共享一个资源时,需要有一个访问互斥机制,访问共享资源部分的代码成为临界区。

通过关中断可以简单的实现临界区,REX还提供一种不太彻底的方式,导出函数可用于锁定或释放公共资源。

2.6  禁用中断后的挂起

只有正在运行的任务可以禁止中断,一旦某个任务禁止中断后挂起,中断状态则一直由该任务保存,任务重新激活后可以恢复中断状态。有一种潜在的危险是中断被下一个激活的任务允许,这一特性在以后的版本中可能被修改,但是程序员不能依赖这一点。

2.7  信号量

通用信号量集与每一个任务关联,这些信号量集作为相关任务的设备环境保存在任务的TCB里,用来表示与任务有关的某类事件发生了。一个任务的信号量可以被所有任务及中断服务程序设置或清除。

关于信号量需要注意的重点:

n  信号量是通用旗语,用户可以任意定义信号量的含义。

n  一个任务可以通过其他任务或中断任意设置一个特定的信号量挂起,需要强调的是任意,如果一个任务并没有因该信号量挂起,则设置的信号量不会对该任务的运行状态产生影响。

2.8  定时器

定时器用来确定某个时间间隔,任务通过定时器来实现一段延时,延时时间到了以后会有一个信号量时标,任务可以查询该时标,或者任务可以挂起自己等待该信号量时标。

定时器可以和其他事件联合使用,这样就可以确定事件是否超时。REX通过维护定时器链表的方式管理所有正在计时的定时器。对定时器的数量没有限制,定时器越多则每次遍历链表耗时越长,但是新增一个定时器对性能的影响是微弱的。已经到期的计数器不会产生管理开销。

2.9  扩展接口

REX提供了一些扩展接口以增强可操作性,例如异步进程调用,延迟进程调用,内存管理。详情参考REX++—REX扩展用户使用手册(80-V3083-1)。


第三章           任务

本章详细描述了REX的调度及设备环境调度机制。

3.1  任务的创建

创建新任务使用REX提供的函数rex_def_task()。REX并不为任务堆栈和TCB分配空间。这一工作由调用创建任务函数的程序负责。不过REX要求避免用户直接操作TCB的数据结构。

任务创建函数rex_def_task()完成以下工作:

  1. 将设备环境数据入栈
  2. 设置TCB
  3. 将要创建的任务加入任务列表
  4. 调度处理

如果新建任务优先级高于当前任务,则新任务取得控制权,否则,调度直接返回而不发生任务切换。新建的任务直到成为优先级最高的就绪任务时开始执行。

任务的入口由rex_def_task()指定为rex_task_preamble(),任务在第一次获得控制权时初始化堆栈。

任务永远不会返回,因此rex_task_preamble()在调用了任务函数之后,会调用rex_kill_task(),这样的话一旦任务异常返回,则该任务会被REX终止。

3.1.1       调度

任务的调度由函数实现,调度函数为REX保留的内部函数,不能被应用程序直接调用。调用该函数时必须禁止中断,调用函数rex_sched()之前必须先设置全局变量rex_best_task并将该变量指向优先级最高的就绪任务的TCB。

调度算法的基本规则为:

  1. 调度器检查rex_best_task是否指向当前正运行的任务rex_curr_task。
  2. 如果rex_best_task与rex_curr_task相同,则调度器直接返回。
  3. 如果rex_best_task与rex_curr_task不同,将rex_best_task的值赋给rex_curr_task,然后调度器检查是否在中断服务程序,如果在中断服务程序,则调度器返回。
  4. 如果系统未在中断服务,则调度器开始执行设备环境切换,将当前的设备环境入栈,栈顶指针存入当前运行任务的TCB,并从rex_curr_task指向的任务的TCB中恢复堆栈指针,再从堆栈中恢复设备环境。

3.1.2       ARM平台上任务的特殊性

ARM是一款32位微处理器,具有线性地址空间,因此堆栈指针仅占用TCB的一个字段:sp。

设备环境数据结构

设备环境包括r0-r12,lr,pc,CPSR寄存器,图3-1表示了一个挂起任务的堆栈状态。

 

 

程序状态寄存器CPSR(这一格为栈顶)

r0-r12

连接寄存器lr

返回地址

应用程序堆栈数据

图3-1     ARM系统中休眠态的任务堆栈示例

  

由于rex_sched()运行时中断被禁止,在ARM处理器上,设备环境的保存与恢复必须由rex_sched()自己处理。程序状态寄存器保存的是函数入口时的CPSR值,因此设备环境保存了中断状态。

保存设备环境需进行如下操作:

  1. 从rex_curr_task指向的任务TCB中取出堆栈指针。
  2. 将已保存的PSR值考入SPSR寄存器。
  3. 带^后缀调用ldmfd指令

n  装载r0-r12,lr,pc

n  切换到Thumb模式

n  返回到任务

注:为保证同时恢复PC和CPSR,调用ldmfd指令一定要记得使用^后缀。


第四章           中断处理

本章叙述了REX在进入中断和退出中断时所进行的操作。

ARM有两级中断,FIQ和IRQ,本章只讨论IRQ,因为FIQ中断处理期间不需要进行设备环境切换,所以FIQ非常简单。

当产生中断时,采用ARM汇编语言实现的函数IRQ_Handler()取得控制权,根据异常事件中断向量表调用相应的中断服务处理。rex_set_interrupt_vector()用来加载中断向量(该函数的详细说明见第八章7.19节)。产生中断时,ARM处理器切换到IRQ模式,使用IRQ寄存器区,将控制权交给IRQ_Handler(),由其进行以下操作:

  1. 中断服务程序要使用的寄存器入栈,如果函数是由C语言编写,则受保护的寄存器包括:r0-r3,r10,r12,r14,如果中断嵌套,还要保护SPSR。
  2. 中断嵌套计数加一。
  3. 切换回系统模式,并保存系统模式的lr寄存器,因为该寄存器在后面的处理中要使用。
  4. 调用中断向量表注册了的中断服务处理程序。
  5. 中断服务处理完毕返回后,恢复lr寄存器,工作模式切换回IRQ模式。
  6. 中断嵌套计数减一。
  7. 如果嵌套计数减为0,并且rex_best_task不等于rex_curr_task时,需要进行任务切换。
  8. 如需要任务切换,作如下处理:
    1. a.         从IRQ的堆栈恢复SPSR及其它保护的寄存器。
    2. b.        切换到超级用户模式。
    3. c.         在当前任务的堆栈中保存其设备环境
    4. d.        将超级用户模式的堆栈指针保存至任务的TCB中。
    5. e.         rex_best_task的值赋给rex_curr_task。
    6. f.          恢复rex_curr_task的设备环境。
  9. 如果不需要任务切换,则从IRQ堆栈中恢复SPSR及其它受保护的寄存器,切换回超级用户模式。

:某些版本的IRQ_Handler不切换回系统模式处理中断,他们不支持中断嵌套,因此也不管理中断嵌套。

       这种机制通过中断时的任务切换实现了强占式多任务,在中断到来时,既是当前任务正在运行,也会切换到更高优先级的就绪任务。


第五章           信号量和定时器

REX使用信号量和定时器来实现任务间的同步机制及内部定时。

5.1  信号量

REX为任务间通讯提供了信号量机制,信号量集与每个任务都关联,这样任何一个任务都可以通过rex_get_sigs(),rex_set_sigs()和rex_clr_sigs()函数读取、置位、清除任何其它任务使用的信号量。

任务可以调用rex_wait()函数来设置自己所等待的信号量子集,但不能等待别的任务的信号量。使用或操作可以设置等待多个信号量,任意信号量有效即可唤醒该任务。如果任务要等待的信号亮已经被置位则等待函数立即返回而不会挂起任务。

信号量值为一整形变量,ARM处理器支持32位整形变量,因此一个任务可以同时设置32个信号量。

5.2  定时器

通过调用rex_def_timer()函数可以定义一个定时器,任务可以使用定时期来获得需要的时间间隔。

*  函数rex_set_timer()启动定时器。

*  函数rex_pause_timer()暂停定时器。

*  函数rex_resume_timer()重启定时器。

任务如果需要一个延时,可以调用rex_timed_wait()函数,然后等待超时信号量。定时时间到了定时器会设置信号量通知任务。

:需要注意任务必须设置为等待一个定时器信号量的超集,这一点非常重要,因为定时器通常已经在程序一开始就初始化过了,这时它的信号量属性已经确定。如果没有注意这一点,当一个任务调用rex_timed_wait()时所指定的信号量如果未包含在定时器的信号量集里,任务将捕获不到任何信号量。

时间的单位以毫秒计,最小计时单位为1毫秒。定时的精度与硬件平台有关,为每毫秒时钟嘀嗒中断的次数。

定时通过在每个时钟嘀嗒中断服务程序里调用rex_tick()来实现,需要定时多少毫秒,就相应调用多少次,定时结束后,将信号量写入等待定时任务的TCB中。

REX负责维护所有未结束的定时器列表,一旦定时时间到,将从列表中删除该定时器,不过REX并不知道定时器的数据结构,因此,为定时器数据结构申请空间并维护由任务来完成。

:对调用rex_def_timer()函数的次数没有限制,但是不能对已有的定时器调用该函数,否则会破坏定时器列表,对任务来讲是致命错误。


第六章           REX内幕

本章描述了一些REX内部的数据结构及变量,虽然这些数据可以访问到,但是它们不应该被应用程序直接操作,了解这一点有助于查找程序的问题。

6.1  任务控制块

每一个任务都有一个任务控制块来保存任务的设备环境。我们比较感兴趣的字段包括:

n  sp——堆栈指针,指示挂起的任务堆栈的栈顶位置。

n  stack_limit——指示任务的堆栈大小,堆栈指针不能小于这一值。

n  sigs——保存任务当前设置的信号量集。

n  wait——保存任务正在等待的信号量,如果该值不为零则表明任务处于挂起,如果该值为零则表明任务已经就绪或正在运行。

n  pri——任务的优先级

6.2  当前任务

全局指针变量rex_curr_task指向当前正在运行任务的TCB。

6.3  优选任务

全局指针变量rex_best_task指向优先级最高就绪任务的TCB。

6.4  任务列表

所有任务的TCB都按优先级排队保存在队列中,该队列为双向链表结构,next_ptr和prev_ptr分别指向下一个和前一个数据。全局结构变量rex_task_list作为队首,它的next_ptr指针指向系统中的最高优先级任务(无论该任务是否就绪)。通过前向遍历该队列,查找TCB中wait字段为0的任务即可找到最高优先级的就绪任务,空闲任务以优先级0排在队尾,并且该任务总是处于就绪态,因此可以保证遍历总是能完成。

:优先级0被保留,用户任务不能占用该优先级。


第七章           API参考

本章列出了REX提供的API参考,由于REX不对函数的入口参数作检验,因此程序员在调用这些API时必须符合接口规范。系统提供了如下函数:

n  rex_init( )

n  rex_def_task( )

n  rex_set_sigs( )

n  rex_clr_sigs( )

n  rex_get_sigs( )

n  rex_wait( )

n  rex_def_timer( )

n  rex_set_timer( )

n  rex_get_timer( )

n  rex_clr_timer( )

n  rex_pause_timer( )

n  rex_resume_timer( )

n  rex_tick( )

n  rex_timed_wait( )

n  rex_self( )

n  rex_get_pri( )

n  rex_set_pri( )

n  rex_task_pri( )

n  rex_set_interrupt_vector( )

n  rex_enable_interrupt( )

n  rex_disable_interrupt( )

n  rex_init_crit_sect( )

n  rex_enter_crit_sect( )

n  rex_leave_crit_sect( )

 

7.1   rex_init()

原形

extern void rex_init (

void *                  p_istack,           /* interrupt stack  */

rex_stack_word_type     p_istksiz,         /* interrupt stack size */

rex_tcb_type            *p_tcb,             /* task control block */

void *                  p_stack,            /* stack */

rex_stack_word_type     p_stksiz,           /* stack size */

rex_priority_type       p_pri,              /* task priority */

void                    (*p_task)( dword ), /* task function */

dword                   p_param             /* task parameter */

);

描述

函数rex_init()初始化REX,必须在上电后首先调用该函数,并且只能调用一次,之后才允许调用其它API。只有调用该函数后中断才被允许。

功能

1.  创建第一个用户堆栈p_task,并作如下处理

n  堆栈大小为p_istksiz

n  优先级为p_pri

n  p_tcb作为任务的TCB

2.  调用调度器,将控制权交给创建的任务。

前两个参数p_istack和p_istksiz是为了与以前的版本兼容,以后的版本不再使用这两个参数。

副作用

返回值

该函数不返回。

用法

#include “rex.h”

#define FIRST_STACK_SIZE        512     /* 1024 bytes */

#define FIRST_TASK_PRI      200     /* Some non-zero unique value */

#define FIRST_TASK_PARAM        101     /* Some value understood by the new task */

rex_tcb_type first_task_tcb;

int first_task_stack[FIRST_STACK_SIZE];

 

void first_task(dword p)

{

/*

* This is the first task in the system.

* When control comes here …

* - p has been set to FIRST_TASK_PARAM by REX

* - first_task_stack is in use 11

* - this function is not supposed to return.

*/

}

 

上电初始化期间:

rex_init (

NULL,

0,

&first_task_tcb,

first_task_stack,

FIRST_STACK_SIZE * sizeof (int) / sizeof (word),

FIRST_TASK_PRI,

first_task,

FIRST_TASK_PARAM

);

 

7.2   rex_def_task()

原形

extern void rex_def_task (

rex_tcb_type            *p_tcb,             /* valid TCB for new task */

void *              p_stack,                /* stack for new task */

rex_stack_word_type p_stksiz,           /* stack size for new task */

rex_priority_type   p_pri,              /* priority for new task */

void                (*p_task)(dword),   /* task startup function */

dword               p_param             /* parameter for new task */

);

 

描述

函数rex_def_task 创建一个新任务,由p_tcb所指向的数据作为新任务的TCB,内存的分配由调用函数负责。

功能

新任务的设备环境为:

n  任务的私有堆栈,栈底为p_stack,栈空间为p_stksiz字(字长16bit)

n  任务优先级为p_pri,入口地址p_task,入口参数p_param在第一次运行任务时传递给任务函数,REX不处理该数据,用户可根据需要定义入口参数的用处。

REX定义了新任务后,立刻调用调度器,但是并不能保证新建的任务会立即获得控制权,仍然要视新任务的优先级相对于就绪任务是否为最高,新建任务为就绪态。

副作用

调用本函数会引起一次任务调度。

返回值

用法

#include “rex.h”

#define NEW_STACK_SIZE  512     /* 1024 bytes */

#define NEW_TASK_PRI        200     /* Some non-zero unique value */

#define NEW_TASK_PARAM  101     /* Some value understood by the new task */

rex_tcb_type new_task_tcb;

int new_task_stack[NEW_STACK_SIZE];

 

void new_task(dword p)

{

/*

* When control comes here …

* - p has been set to NEW_TASK_PARAM by REX

* - new_task_stack is in use

* - this function is not supposed to return.

*/

}

 

void existing_task(dword p)

{

rex_def_task ( &new_task_tcb,

new_task_stack,

NEW_STACK_SIZE * sizeof (int) / sizeof (word) ,

NEW_TASK_PRI,

new_task,

NEW_TASK_PARAM

);

}

 

7.3   rex_set_sigs()

原形

extern rex_sigs_type rex_set_sigs (

rex_tcb_type *p_tcb,        /* TCB for which the sigs will be set */

rex_sigs_type p_sigs        /* the sigs to set */

);

 

描述

函数rex_set_sigs将一个信号量p_sigs以掩码的方式写入p_tcb所指任务的TCB,p_sigs为位寻址变量,每一位代表特定的信号量,任务获得了其中任何一个信号量后,重新作为候选的待执行任务。

副作用

调用本函数会引起一次任务调度。

返回值

返回原始的任务信号量。

用法

#include “rex.h”

#define NEW_SIGNALS         0x3             /* Some signals. */

extern rex_tcb_type         target_task;

void signal_setting_task( dword p)

{

rex_sigs_type old_sigs;

old_sigs = rex_set_sigs( &target_task, (rex_sigs_type)NEW_SIGNALS);

}

 

7.4   rex_clr_sigs()

原形

extern rex_sigs_type rex_clr_sigs (

rex_tcb_type *p_tcb,            /* TCB for which the signals will be cleared */

rex_sigs_type p_sigs            /* which signals to clear */

);

 

描述

函数rex_clr_sigs清除p_tcb指向任务的信号量,不调用调度器。

返回值

返回原始的任务信号量。

用法

#include “rex.h”

#define   CLEAR_THESE     0x30             /* Some signals. */

extern rex_tcb_type       target_task;

void signal_clearing_task( dword p)

{

rex_sigs_type old_sigs;

old_sigs = rex_clr_sigs(&target_task, (rex_sigs_type)CLEAR_THESE);

}

 

http://www.cnblogs.com/hongzg1982/articles/2312794.html

你可能感兴趣的:(Real-Time Executive (REX)使用手册)