FreeRTOS任务切换的实现方式--Apple的学习笔记

继上一篇Tricore内核上下文切换机制CSA--Apple的学习笔记
后,今天看了下运行过程中的任务切换。然后让我觉得我上一篇对于从PCXI读取地址的理解是错误的,我之前理解PCXI读取值时候会将它释放move到FCX,其实这个理解是错误的。PCXI为当前上下文,可以通过指针方式读取地址,然后通过指针offset修改其值。因为运行过程中的任务切换就是通过此种方式实现。
那么既然可以通过此种方式修改返回地址(RA),那么是否我上一篇文章中提出的问题,仅初始化使用一个CSA可以解决吗?为什么初始化的时候一定要创建2个CSA,并且必须lowerCSA->UpperCSA->NULL的顺序?

问题已解

今天看了运行过程中的任务切换得到了答案。首先在工程中搜索vTaskSwitchContext,就可以发现port.c中有3个函数在调用。

  1. prvSystemTickHandler,通过时间片来触发任务切换。stm中断
  2. prvTrapYield,通过delete/delay任务等方式来触发。trip中断
  3. prvInterruptYield,通过sw中断来触发。gpsr中断(这个是今天新学的,Tricore内核提供4个,其实就是给RTOS准备的)
    炸一看,进入中断会自动保存UpperCSA。也没有说到lowerCSA的存在呀!是否用一个就可以了呢?(当然lowerCSA里面包括参数,从功能完整性来说,其实必须要使用2个CSA的)但是看了中断汇编函数可以发现,都是先恢复lower再恢复Upper,所以初始化时候的顺序必须是lower->upper


    中断汇编.png

运行过程中的任务切换

今天的主角上场,3个函数中都是一样的code如下。

        _disable();
        _dsync();
        xUpperCSA = _mfcr( $PCXI );
        pxUpperCSA = portCSA_TO_ADDRESS( xUpperCSA );
        *pxCurrentTCB = pxUpperCSA[ 0 ];
        vTaskSwitchContext();
        pxUpperCSA[ 0 ] = *pxCurrentTCB;
        SRBs[cpuid]->B.TRIG0 = 0;       //ATEN
        _isync();

首先我的第一个问题就是为什么PCXI读取的是UpperCSA?这个是我调试时候发现的,UL都是1。应该算是中断中的函数调用,所以函数调用为使用一个UpperCSA。最关键的就是取出当前PCXI的地址pxUpperCSA,然后为它赋值期望的最高优先级TCB,pxUpperCSA[ 0 ] = *pxCurrentTCB;此步骤理解为串改RA地址。调试时候可以看到PCXI有如下变化


调试结果.png

然后运行最后一句SRBs[cpuid]->B.TRIG0 = 0;的时候会跳转到上面汇编的rslcx语句,并且UL为0,说明此时PCXI指向lowerCSA。
其实这是一次UpperCSA的恢复,并且我将UpperCSA中的下一个指向地址之前串改为新的TCB。
然后运行rslcx指向lowerCSA进行第二次恢复,那么就把TCB的lowerCSA的16字节内容恢复出来,并且其中的LINK WORD为TCB的UpperCSA。(就是初始化时候的lower,而不是先Upper就是这个原因)
最后运行rfe进行第三次恢复,将TCB的UpperCSA内容还原。而UpperCSA的RA地址就是我们期望的下一个任务的函数地址。初始化的时候就已经赋值过了。
原理都讲清楚了,也理解为什么必须是lower执行Upper,因为要配合中断汇编。但是为什么中断汇编中有2个还原恢复,而且顺序必须是这样的。这个汇编在中断向量表BTV中没看到呀,是编译器自动生成的吗?其它编译器生成的也是这样的结构进行中断恢复吗?我觉得要么是固定的格式,要么我自己可以设置的。

函数返回恢复和中断返回恢复指令不同

通过3次恢复。先Upper再汇编中的lower及Upper可以发现区别。这个原因应该是中断处理只有32个字节,我运行的c代码是在中断调用的函数。所以我第一次算函数通过UpperCSA进行返回到中断,这也说明了函数返回恢复只要一个UpperCSA恢复,而中断恢复需要先用lowerCSA再用UpperCSA。

你可能感兴趣的:(FreeRTOS任务切换的实现方式--Apple的学习笔记)