FreeRTOS Code Reading (Arduino編, #1)

興味があるのでFreeRTOSのソースコードを読んでみる。 あとで修正するかもしれませんが、しばらくは体裁が整っていない自分用のメモです。

なぜ読むか?

RTOSの実装に興味があり、Arduino版ならコード量の少なく読みやすそうだったので。

ソースコードはここ。github.com MPU(atmega328p)のdatasheetはここ。https://avr.jp/user/DS/PDF/mega328P.pdf

もしくはここ。http://akizukidenshi.com/download/ds/microchip/atmega328.pdf

読み方、ソースコード引用の方針

どこから読み始めるか

161204_Mastering_the_FreeRTOS_Real_Time_Kernel-A_Hands-On_Tutorial_Guide.pdf に variantHook.cpp から読めと書いてあるのでそこから始める。 (↑は昔書いたメモだけど、見当たらないorz)

読む

  • Arduino_FreeRTOS_Library\src\variantHooks.cpp
extern void setup(void);
extern void loop(void);

/*-----------------------------------------------------------*/

void initVariant(void) __attribute__ ((OS_main));
void initVariant(void)
{
#if defined(USBCON)
    USBDevice.attach();
#endif

    setup();        // the normal Arduino setup() function is run here.
    vTaskStartScheduler(); // initialise and run the freeRTOS scheduler. Execution should never return here.
}

setup(), loop()はexternされていて、実体は Arduinoのsketchが参照される。

  • task.c
void vTaskStartScheduler( void )
{
BaseType_t xReturn;

    /* Add the idle task at the lowest priority. */
    #if( configSUPPORT_STATIC_ALLOCATION == 1 )
    {
        ...
    }
    #else
    {
        /* The Idle task is being created using dynamically allocated RAM. */
        xReturn = xTaskCreate(  prvIdleTask,
                                configIDLE_TASK_NAME,
                                configMINIMAL_STACK_SIZE,
                                ( void * ) NULL,
                                portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
    }
    #endif /* configSUPPORT_STATIC_ALLOCATION */

configSUPPORT_STATIC_ALLOCATION は, config次第だが多分0(要確認)。

#ifndef configSUPPORT_STATIC_ALLOCATION
    /* Defaults to 0 for backward compatibility. */
    #define configSUPPORT_STATIC_ALLOCATION 0
#endi

スケジューラをタスクの1つとして登録する。

        /* The Idle task is being created using dynamically allocated RAM. */
        xReturn = xTaskCreate(  prvIdleTask,
                                configIDLE_TASK_NAME,
                                configMINIMAL_STACK_SIZE,
                                ( void * ) NULL,
                                portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
    if( xReturn == pdPASS )
    {
        ...

        /* Interrupts are turned off here, to ensure a tick does not occur
        before or during the call to xPortStartScheduler().  The stacks of
        the created tasks contain a status word with interrupts switched on
        so interrupts will automatically get re-enabled when the first task
        starts to run. */
        portDISABLE_INTERRUPTS();

        #if ( configUSE_NEWLIB_REENTRANT == 1 )
        {
            /* Switch Newlib's _impure_ptr variable to point to the _reent
            structure specific to the task that will run first. */
            _impure_ptr = &( pxCurrentTCB->xNewLib_reent );
        }
        #endif /* configUSE_NEWLIB_REENTRANT */

        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;

        /* If configGENERATE_RUN_TIME_STATS is defined then the following
        macro must be defined to configure the timer/counter used to generate
        the run time counter time base.   NOTE:  If configGENERATE_RUN_TIME_STATS
        is set to 0 and the following line fails to build then ensure you do not
        have portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() defined in your
        FreeRTOSConfig.h file. */
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        traceTASK_SWITCHED_IN();

        /* Setting up the timer tick is hardware specific and thus in the
        portable interface. */
        if( xPortStartScheduler() != pdFALSE )
        {
            /* Should not reach here as if the scheduler is running the
            function will not return. */
        }
        else
        {
            /* Should only reach here if a task calls xTaskEndScheduler(). */
        }
    }
    else
    {
        ...
    }
  1. スケジューラのタスクの生成に成功したか(xReturn == pdPASS)を確認する
  2. xPortStartScheduler() を呼ぶまで割り込み禁止にする
        xNextTaskUnblockTime = portMAX_DELAY;
        xSchedulerRunning = pdTRUE;
        xTickCount = ( TickType_t ) configINITIAL_TICK_COUNT;
  • xNextTaskUnblockTime は、...
  • xSchedulerRunning は、スケジューラが動作中はpdTRUEvoid vTaskEndScheduler( void ) が呼ばれると pdFALSE に設定される
  • xTickCount は、次のスケジュールのタスクの割り込みまでの時間。
        portCONFIGURE_TIMER_FOR_RUN_TIME_STATS();

        traceTASK_SWITCHED_IN();

この2つのマクロはdefaultはカラ。

        if( xPortStartScheduler() != pdFALSE )
  • port.c
BaseType_t xPortStartScheduler( void )
{
    /* Setup the relevant timer hardware to generate the tick. */
    prvSetupTimerInterrupt();

    /* Restore the context of the first task that is going to run. */
    portRESTORE_CONTEXT();

    /* Simulate a function call end as generated by the compiler.  We will now
   jump to the start of the task the context of which we have just restored. */
    __asm__ __volatile__ ( "ret" );

    /* Should not get here. */
    return pdTRUE;
}
//initialize watchdog
void prvSetupTimerInterrupt( void )
{
    //reset watchdog
    wdt_reset();

    //set up WDT Interrupt (rather than the WDT Reset).
    wdt_interrupt_enable( portUSE_WDTO );
}
  • prvSetupTimerInterrupt() は、WDT(Watch Dog Timer)をリセットし、WDT割り込みを有効にしている。 ** `wdt_rest(), wdt_interrupt_enable()' は要確認
  • portRESTORE_CONTEXT() は、CPUレジスタを戻して(pop)、xPortStartScheduler() が呼ばれる前の状態(context)に戻している。
  • ret を発行して関数から戻る。

ここまででスケジューラタスクの登録し、実行した状態になった。

次回は、少し戻ってスケジューラタスクの生成処理を読み解いてみる。

        /* The Idle task is being created using dynamically allocated RAM. */
        xReturn = xTaskCreate(  prvIdleTask,
                                configIDLE_TASK_NAME,
                                configMINIMAL_STACK_SIZE,
                                ( void * ) NULL,
                                portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                &xIdleTaskHandle ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */