前言/声明/必读:
- 此文档未经过本人授权禁止以任何形式进行修改,转载和保存;
- 此文档本人学习ETAS OS代码的总结心得,作为在线笔记的使用目的;
- 此文档中所有内容均是个人学习研究推测,文中任何观点仅代表个人观点,与ETAS无关,本人不对其中的任何内容负责,如需更精确完整的分析,请联系ETAS官方;
- 此文档仅供学习探讨,不存在任何相关产品的优劣讨论,不存在也不允许以任何形式进行任何商业用途;
- 如此文档侵害了任何个体,机构,公司等的权利,请及时联系本人并进行删除操作。
ETAS OS源码文件整体介绍:
ETAS工具生成的OS源码应当位于一个名为xxx_CFG_OS的文件夹下,其中include文件夹包含OS所需要的部分头文件。ETAS将Autosar OS中规定的每个API都单独创建一个源文件用以实现,文件按照<API_name>.c格式命名,如ActivateTask.c用于实现Autosar OS提供的ActivateTask接口的功能。在每一个单独的源文件中,ETAS不会包含任务不必要的头文件,所有该文件所用到的宏定义,外部引用函数,数据类型(除基础类型以外),全局变量的应用,均在本文件内部完成,由于不会引用其他头文件,所以不必担心重复声明和重复定义等编译问题的产生,但代价就是每个源文件都显得格外冗长繁杂,对代码阅读造成不必要的麻烦,在调查软件问题的时候代码阅读相对困难。
OS配置文件介绍:
Os_Cfg.c:
在ETAS OS的内核源码中,Os_Cfg.c是非常重要的文件。Os_Cfg.c定义了ETAS OS运行过程中所必须的内核对象,包括Core的配置,中断的配置,Task的配置等几乎所有Autosar OS中所描述的对象,读懂Os_Cfg.c将对ETAS OS的源码分析有着至关重要的作用。以下将对该文件的部分代码进行剖析。
由于ETAS OS的文件风格导致,Os_Cfg.c的前面一大半部分都是对一些宏的定义,外部函数的声明,变量类型的定义,全局变量的声明,这些内容不会影响我们的代码阅读,只需要在阅读代码时能找到对应的定义并理解其含义即可。接下来则对Os_Cfg.c里的变量及数据类型进行剖析。
Os_Cfg_Counters.c:
基于作者目前的配置,Os_Cfg_Counters.c中主要包含Counter的描述配置块,调度表的描述配置块,以及调度表每个调度点的配置。除此之外,该文件还包括了系统时基中断的服务函数的实现以及基于时基的调度策略。以下将对该文件的部分代码进行剖析。
配置块描述:
Os_const_coreconfiguration:
Os_const_coreconfiguration结构体数组位于Os_Cfg.c中,描述了ETAS OS所有核的基础配置,如下所示,是ETAS关于SPC58NN生成的内核相关配置数据。
CONST(Os_CoreConfiguration, OS_CONST_FAST) Os_const_coreconfiguration[3U] = {
{
/* controlled */ &Os_ControlledCoreInfo[0U],
/* any */ &Os_AnyCoreInfo[0U],
/* state */ &Os_CoreState[0U],
/* dispatch */ Os_Dispatch0,
/* intc_cpr */ OS_REGISTER_ADDR(Os_imaskType, 0xF4044018UL),
/* intc_eoir */ OS_REGISTER_ADDR(uint32, 0xF4044038UL),
/* intc_iackr */ OS_REGISTER_ADDR(uint32, 0xF4044028UL),
/* core_id */ 0U
},
{
&Os_ControlledCoreInfo[1U],
&Os_AnyCoreInfo[1U],
&Os_CoreState[1U],
Os_Dispatch1,
OS_REGISTER_ADDR(Os_imaskType, 0xF4044014UL), OS_REGISTER_ADDR(uint32, 0xF4044034UL), OS_REGISTER_ADDR(uint32, 0xF4044024UL),
1U
},
{
&Os_ControlledCoreInfo[2U],
&Os_AnyCoreInfo[2U],
&Os_CoreState[2U],
Os_Dispatch2,
OS_REGISTER_ADDR(Os_imaskType, 0xF4044010UL), OS_REGISTER_ADDR(uint32, 0xF4044030UL), OS_REGISTER_ADDR(uint32, 0xF4044020UL),
2U
},
};
我们将尝试理解该配置中每个元素的含义,实际上就是需要理解数据类型Os_CoreConfiguration每个成员的含义,具体定义可跳转到Os_CoreConfiguration类型描述。
关联:
- Os_Dispatch<CoreID>
- ActivateTask
- ChainTask
- TerminateTask
- Os_ActivateTaskKL
- CounterType/Os_CounterType
- GetCounterValue
- Os_IncrementCounter_Rte_TickCounter
Os_const_applications:
Os_const_applications数组位于Os_Cfg.c中,是ETAS OS描述所有Application的配置块,其中描述了Application对应的权限和AppID,Os_const_applications的数据类型说明可跳转到Os_ApplicationConfigurationType类型描述,配置信息如下所示:
CONST(Os_ApplicationConfigurationType, OS_CONST_FAST) Os_const_applications[1U + OS_NUM_APPLICATIONS] = {
{
/*app_id */ 0U,
/* access */ 0U,
},
{1U,
2U,
},
{2U,
4U,
},
{3U,
8U,
},
};
关联:
Os_const_isrs:
Os_const_isrs数组位于Os_Cfg.c中,是ETAS OS描述所有二类中断的配置块,ETAS OS将所有核的二类中断的配置都放在这个结构体数组中,数组的变量类型可跳转到Os_ISRType类型描述,里面包含了二类中断的入口,优先级,操作权限,和所属Application配置信息,如下所示:
CONST(Os_ISRType, OS_CONST_FAST) Os_const_isrs[OS_NUM_ISRS] = {
{
/* entry_function */ Os_Entry_Gpt_PIT_TIMER_0_ISR,
/* imask */ 32776U,
/* access */ 3U,
/* application */ 1U,
},
{Os_Entry_Spi_LLD_IsrRxDma_DSPI_3,
32777U,
3U,
1U,
},
{Os_Entry_Gpt_PIT_TIMER_3_ISR,
32778U,
3U,
1U,
}
}
Os_const_tasks<CoreID>:
Os_const_tasks<CoreID>数组位于Os_Cfg.c中,描述了每个内核的Task的配置控制块,包括每个Task的入口地址,优先级,允许被操作的权限等信息,优先级越高的Task在该数组中被安排的位置越靠后,且在核内,Task的优先级是有序步进的,其变量类型可跳转到Os_TaskType变量类型,配置块如下所示:
CONST(Os_TaskType, OS_CONST_FAST) Os_const_tasks0[2U] = {
{
/* dynamic */ &Os_dyn_tasks[0],
/* entry_function */ Os_Entry_OsTask_SwcRequest,
/* pset */ { 1U },
/* base_tpmask */ { 1U },
/* tpmask */ { 16777215U },
/* core_id */ 0U,
/* index */ 0U,
/* activation_count */ 2U,
/* activation_fifo */ (void *)0,
/* access */ 3U,
/* application */ 1U,
},
{
&Os_dyn_tasks[1],
Os_Entry_OsTask_ASW_Core0_1000ms,
{ 2U },
{ 3U },
{ 7U },
0U,
1U,
1U,
(&Os_const_fifocontrol[0]),
3U,
1U,
}
};
关联:
Os_const_tasks:
Os_const_tasks数组位于Os_Cfg.c中,以数组的方式保存了每一个Task配置块的指针,并且每个Task的配置块指针按照Task的Index属性依次存放,在软件中就可以按照Task的Index属性作为索引找到Task的配置描述块。其变量类型可跳转到TaskType变量类型,配置块如下所示:
CONST(TaskType, OS_CONST_FAST) Os_const_tasks[OS_NUM_TASKS] = {
&Os_const_tasks0[0],
&Os_const_tasks0[1],
};
Os_const_resources:
Os_const_resources数组位于Os_Cfg.c中,描述了每一个资源锁(Resource)的配置块信息,其中包含了资源锁保存临时运行信息的地址,资源锁对优先级和允许操作该资源锁的权限。Os_const_resources的数据类型可跳转到Os_ResourceType类型描述。配置快如下所示:
CONST(Os_ResourceType, OS_CONST_FAST) Os_const_resources[OS_NUM_RESOURCES] = {
{
/* dynamic */ &Os_dyn_resources[0],
/* tpmask */ { 2047U },
/* access */ 4U
},
{ &Os_dyn_resources[1],
{ 2047U }
, 8U
},
{ &Os_dyn_resources[2],
{ 32767U }
, 2U
},
{ &Os_dyn_resources[3],
{ 16777215U }
, 2U
},
};
Os_const_fifocontrol:
Os_const_fifocontrol数组位于Os_Cfg.c中,描述了每个优先级的Task的调度队列控制块,包括调度队列的起始地址,结束地址,调度队列的读写指针以及调度队列对应的优先级,具体信息可跳转到Os_TaskFifoControl类型描述,该类型定义比较复杂晦涩,是理解调度策略的核心内容。Os_const_fifocontrol控制描述块定义如下所示:
CONST(Os_TaskFifoControl, OS_CONST) Os_const_fifocontrol[3] = {
{
&Os_TaskFifo[0U],
&Os_TaskFifo[1U],
0U,
{ 7U }
},
{
&Os_TaskFifo[2U],
&Os_TaskFifo[3U],
2U,
{ 31U }
},
{
&Os_TaskFifo[4U],
&Os_TaskFifo[5U],
4U,
{ 127U }
}
};
Os_const_counters:
Os_const_counters数组位于Os_Cfg_Counters.c中,描述了ETAS OS中所有Counter的配置块,包括Counter的动态运行数据,Counter的中断回调函数,Counter等基本信息,该配置的具体信息可参考CounterType数据类型的描述,Os_const_counters配置块的定义如下所示:
CONST(Os_CounterType, OS_CONST_FAST) Os_const_counters[OS_NUM_COUNTERS] = { /* $UKS 210 */
{
/* dynamic */ &Os_dyn_counters[0],
/* advincr */ Os_IncrementCounter_Rte_TickCounter,
/* base */ {65535U, 1U, 1U},
/* core */ (const void *)&Os_const_coreconfiguration[0], /*lint !e9087 !e9005 casting to void to match type */
/* access */ 3U,
/* application */ 1U,
},
};
Os_const_scheduletables:
Os_const_scheduletables数组位于Os_Cfg_Counters.c中,描述了ETAS OS中所有调度表的配置块,其中包含了调度表的动态运行数据,驱动调度对应的Counter标识索引,是否循环等调度表的配置信息,该配置的具体信息可参考变量类型Os_ScheduleTableType的描述,Os_const_scheduletables的定义如下所示:
CONST(Os_ScheduleTableType, OS_CONST_FAST) Os_const_scheduletables[OS_NUM_SCHEDULETABLES] = {
{
/* dynamic */ &Os_dyn_scheduletables[0],
/* counter */ &Os_const_counters[0],
/* repeat */ TRUE,
/* config */ 0U,
/* initial */ 0U,
/* access */ 3U,
/*application */ 1U,
},
};
Os_const_expiry_intervals:
Os_const_expiry_intervals数组位于Os_Cfg_Counters.c中,描述了所有调度表在配置工作中配置的到期点时间,每一个元素都表示距离下一个到期点的时间差值,工具在生成代码的时候将会自动计算相邻两个到期点Tick差值填到该数组中。具体使用可参考Os_PerformEP函数描述,Os_const_expiry_intervals定义如下所示:
CONST(uint8, OS_CONST) Os_const_expiry_intervals[] = {
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,1U,
};
关联:
Os_const_expiry_actions:
Os_const_expiry_actions数组位于Os_Cfg_Counters.c中,描述了所有调度表每个到期点所对应的动作,该数组中的任何值都可以通过一个固定的公式计算得到调到表到期时应该触发的动作。Autosar中规定调度表中的每一个调度点都可以执行多个动作,包括激活多个Task和触发多个Event,所以Os_const_expiry_intervals表中一个到期点将有可能对应Os_const_expiry_actions多个动作。由于作者的配置工程没有配置扩展Task和Event事件,在代码中并没有生成有关Event的处理策略,所以目前作者之分析出来ETAS OS对激活Task的动作和将Os_const_expiry_actions中每个元素转换成对应Task的公式,其转换公式和使用方法具体参考函数Os_PerformEP的实现。Os_const_expiry_actions的定义如下所示:
CONST(uint8, OS_CONST) Os_const_expiry_actions[] = {
/* Rte_ScheduleTable */
4U, 28U, 52U, 76U, 20U, 44U, 68U, 12U, 36U, 60U, 6U, 30U, 54U, 78U, 22U, 46U, 70U, 14U,
38U, 62U, 8U, 32U, 56U, 80U, 24U, 48U, 72U, 16U, 40U, 64U, 10U, 34U, 58U, 82U, 26U, 50U,
74U, 18U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U,
80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U,
76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U,
82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U,
58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U,
80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U,
76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U,
82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U,
48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U,
82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U,
80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U,
78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U,
56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U,
82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U,
80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U,
78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U,
70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U,
78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U,
76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U,
80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U,
60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U,
80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U,
80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U,
82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U,
52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U,
74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U,
76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U,
78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U,
76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U,
74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U,
76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U,
78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U,
76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U,
64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U,
76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U,
82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U,
80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U,
72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U,
76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 20U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 22U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 24U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 26U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 20U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 22U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 24U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 26U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 44U, 68U, 12U, 36U, 60U, 30U, 54U, 78U, 46U, 70U, 14U, 38U, 62U, 32U, 56U, 80U, 48U, 72U, 16U, 40U, 64U, 34U, 58U, 82U, 50U, 74U, 18U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 20U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 22U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 24U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 26U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 20U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 22U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 24U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 26U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 28U, 52U, 76U, 44U, 68U, 36U, 60U, 30U, 54U, 78U, 46U, 70U, 38U, 62U, 32U, 56U, 80U, 48U, 72U, 40U, 64U, 34U, 58U, 82U, 50U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 36U, 60U, 54U, 78U, 70U, 38U, 62U, 56U, 80U, 72U, 40U, 64U, 58U, 82U, 74U, 42U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 44U, 68U, 60U, 54U, 78U, 46U, 70U, 62U, 56U, 80U, 48U, 72U, 64U, 58U, 82U, 50U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 52U, 76U, 68U, 60U, 54U, 78U, 70U, 62U, 56U, 80U, 72U, 64U, 58U, 82U, 74U, 67U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 60U, 78U, 62U, 80U, 64U, 82U, 67U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 83U, 76U, 68U, 78U, 70U, 80U, 72U, 82U, 75U, 76U, 78U, 80U, 82U, 1U,
};
关联:
数据类型描述:
Os_CoreConfiguration:
描述每个内核相关配置数据,数据类型定义如下所示:
typedef struct Os_CoreConfiguration_s {
CONSTP2VAR(Os_ControlledCoreType, OS_VAR, OS_CONST) controlled;
CONSTP2VAR(Os_AnyCoreType, OS_VAR, OS_CONST) any;
CONSTP2VAR(OS_VOLATILE Os_CoreStateType, OS_VAR, OS_CONST) state;
Os_VoidVoidFunctionType dispatch;
volatile Os_imaskType *intc_cpr;
volatile uint32 *intc_eoir;
volatile Os_imaskType *intc_iackr;
CoreIdType core_id;
} Os_CoreConfiguration;
controlled:个人理解为内核运行时状态描述变量,该变量指示着当前正在运行的Task/Cat2 ISR.当前正在运行的Task的最高优先级掩码等内容,具体定义可跳转到Os_ControlledCoreType类型描述。
any:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
state:该变量类型为“typedef unsigned long Os_CoreStateType”,主要在StartOS阶段用于做多核同步,简单来说在需要多核同步时,需要每个核记录的state变量处于同一值系统才可以继续往下执行;该变量同时也可以用作内核状态的参考。
dispatch:该变量是一个函数指针,变量类型为“typedef void (*Os_VoidVoidFunctionType)(void)”,该变量记录了内核的调度函数,是OS的核心函数,ETAS OS为了提高代码的执行效率,为每个核单核生成一个属于核自己的调度函数,名称为Os_Dispatch<CoreID>,其中包含了ETAS OS的调度策略,上下文切换等重要功能,读懂该函数对于ETAS OS的了理解至关重要。函数具体理解可以跳转到Os_Dispatch<CoreID>函数描述。
intc_cpr:该变量为一个指针变量,记录的是SPC58NN每个核的操作中断屏蔽掩码寄存器(INTC_CPR)的地址指针,往该地址写对应的值即可实现对中断进行按优先级屏蔽的功能。在软件中,ETAS OS将其操作定义成宏OS_INTC_CPR(如下所示),后续中只需要操作该宏即可实现对寄存器(INTC_CPR)的操作。
#define OS_INTC_CPR0 OS_REGISTER(Os_imaskType, 0xF4044018UL)
#define OS_INTC_CPR1 OS_REGISTER(Os_imaskType, 0xF4044014UL)
#define OS_INTC_CPR2 OS_REGISTER(Os_imaskType, 0xF4044010UL)
#define OS_INTC_CPR3 OS_REGISTER(Os_imaskType, 0xFFFFFFFFUL)
#define OS_INTC_CPR (*(os_current_core_const->intc_cpr))
intc_eoir:该变量为一个指针变量,记录SPC58NN每个核中用以结束中断的寄存器(INTC_EOIR)的地址指针,往该寄存器写值将代码着当前中断以及结束,同时INTC_CPR会从FILO中将被嵌套的中断取出,以此实现中断嵌套的效果,该变量在ETAS OS中被定义成宏OS_INTC_EOIR<CoreId>操作,具体定义如下所示,目前该变量主要应用与二类中断的处理中用于结束二类中断的运行以开始新的调度,而二类中断的总入口在ETAS OS中按不同的核定义了不同名称Os_ISRWrapper<CoreID>,具体可跳转到Os_ISRWrapper<CoreID>函数描述。
#define OS_INTC_EOIR0 OS_REGISTER(uint32, 0xF4044038UL)
#define OS_INTC_EOIR1 OS_REGISTER(uint32, 0xF4044034UL)
#define OS_INTC_EOIR2 OS_REGISTER(uint32, 0xF4044030UL)
#define OS_INTC_EOIR3 OS_REGISTER(uint32, 0UL)
#define OS_INTC_EOIR (*(os_current_core_const->intc_eoir))
intc_iackr:该变量为一个指针变量,记录的是SPC58NN每个核的操作中断确认寄存器(INTC_IACKR)的地址指针,当硬件触发中断时,读该寄存器可以确定当前触发的具体中断,以便从中断向量表中调用对应的中断服务函数,同时该寄存器可以设置中断向量表的基地址,作者使用的配置工程生成的代码中未发现对该寄存器的使用,但在中断总入口(汇编函数)可以看到对该寄存器的操作Os_mid_wrapper,用于退出中断时实现返回到被嵌套的中断中,具体可跳转到Os_mid_wrapper<CoreID>函数描述。
core_id:记录当前的核的逻辑核ID,用于软件去区别不同的物理内核。
关联:
理解完Os_CoreConfiguration数据类型每个成员的含义基本可以对ETAS OS有个较为简单的认识,同时在OS出现问题时应当也有了一定程度的调查手段。
Os_ControlledCoreType:
该变量类型描述了内核运行时的状态,根据该变量可以知道当前内核正在运行那个现场,现场状态等信息。数据类型定义如下所示:
typedef struct Os_ControlledCoreType_s {
OS_VOLATILE Os_Lockable lock_taskaccess;
OS_VOLATILE Os_psetType ReadyTasks ;
OS_VOLATILE ISRType RunningISR;
OS_VOLATILE TaskType RunningTask;
OS_VOLATILE Os_tpmaskType RunningTPMask ;
uint8 AppAccess;
OS_VOLATILE ApplicationType AppOverride;
Os_StackSizeType GetStackValueAdjust;
OS_VOLATILE TaskType ChainTaskRef;
boolean CoreIsActive;
boolean InShutdownHook;
} Os_ControlledCoreType;
lock_taskaccess:该变量为ETAS OS内置的一个自旋锁(Spinlock)变量,在多核系统中,可能存在内核激活别的核的Task的情况,这时就可能出现多核同时访问同一个变量或物理内存的情况,为了解决这种现象导致内存踩踏事件,ETAS OS使用该变量来确保系统操作数据的原子性,多用于现场切换等情况,可跳转到Os_Dispatch<CoreID>,ActivateTask,ChainTask,Os_ActivateTaskKL等函数查看使用情况。
ReadyTasks:该变量指示内核当前就绪的Task的最高优先级,注意作者配置的工程Task优先级等级小于36,所以该变量每一个bit表示一个优先级是否有task处于就绪状态,ETAS OS的Task最多支持1024个优先级,超过36个优先级的情况作者暂未配置,也未分析,后续如有机会再行完善。ReadyTasks的变量类型可跳转到Os_psetType类型描述。
RunningISR:该变量用于指示当前OS正在运行的二类中断标识,通过该变量可以知道当前是否有二类中断正在运行,并且可以知道具体运行的二类中断。RunningISR的变量类型ISRType可跳转到ISRType类型描述。
RunningTask:该变量用于指示当前OS正在运行的Task标识,通过该变量可以知道当前正在运行的Task。RunningTask的变量类型可跳转到TaskType类型描述。
RunningTPMask:该变量用于指示当前运行的现场的Task的优先级掩码,只有优先级大于该变量的Task才可以抢占当前现场。
AppAccess:该变量可以用于指示OS当前运行的Application,该变量在软件中可以用来判断操作权限,Autosar OS中规定了对象(如Task)需要配置允许操作的Application权限,ETAS OS就是使用该变量进行判断。
AppOverride:该变量可以指示内核当前正在运行的Application ID。
GetStackValueAdjust:该变量记录调用一个函数时需要的栈的数量,用于后面调用GetStackValue时能够准确获取栈的地址。
ChainTaskRef:该变量用于记录系统调用ChainTask还未执行对于Task的Task标识。
CoreIsActive:该变量为boolean类型,TRUE表示当前核处于运行状态中,FALSE表示当前核已关停,一般调用ShutdownOS使内核关停。
InShutdownHook:判断内核是否正在调用ShutdownHook,该变量在多核下电时十分重要,起到多核同步的作用。
关联:
- Os_CoreConfiguration
- Os_Dispatch<CoreID>
- Os_ISRWrapper<CoreID>
- ActivateTask
- ChainTask
- TerminateTask
- Os_ActivateTaskKL
- GetCounterValue
- Os_IncrementCounter_Rte_TickCounter
Os_AnyCoreType:
该变量记录了内核运行时禁止中断时的一些状态记录,数据类型定义如下所示:
typedef struct Os_AnyCoreType_s {
OS_VOLATILE Os_imaskType DisableAllImask;
OS_VOLATILE Os_imaskType SuspendAllImask;
OS_VOLATILE Os_imaskType SuspendOSImask;
OS_VOLATILE uint32 DisableAllCount;
OS_VOLATILE uint32 SuspendAllCount;
OS_VOLATILE uint32 SuspendOSCount;
OS_VOLATILE Os_OrtiIdType OrtiApiID;
StatusType OrtiLastError;
Os_jump_buf RestartJumpBuf;
boolean Restartable;
} Os_AnyCoreType;
DisableAllImask:该变量记录了调用DisableAllInterrupts前的中断屏蔽状态,以便在调用EnableAllInterrupts后能够恢复到原有状态。
SuspendAllImask:该变量记录了调用SuspendAllInterrupts前的中断屏蔽状态,以便在调用ResumeAllInterrupts后能够恢复到原有状态。
SuspendOSImask:该变量记录了调用SuspendOSInterrupts前的中断屏蔽状态,以便在调用ResumeOSInterrupts后能够恢复到原有状态。
DisableAllCount:该变量记录了调用DisableAllInterrupts的嵌套层数,即调用了DisableAllInterrupts还没有调用EnableAllInterrupts的数量。注意:Autosar OS规定DisableAllInterrupts不允许嵌套,所以该值理论上来说最大为1。
SuspendAllCount:该变量记录了调用SuspendAllInterrupts的嵌套层数,即调用了SuspendAllInterrupts还没有调用ResumeAllInterrupts的数量。
SuspendOSCount:该变量记录了调用SuspendOSInterrupts的嵌套层数,即调用了SuspendOSInterrupts还没有调用ResumeOSInterrupts的数量。
OrtiApiID:该变量记录了OS当前正在调用的服务(API)ID。
OrtiLastError:由于作者当前配置生成的代码没有对该变量有过多的使用,个人推测该变量用于记录OS最后一次上次的错误。
RestartJumpBuf:该变量指示OS重启时的重启入口。
Restartable:该变量标志着OS在进入ShutdownOS后是否允许重启,该标志需要在StartOS之前调用接口Os_SetRestartPoint来实现。
关联:
- Os_CoreConfiguration
- ActivateTask
- ChainTask
- TerminateTask
- Os_ActivateTaskKL
- GetCounterValue
- Os_IncrementCounter_Rte_TickCounter
TaskType/Os_TaskType:
TaskType/Os_TaskType是Task的配置信息描述,同时也是Task的标识,其定义如下所示:
typedef struct Os_TaskType_s {
CONSTP2VAR(Os_TaskDynType, OS_VAR, OS_CONST) dynamic;
Os_VoidVoidFunctionType entry_function;
Os_psetType pset;
Os_tpmaskType base_tpmask;
Os_tpmaskType tpmask;
CoreIdType core_id;
uint32 index;
Os_ActivationCountType activation_count;
CONSTP2CONST(struct Os_TaskFifoControl_s, OS_CONST, OS_CONST) activation_fifo;
uint8 access;
ApplicationType application;
} Os_TaskType;
typedef P2CONST(Os_TaskType, TYPEDEF, OS_VAR) TaskType;
dynamic:该变量描述了Task运行过过程中的一些动态信息,包括Task现场信息,Task的激活次数以及Task使用的资源锁(Resource)的嵌套层数,具体可跳转到Os_TaskDynType类型描述。
entry_function:这个一个函数指针,类型定义为typedef void (*Os_VoidVoidFunctionType)(void)。该变量描述了Task执行体的入口函数。
pset:该变量标识Task的优先级,其中变量的每一位代表一个优先级,pset的变量类型可跳转到Os_psetType类型描述。
base_tpmask:该参数的生成严格按照本核生成的Task的顺序,且核内每个Task的生成值均不一样。该参数在作者目前使用的配置中只发现在GetResource和ReleaseResource这两个接口中使用,目前推测可能与资源锁(Resource)有关,目前猜想可能是ETAS OS在生成资源锁时为了明确资源锁使用的Task现场,后面配置多了或有足够的代码分析再对此参数进行补充。
tpmask:该Task的优先级掩码,生成原则与该Task配置时同一优先级的Task生成的优先级pset的后导0位全部置1,在Task切换调度的时候,要求目标激活的Task的优先级必须大于该值才能后切换。如果同一个核多个Task在配置的时候优先级配置得一样,尽管他们生成的优先级pset是不一样的,但生成的tpmask值也是一样的,这样也能保证配置的同级Task不能相互打断。而同级Task的调度策略遵循FIFO策略。该参数在代码中的作用可以参考Os_Dispatch<CoreID>函数的实现。
core_id:该参数指示Task所属的逻辑核ID。
index:该参数指示配置时Task的ID,与在工具中配置的Task的顺序有关,每个Task的index都是唯一 的。
activation_count:该参数指示Task对最多激活但未执行的次数,起作用可以参考Os_TaskDynType对于activation_count描述,二者结合起来一起组成对E_OS_LIMIT错误检测的实现。
activation_fifo:指示Task所属的调度队列的相关信息,其中包括队列的起始位置,结束位置,以及被Task的优先级掩码,与Task的tpmask的值一致,ETAS OS中核内同一优先级的Task使用同一个调度队列范围,activation_fifo的变量类型可跳转到Os_TaskFifoControl类型描述。
access:该变量描述了允许操作该Task的Application,该变量每一位表示一个application,如要appId为1的application是否能够操作该中断,则需要判断变量access的第一位是否为1。
application:该变量描述了该Task所属的application id。
关联:
ISRType/Os_ISRType:
ISRType/Os_ISRType是二类中断的配置信息描述,同时也是二类中断的标识,其定义如下所示:
typedef struct Os_ISRType_s {
Os_VoidVoidFunctionType entry_function;
Os_imaskType imask;
uint8 access;
ApplicationType application;
} Os_ISRType;
typedef P2CONST(Os_ISRType, TYPEDEF, OS_VAR) ISRType;
entry_function:这是一个函数指针,其类型定义为typedef void (*Os_VoidVoidFunctionType)(void)。该变量描述了二类中断的入口函数。
imask:该变量描述了二类中断的优先级,如果同作者一样,使用的是SPC58NN,则要注意只有低6位才表示优先级,第15位表示全局中断的使能状态(MSR.EE)。
access:该变量描述了允许操作该二类中断的Application,该变量每一位表示一个application,如要appId为1的application是否能够操作该中断,则需要判断变量access的第一位是否为1。
application:该变量描述了该二类中断所属的application id。
关联:
Os_psetType:
Os_psetType是一个共同体,其中主要描述了Task的优先等级,用于Os_ControlledCoreType中描述内核中以就绪的Task的优先等级;在TaskType表示该Task的优先等级。其定义如下所示,其中不同名称的成员在不同的逻辑核中使用标识,所有配置中有多少个内核将会生成多少个成员。
typedef union {
Os_pset0Type p0;
Os_pset1Type p1;
Os_pset2Type p2;
} Os_psetType;
其中每个成员的定义均是一样的如所示:
typedef unsigned int Os_bitmask;
typedef Os_bitmask Os_pset0Type;
typedef Os_bitmask Os_pset1Type;
typedef Os_bitmask Os_pset2Type;
关联:
Os_TaskDynType:
Os_TaskDynType该变量描述了Task运行过过程中的一些动态信息,定义如下所示:
typedef struct Os_TaskDynType_s {
VAR(Os_jump_buf, OS_VAR_NOINIT) terminate_jump_buf;
Os_ActivationCountType activation_count;
Os_ResourceCountType resource_count;
} Os_TaskDynType;
terminate_jump_buf:该变量为一个数组,用以记录Task现场信息,包括通用寄存器状态和一些特殊寄存器的状态,现场切换时,会将Task的现场信息保存在这个数组中,在恢复Task现场时,会把这个数组中保存的信息恢复出来,已达到切换现场的目的。切换现场可参考Os_setjmp和Os_longjmp函数描述。
activation_count:Autosr OS规定需要为每个Task配置一个激活但未运行的次数,一旦超过这个次数则会触发E_OS_LIMIT错误,而该变量记录了Task已激活但未运行的次数。
resource_count:该变量记录了该Task在运行期间资源锁(Resource)的嵌套次数。
关联:
Os_TaskFifoControl:
Os_TaskFifoControl描述了Task调度队列有关信息,核内同一优先级的Task共用一组调度队列范围,定义如下所示:
typedef struct Os_TaskFifoControl_s {
CONSTP2VAR(TaskType, OS_VAR, OS_CONST) start;
CONSTP2VAR(TaskType, OS_VAR, OS_CONST) end;
const uint8_least dyn_index;
const Os_tpmaskType base_tpmask;
} Os_TaskFifoControl;
start:该变量实际为一个数组指针,其类型为TaskType,描述该Task在使用的调度队列的起始地址,在ETAS OS中,所有Task都使用同一个数组(Os_TaskFifo)作为调度队列,不同的是,不同的Task使用的调度队列的范围不一样,在上位机中配置优先级一样的Task使用同一范围的调度队列,Task使用调度队列的大小与同优先级的Task的最大允许激活次数总数有关。
end:该变量实际为一个数组指针,其类型为TaskType,描述该Task在使用的调度队列的结束地址,与start一起限制Task使用的调度队列的大小。
dyn_index:该变量描述起来比较负载,首先该变量是个常量,在OS运行期间并不会改变该变量的值。OS在调度过程中需要将已激活的Task放入到调度队列中,在运行Task时,需要将Task从调度队列中取出来,这个时候就涉及到对队列的读写操作,ETAS OS为了解决多核可同时读写的问题(作者猜想),专门使用一个指针数组Os_dyn_fifocontrol去充当Write指针和Read指针,每个调度队列都需要Write和Read两个指针,这就需要指示调度队列使用指针数组Os_dyn_fifocontrol哪一个位置作为Read指针,然后往后偏移一个位置作为Write指针。简单来说,dyn_index变量指示的是Os_dyn_fifocontrol指针数组哪一个位置开始充当队列的Read指针和Write指针。Os_dyn_fifocontrol的初始化可参考Os_StartOS_CommonInit函数描述,Os_dyn_fifocontrol的定义如下所示:
P2VAR(TaskType, OS_VAR, OS_VAR_NO_INIT) Os_dyn_fifocontrol[22U];
base_tpmask:该变量记录了调度队列中Task对优先级掩码。
关联:
Os_ApplicationConfigurationType:
Os_ApplicationConfigurationType描述了Application的基本信息,包含AppID和Application对应的权限,定义如下所示:
typedef struct Os_ApplicationConfigurationType_s {
ApplicationType app_id;
uint8 access;
} Os_ApplicationConfigurationType;
app_id:Application对应的ID标识,每个Application的ID标识都是唯一的。
access:Application对应的权限,与app_id有着关联性,其取值符合access = 0x01 << app_id的关系,其中app_id为0时标识无效Application,其access也是0。
关联:
Os_ResourceType:
Os_ResourceType描述了资源锁运行时动态数据的保存地址,优先级和允许被操作的权限,定义如下所示:
typedef struct Os_ResourceType_s {
CONSTP2VAR(Os_ResourceDynType, OS_VAR, OS_CONST) dynamic;
Os_tpmaskType tpmask;
uint8 access;
} Os_ResourceType;
dynamic:描述了资源锁动态运行时的数据,根据该变量可以得知资源锁有没有上锁,获得锁的对象以及获得锁的对象的优先级等信息。具体描述可跳转到Os_ResourceDynType数据类型描述。
tpmask:该变量描述了所有拥有该资源锁的现场的最高优先级,如果该锁被Task获取,将禁止所有优先级低于tpmask的Task运行,以防止出现优先级翻转的情况。
access:该变量描述了资源锁允许被操作的application权限。
关联:
Os_ResourceDynType:
Os_ResourceDynType描述了资源锁动态运行时的数据,其定义如下所示:
typedef struct Os_ResourceDynType_s {
Os_ResourceCountType access_count;
TaskType locker;
union {
Os_tpmaskType tpmask;
} saved_priority;
} Os_ResourceDynType;
access_count:该变量描述了该资源锁被获取未释放的嵌套层数,Autosar OS规定,一个资源锁在同一时间只能被获取一次,因此该值理论上来说最大只能为1。
locker:该变量描述了资源锁被获取的对象,该对象可能是Task,也可能是二类中断,其变量类型可跳转到TaskType类型描述。
saved_priority:该变量描述了保存了资源锁时正在运行的Task的优先级(一般就是该Task获取资源锁),以便在释放资源锁的时候可以恢复OS正在运行的Task优先级。
关联:
CounterType/Os_CounterType:
Os_CounterType数据类型类型记录了Autosar OS对象Counter的配置信息,其变量类型定义如下所示:
typedef struct Os_CounterType_s {
CONSTP2VAR(Os_CounterDynType, OS_VAR, OS_CONST) dynamic;
Os_CounterIncrAdvType advincr;
AlarmBaseType base;
const void *core;
uint8 access;
ApplicationType application;
} Os_CounterType;
dynamic:该变量记录了Counter对应的动态运行数据,主要记录了Counter的动态计数值,其详细描述可跳转到数据类型Os_CounterDynType的相关描述。
advincr:该变量的数据类型为一个函数指针,其数据类型定义如下所示,其描述了Counter对应的回调函数。
typedef StatusType (*Os_CounterIncrAdvType)(void);
base:该变量描述了Counter的基本信息,包括配置Counter的最大值,自定义周期对应的Counter的Tick数量,以及该Counter驱动的对象(Alarm/Schedule table)最小循环周期,具体可以参考其数据类型AlarmBaseType的描述。
core:该数据指示着Counter所属内核的配置描述信息控制块,参考Os_const_coreconfiguration。
access:该变量描述了允许操作该Counter的Application,该变量每一位表示一个application,如要appId为1的application是否能够操作该中断,则需要判断变量access的第一位是否为1。
application:该变量描述了该Counter所属的application id
关联:
Os_CounterDynType:
Os_CounterDynType数据类型记录了对应Counter的动态运行数据,主要包括了Counter的动态计数值,可以通过Autosar OS提供的API接口GetCounterValue去获取Counter的计数值。该数据类型的定义如下所示:
typedef struct Os_CounterDynType_s {
union {
struct s_swd { /* [$UKS 211] */
TickType count;
} sw;
} type_dependent; /* [MISRA 2012 Rule 19.2] */ /*lint !e9018 */
} Os_CounterDynType;
从ETAS给出的数据类型定义来看,ETAS OS可能分别为软件Counter和硬件Counter分别设计了对应的动态数据属性,然后封装在联合体type_dependent中,但作者目前的配置工程中只配置了软件Counter,所以作者猜想,ETAS OS才会只生成软件Counter对应的数据属性。且从生成的数据类型定义来看,软件Counter的动态数据属性只有一个计数器count。
关联:
TickRefType:
TickRefType以指针的形式传入如GetCounterValue等函数后,将Counter的计数值传回调用方,其基础类型可参考TickType的数据类型描述,TickRefType的定义如下所示:
typedef P2VAR(TickType, TYPEDEF, OS_VAR) TickRefType;
关联:
TickType:
TickType用于描述Counter相关的计数值,本身是一个基础数据类型,其定义如下所示:
typedef unsigned int TickType;
关联:
AlarmBaseType:
AlarmBaseType描述了Alarm的基础属性,也可用于描述Counter对应的基础属性,其数据类型的定义如下所示:
typedef struct {
TickType maxallowedvalue;
TickType ticksperbase;
TickType mincycle;
} AlarmBaseType;
maxallowedvalue:对应Counter允许的最大计数值;
ticksperbase:自定义周期所对应的Counter的Tick数量;
mincycle:对应Counter所驱动的Alarm的最小循环周期。
关联:
Os_ScheduleTableType:
Os_ScheduleTableType描述了调度表的配置信息,其中包括了驱动调度表的Counter索引,调度表的循环属性,初始偏移量等信息,该数据类型的定义如下所示:
typedef struct Os_ScheduleTableType_s {
CONSTP2VAR(struct Os_ScheduleTableDynType_s, OS_VAR, OS_CONST) dynamic;
CounterType counter;
boolean repeat;
uint32 config;
uint8 initial;
uint8 access;
ApplicationType application;
} Os_ScheduleTableType;
dynamic:该变量记录了调度表运行过程中的动态运行数据,包括调度表下一个到期点在ounter上匹配的Tick值,调度表的后继表,调度表的状态等信息,具体可参考数据类型Os_ScheduleTableDynType_s的描述。
repeat:该变量描述了调度表是否为循环表,TRUE表示调度表为循环表,FALSE则表示调度表为单词表。
config:该变量描述了调度表的第一个到期点相关信息的索引,该变量的低10位可以在数组Os_const_expiry_intervals中索引到相邻的两个到期点直接的时间差值;而该变量高22位可以在数组Os_const_expiry_actions中索引到具体到期点的动作。具体逻辑可参考函数Os_PerformEP的描述。
initial:该变量描述了调度表第一个到期点距离调度表开始时间的时间差值。
access:该变量描述了允许操作该调度表的Application,该变量每一位表示一个application,如要appId为1的application是否能够操作该中断,则需要判断变量access的第一位是否为1。
application:该变量描述了该调度表所属的application id。
关联:
- Os_ScheduleTableDynType_s
- Os_const_scheduletables
- Os_IncrementCounter_Rte_TickCounter
- Os_PerformEP
- ScheduleTableType
Os_ScheduleTableDynType/Os_ScheduleTableDynType_s:
Os_ScheduleTableDynType_s描述了调度表运行过程中所需要的动态数据,理解该数据类型将对理解调度表的运行机制有着十分重要的作用,该数据类型的定义如下所示:
typedef struct Os_ScheduleTableDynType_s {
TickType match;
ScheduleTableType next;
ScheduleTableStatusType state;
uint32 config;
} Os_ScheduleTableDynType;
match:该变量记录着调度表下一个到期点的Tick值,OS运行过程中,将会把该值与驱动该调度表的对应Counter的计数值count(可参考Os_CounterDynType关于count的描述)进行匹配,一旦二者的值一致,则表示着调度表到达到期点,需要进行相应的动作处理。
next:该变量为Os_ScheduleTableType的指针,其定义参考ScheduleTableType数据类型描述,是指示着调度表当前周期结束后是否需要结束当前调度表(即使当前调度表可能配置的是循环表),转而执行该变量指示的调度表标识(后继表),若该变量为空指针,则表示当前调度表没有后继表需要被执行。该变量是实现Autosar OS提供的NextScheduleTable接口的关键变量。
state:该变量表示在调度表当前正在执行的状态,调度表包含同步表在内总共有六种状态,其变量类型参考数据类型ScheduleTableStatusType的相关描述。
config:该变量是调度表运行过程中最重要的一个变量,理解该变量的作用是理解整个调度表的运行逻辑的核心。该变量的低10位可以在数组Os_const_expiry_intervals中索引到相邻的两个到期点直接的时间差值;而该变量高22位可以在数组Os_const_expiry_actions中索引到具体到期点的动作。具体逻辑可参考函数Os_PerformEP的描述。
关联:
ScheduleTableType:
ScheduleTableType是指针类型,其数据类型原型可参考Os_ScheduleTableType的相关描述,其定义如下所示:
typedef P2VAR(ScheduleTableType, TYPEDEF, OS_VAR) ScheduleTableRefType;
关联:
ScheduleTableStatusType:
ScheduleTableStatusType数据类型表示对应调度表当前所处的状态,其定义如下所示:
enum Os_ScheduleTableStatusType {
SCHEDULETABLE_STOPPED = 0U,
SCHEDULETABLE_NEXT,
SCHEDULETABLE_WAITING,
SCHEDULETABLE_RUNNING,
SCHEDULETABLE_RUNNING_AND_SYNCHRONOUS};
typedef enum Os_ScheduleTableStatusType ScheduleTableStatusType;
关联:
函数描述:
Os_Dispatch<CoreID>:
该函数位于源文件DispatchTask.c内,为每个逻辑核都生成一个属于自己的函数,函数按照Os_Dispatch<CoreID>的规则生成,该函数是实现内核调度的重要函数,里面实现了按优先级调度的重要调度策略,展示代码以SPC58NN的核0为例,经过测试发现,ETAS OS根据配置不同该函数的生成略有差异,本文旨在让大家从内核角度理解ETAS OS,不探讨所有关于ETAS OS对Autosar OS的实现机制,且作者手上目前也只有该配置,后期如有需要再继续深入探讨。函数实现如下所示:
FUNC(void, OS_CODE_CORE0) Os_Dispatch0(void) {
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[0U];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
P2CONST(Os_ApplicationConfigurationType, TYPEDEF, OS_VAR) app;
const Os_tpmaskType previous_priority = Os_RunningTPMask; /* save active priority */ /*lint !e9018 A union type */
const TaskType previous_task = Os_RunningTask; /* save current task */
ApplicationType previous_appoverride;
uint8 previous_access = Os_AppAccess;
previous_appoverride = Os_AppOverride; Os_AppOverride = INVALID_OSAPPLICATION; /* [$EHI 559562] */
do {
Os_RunningTask = (&Os_const_tasks0[31U - __CLZ32(Os_ControlledCoreInfo[0U].ReadyTasks.p0)]); /*lint !e931 !e9034 !e747 */
/* Remove from head of the fifo if there is one */
if (Os_RunningTask->activation_fifo != (void *)0) {
Os_RunningTask = *(Os_dyn_fifocontrol[dfifo->dyn_index]); /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[dfifo->dyn_index] = (Os_dyn_fifocontrol[dfifo->dyn_index] == dfifo->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 !e931 !e564 */
dfifo->start : (Os_dyn_fifocontrol[dfifo->dyn_index] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 !e9016 */
}
Os_RunningTask->dynamic->resource_count = 0U;
Os_RunningTPMask = Os_RunningTask->tpmask; /* [$UKS 199] [$UKS 201] */
app = &Os_const_applications[Os_RunningTask->application];
Os_AppAccess = app->access;
if (0 == Os_setjmp(&(Os_RunningTask->dynamic->terminate_jump_buf))) { /*lint !e545 Address of what might be an array */
OS_BARRIER(); OS_INTC_CPR = 0U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); /* [$UKS 93] Allow interrupts precedence */
Os_RunningTask->entry_function();
}
OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_RunningTask->dynamic->activation_count--;
if (Os_RunningTask->dynamic->activation_count == 0U) {
Os_ControlledCoreInfo[0U].ReadyTasks.p0 &= ~(Os_RunningTask->pset.p0); /*lint !e931 PC-lint thinks there are side effects here */
}
if (INVALID_TASK != Os_ChainTaskRef) {
Os_ChainTaskRef->dynamic->activation_count++;
{ /* Add to tail of fifo [$UKS 52] [$UKS 53] [$UKS 789] */
if ((void *)0 != (Os_ChainTaskRef->activation_fifo)) {
*(Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U]) = Os_ChainTaskRef; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] == (Os_ChainTaskRef->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(Os_ChainTaskRef->activation_fifo)->start : (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
}
}
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= Os_ChainTaskRef->pset.p0; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_ChainTaskRef = INVALID_TASK;
}
} while ((Os_ControlledCoreInfo[0U].ReadyTasks.p0 > previous_priority.t0) /*lint !e931 PC-lint thinks there are side effects here */);
Os_RunningTPMask = previous_priority; /* [$UKS 77] [$UKS 78] */
Os_AppOverride = previous_appoverride;
Os_RunningTask = previous_task;
Os_AppAccess = previous_access;
}
以下将对该函数尝试进行逐句解释。先关注该函数的局部变量/常量定义和描述。
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[0U];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
P2CONST(Os_ApplicationConfigurationType, TYPEDEF, OS_VAR) app;
const Os_tpmaskType previous_priority = Os_RunningTPMask; /* save active priority */ /*lint !e9018 A union type */
const TaskType previous_task = Os_RunningTask; /* save current task */
ApplicationType previous_appoverride;
uint8 previous_access = Os_AppAccess;
previous_appoverride = Os_AppOverride; Os_AppOverride = INVALID_OSAPPLICATION;
os_current_core_const:获取当前核(核0)的配置描述信息控制块,参考Os_const_coreconfiguration。
os_current_controlled_core:获取当前核(核0)运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
app:将用来记录即将运行的Application的描述块,该变量里的成员作用参考Os_ApplicationConfigurationType的变量类型描述。
previous_priority:改变量用来记录被切换前(当前正在运行)的Task(现场)的最高优先级,其取值Os_RunningTPMask的宏定义如下所示:
#define Os_RunningTPMask (os_current_controlled_core->RunningTPMask)
previous_task:改变量用来记录被切换前(当前正在运行)的Task的配置描述块,该变量里的成员作用参考TaskType的变量类型描述。其取值Os_RunningTask的宏定义如下所示:
#define Os_RunningTask (os_current_controlled_core->RunningTask)
previous_appoverride:该变量用来记录被切换前(当前正在运行)的Application ID。
previous_access:该变量用于记录被切换前(当前正在运行)的现场的Application权限。其取值Os_AppAccess的宏定义如下所示:
#define Os_AppAccess (os_current_controlled_core->AppAccess)
整个函数的执行体机会就是内部“do{}while;”循环体的执行,循环体中将按照优先级依次从高到底执行对应的Task,并且,Task在执行完之后,通过调用TerminateTask或ChainTask将回到该函数,以此作为Task切换现场和结束现场的入口和出口。接下来我们将依次分析循环体内容。
Os_RunningTask = (&Os_const_tasks0[31U - __CLZ32(Os_ControlledCoreInfo[0U].ReadyTasks.p0)]);
Os_ControlledCoreInfo[0U].ReadyTasks.p0:该变量表示当前核(核0)中所有已经处于就绪态(Ready)的Task的优先级,具体可参考Os_ControlledCoreType中对于ReadyTasks的描述。
__CLZ32():该函数获取传入其形参的“前导0”的值。(前导0:传入参数以二进制的方式从最高位开始数0的个数,一直数到非0位置,例如__CLZ32(0x0FFFFFFF) = 4)
Os_const_tasks0:描述了本核(核0)的所有task的配置信息描述体具体可参考Os_const_tasks<CoreID>的信息描述。
所以本语句的意思将是获取一个Task的配置信息描述块给到Os_RunningTask,作为当前正在运行的Task,而获取原则需要根据当前正处于就绪状态的Task而定,通过前导0算法可取出当前处于就绪状态的最高优先级的Task的序号,而通过“31U - 前导0计算的值”即可获取对应Task在Os_const_tasks<CoreID>中的位置,从而获取到当前处于就绪状态的最高优先级的Task,这也是Os_const_tasks<CoreID>中要按Task优先级安排Task的位置的原因。
if (Os_RunningTask->activation_fifo != (void *)0) {
Os_RunningTask = *(Os_dyn_fifocontrol[dfifo->dyn_index]); /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[dfifo->dyn_index] = (Os_dyn_fifocontrol[dfifo->dyn_index] == dfifo->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 !e931 !e564 */
dfifo->start : (Os_dyn_fifocontrol[dfifo->dyn_index] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 !e9016 */
}
Os_RunningTask->activation_fifo:描述Task对应的调度队列的描述信息,具体可参考Os_TaskFifoControl的数据类型描述。如果调度出来的最高优先级的Task没有调度队列描述信息,则说明没有同该Task有相同的优先级的Task,并且该Task最高运行被激活但未运行的次数为1,无须进入if执行体获取当前需要执行的Task;否则则需要进入if执行体获取当前需要执行的Task,由于ETAS OS允许配置多个Task为同一优先级,所以,同一个调度队列中可能记录着多个Task的激活信息,而同优先级的Task需要遵循FIFO的调度策略。
dfifo->dyn_index:描述了Task调度队列信息记录Read指针的位置,dfifo其实就是上文中的Os_RunningTask->activation_fifo,定义如下所示,具体可参考Os_TaskFifoControl的数据类型描述。
#define dfifo ((const Os_TaskFifoControl *)Os_RunningTask->activation_fifo)
Os_dyn_fifocontrol:该变量是个数组,里面记录了每个Task调度队列的Read指针和Write指针,通过每个调度调度了dyn_index进行索引,Os_dyn_fifocontrol的初始化可参考Os_StartOS_CommonInit函数描述。
Os_RunningTask = *(Os_dyn_fifocontrol[dfifo->dyn_index]);
所以该语句的意思就是通过Os_RunningTask重新从以获取的Os_RunningTask的调度队列中重新获取出一个最先入队的Task出来再次赋值给Os_RunningTask,作为心中正在运行的Task运行。
Os_dyn_fifocontrol[dfifo->dyn_index] = (Os_dyn_fifocontrol[dfifo->dyn_index] == dfifo->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 !e931 !e564 */
dfifo->start : (Os_dyn_fifocontrol[dfifo->dyn_index] + 1U);
该语句的意思就是将调度队列的Read指针向后偏移一个位置,如果偏移后的位置超过了队列的最后一个位置,则将调度队列的Read指针偏移到调度队列的起始地址。
以上则是ETAS OS按照调度队列来调度Task的核心逻辑,相对来说在理解了所有变量的含义的前提下还算是比较容易。
接下来则是对以下运行变量进行修改,以便符合切换到对应的现场的数据,代码如下所示:
Os_RunningTask->dynamic->resource_count = 0U;
Os_RunningTPMask = Os_RunningTask->tpmask; /* [$UKS 199] [$UKS 201] */
app = &Os_const_applications[Os_RunningTask->application];
Os_AppAccess = app->access;
resource_count:首先初始化即将进入的Task的获取资源(Resource)的嵌套值为0。
Os_RunningTPMask:更新内核即将运行的现场的优先级,以此防止优先级翻转等情况的产生。
app:获取当前即将运行的Task所在的Application信息描述块。
Os_AppAccess:更新即将运行的Apllication的权限。
更新完现场的运行参数就可以准备进行现场切换了,具体则是进行保存和恢复现场的操作,代码如下所示:
if (0 == Os_setjmp(&(Os_RunningTask->dynamic->terminate_jump_buf))) { /*lint !e545 Address of what might be an array */
OS_BARRIER(); OS_INTC_CPR = 0U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); /* [$UKS 93] Allow interrupts precedence */
Os_RunningTask->entry_function();
}
Os_setjmp:该函数是一个汇编函数,该函数的作用是保存即将切换的Task的现场,入参是Task的现场信息保存块。这里可能会有一点点疑惑,切换到目标现场,为什么还要保存目标现场呢,不怕将目标现场破坏吗?这里作者配置的是“单栈”模式,即同一个核中的所有现场均使用同一段栈空间,至于为什么使用同一段栈空间不会导致栈踩踏这里不做解释,但稍微一想应该也能明白。而在单栈模式下,ETAS OS切换现场是直接通过回调的方式进行切换,所以即使保存了现场也不会破坏掉即将进入的现场,并且,这里保存现场的最大作用是确保Task执行完后,调用TerminateTask/ChainTask能够准确返回到Os_Dispatch<CoreID>中,所以这里保存现场主要是用于结束Task使用,具体可参考Os_setjmp函数描述。
Os_setjmp函数的返回值总为0,也就是代码执行到该处时,该if判断条件始终成立,那么该条件有什么存在意义呢?由于Os_setjmp保存现场,所以TerminateTask/ChainTask后会返回再次返回到该条件的判断,结束的时候时候代码获取到的返回值就是非0,可防止再次执行if执行体,具体逻辑可参考Os_longjmp函数(该函数的返回值就是非0)描述,该设计可以说十分巧妙。
OS_BARRIER(); OS_INTC_CPR = 0U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
该语句主要是为进入Task现场前设置一些硬件环境,OS_INTC_CPR = 0U是为了进入Task现场时确保所有已经使能的中断没有被优先级屏蔽,该操作大大提高了系统的健壮性,但绕过了Autosar OS结束/开始Task对中断疲惫检查,作者也不知道该设计的优劣;OS_SET_EE()的作用则是使能全局中断,确保进入Task后中断能够正常响应。其他的则是指令同步,内存同步等操作。
Os_RunningTask->entry_function();
该语句则是正式通过回调的方式进入Task,具体可跳转到TaskType关于entry_function的描述。
经过上面的语句将进入到对应的Task执行,当Task执行完后将返回到Os_Dispatch<CoreID>中继续往下执行。
OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
首先调用OS_CLEAR_EE()禁止全局中断,目的为了调用OS_INTC_CPR = 55U通过中断优先级的方式禁止中断前确保临界保护,具体禁止的优先级是根据配置的最高优先级来生成的。在通过中断优先级的方式禁止中断后再通过调用 OS_SET_EE()使能全局中断开关,但此时由于优先级禁止中断,所以中断仍然不能触发。
Os_RunningTask->dynamic->activation_count--;
if (Os_RunningTask->dynamic->activation_count == 0U) {
Os_ControlledCoreInfo[0U].ReadyTasks.p0 &= ~(Os_RunningTask->pset.p0); /*lint !e931 PC-lint thinks there are side effects here */
}
activation_count:Task已正常结束一次运行,所以Task的已激活但未执行的次数记录变量activation_count需要减一次,具体可可跳转到Os_TaskDynType关于activation_count的描述。
判断activation_count次数是否已经达到0,也就是Task已激活的次数是否全部运行完,如果已经运行完了(if条件成立),则需要将将Task对应的优先级从内核已就绪的优先级中去除,否则按照Autosar OS规范,需要再次连续执行该Task(没有比Task优先级还高的Task),直到该Task全部执行完毕。
if (INVALID_TASK != Os_ChainTaskRef) {
Os_ChainTaskRef->dynamic->activation_count++;
{ /* Add to tail of fifo [$UKS 52] [$UKS 53] [$UKS 789] */
if ((void *)0 != (Os_ChainTaskRef->activation_fifo)) {
*(Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U]) = Os_ChainTaskRef; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] == (Os_ChainTaskRef->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(Os_ChainTaskRef->activation_fifo)->start : (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
}
}
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= Os_ChainTaskRef->pset.p0; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_ChainTaskRef = INVALID_TASK;
}
该处主要是判断Task是否是通过ChainTask结束的,如果通过ChainTask结束的,Os_ChainTaskRef将记录ChainTask的传入形参对应的Tsak标识,关于ChainTask可跳转到对应函数理解其逻辑,Os_ChainTaskRef可参考Os_ControlledCoreType关于ChainTaskRef的描述,Os_ChainTaskRef定义如下所示:
#define Os_ChainTaskRef (os_current_controlled_core->ChainTaskRef)
一旦if (INVALID_TASK != Os_ChainTaskRef)成立,即表示Task是否是通过ChainTask结束,即进入if成立的程序运行。
Os_ChainTaskRef->dynamic->activation_count++;
Os_ChainTaskRef对应的激活次数加1。
if ((void *)0 != (Os_ChainTaskRef->activation_fifo)) {
*(Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U]) = Os_ChainTaskRef; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] == (Os_ChainTaskRef->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(Os_ChainTaskRef->activation_fifo)->start : (Os_dyn_fifocontrol[(Os_ChainTaskRef->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
该处代码是将Os_ChainTaskRef入队到对应的调度队列,其中调度队列对应的索引标识dyn_index代表找Read指针,而dyn_index+ 1U则表示Write指针,所以该代码则是将Task插入到对应的调度队列,并将调度队列的Write指针往后偏移一个位置,如果偏移的位置超过了调度队列的最后一个位置,则将Write指针偏移到调度队列的第一个位置,至于调度队列索引逻辑不再赘述了。
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= Os_ChainTaskRef->pset.p0;
将Os_ChainTaskRef的优先级放入到内核已就绪的Task的优先级中,以便下次调度的时候可以按照优先级的策略将Task调度出来执行。
Os_ChainTaskRef = INVALID_TASK;
Os_ChainTaskRef赋值为无效Task,为下次可能调用ChainTask做准备。
while ((Os_ControlledCoreInfo[0U].ReadyTasks.p0 > previous_priority.t0)
判断do{}while()循环是否达到退出的条件,只要系统中所有的已就绪的Task存在优先级比被调度前的Task的优先级要高,则继续执行循环体,否则就退出循环体。
Os_RunningTPMask = previous_priority; /* [$UKS 77] [$UKS 78] */
Os_AppOverride = previous_appoverride;
Os_RunningTask = previous_task;
Os_AppAccess = previous_access;
函数的最后则是恢复调用Os_Dispatch<CoreID>函数前的OS运行环境,包括恢复被打断前的现场最高优先级previous_priority到Os_RunningTPMask;恢复正在执行的Application现场;恢复正在运行的Task现场;恢复正在运行的Application权限。
至此,整个Os_Dispatch<CoreID>函数已经分析完毕。总体来说,ETAS OS的调度策略还算比较容易。
关联:
- Os_CoreConfiguration
- Os_const_isrs
- TaskType
- Os_ISRWrapper<CoreID>
- ActivateTask
- ChainTask
- TerminateTask
- Os_ActivateTaskKL
- Os_PerformEP
- Os_IncrementCounter_Rte_TickCounter
Os_ISRWrapper<CoreID>:
该函数位于源文件Os_Wrapper.c内,该函数是每个核各自的二类中断总入口,内核的二类中断都要经过该函数通过函数回调的方式进入真正的中断服务函数,为每个逻辑核都生成一个属于自己的函数,函数按照Os_ISRWrapper<CoreID>的规则生成,该函数是实现内核调度的重要函数,里面包含了二类中断的实现机制,以及部分调度策略的实现,该函数通过汇编函数Os_mid_wrapper<CoreID>调用,入参为二类中断的编号,以下以核0的二类中断总入口实现为例:
Os_imaskType Os_ISRWrapper0(uint32 isr_index) {
Os_imaskType Os_old_imask;
ISRType saved_ISR;
uint8 previous_access;
P2CONST(Os_ApplicationConfigurationType, TYPEDEF, OS_VAR) app;
ApplicationType previous_appoverride;
Os_ControlledCoreType * const os_current_controlled_core = &Os_ControlledCoreInfo[0U];
previous_appoverride = Os_AppOverride; Os_AppOverride = INVALID_OSAPPLICATION; /* [$EHI 559562] */
previous_access = Os_AppAccess;
saved_ISR = Os_RunningISR;
Os_RunningISR = &Os_const_isrs[isr_index];
app = &Os_const_applications[Os_RunningISR->application];
Os_AppAccess = app->access;
Os_RunningISR->entry_function();
OS_CLEAR_EE();
OS_INTC_EOIR0 = 0U; /* Pop off the previous IPL from the LIFO into INTC_CPR */
OS_MBAR(); OS_ISYNC();
Os_old_imask = OS_INTC_CPR0;
OS_BARRIER(); OS_INTC_CPR0 = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_RunningISR = saved_ISR;
OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR0 = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
{
boolean want_dispatch;
if (0U == Os_old_imask) {
want_dispatch = (Os_ReadyTasks.p0 > Os_RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */;
if (want_dispatch) { /* [$UKS 99] */
Os_Dispatch0(); /* [$UKS 170] */
}
}
}
Os_AppAccess = previous_access;
Os_AppOverride = previous_appoverride; /* [$EHI 559562] */
return(Os_old_imask);
}
首先分析函数内部的局部变量和常量,由于上文对Os_Dispatch<CoreID>描述比较详细,本函数只对本函数或还未出现的数据进行详细描述。
Os_imaskType Os_old_imask;
ISRType saved_ISR;
uint8 previous_access;
P2CONST(Os_ApplicationConfigurationType, TYPEDEF, OS_VAR) app;
ApplicationType previous_appoverride;
Os_ControlledCoreType * const os_current_controlled_core = &Os_ControlledCoreInfo[0U];
Os_old_imask:该变量记录了被即将执行二类中断打断前的中断优先级掩码的值,以便在退出中断嵌套时能正确恢复中断优先级掩码。
saved_ISR:该变量保存了被即将执行的二类中断打断的二类中断的中断标识,以便退出中断时能正确恢复被打断的二类中断现场。
previous_access:该变量用于记录被打断前(当前正在运行)的现场的Application权限。
app:描述即将运行的二类中断所对应的Application信息描述体。
previous_appoverride:该变量记录了被打断前(当前正在运行)的现场的Application ID。
os_current_controlled_core:获取当前核(核0)运行时的状态。
previous_appoverride = Os_AppOverride; Os_AppOverride = INVALID_OSAPPLICATION; /* [$EHI 559562] */
previous_access = Os_AppAccess;
saved_ISR = Os_RunningISR;
该部分代码为记录被打断前(当前正在运行)的现场信息,具体参考上文对局部变量的描述。
Os_RunningISR = &Os_const_isrs[isr_index];
app = &Os_const_applications[Os_RunningISR->application];
Os_AppAccess = app->access;
该部分代码为即将执行的二类中断准备OS环境:
Os_RunningISR:该变量记录了当前正在运行的二类中断标识,其定义如下所示,其定义参考Os_ControlledCoreType的成员RunningISR的描述。
#define Os_RunningISR (os_current_controlled_core->RunningISR)
app:该变量记录为该二类中断所对应的Application标识。
Os_AppAccess:将OS运行时的Application权限修改为该二类中断所对应的Application权限。
Os_RunningISR->entry_function();
通过函数回调的方式执行二类中断的中断服务函数,具体描述参考ISRType的entry_function成员信息描述。
OS_INTC_EOIR0 = 0U; /* Pop off the previous IPL from the LIFO into INTC_CPR */
OS_MBAR(); OS_ISYNC();
Os_old_imask = OS_INTC_CPR0;
OS_BARRIER(); OS_INTC_CPR0 = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
OS_INTC_EOIR0:在SPC58NN中的作用是结束中断,往对应寄存器写任意值即可结束当前中断,具体参考Os_CoreConfiguration的成员intc_eoir描述信息。
Os_old_imask:记录内核的中断优先级等级,在SPC58NN中,操作OS_INTC_EOIR0对应的寄存器后,中断控制器(INTC)将会把中断从FILO中弹出,同时对应的中断优先级掩码INTC_CPR也会从FILO中弹出,所以后文将会根据该值判断接下来的现场是中断还是Task现场。
记录完当前的INTC_CPR后,将会已中断优先级掩码的形式禁止所有外设中断,然后使能全局中断。
Os_RunningISR = saved_ISR;
OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR0 = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
从之前记录的saved_ISR中恢复当前正在运行的二类中断标识。
boolean want_dispatch;
if (0U == Os_old_imask) {
want_dispatch = (Os_ReadyTasks.p0 > Os_RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */;
if (want_dispatch) { /* [$UKS 99] */
Os_Dispatch0(); /* [$UKS 170] */
}
}
判断Os_old_imask是否为0,也就是判断当前是否还有中断未被处理完成,如果所有中断都被处理完成,则记录INTC_CPR的Os_old_imask应该为0,此时则应该恢复现场到Task,当然在切换现场前需要先判断OS记录的所有已就绪的Task的优先级有没有高于当前正在运行的Task的优先级,有则进入Os_Dispatch<CoreID>进行调度处理,否则则通过返回Os_mid_wrapper<CoreID>函数去恢复被打断的现场。
Os_AppAccess = previous_access;
Os_AppOverride = previous_appoverride; /* [$EHI 559562] */
恢复二类中断打断前的现场,依次恢复被打断的现场的Application权限和Application ID。
return(Os_old_imask);
返回Os_mid_wrapper<CoreID>函数,并返回即将触发/恢复的中断优先级,一次在Os_mid_wrapper<CoreID>中判断是调度到Task现场还是去中断现场,0则是Task现场。
关联:
Os_mid_wrapper<CoreID>:
该函数位于汇编文件Os_Vectors.s内,该函数是每个核各自的二类中断的汇编总入口,内核的二类中断都要经过该汇编函数保存现场,调用Os_ISRWrapper<CoreID>进入二类中断总入口,再恢复现场,为每个逻辑核都生成一个属于自己的函数,函数按照Os_mid_wrapper<CoreID>的规则生成,该函数有中断机制通过中断向量表进入,以下以核0的二类中断汇编总入口实现为例:
Os_mid_wrapper0:
; Save interrupt context following the EABI
; Note: 8 bytes are reserved by the called function in order to store the back chain and link register
se_stw %r0, 8(%r1) ; Preserve scratch register
mfspr %r0, srr0 ; SRR0,1 contain ISR return context.
se_stw %r0, 12(%r1) ; Push it to allow nested interrupts
mfspr %r0, srr1
se_stw %r0, 16(%r1)
se_stw %r4, 44(%r1) ; Preserve %r4
se_stw %r5, 48(%r1) ; Preserve %r5
se_stw %r6, 52(%r1) ; Preserve %r6
se_bclri %r0, OS_PR_BIT_POS ; Make sure we stay in supervisor mode
mtmsr %r0 ; Restore pre-interrupted msr
; (i.e. set EE bit and SPE bit if enabled)
; Blocking of Cat 1 ends here
mfcr %r0
se_stw %r0, 28(%r1) ; Save CR
mfspr %r0, ctr ; Save the non GPR regs
se_stw %r0, 20(%r1)
mfspr %r0, xer
se_stw %r0, 24(%r1)
mfspr %r0, lr
se_stw %r0, 32(%r1)
; %r3, %r4, %r6 already stored
; [$TargetGHS 255]
se_stw %r7, 56(%r1) ; %r7
e_stw %r8, 60(%r1)
e_stw %r9, 64(%r1)
e_stw %r10, 68(%r1)
e_stw %r11, 72(%r1)
e_stw %r12, 76(%r1)
e_bl Os_ISRWrapper0
; return from inner wrapper with %r3 holding the old imask
; Restore most of the context
se_lwz %r0, 32(%r1) ; Restore the LR with a load inserted
e_lwz %r12, 76(%r1)
mtspr lr, %r0
e_lwz %r11, 72(%r1)
e_lwz %r10, 68(%r1)
e_lwz %r9, 64(%r1)
e_lwz %r8, 60(%r1)
se_lwz %r7, 56(%r1)
se_lwz %r6, 52(%r1)
se_lwz %r0, 28(%r1) ; Restore the CRF with a load inserted
mtcrf 0xff, %r0
se_lwz %r0, 24(%r1)
mtspr xer, %r0
se_lwz %r0, 20(%r1)
mtspr ctr, %r0
; Cat 1 blocking starts here:
wrteei 0 ; Clear EE bit
; Return the IPL level to that before the interrupt triggered
e_lis %r4,%hiadj(0xF4044018)
e_stw %r3,%lo(0xF4044018)(%r4)
se_lwz %r5, 48(%r1)
se_lwz %r4, 44(%r1)
se_lwz %r3, 40(%r1)
; Restore the remaining context
se_lwz %r0, 16(%r1) ; Restore SRR0/1
mtspr srr1, %r0
se_lwz %r0, 12(%r1)
mtspr srr0, %r0
se_lwz %r0, 8(%r1)
e_add16i %r1, %r1, 80 ; Restore the SP
se_rfi ; Return from interrupt - Cat 1 blocking ends
.global Os_ISRWrapper1
.global Os_mid_wrapper1
.align 2
由于不同芯片此部分处理将会存在差异,因此此部分汇编不具体描述,只描述二类中断处理流程和关键地方,如有兴趣可自行研究保存和恢复现场的过程,对OS的现场切换机制将大有脾益。SPC58NN的二类中断的处理流程为,外设中断触发->硬件根据中断向量表(Os_INTC_vectors)跳转到对应的中断入口函数(Os_INTCHandler_xxx)->进入二类中断汇编总入口(Os_mid_wrapper<CoreID>)->进入二类中断总入口(Os_ISRWrapper<CoreID>)->进入二类中断服务函数(entry_function)->返回到Os_ISRWrapper<CoreID>->返回到Os_mid_wrapper<CoreID->返回被打断的现场。
Os_INTCHandler_xxx函数的定义如下所示:
Os_INTCHandler_226:
e_stwu %r1, -80(%r1)
se_stw %r3, 40(%r1)
e_li %r3, 0
e_b Os_mid_wrapper0
在Os_INTCHandler_xxx中前对栈指针SP(R1)和通用寄存器R3保存在当前的栈中,然后将对应中断的index(工具中配置的二类中断的顺序)赋值给R3(e_li %r3, 0)作为Os_ISRWrapper<CoreID>的入参。
关联:
ActivateTask:
ActivateTask是Autosar OS中规定用于手动激活Task的API服务,该函数位于ETAS OS的ActivateTask.c文件中,该函数定义如下所示:
FUNC(StatusType, OS_CODE) Os_ActivateTask(TaskType TaskID) {
StatusType api_retval = E_OK;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U) | (Os_OrtiIdType)1U); /* [$UKS 61] [$UKS 790] [$UKS 63] [$UKS 51] [$UKS 166] [$UKS 1530] [$UKS 1531] */
if (!(Os_IsTaskValid(TaskID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 792] Error if the task is not valid */
/* [$UKS 793] [$UKS 1528] */
if ((0U == (TaskID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; }
if (!(Os_const_coreconfiguration[TaskID->core_id].controlled->CoreIsActive)) { api_retval = E_OS_CORE; goto api_exit; } /* [$UKS 1529] */
switch (TaskID->core_id) {
case 0U:
/* No Tasks on core 0 are accessible from other cores. Activation can only be done from the owning core. */
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 */
if (0U != os_current_core_const->core_id) { api_retval = E_OS_ACCESS; goto api_exit; }
/*lint -restore */
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if (TaskID->dynamic->activation_count == TaskID->activation_count) { api_retval = E_OS_LIMIT; goto api_exit; } /* [$UKS 791] [$UKS 64] */
/*lint -restore */
/* [$UKS 65] */
TaskID->dynamic->activation_count++;
{ /* Add to tail of fifo [$UKS 52] [$UKS 53] [$UKS 789] */
if ((void *)0 != (TaskID->activation_fifo)) {
*(Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U]) = TaskID; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] == (TaskID->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(TaskID->activation_fifo)->start : (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
}
}
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= TaskID->pset.p0; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
{
boolean want_dispatch = (Os_ControlledCoreInfo[0U].ReadyTasks.p0 > Os_ControlledCoreInfo[0U].RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */;
if ((32768U == (previous_imask)) && want_dispatch) {
Os_Dispatch0();
}
}
break;
case 1U:
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[1U].lock_taskaccess)) { /* spin */ }
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if ((0U != (Os_ControlledCoreInfo[1U].ReadyTasks.p1 & TaskID->pset.p1))) { api_retval = E_OS_LIMIT; Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess); goto api_exit; } /* [$UKS 791] [$UKS 64] */
/*lint -restore */
/* [$UKS 65] */
Os_ControlledCoreInfo[1U].ReadyTasks.p1 |= TaskID->pset.p1; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
{
boolean want_dispatch = (Os_ControlledCoreInfo[1U].ReadyTasks.p1 > Os_ControlledCoreInfo[1U].RunningTPMask.t1) /*lint !e931 PC-lint thinks there are side effects here */;
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
if (os_current_core_const->core_id == 1U) {
if ((32768U == (previous_imask)) && want_dispatch) {
Os_Dispatch1();
}
} else {
Os_RaiseCrossCoreISR(1U);
}
}
break;
case 2U:
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[2U].lock_taskaccess)) { /* spin */ }
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if ((0U != (Os_ControlledCoreInfo[2U].ReadyTasks.p2 & TaskID->pset.p2))) { api_retval = E_OS_LIMIT; Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess); goto api_exit; } /* [$UKS 791] [$UKS 64] */
/*lint -restore */
/* [$UKS 65] */
Os_ControlledCoreInfo[2U].ReadyTasks.p2 |= TaskID->pset.p2; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
{
boolean want_dispatch = (Os_ControlledCoreInfo[2U].ReadyTasks.p2 > Os_ControlledCoreInfo[2U].RunningTPMask.t2) /*lint !e931 PC-lint thinks there are side effects here */;
Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess);
if (os_current_core_const->core_id == 2U) {
if ((32768U == (previous_imask)) && want_dispatch) {
Os_Dispatch2();
}
} else {
Os_RaiseCrossCoreISR(2U);
}
}
break;
default:
/* No code needed here for the current configuration */;
break;
}
api_exit:
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U);
return api_retval;
}
首先分析函数中的局部变量/常量,由于上文对部分变量已经有较为详细的说明,此处只对之前未出现的变量进行较为详细的说明。
StatusType api_retval = E_OK;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
api_retval:该变量记录了调用OS API接口时产生的错误,如果API接口调用正常,则返回E_OK,否则返回其他异常码,用户可以根据返回值来判断API的执行情况。
previous_imask:记录调用该接口前的系统中断屏蔽状态。
os_current_core_const:获取当前核的配置描述信息控制块,参考Os_const_coreconfiguration。其中OS_MFPMR(384U)为ETAS OS在SPC58NN中实现获取CoreId的方法。
os_current_controlled_core:获取当前核运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
os_current_core:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U) | (Os_OrtiIdType)1U);
该处代码使用previous_imask记录了调用接口前的系统中断屏蔽状态,以及通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID定义如下所示,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。
#define Os_OrtiApiID (os_current_core->OrtiApiID)
if (!(Os_IsTaskValid(TaskID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 792] Error if the task is not valid */
/* [$UKS 793] [$UKS 1528] */
if ((0U == (TaskID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; }
if (!(Os_const_coreconfiguration[TaskID->core_id].controlled->CoreIsActive)) { api_retval = E_OS_CORE; goto api_exit; }
该部分代码主要是对调用该接口时的运行时环境检查,从上到下一次是:
检查TaskID标识是否有效,如出现未配置的TaskID标识,则返回E_OS_ID错误;
检查当前Application是否具有操作该Task的权限,如果没有则返回E_OS_ACCESS错误;
检查目标Task所在核是否处于有效激活状态,如不是处于有效激活状态,则返回E_OS_CORE错误。
ETAS OS会通过switch语法根据Task对应的目标核ID,进入不同的处理分支,且不同的处理分支会根据不同的核的Task配置情况来生成不同的代码,但底层逻辑都差不多,只需要读懂一个核的处理逻辑,其他核相信问题也不大,这里以核0的处理为例,代码如下所示:
if (0U != os_current_core_const->core_id) { api_retval = E_OS_ACCESS; goto api_exit; }
/*lint -restore */
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if (TaskID->dynamic->activation_count == TaskID->activation_count) { api_retval = E_OS_LIMIT; goto api_exit; } /* [$UKS 791] [$UKS 64] */
/*lint -restore */
/* [$UKS 65] */
TaskID->dynamic->activation_count++;
{ /* Add to tail of fifo [$UKS 52] [$UKS 53] [$UKS 789] */
if ((void *)0 != (TaskID->activation_fifo)) {
*(Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U]) = TaskID; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] == (TaskID->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(TaskID->activation_fifo)->start : (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
}
}
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= TaskID->pset.p0; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
{
boolean want_dispatch = (Os_ControlledCoreInfo[0U].ReadyTasks.p0 > Os_ControlledCoreInfo[0U].RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */;
if ((32768U == (previous_imask)) && want_dispatch) {
Os_Dispatch0();
}
}
该部分代码一开始对内核进行验证,判断当前核是否是核0(注意,这里是以核0为例,且配置限制了不可能由其他核去激活核0的Task,ETAS OS才会生成该判断),否则则返回E_OS_ACCESS错误;
接着判断了目标Task当前激活次数是否超过了激活上限,如果超过了则返回E_OS_LIMIT错误,这是Autosar OS规定的错误检查;
如果一切检查均正常,Task的动态参数记录的激活次数则会+1;接着便是将Task添加到对应的调度队列中,此部分代码逻辑请参考Os_Dispatch<CoreID>中关于Os_ChainTaskRef激活操作;
目标Task正常入队后则会将目标Task的优先级更新到系统当前现场最高优先级当中,操作逻辑也可以参考Os_Dispatch<CoreID>中关于Os_ChainTaskRef激活操作。
最后判断当前当前已就绪的所有task的优先级是否已经大于当前正在运行现场的优先级,如果大于并且调用Api前系统是处于使能中断的情况下,则触发调度进入Os_Dispatch<CoreID>中执行调度。
此处对于调用API前系统是否处于使能中断的情况的判断略微说明以下,前文已经对previous_imask做了简单介绍,保存的是API前系统是否处于使能中断的情况,但其实该变量保存了两个寄存器对应的值,一个是MSR.EE,该寄存器在SPC58NN中是全局中断使能位,该位为1标识全局中断以使能,保存到previous_imask的掩码为OS_EE_BIT(0x8000U),也就是当previous_imask&0x8000U的值为非0时,代表着全局中断使能,而低16位记录着中断控制INTC_CPR的值,该寄存器前文已有描述,表示中断优先级屏蔽寄存器,当该寄存器为0时,则不屏蔽任何优先级的中断,所i代码中当previous_imask==32768U(0x8000U)时表示调用API前中断不处于禁止状态,则可以进行系统调度。
该函数最后一部分则是恢复系统中断屏蔽状态以及将Os_OrtiApiID的值修改成退出API的状态,代码如下所示:
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U);
关联:
ChainTask:
ChainTask是Autosar OS中规定用于结束当前Task并激活目标Task的API服务,该函数位于ETAS OS的ChainTask.c文件中,该函数定义如下所示:
FUNC(StatusType, OS_CODE) Os_ChainTask(TaskType TaskID) {
StatusType api_retval = E_OK;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ChainTask) << 1U) | (Os_OrtiIdType)1U); /* [$UKS 51] [$UKS 73] [$UKS 74] [$UKS 75] [$UKS 76] [$UKS 79] [$UKS 80] [$UKS 165] [$UKS 835] [$UKS 172] [$UKS 1533] */
/* Error if the task is not valid */
if (!(Os_IsTaskValid(TaskID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 798] */
if ((INVALID_TASK == Os_RunningTask)) { api_retval = E_OS_ID; goto api_exit; }
if (!(Os_const_coreconfiguration[TaskID->core_id].controlled->CoreIsActive)) { api_retval = E_OS_CORE; goto api_exit; } /* [$UKS 1534] */
if ((0U == (TaskID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; } /* [$UKS 801] */
if (Os_RunningTask->dynamic->resource_count != 0U) { api_retval = E_OS_RESOURCE; goto api_exit; } /* [$UKS 799] */
/* [$UKS 800] Call at interrupt level, E_OS_CALLEVEL */
if (!((32768U == (previous_imask)))) { api_retval = E_OS_CALLEVEL; goto api_exit; }
if (TaskID != Os_RunningTask) {
switch (TaskID->core_id) {
case 0U:
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if (TaskID->dynamic->activation_count == TaskID->activation_count) { api_retval = E_OS_LIMIT; goto api_exit; }
/*lint -restore */
break;
case 1U:
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[1U].lock_taskaccess)) { /* spin */ }
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if ((0U != (Os_ControlledCoreInfo[1U].ReadyTasks.p1 & TaskID->pset.p1))) { api_retval = E_OS_LIMIT; Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess); goto api_exit; }
/*lint -restore */
if (os_current_core_const->core_id != TaskID->core_id) {
Os_ControlledCoreInfo[1U].ReadyTasks.p1 |= TaskID->pset.p1; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
Os_RaiseCrossCoreISR(1U);
} else {
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
}
break;
case 2U:
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[2U].lock_taskaccess)) { /* spin */ }
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if ((0U != (Os_ControlledCoreInfo[2U].ReadyTasks.p2 & TaskID->pset.p2))) { api_retval = E_OS_LIMIT; Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess); goto api_exit; }
/*lint -restore */
if (os_current_core_const->core_id != TaskID->core_id) {
Os_ControlledCoreInfo[2U].ReadyTasks.p2 |= TaskID->pset.p2; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess);
Os_RaiseCrossCoreISR(2U);
} else {
Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess);
}
break;
default:
/* No code needed here for the current configuration */;
break;
}
}
if (INVALID_TASK != Os_RunningTask) {
if (TaskID->core_id == os_current_core_const->core_id) {
Os_ChainTaskRef = TaskID;
}
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ChainTask) << 1U);
Os_longjmp(&(Os_RunningTask->dynamic->terminate_jump_buf),1); /*lint !e545 Address of what might be an array */
}
api_exit:
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ChainTask) << 1U);
return api_retval;
}
首先分析函数中的局部变量/常量,由于上文对部分变量已经有较为详细的说明,此处只对之前未出现的变量进行较为详细的说明。
StatusType api_retval = E_OK;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
api_retval:该变量记录了调用OS API接口时产生的错误,如果API接口调用正常,则返回E_OK,否则返回其他异常码,用户可以根据返回值来判断API的执行情况。
previous_imask:记录调用该接口前的系统中断屏蔽状态。
os_current_core_const:获取当前核的配置描述信息控制块,参考Os_const_coreconfiguration。其中OS_MFPMR(384U)为ETAS OS实现获取CoreId的方法。
os_current_controlled_core:获取当前核运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
os_current_core:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ChainTask) << 1U) | (Os_OrtiIdType)1U);
该处代码使用previous_imask记录了调用接口前的系统中断屏蔽状态,以及通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。
if (!(Os_IsTaskValid(TaskID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 798] */
if ((INVALID_TASK == Os_RunningTask)) { api_retval = E_OS_ID; goto api_exit; }
if (!(Os_const_coreconfiguration[TaskID->core_id].controlled->CoreIsActive)) { api_retval = E_OS_CORE; goto api_exit; } /* [$UKS 1534] */
if ((0U == (TaskID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; } /* [$UKS 801] */
if (Os_RunningTask->dynamic->resource_count != 0U) { api_retval = E_OS_RESOURCE; goto api_exit; } /* [$UKS 799] */
/* [$UKS 800] Call at interrupt level, E_OS_CALLEVEL */
if (!((32768U == (previous_imask)))) { api_retval = E_OS_CALLEVEL; goto api_exit; }
该部分代码主要是对调用该接口时的运行时环境检查,从上到下一次是:
检查TaskID标识是否有效,如出现未配置的TaskID标识,则返回E_OS_ID错误;
检查当前正在运行的Task(Os_RunningTask)标识是否有效,如果无效则返回E_OS_ID错误;
检查目标Task所在核是否处于有效激活状态,如不是处于有效激活状态,则返回E_OS_CORE错误;
检查当前Application是否具有操作该Task的权限,如果没有则返回E_OS_ACCESS错误;
检查当前正在运行的Task(Os_RunningTask)是否已经是否了所有已占有的资源(Resource),如存在未释放的资源则返回E_OS_RESOURCE错误,resource_count的说明可以参考Os_TaskDynType对于resource_count的描述;
检查当前现场是存在禁止中断的情况,如果存在则返回E_OS_CALLEVEL。
OS接下来会判断当前正在运行(即将被结束)的Task是否时需要执行的目标Task,如果不是则根据即将激活的目标Task所对应的逻辑核ID进入不同的switch执行体,这里以核1的处理为例。
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[1U].lock_taskaccess)) { /* spin */ }
/* [MISRA 2012 Rule 16.1] */ /*lint -save -e9042 -e9008 */
if ((0U != (Os_ControlledCoreInfo[1U].ReadyTasks.p1 & TaskID->pset.p1))) { api_retval = E_OS_LIMIT; Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess); goto api_exit; }
/*lint -restore */
if (os_current_core_const->core_id != TaskID->core_id) {
Os_ControlledCoreInfo[1U].ReadyTasks.p1 |= TaskID->pset.p1; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
Os_RaiseCrossCoreISR(1U);
} else {
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
}
由于核1的Task在配置的时候有配置其他核的App使用的权限,因此,为了保证操作的原子性,需要在操作之前对多核可能同时访问的数据进行OS内部自旋锁上锁操作,即调用Os_TestAndSet接口,该接口和下文释放自旋锁的接口Os_TestReset实现如下所示,此处不做额外解释,关于Os_ControlledCoreInfo[1U].lock_taskaccess的描述参考Os_ControlledCoreType类型关于lock_taskaccess的描述。
Os_Lockable Os_TestAndSet(Os_Lockable volatile *the_lock) {
Os_Lockable prev_value;
OS_BARRIER(); prev_value = __LWDCBX((const unsigned int *)the_lock /* Address */, 0x50000001U /* Decoration */); OS_BARRIER();
OS_ISYNC();
if (0U != prev_value) {
/* Failed to get lock. Don't block other cores by hitting the same code too often */
OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();OS_NOP();
}
return prev_value;
}
/******************************************************************************/
void Os_TestReset(Os_Lockable volatile *the_lock) {
OS_MSYNC();
OS_BARRIER(); __STWDCBX((unsigned int *)the_lock /* Address */, 0x0U /* Decoration */, 0x0U /* Lock value */); OS_BARRIER();
}
成功获取自旋锁后判断即将激活运行的Task的优先级是否已存在于系统的就绪队列中,如存在则返回E_OS_LIMIT错误,Autosar中并没有对该判断的说明,作者猜想ETAS OS期望用这种方式判断T目标Task的已激活但未执行的次数是否已超过限制,但这需要条件,就是配置该Task的最大允许激活次数必须为1。后续如作者发现此处描述有误将会进行修正。
然后判断调用方的逻辑核ID和目标Task的核ID是否一致,如果不一致则需要通过激活多核服务中断(Os_RaiseCrossCoreISR)来实现该功能,在激活多核服务中断之前还需要将目标Task的优先级加入到对应核的就绪Task的优先级中,并释放自旋锁,这样才有可能通过多核服务中断,调度出该Task 出来执行(按照优先级的策略);如果双方核ID一致,则不需要这么麻烦,只需要释放自旋锁就可以了,因为具体的操作将在Os_Dispatch<CoreID>中完成。
等按照期望激活的目标Task按照逻辑核ID处理完switch分支结构后,则可以进行接下来的动作,如下所示:
if (INVALID_TASK != Os_RunningTask) {
if (TaskID->core_id == os_current_core_const->core_id) {
Os_ChainTaskRef = TaskID;
}
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ChainTask) << 1U);
Os_longjmp(&(Os_RunningTask->dynamic->terminate_jump_buf),1); /*lint !e545 Address of what might be an array */
}
首先判断当前正在允许的Task是否时有效Task,接着判断调用方的核和目标的核ID是否一致,一致才更新当前核的Os_ChainTaskRef以便在Os_Dispatch<CoreID>中完成后继Task激活操作,不一致则通过上文中的多核服务中断解决相关逻辑。然后更新Os_OrtiApiID的值为退出对应API的状态,调用Os_longjmp接口退出当前Task,恢复到Os_Dispatch<CoreID>现场中。一般情况下,API如果能正确运行,Task运行到这后就结束了,接下来的代码不会再运行,但ETAS OS为了考虑到API执行出错和了系统的完整性,在后面还是做了恢复中断屏蔽状态和退出对应API的动作,代码如下所示:
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_ChainTask) << 1U);
return api_retval;
关联:
TerminateTask:
Autosar OS规定,Task必须以调用ChainTask/TerminateTask结束,上文中已经分析了ETAS OS对于ChainTask的实现逻辑,接下来分析一下TerminateTask的实现逻辑,ETAS OS的TerminateTask实现放在TerminateTask.c文件中,函数定义如下所示:
FUNC(StatusType, OS_CODE) Os_TerminateTask(void) {
StatusType api_retval = E_OK;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_TerminateTask) << 1U) | (Os_OrtiIdType)1U); /* [$UKS 66] [$UKS 67] [$UKS 68] [$UKS 71] [$UKS 72] [$UKS 69] [$UKS 794] [$UKS 164] [$UKS 200] [$UKS 171] */
if (INVALID_ISR != Os_RunningISR) {
api_retval += E_OS_CALLEVEL;
} else if (INVALID_TASK != Os_RunningTask) {
if (Os_RunningTask->dynamic->resource_count!= 0U) { api_retval = E_OS_RESOURCE; goto api_exit; } /* [$UKS 795] */
/* [$UKS 796] Call at interrupt level, E_OS_CALLEVEL */
if (!((32768U == (((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR))))) { api_retval = E_OS_CALLEVEL; goto api_exit; }
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_TerminateTask) << 1U);
Os_longjmp(&(Os_RunningTask->dynamic->terminate_jump_buf),1); /* [$UKS 70] */ /*lint !e545 Address of what might be an array */
} else { /* Neither task nor ISR is running */ }
api_exit:
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_TerminateTask) << 1U);
return api_retval;
} /* TerminateTask */
相对于ChainTask来说,TerminateTask在ETAS OS中的实现就要简单很多,按照惯例,首先分析一下函数内部局部变量/常量。
api_retval:该变量记录了调用OS API接口时产生的错误,如果API接口调用正常,则返回E_OK,否则返回其他异常码,用户可以根据返回值来判断API的执行情况。
os_current_core_const:获取当前核的配置描述信息控制块,参考Os_const_coreconfiguration。其中OS_MFPMR(384U)为ETAS OS实现获取CoreId的方法。
os_current_controlled_core:获取当前核运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
os_current_core:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
接着通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。
判断当前是否存在二类中断的运行,其实也是判断当前是否处于二类中断的现场,如果当前正在与运行的二类中断无效值,则需要返回错误E_OS_CALLEVEL;
然后判断玩当前正运行的Task不是无效值后(理论上不在在Task现场调用该API就不会出现Os_RunningTask==INVALID_TASK的情况),检查需要被结束的Task是否已经是否所有持有的资源(Resource),如果存在未释放的资源则需要返回错误E_OS_RESOURCE;
检查当前现场的中断状态是否处于释放状态,否则需要返回错误E_OS_CALLEVEL;
然后就可以更新Os_OrtiApiID的值为退出对应API的状态,调用Os_longjmp接口退出当前Task,恢复到Os_Dispatch<CoreID>现场中。一般情况下,API如果能正确运行,Task运行到这后就结束了,接下来的代码不会再运行,但ETAS OS为了考虑到API执行出错和了系统的完整性,在后面还是做了恢复中断屏蔽状态和退出对应API的动作,代码如下所示:
api_exit:
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_TerminateTask) << 1U);
return api_retval;
关联:
Os_ActivateTaskKL:
Os_ActivateTaskKL是ETAS OS内部自定义的一个用于激活Task的函数,配置了自启动的Task将会在OS内部通过StartOS调用该接口激活,还有一个作用就是在Alarm或调度表到期的时候会通过该函数去激活对应的Task,该函数与ActivateTask不同的是不会直接进入Os_Dispatch<CoreID>中执行调度,该函数不会直接去激活。该接口位于Os_Cfg_KL.c内,定义如下所示:
FUNC(void, OS_CODE) Os_ActivateTaskKL(TaskType TaskID, const Os_CoreConfiguration *os_current_core_const, Os_ControlledCoreType *os_current_controlled_core) {
Os_AnyCoreType *os_current_core = os_current_core_const->any;
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U) | (Os_OrtiIdType)1u);
switch (TaskID->core_id) {
case 0U:
if (!os_current_controlled_core->CoreIsActive) {
break; /* Early break */
}
if (TaskID->dynamic->activation_count == TaskID->activation_count) { /* [$UKS 1897] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U));
return; /* [MISRA 2012 Rule 15.5] */ /*lint !e904 */
} /* [$UKS 791] [$UKS 64] */
TaskID->dynamic->activation_count++;
{ /* Add to tail of fifo [$UKS 52] [$UKS 53] [$UKS 789] */
if ((void *)0 != (TaskID->activation_fifo)) {
*(Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U]) = TaskID; /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 */
Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] = (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] == (TaskID->activation_fifo)->end) ? /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e931 !e564 */
(TaskID->activation_fifo)->start : (Os_dyn_fifocontrol[(TaskID->activation_fifo)->dyn_index + 1U] + 1U); /* [MISRA 2012 Rule 18.5] */ /*lint !e679 !e9025 !e9016 */
}
}
Os_ControlledCoreInfo[0U].ReadyTasks.p0 |= TaskID->pset.p0; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
/* No Tasks on core 0 are accessible from other cores. Activation can only be done from the owning core. */
break;
case 1U:
if (!os_current_controlled_core->CoreIsActive) {
break; /* Early break */
}
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[1U].lock_taskaccess)) { /* spin */ }
if ( (0U != (Os_ControlledCoreInfo[1U].ReadyTasks.p1 & TaskID->pset.p1))) { /* [$UKS 1897] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U));
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
return; /* [MISRA 2012 Rule 15.5] */ /*lint !e904 */
} /* [$UKS 791] [$UKS 64] */
Os_ControlledCoreInfo[1U].ReadyTasks.p1 |= TaskID->pset.p1; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
if ( (os_current_core_const->core_id != 1U) && (*os_current_core_const->state > 4U) ) { /* [$UKS 1559] Cross core activation can happen here only after StartOS */
Os_RaiseCrossCoreISR(1U);
}
break;
case 2U:
if (!os_current_controlled_core->CoreIsActive) {
break; /* Early break */
}
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[2U].lock_taskaccess)) { /* spin */ }
if ( (0U != (Os_ControlledCoreInfo[2U].ReadyTasks.p2 & TaskID->pset.p2))) { /* [$UKS 1897] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U));
Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess);
return; /* [MISRA 2012 Rule 15.5] */ /*lint !e904 */
} /* [$UKS 791] [$UKS 64] */
Os_ControlledCoreInfo[2U].ReadyTasks.p2 |= TaskID->pset.p2; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[2U].lock_taskaccess);
if ( (os_current_core_const->core_id != 2U) && (*os_current_core_const->state > 4U) ) { /* [$UKS 1559] Cross core activation can happen here only after StartOS */
Os_RaiseCrossCoreISR(2U);
}
break;
default:
/* No code needed here for the current configuration */;
break;
}
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << (Os_OrtiIdType)1U));
}
首先分析一下使用该接口使用的局部变量/常量,分别是:
TaskID:期望别激活的目标Task标识,作为形参传进来;
os_current_core_const:获取当前核的配置描述信息控制块,参考Os_const_coreconfiguration。作为形参传进来;
os_current_controlled_core:获取当前核运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。作为形参传进来;
os_current_core:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
接着通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。作者分析虽然该函数不是ActivateTask,但起到的作用与ActivateTask的作用一致,因此记录的OS服务就是OSServiceId_ActivateTask的值。
该函数与ActivateTask一样,将根据目标激活Task所对应的逻辑核ID分别执行不同的switch分支,这里以核1的分支为例介绍函数实现逻辑,如下所示:
if (!os_current_controlled_core->CoreIsActive) {
break; /* Early break */
}
while (0U != Os_TestAndSet(&Os_ControlledCoreInfo[1U].lock_taskaccess)) { /* spin */ }
if ( (0U != (Os_ControlledCoreInfo[1U].ReadyTasks.p1 & TaskID->pset.p1))) { /* [$UKS 1897] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_ActivateTask) << 1U));
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
return; /* [MISRA 2012 Rule 15.5] */ /*lint !e904 */
} /* [$UKS 791] [$UKS 64] */
Os_ControlledCoreInfo[1U].ReadyTasks.p1 |= TaskID->pset.p1; /*lint !e931 PC-lint thinks there are side effects here */ /* [$UKS 62] */
Os_TestReset(&Os_ControlledCoreInfo[1U].lock_taskaccess);
if ( (os_current_core_const->core_id != 1U) && (*os_current_core_const->state > 4U) ) { /* [$UKS 1559] Cross core activation can happen here only after StartOS */
Os_RaiseCrossCoreISR(1U);
}
首先判断目标核是否处于优先激活状态,如不处于则直接退出switch分支操作;
而接下来的代码逻辑则可以参考ChainTask激活Task的逻辑,不同的是,不管调用方的核ID和目标的核ID是否一致,Os_ActivateTaskKL函数都会将需要激活的Task的优先级添加都内核就绪Task的优先级中。
退出switch分支后,整个函数逻辑也就运行完了,剩下的就是操作Os_OrtiApiID退出对应的API动作。
关联:
Os_setjmp:
Os_setjmp是ETAS OS切换Task前保存现场的重要函数,该函数也是移植ETAS OS到不同的硬件平台和编译平台的核心函数,因此理解该函数对于ETAS OS的平台移植非常重要。该函数位于汇编文件Os_setjmp中,函数定义如下所示:
Os_setjmp:
se_mflr %r4 ; Store link register
se_stw %r4, 8(%r3)
; Store non-scratch registers
se_stw %r1, 12(%r3) ; Store the stack pointer r1 (we do not need to store r1 as 64 bit)
se_stw %r2, 16(%r3)
e_stw %r13, 20(%r3)
e_stw %r14, 24(%r3)
e_stw %r15, 28(%r3)
e_stw %r16, 32(%r3)
e_stw %r17, 36(%r3)
e_stw %r18, 40(%r3)
e_stw %r19, 44(%r3)
e_stw %r20, 48(%r3)
e_stw %r21, 52(%r3)
e_stw %r22, 56(%r3)
e_stw %r23, 60(%r3)
e_stw %r24, 64(%r3)
e_stw %r25, 68(%r3)
e_stw %r26, 72(%r3)
e_stw %r27, 76(%r3)
e_stw %r28, 80(%r3)
e_stw %r29, 84(%r3)
e_stw %r30, 88(%r3)
e_stw %r31, 92(%r3)
se_mfctr %r4 ; Store count register
se_stw %r4, 0(%r3)
se_lwz %r7, 0(%r1) ; Store the value pointed to by the stack pointer
se_stw %r7, 4(%r3) ; (the previous stack frame)
se_li %r3, 0x0 ; Set return value to 0
se_blr
注意,不同硬件平台和不同编译平台对该函数的实现存在差异,因此这里不对该函数进行过分解读,只对该函数的调用方式和实现内容进行分析。
该函数用于Os_Dispatch<CoreID>中激活一个新的Task前需要调用Os_setjmp接口去保存进入目标Task现场前的现场信息,一般需要保存内核的通用寄存器,程序状态字以及部分OS所需要的特殊寄存器等,该函数的返回值为0(函数的倒数第二行“se_li %r3, 0x0”),返回值是0的原因请参考Os_Dispatch<CoreID>中对此部分内容的描述。
关联:
Os_longjmp:
Os_longjmp是ETAS OS结束Task后恢复现场到Os_Dispatch<CoreID>中的重要函数,该函数也是移植ETAS OS到不同的硬件平台和编译平台的核心函数,因此理解该函数对于ETAS OS的平台移植非常重要。该函数位于汇编文件Os_setjmp中,函数定义如下所示:
Os_longjmp:
se_lwz %r5, 8(%r3) ; Restore the link register
se_mtlr %r5
; Restore the non-scratch registers
se_lwz %r2, 16(%r3)
e_lwz %r13, 20(%r3)
e_lwz %r14, 24(%r3)
e_lwz %r15, 28(%r3)
e_lwz %r16, 32(%r3)
e_lwz %r17, 36(%r3)
e_lwz %r18, 40(%r3)
e_lwz %r19, 44(%r3)
e_lwz %r20, 48(%r3)
e_lwz %r21, 52(%r3)
e_lwz %r22, 56(%r3)
e_lwz %r23, 60(%r3)
e_lwz %r24, 64(%r3)
e_lwz %r25, 68(%r3)
e_lwz %r26, 72(%r3)
e_lwz %r27, 76(%r3)
e_lwz %r28, 80(%r3)
e_lwz %r29, 84(%r3)
e_lwz %r30, 88(%r3)
e_lwz %r31, 92(%r3)
se_lwz %r5, 0(%r3) ; Restore the count register
se_mtctr %r5
se_lwz %r7, 4(%r3) ; Restore the value pointed to by the stack pointer
se_lwz %r5, 12(%r3) ; Get the stack pointer so that we can restore
se_stw %r7, 0(%r5) ; the stack pointer value (previous stack frame)
se_mr %r1, %r5 ; Restore the stack pointer r1
se_li %r3, 0x1 ; Set return value to 1
se_blr
注意,同Os_setjmp函数一样不同硬件平台和不同编译平台对该函数的实现存在差异,因此这里也不对该函数进行过分解读,只对该函数的调用方式和实现内容进行分析。
该函数用于TerminateTask/ChainTask后中结束当前Task后恢复到Os_setjmp函数保存的现场状态,恢复的目标地点请参考Os_Dispatch<CoreID>中对此部分内容的描述,并且该函数的范返回值为1 ,该函数的返回值为0(函数的倒数第二行“se_li %r3, 0x1”),返回值是1的原因请参考Os_Dispatch<CoreID>中对此部分内容的描述,可避免再次重复激活同一个Task。
关联:
Os_StartOS_CommonInit:
Os_StartOS_CommonInit位于源文件Os_Cfg.c中,作为OS运行前的部分变量的初始化函数,该函数直接决定了OS能不能正常运行,其定义如下所示:
FUNC(void, OS_CODE) Os_StartOS_CommonInit(void) { /* First AUTOSAR core */
Os_ShutdownAllCores_Indicator = 0U;
Os_TestReset(&Os_lock_alarmaccess);
Os_memclr((uint8 *)Os_dyn_tasks, sizeof(Os_dyn_tasks)/sizeof(uint8)); /* [MISRA 2012 Rule 11.3] */ /*lint !e9087 !e9087 !e740 */ /* [$UKS 1245] */
Os_memclr((uint8 *)Os_dyn_resources, sizeof(Os_dyn_resources)/sizeof(uint8)); /* [MISRA 2012 Rule 11.3] */ /*lint !e9087 !e9087 !e740 */
Os_memclr((uint8 *)Os_dyn_counters, sizeof(Os_dyn_counters)/sizeof(uint8)); /* [MISRA 2012 Rule 11.3] */ /*lint !e9087 !e9087 !e740 */ /* [$UKS 14] [$UKS 1962] */
Os_memclr((uint8 *)Os_dyn_scheduletables, sizeof(Os_dyn_scheduletables)/sizeof(uint8)); /* [MISRA 2012 Rule 11.3] */ /*lint !e9087 !e9087 !e740 */
Os_memclr((uint8 *)Os_dyn_appstate, sizeof(Os_dyn_appstate)/sizeof(uint8)); /* [MISRA 2012 Rule 11.3] */ /*lint !e9087 !e9087 !e740 */ /* [$UKS 1309] */
Os_dyn_fifocontrol[1U] = Os_const_fifocontrol[0U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[0U] = Os_dyn_fifocontrol[1U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[3U] = Os_const_fifocontrol[1U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[2U] = Os_dyn_fifocontrol[3U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[5U] = Os_const_fifocontrol[2U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[4U] = Os_dyn_fifocontrol[5U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[7U] = Os_const_fifocontrol[3U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[6U] = Os_dyn_fifocontrol[7U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[9U] = Os_const_fifocontrol[4U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[8U] = Os_dyn_fifocontrol[9U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[11U] = Os_const_fifocontrol[5U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[10U] = Os_dyn_fifocontrol[11U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[13U] = Os_const_fifocontrol[6U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[12U] = Os_dyn_fifocontrol[13U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[15U] = Os_const_fifocontrol[7U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[14U] = Os_dyn_fifocontrol[15U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[17U] = Os_const_fifocontrol[8U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[16U] = Os_dyn_fifocontrol[17U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[19U] = Os_const_fifocontrol[9U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[18U] = Os_dyn_fifocontrol[19U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[21U] = Os_const_fifocontrol[10U].start; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_dyn_fifocontrol[20U] = Os_dyn_fifocontrol[21U]; /* [MISRA 2012 Rule 18.5] */ /*lint !e9025 */
Os_IocInit(); /* [$UKS 1466] */
Os_RunningCat1ISR0 = (uint32)INVALID_ISR;
Os_RunningCat1ISR1 = (uint32)INVALID_ISR;
Os_RunningCat1ISR2 = (uint32)INVALID_ISR;
}
Os_ShutdownAllCores_Indicator:该变量指示着ShutdownAllCores的原因,初始值为0;
该核释放OS内部自旋锁;
初始化各种动态运行数据为0;
Os_dyn_fifocontrol:初始化给个调度队列的Write/Read指针的初始值,可参考Os_dyn_fifocontrol对dyn_index对象的描述,此部分对理解ETAS OS的调度策略有着十分重要的作用;
Os_IocInit:Ioc对象的初始化;
Os_RunningCat1ISR<CoreID>:该变量指示着内核正在运行的一类中断,目前作者的配置中生成的代码,此参数并无实际意义。
关联:
GetCounterValue:
该函数位于源文件GetCounterValue.c内,该接口为Autosar OS提供的获取Counter计数值的标准接口,在ETAS中,其函数定义如下所示:
FUNC(StatusType, OS_CODE) Os_GetCounterValue(CounterType CounterID, TickRefType Value) {
StatusType api_retval = E_OK;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[OS_MFPMR(384U)];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_GetCounterValue) << 1U) | (Os_OrtiIdType)1U); /* [$UKS 226] [$UKS 1554] */
/* [$UKS 230] */
/* Error if the counter is not valid */
if (!(Os_IsCounterValid(CounterID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 227] */
if ((0U == (CounterID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; } /* [$UKS 849] */
/* [$UKS 228] [$UKS 229] */
*Value = CounterID->dynamic->type_dependent.sw.count;
api_exit:
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_GetCounterValue) << 1U);
return api_retval;
} /* GetCounterValue */
首先依次分析其局部变量/常量:
CounterID:表示需要获取的Counter计数值所对应的Counter标识,由函数入参传入,该数据的数据类型可参考CounterType的相关描述;
Value:该变量为一个指针,目的是将获取到的Counter的计数值回传给调用方,由函数入参传入,Value的数据类型定义可参考TickRefType的相关描述;
api_retval:该变量记录了调用OS API接口时产生的错误,如果API接口调用正常,则返回E_OK,否则返回其他异常码,用户可以根据返回值来判断API的执行情况。
previous_imask:记录调用该接口前的系统中断屏蔽状态。
os_current_core_const:获取当前核(核0)的配置描述信息控制块,参考Os_const_coreconfiguration。其中OS_MFPMR(384U)为ETAS OS在SPC58NN中实现获取CoreId的方法。
os_current_controlled_core:获取当前核(核0)运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
了解了其每个局部变量的含义就可以对其代码进行分析。
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE();
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_GetCounterValue) << 1U) | (Os_OrtiIdType)1U); /* [$UKS 226] [$UKS 1554] */
该处代码使用previous_imask记录了调用接口前的系统中断屏蔽状态,记录完即通过中断优先级掩码的方式屏中断。通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID定义如下所示,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。
if (!(Os_IsCounterValid(CounterID))) { api_retval = E_OS_ID; goto api_exit; } /* [$UKS 227] */
if ((0U == (CounterID->access & Os_AppAccess))) { api_retval = E_OS_ACCESS; goto api_exit; }
此处代码的主要目的是对调用该GetCounterValue的运行时环境进行检测。
首先判断目标CounterID是否是已配置的有效Counter标识,如果不是则返回E_OS_ID错误;
然后检测当前正在运行的Application是否有权限操作该Counter,如果没有则返回E_OS_ACCESS错误;
*Value = CounterID->dynamic->type_dependent.sw.count;
将Counter当前已经记录到的计数值通过指针的方式回传给调用方。
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
Os_OrtiApiID = (Os_OrtiIdType)((OSServiceId_GetCounterValue) << 1U);
return api_retval;
尝试恢复屏蔽中断前的中断屏蔽状态,以及退出该Api的调用并返回API执行状态码。
关联:
Os_PerformEP:
Os_PerformEP位于Os_Cfg_Counters.c内,是基于时基调度的核心函数,理解该函数对于理解Alarm和调度表的运行逻辑有着十分重要的作用,该函数被Coounter的回调函数Os_IncrementCounter_Rte_TickCounter调用,该函数的定义如下所示:
Os_ScheduleTableDynType *Os_PerformEP(ScheduleTableType stc, Os_ScheduleTableDynType *stdref, TickType wrapval, const Os_CoreConfiguration *os_current_core_const, Os_ControlledCoreType *os_current_controlled_core)
{ /* [$UKS 1566] [$UKS 1567] */
Os_ScheduleTableDynType *current_std = stdref;
ScheduleTableType current_stc = stc;
uint8 cfg_index;
TickType next_interval;
P2CONST(uint8, TYPEDEF, OS_VAR) current_action;
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
next_interval = (Os_const_expiry_intervals[(current_std->config & 1023U)]);
current_action = (&Os_const_expiry_actions[current_std->config >> 10]);
Os_TestReset(&Os_lock_alarmaccess);
for(;;) { /*lint !e716 */
cfg_index = (((*current_action & 126U) >> 1U)); /* [MISRA 2012 Rule 10.6] */ /*lint !e9031 !e9034 */
if (cfg_index == 0U) {
break; /* Final */
}
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
current_std->config += 1024U;
Os_TestReset(&Os_lock_alarmaccess);
Os_ActivateTaskKL(Os_const_tasks[cfg_index - 1U], os_current_core_const, os_current_controlled_core);
if ((*(current_action++) & 1U) != 0U) {
break; /* Final action */ /* [MISRA 2012 Rule 15.4] */ /*lint !e9011 */
}
}
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
current_std->config += 1U;
if (cfg_index == 0U) { /* [$UKS 314] */
current_std->state = SCHEDULETABLE_STOPPED;
if (current_std->next != (void *)0) {
current_std->next->dynamic->state = SCHEDULETABLE_RUNNING; /* [$UKS 310] */
next_interval += ((TickType)(current_std->next->initial) - (TickType)(current_stc->initial)); /* [MISRA 2012 Rule 10.6] */ /*lint !e9031 !e9034 */
if (next_interval == 0U) {
Os_TestReset(&Os_lock_alarmaccess);
(void)Os_PerformEP(current_std->next, current_std->next->dynamic, wrapval, os_current_core_const, os_current_controlled_core);
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
} else {
/* switch refs so that next match gets set on the new table */
current_std->next->dynamic->match = current_std->match;
current_std = current_std->next->dynamic;
current_std->next = (ScheduleTableType)0; /* This is the next of the one we have just switched to */
}
} else {
if (current_stc->repeat) {
current_std->config = current_stc->config;
current_std->state = SCHEDULETABLE_RUNNING;
}
} /* [$UKS 311] [$UKS 312] */
}
if (current_std->state == SCHEDULETABLE_RUNNING) {
/* Set next match */
if (current_std->match < (wrapval - next_interval)) {
current_std->match += next_interval;
} else {
current_std->match -= (wrapval - next_interval);
}
}
Os_TestReset(&Os_lock_alarmaccess);
return current_std;
}
按照惯例,首先分析一下函数内部使用到的局部变量:
current_std:该指针变量描述了当前正常处理的调度表的动态运数据,由调用方使用形参stdref传入,指针的原类型可以参考数据类型Os_ScheduleTableDynType的相关描述;
current_stc:该指针变量描述了当前正在处理的调度表的信息配置块,调用方使用形参stc传入,指针的原类型可以参考数据类型ScheduleTableType的相关描述;
cfg_index:此变量将在后文中用来记录应该处理的动作信息;
next_interval:此变量将在后文中用来记录当前处理的到期点记录下一个到期点的时间差值;
current_action:此指针变量将索引到数组Os_const_expiry_actions中具体的到期点动作的位置,取出的值可以用来计算出具体的执行动作信息cfg_index;
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
由于调度表和Alarm支持跨核操作,为了避免多核同时访问同一变量或内存区域导致的内存踩踏,ETAS OS内部使用了一个内置自旋锁,在操作可能多核访问的全局变量前先上锁持有资源,对OS内置自旋锁的操作可以参考Os_Dispatch<CoreID>对内置自旋锁的描述。
next_interval = (Os_const_expiry_intervals[(current_std->config & 1023U)]);
current_action = (&Os_const_expiry_actions[current_std->config >> 10]);
此部分代码主要是为了获取到即将执行动作的到期点的相关信息。
next_interval将通过调度表的动态运行数据记录到的config信息为索引,在Os_const_expiry_intervals中索引出距离下一个到期点的时间差值,具体索引策略为,使用config的低10位,同时该变量的低10位在每处理完一个到期点后将+1,以便下次到期点处理的时候的索引;
current_action也是通过调度表的动态运行数据记录到的config信息为索引,在Os_const_expiry_actions中索引出当前到期点执行的动作的位置,具体索引策略为,使用config的高22位,同时该变量的高22位每处理完一个动作将会+1024,以便对下个动作的处理;
next_interval和current_action的不同在于,next_interval是一个到期点一次索引,而current_action是一个动作一次索引,所以一个next_interval对应的到期点将有可能对应多个current_action的执行动作。
Os_TestReset(&Os_lock_alarmaccess);
获取完具体到期点的相关信息后就可以暂时性的释放内置的Os_lock_alarmaccess自旋锁资源,以防止其他核阻塞太长的时间。
for(;;) { /*lint !e716 */
cfg_index = (((*current_action & 126U) >> 1U)); /* [MISRA 2012 Rule 10.6] */ /*lint !e9031 !e9034 */
if (cfg_index == 0U) {
break; /* Final */
}
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
current_std->config += 1024U;
Os_TestReset(&Os_lock_alarmaccess);
Os_ActivateTaskKL(Os_const_tasks[cfg_index - 1U], os_current_core_const, os_current_controlled_core);
if ((*(current_action++) & 1U) != 0U) {
break; /* Final action */ /* [MISRA 2012 Rule 15.4] */ /*lint !e9011 */
}
}
该部分代码将会依次处理当前到期点的所有执行动作,cfg_index通过当前执行动作current_action索引到的值进行相关变换得到应该激活的Task(作者未配置触发Event动作,所以ETAS OS这里未生成有关触发Event的动作内容),这里虽然经过了转换,但仍然不是最终的Task对应的Index索引,经过转换后的cfg_index值如果是0,则不仅表示当前到期点所有的执行动作均处理完,还表示整个调度表已经处理到了Final点(调度表的最后一个到期点),这时就需要跳出循环体,这就是为什么转换完cfg_index的值不能直接使用的原因,否则无法识别调度表的Final节点和到期点的最后一个执行动作点;
由于接下来还有对全局变量config 的操作,所以在操作该变量的前后需要加上自旋锁进行临界保护,这里每执行一个动作则需要给config 加上1024,因为该变量只有高22位才表示调度表对执行动作的索引;
然后通过cfg_index-1U的值作为即将激活的Task的index索引,调用Os_ActivateTaskKL去激活对应的Task;
最后在根据current_action索引到的值的第0个bit来判断当前到期点是否非0,非0则表示当前到期点的所有执行动作都以执行完,则跳出循环体。
ETAS OS此处的设计不可谓之不玄妙,也是作者最看好的一个设计,虽然代码比较晦涩难懂,但这个代码过程极其简洁,代码时间开销和内存开销都极为优秀,该处代码值得反复细读。
这里再回过头来看看Os_ScheduleTableDynType对于config成员的描述和关于数组Os_const_expiry_intervals的描述,应该就比较清晰,如果不读这段代码,将很难理解ETAS OS对于数组Os_const_expiry_actions的设计。
现在反过来去看一下数组Os_const_expiry_actions的设计,该数组中的每个元素均代表着一个到期点的动作,当这个元素除了第0位,其余位都是0的时候,则表示该调度表已经执行到了Final节点;当该元素的第0位非0的且其余位有值的时候,则表示该动作不仅一个正常的到期点执行动作,还是这个到期点的最后一个动作,执行完这个动作后就表示着这个到期点已经处理完了;当这个元素的第0位是0且其余位有值的时候,则表示该动作是一个正常到期点的执行动作,且该动作并不是这个到期点的最后一个动作。
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
current_std->config += 1U;
在执行完当前到期点的所有动作后,系统再次尝试获取自旋锁来确保数据的安全,在成功获取到锁以后,config就可以+1来指示到下一个到期点,以便下个到期点到达的时候能够正确处理。
if (cfg_index == 0U) { /* [$UKS 314] */
current_std->state = SCHEDULETABLE_STOPPED;
if (current_std->next != (void *)0) {
current_std->next->dynamic->state = SCHEDULETABLE_RUNNING; /* [$UKS 310] */
next_interval += ((TickType)(current_std->next->initial) - (TickType)(current_stc->initial)); /* [MISRA 2012 Rule 10.6] */ /*lint !e9031 !e9034 */
if (next_interval == 0U) {
Os_TestReset(&Os_lock_alarmaccess);
(void)Os_PerformEP(current_std->next, current_std->next->dynamic, wrapval, os_current_core_const, os_current_controlled_core);
while (0U != Os_TestAndSet(&Os_lock_alarmaccess)) { /* spin */ }
} else {
/* switch refs so that next match gets set on the new table */
current_std->next->dynamic->match = current_std->match;
current_std = current_std->next->dynamic;
current_std->next = (ScheduleTableType)0; /* This is the next of the one we have just switched to */
}
} else {
if (current_stc->repeat) {
current_std->config = current_stc->config;
current_std->state = SCHEDULETABLE_RUNNING;
}
} /* [$UKS 311] [$UKS 312] */
}
这部分代码是调度表的Final节点处理内容,当调度表执行到Final节点后,也就代表着调度表的一次执行周期已经结束,按上文的描述,cfg_index的值如果是0的话就代表着调度表正在调度Final节点,正式进入Final的处理流程。
进入Final的处理流程后,首先需要将调度表的状态设置成停止状态(SCHEDULETABLE_STOPPED),当后面识别到调度表是循环表后再将调度表的状态重新设置回运行状态(SCHEDULETABLE_RUNNING);
根据调度表的动态运行参数current_std的next参数判断该调度表是否存在后继表,如果存在后继表则需要启动后继表,当前调度表则需要结束运行;
如果识别到存在后继表后则需要启动对后继表的运行,将后继表的状态设置成运行状态(SCHEDULETABLE_RUNNING),重新计算下一个到期点距离当前到期点的时间(next_interval),按照Autosar OS对后继表的第一个激活点的时间表述,应当实在当前调度表处理完Final到期点后在加上后继表的第一个到期点的时间,而前面在计算next_interval时,ETAS OS已经将当前表的到期时间算进了next_interval中,那这个时候就需要先进去当前表的第一个到期点的时间initial,在加上后继表的第一个到期点的时间initial,关于initial的描述可参考数据类型Os_ScheduleTableType关于initial成员的描述;如果最终计算出来next_interval的值为0的话,就代表着后继表需要立即开始进行第一个到期点的调度,则需要再次调用Os_PerformEP执行;否则(next_interval的值不为0)的话,则需要设置后继表的下一个到期时间点为当前到期时间点,以便后文对调度表的触发时间重新计算的时候有参考依据。由于当前表已经处理结束,接下来需要处理后继表的环境参数,为了方便处理,将指针变量current_std修改成后继表的动态运行数据,同时将后继表自己的后继表参数初始化为空。
如果没有识别到存在后继表,则需要判断当前表是否是循环表,如果是循环表的话,则需要开启当前表的下一个循环,调度表的动态运行参数的config值初始化为调度表的配置信息描述块的config值(可参考数据类型Os_ScheduleTableType关于config成员的描述),将当前表的状态重新设置成运行状态(SCHEDULETABLE_RUNNING),依次实现循环表的功能。
if (current_std->state == SCHEDULETABLE_RUNNING) {
/* Set next match */
if (current_std->match < (wrapval - next_interval)) {
current_std->match += next_interval;
} else {
current_std->match -= (wrapval - next_interval);
}
}
Os_TestReset(&Os_lock_alarmaccess);
return current_std;
该部分代码主要是对当前表(如果前文处理过程中存在后继表的操作,那么此时后继表就是这里描述的当前表)的下一个到期点的时间进行赋值,只有当前表处于运行状态(SCHEDULETABLE_RUNNING)才需要进行此操作,首先需要判断当前到期点的时间和下一个到期点的时间偏差next_interval是否超过了驱动该调度表的Counter的最大计数值,如果没有超过,则直接加上下一个到期点的时间偏差next_interval就可以了,否则就需要考虑翻转的情况。
以上就是ETAS OS对于整个基于时基调度的代码分析,总得来说,该部分代码较为精炼,代码的时间开销和内存开销都比较优秀,代码质量比较高,但在分析执行动作的时候,代码的阅读性较差,可一个优秀的代码如果能提高代码运行效率和降低内存开销,牺牲一点可读性也无可厚非,个人比较赞善该部分代码的实现和数据结构的实现。
关联:
- Os_const_expiry_intervals
- Os_const_expiry_actions
- Os_ScheduleTableDynType_s
- Os_IncrementCounter_Rte_TickCounter
Os_IncrementCounter_Rte_TickCounter:
Os_IncrementCounter_Rte_TickCounter是ETAS OS根据Coounter的配置的回调函数,不同的Counter可配置不同的回调函数名称,具体可参考CounterType参考关于advincr的相关描述,该函数主要实现了对于Counter的计数,以及通过调用Os_PerformEP实现了OS基于时基的调度策略。其定义如下所示:
FUNC(StatusType, OS_CODE) Os_IncrementCounter_Rte_TickCounter(void) {
TickType now;
Os_imaskType previous_imask;
const Os_CoreConfiguration *os_current_core_const = &Os_const_coreconfiguration[0U];
Os_ControlledCoreType *os_current_controlled_core = os_current_core_const->controlled;
Os_AnyCoreType *os_current_core = os_current_core_const->any;
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); /* [$UKS 242] [$UKS 856] [$UKS 270] [$UKS 271] [$UKS 1121] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_IncrementCounter) << 1U) | (Os_OrtiIdType)1U);
now = 1U + Os_dyn_counters[0].type_dependent.sw.count;
if (now > Os_const_counters[0].base.maxallowedvalue) {
now = 0U;
}
Os_dyn_counters[0].type_dependent.sw.count = now;
{ /* [$UKS 304] [$UKS 306] [$UKS 307] [$UKS 910] */
Os_ScheduleTableDynType *current_std = &Os_dyn_scheduletables[0];
ScheduleTableType current_stc = &Os_const_scheduletables[0];
while (current_std < &Os_dyn_scheduletables[1]) { /*lint !e946 pointer does reference the same array */
if ((current_std->state == SCHEDULETABLE_RUNNING) && (now == current_std->match)) {
(void)Os_PerformEP(current_stc, current_std, (TickType)(65536U), os_current_core_const, os_current_controlled_core);
}
current_std++;
current_stc++;
} }
if ((32768U == (previous_imask)) && (Os_ControlledCoreInfo[0U].ReadyTasks.p0 > Os_ControlledCoreInfo[0U].RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */) {
Os_Dispatch();
}
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_IncrementCounter) << 1U));
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
return E_OK;
}
首先分析一下函数内部使用的局部变量/常量:
now:该变量将用来记录对应Counter标识当前的计数值。
previous_imask:该变量将用来记录调用该接口前的系统中断屏蔽状态。
os_current_core_const:获取当前核的配置描述信息控制块,参考Os_const_coreconfiguration。其中OS_MFPMR(384U)为ETAS OS在SPC58NN中实现获取CoreId的方法。
os_current_controlled_core:获取当前核运行时的状态,该变量里的成员是本函数主要操作的变量,变量作用参考Os_ControlledCoreType。
os_current_core:该变量记录了内核运行时禁止中断时的一些状态记录,如调用了SuspendAllInterrupts等一些禁止中断前的中断状态和嵌套层数,以及记录当前调用的OS接口服务等信息,该变量在定位OS故障时具有极高的参考意义。具体定义可跳转到Os_AnyCoreType类型描述。
previous_imask = ((0U != (OS_MFMSR() & OS_EE_BIT)) ? (OS_INTC_CPR | OS_EE_BIT) : OS_INTC_CPR); OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = 55U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); /* [$UKS 242] [$UKS 856] [$UKS 270] [$UKS 271] [$UKS 1121] */
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_IncrementCounter) << 1U) | (Os_OrtiIdType)1U);
该处代码使用previous_imask记录了调用接口前的系统中断屏蔽状态,记录完即通过中断优先级掩码的方式屏中断。通过Os_OrtiApiID记录了当前正在调用的OS服务接口,Os_OrtiApiID定义如下所示,Os_OrtiApiID有关信息可参考Os_AnyCoreType类型有关OrtiApiID的描述。
now = 1U + Os_dyn_counters[0].type_dependent.sw.count;
if (now > Os_const_counters[0].base.maxallowedvalue) {
now = 0U;
}
Os_dyn_counters[0].type_dependent.sw.count = now;
该部分代码是Counter本身功能的核心代码,增加Counter的计数值,并需要判断当前计数值是否超过了Counter允许的最大计数值,具体可参考CounterType关于base的相关描述,一旦达到最大值则需要循环从0重新开始计数。
Os_ScheduleTableDynType *current_std = &Os_dyn_scheduletables[0];
ScheduleTableType current_stc = &Os_const_scheduletables[0];
while (current_std < &Os_dyn_scheduletables[1]) { /*lint !e946 pointer does reference the same array */
if ((current_std->state == SCHEDULETABLE_RUNNING) && (now == current_std->match)) {
(void)Os_PerformEP(current_stc, current_std, (TickType)(65536U), os_current_core_const, os_current_controlled_core);
}
current_std++;
current_stc++;
}
此部分代码将是实现调度表基于时基调度的重要代码,首先描述该部分代码所需要的局部变量:
current_std:该指针变量描述了所有调度表动态运行数据起始指针,在后续的处理中使用该指针变量描述当前正在处理的调度表的动态运行数据,指针的原类型可以参考数据类型Os_ScheduleTableDynType的相关描述;
current_stc:该指针变量描述了所有调度表的配置信息描述块起始指针,在后续的处理中使用该指针变量描述当前正在处理调度表,指针的原类型可以参考数据类型Os_ScheduleTableType的相关描述;
while循环体将依次处理该Counter所驱动的所有调度表,判断条件就是当前调度表是否已经处理完。作者只配置了一个调度表,所以数组Os_dyn_scheduletables的长度只有1,按代码中的处理当current_std的值处理到了Os_dyn_scheduletables[1]时,即代表着所有调度表都处理完毕,注意此处理方法不符合C语言标准语义,虽然实际运行可能不会存在风险,但个人仍不太建议如此处理;
while循环体中只对当前处于SCHEDULETABLE_RUNNING状态的调度表进行处理,并且需要判断调度表的下一个到期点是否和Counter的当前计数值是否相等,两个条件均成立才能调用Os_PerformEP函数以此实现基于时基的调度策略。
if ((32768U == (previous_imask)) && (Os_ControlledCoreInfo[0U].ReadyTasks.p0 > Os_ControlledCoreInfo[0U].RunningTPMask.t0) /*lint !e931 PC-lint thinks there are side effects here */) {
Os_Dispatch();
}
处理完所有的调度表的到期点后,就可以根据当前内核的所有已就绪的Task进行优先级调度。执行到此时,首先需要根据函数记录的中断屏蔽状态previous_imask来进行判断,只有识别到调用接口前系统未出于禁止中断才有机会进行调度,还需要识别出来当前系统已就绪的优先级中存在比正在运行的Task现场的优先级要高,才能够进行调度,而调度的手段则是调用每个核的调度接口函数Os_Dispatch()该函数在代码中是已宏的形式存在,其定义如下所示,其内容参考Os_Dispatch<CoreID>的相关描述。
#define Os_Dispatch (os_current_core_const->dispatch)
随后便是退出API调用以及恢复禁止中断前的中断状态,代码如下所示:
Os_OrtiApiID = ((Os_OrtiIdType)((OSServiceId_IncrementCounter) << 1U));
if (0U != ((previous_imask) & OS_EE_BIT)) { OS_BARRIER(); OS_INTC_CPR = previous_imask & 63U; OS_MBAR(); OS_ISYNC(); OS_SET_EE(); } else { OS_CLEAR_EE(); OS_BARRIER(); OS_INTC_CPR = previous_imask; OS_MBAR(); OS_ISYNC();}(void)0;
return E_OK;
关联:
结尾:
如读者能耐下心来将整篇文档读完并理解,相信应该对ETAS OS的内核原理有个基础的了解。由于受作者工程配置的原因 ,导致需要模块和功能都无法分析,后续如果有机会将进行补充。希望文档能给各位学习ETAS OS源码有所帮助,也希望能通过此文让更多人了解并使用ETAS,祝ETAS越来越好。