1. 系统Tick

    MPC8313芯片的Tick实现比较特殊,因为在MPC8313芯片中存在名为Decrementer的寄存器,如图 1.1 Decrementer寄存器所示。

    SylixOS最小系统开发(二)_第1张图片

    1.1 Decrementer寄存器

    Decrementer寄存器中保存的数据会每4个总线周期减1,当Decrementer寄存器递减到0时,触发Decrementer异常,CPU会执行异常服务程序。Decrementer的异常服务函数需要在startup.S文件中设置,如图 1.2 startup.S所示。

    SylixOS最小系统开发(二)_第2张图片

    1.2 startup.S

  2. Decrementer异常流程解析

    第一步:当Decrementer寄存器递减到0时,触发Decrementer异常,CPU会执行异常服务程序,系统通过异常向量表跳转到archDecrementerInterruptEntry函数。

    第二步:archDecrementerInterruptEntry函数会执行archDecrementerInterruptHandle函数,在archDecrementerInterruptHandle中会对Decrementer寄存器进行设置并调用系统的中断服务函数。

    第三步:系统的中断服务函数会根据archDecrementerInterruptHandle传入的用户注册的伪中断向量查找用户注册的Tick中断服务函数。

  3. Tick实现

    根据上文所述,可以看出MPC8313的系统Tick可以在Decrementer寄存器的基础之上实现。由于Decrementer寄存器的控制接口已经在PowerPC的系统Base中实现,所以笔者无需自己实现,只需要调用系统接口即可。

    Tick的实现分以下三个步骤:

    第一步:使用非已有中断向量号并且大于255的非负数作为Tick的伪中断向量注册中断,如图 1.3 注册Tick中断所示。

    1.3注册Tick中断

    第二步:声明archDecrementerInit函数,如图 1.4 声明archDecrementerInit所示

    1.4声明archDecrementerInit

    第三步:初始化Decrementer,如图 1.5 初始化Decrementer所示

    1.5初始化Decrementer

  4. 高速定时器

    由于系统Tick是基于Decrementer寄存器实现的,所以高速定时器也需要基于Decrementer寄存器实现。

  5. 高速定时器原理简述

    高速定时器实质上是为了补偿系统Tick的不足,用于统计比一个Tick粒度更小的时间。在MPC8313中笔者通过读取Decrementer寄存器中的值来实现高速定时器的功能。

  6. 高速定时器的实现

    由于Decrementer寄存器无状态位可读取,无法通过读取硬件获得是否产生了Tick中断,因此在base中添加了状态全局变量_G_bTickinterTag,并且在系统进入archDecrementerInterruptHandle时把_G_bTickinterTag置为LW_TRUE。

    高速定时器在bspLib.c下的bspTickHighResolution函数实现,如程序清单 2.1 高速定时器所示。

    程序清单 2.1高速定时器


  7. VOIDbspTickHighResolution (struct timespec *ptv)
    {
        REGISTER UINT32uiCntCur,uiDone;
        ULONGulCPUId =LW_CPU_GET_CUR_ID();
    
    								 
        uiCntCur = ppcGetDEC ();
        uiDone   = GuiFullCnt - uiCntCur;
    
    								 
    /*
    * 由于Decrementer寄存器无状态位可读取,无法通过读取硬件获得是否产生
    * 了Tick中断,因此在base中添加了状态全局变量_G_bTickinterTag
    */
        if (_G_bTickinterTag[ulCPUId] ==LW_TRUE) {
    /*
    * 这里由于 TICK 没有及时更新, 所以需要重新获取并且加上一个 TICK 的时间
    */
            uiCntCur= ppcGetDEC ();
            uiDone =GuiFullCnt - uiCntCur;
    
    								 
            if (uiCntCur != 0) {
                uiDone +=GuiFullCnt;
            }
        }
    
    								 
        ptv->tv_nsec += (LONG)((Gui64NSecPerCnt7 * uiDone) >> 7);
        if (ptv->tv_nsec >= 1000000000) {
            ptv->tv_nsec -= 1000000000;
            ptv->tv_sec++;
        }
    }
  8. 参考资料