RL78 FreeRTOS APIを特別なおまじない記述無しで割り込みルーチンから呼び出せるようにしてみた(CC-RL/GNURL78)

こんにちは。NoMaYです。

別スレッド『Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた』で気付いたことですが、FreeRTOSでは、ポートレイヤーの実装に内蔵周辺のソフトウェア割り込み機能(これはCPUのソフトウェア割り込み命令のことでは無いです)を使用することで、素朴に簡単に割り込みルーチンからFreeRTOS APIを呼び出せるようになることに気付きました。そこで、FreeRTOS v10.2.1 RL78ポートレイヤーをそのように改変してみました。

以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.01/e2 stuiod v7.40+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱))(GNURL78版はe2 studio v7.40+GNURL78 2019q2(4.9.2.201902)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX版と同じにしてあります。([追記] すみません。以下に含まれるMAPファイルが別のものでした。タスク側のクリティカルセクションが以前のままのものでした。この投稿の末尾に追加したMOTファイルのzipファイルには訂正版を入れてあります。)

sim_rl78_freertos_full_demo_ccrl_c_csplus_20190705.zip    628KB
sim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705.zip    582KB

そのスレッドに投稿していたRL78ポートレイヤー(これもFreeRTOS v10.2.1 RL78ポートレイヤーを改変したものです)では、割り込みルーチンからFreeRTOS APIを呼び出すには以下のような特別なおまじないを記述する必要がありました。(以下はCC-RLの場合のものですがGNURL78でも同様です。)

#pragma interrupt r_intc3_interrupt(vect=INTP3)
/* Start user code for pragma. Do not edit comment generated here */
R_PRAGMA_FREERTOS_INTERRUPT(r_intc3_interrupt)
#define r_intc3_interrupt _r_intc3_interrupt
/* End user code. Do not edit comment generated here */

そのスレッドにはRXポートレイヤーの改変についても投稿していたのですが、その時、最初に書いたことに気付きました。RL78の内蔵周辺には専用のソフトウェア割り込み機能はありませんが、以下のハードウェアマニュアルに記載されている通り、プログラムで割り込み要求フラグをセットすると割り込みを発生させることが出来ますので、実質的に任意の空き割り込みをソフトウェア割り込み機能として使うことが出来ます。(今回は、とりあえず、ウォッチドッグタイマのオーバフロー時間の75%到達のインターバル割り込みを使用してみました。)

RL78/G13 ユーザーズマニュアル ハードウェア編からの抜粋
www.renesas.com/ja-jp/doc/products/mpumcu/doc/rl78/r01uh0146jj0340_rl78g13.pdf



なお、今回のRL78ポートレイヤーの改変で、先ほどの特別なおまじないを記述する必要は無くなりますが、割り込みルーチンからFreeRTOS APIを呼び出してブロック解除待ちタスクをブロック解除する場合のタスク切り替えの遅延時間が以下のRenesas RL78 SimulatorのSimulator GUIの画面コピーのように数十クロックほど(32MHz動作では600nsほど)延びます。(以下はCC-RLの場合のものですがGNURL78でも同様(ただし数百nsほど長い)です。) ちなみに、タスク側のクリティカルセクション(その区間では割り込み禁止となる)への出入りによるタイミングへの影響を避ける為、今回はクリティカルセクションとして扱うことしていませんので、以下の画面コピーでは別スレッドの画面コピーよりもポート出力トグルの速さが速くなっています。

今回の改変前:


今回の改変後:


以下、今回のRL78ポートレイヤの改変内容です。コメントの訂正や不要になったルーチンの削除は省略しています。(以下はCC-RLの場合のものですがGNURL78でも同様です。)

src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h
今回の改変前:

/* Task utilities. */
#define portYIELD() __brk()
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken ) if( xHigherPriorityTaskWoken ) vTaskSwitchContext()
#define portNOP()   __nop()

今回の改変後: 赤文字行を追加/変更

#include "iodefine.h"
/* Task utilities. */
#define portYIELD() do{ WDTIIF = 1; } while (0)
#define portYIELD_FROM_ISR( xHigherPriorityTaskWoken )  if( xHigherPriorityTaskWoken ) portYIELD()
#define portNOP()   __nop()

src/FreeRTOS/Source/portable/Renesas/RL78/port.c
今回の改変後: 赤文字行を追加

BaseType_t xPortStartScheduler( void )
{
    /* Setup the hardware to generate the tick.  Interrupts are disabled when
    this function is called. */
    configSETUP_TICK_INTERRUPT();

    /* Setup the hardware to generate the yield interrupt. */
    WDTIPR1 = 1;
    WDTIPR0 = 1;
    WDTIMK = 0;

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

    /* Execution should not reach here as the tasks are now running! */
    return pdTRUE;
}

src/FreeRTOS/Source/portable/Renesas/RL78/portasm.asm
今回の改変前:

_vPortYield:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call@      _vTaskSwitchContext  ; Call the scheduler to select the next task.
    portRESTORE_CONTEXT             ; Restore the context of the next task to run.
    retb
    _vPortYield      .VECTOR    0x7E

今回の改変後: 赤文字行を変更

_vPortYield:
    portSAVE_CONTEXT                ; Save the context of the current task.
    call@      _vTaskSwitchContext  ; Call the scheduler to select the next task.
    portRESTORE_CONTEXT             ; Restore the context of the next task to run.
    reti
    _vPortYield      .VECTOR    0x04

以下、今回のタスク側のプログラムです。先ほど書いた通り、今回は各タスクのポート出力をトグルさせる部分をクリティカルセクションとして扱うことしていません。(なお、src/frtos_config/FreeRTOSConfig.hの#define FREERTOS_USER_MAIN 0の行で0→1の変更を行うことで以下が実行されるようになります。)

src/user_main.c

void main_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no6 = !P1_bit.no6;
    }
}

void second_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no5 = !P1_bit.no5;
    }
}

void third_task(void *pvParameters)
{
    (void) pvParameters;

    while (1)
    {
        P1_bit.no4 = !P1_bit.no4;
    }
}

void intp3_task(void *pvParameters)
{
    (void) pvParameters;

    R_INTC3_Start();

    LED_INIT();
    while (1)
    {
        xSemaphoreTake( xSemaphoreINTP3, portMAX_DELAY );
        LED_BIT = !LED_BIT;
    }
}


[追記]

MOTファイル

sim_rl78_freertos_full_demo_ccrl_c_csplus_20190705_mot.zip    2019/07/12追加
sim_rl78_freertos_full_demo_gnurl78_c_e2v740_20190705_mot.zip    2019/07/24追加

Parents
  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知ですが、なにか手軽そうですね。先日投稿したSample Programではセマフォを使用していたのですが、タスク通知を使用する方法に書き換え中です。ただ、この機能はFreeRTOSや同じコードベースの商用版のOpenRTOSやAPI互換の商用機能安全版のSafeRTOSに特有なものなのかな、みたいな感じがしますので、いわゆるOS Abstraction layerとかでは対応されないものなのかも、という気もします。

    また、このスレッドの前に投稿していた以下のスレッドで、RL78/G13ですが、UART受信をDMAで行う際に受信エラーを返せるようにしたFreeRTOSサンプルプログラムを投稿した際に遭遇していた、予測外の割り込みによる誤動作に対しては、以下のように似たような小技で対処が出来るような感じになっていました。

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「ダミーのブロック解除待ちを入れることでも対処することが出来ました」
    japan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32254#32254

    タスク通知 - AWS » ドキュメント » FreeRTOS Kernel » 開発者ガイド »
    周辺機器デバイスドライバで使用されるタスク通知: UARTの例 ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#task-notifications-used-in-peripheral-device-drivers-uart-example

    BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength )
    {
        BaseType_t xReturn;

        /* Save the handle of the task that called this function. The book text
        contains notes as to whether the following line needs to be protected
        by a critical section or not. */
        pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Ensure the calling task does not already have a notification pending
        by calling  ulTaskNotifyTake() with the xClearCountOnExit parameter set
        to pdTRUE, and a block time of 0  (don't block). */
        ulTaskNotifyTake( pdTRUE, 0 );

        /* Start the transmission. */
        UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );

        /* Block until notified that the transmission is complete.
        If the notification is received, then xReturn will be set to 1
        because the ISR will have incremented this task's notification value to 1 (pdTRUE).
        If the operation times out, then xReturn will be 0 (pdFALSE)
        because this task's notification value will not have been changed
        since it was cleared to 0 above.
        If the ISR executes between the calls to UART_low_level_send()
        and the call to ulTaskNotifyTake(), then the event will be latched
        in the task's notification value, and the call to ulTaskNotifyTake()
        will return immediately.*/
        xReturn = ( BaseType_t ) ulTaskNotifyTake( pdTRUE, pxUARTInstance->xTxTimeout );

        return xReturn;
    }

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「受信エラー割り込みが複数回発生しないようにするやり方でも対処してみました」
    apan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32271#32271

    www.freertos.org » Kernel » Developer Docs » Direct To Task Notifications » ...As Binary Semaphore
    RTOS Task Notifications
    Used As Light Weight Binary Semaphores ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    www.freertos.org/RTOS_Task_Notification_As_Binary_Semaphore.html

    void StartTransmission( uint8_t *pcData, size_t xDataLength )
    {
        /* At this point xTaskToNotify should be NULL as no transmission
        is in progress.  A mutex can be used to guard access to the
        peripheral if necessary. */
        configASSERT( xTaskToNotify == NULL );

        /* Store the handle of the calling task. */
        xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Start the transmission - an interrupt is generated when the
        transmission is complete. */
        vStartTransmit( pcData, xDatalength );
    }
    void vTransmitEndISR( void )
    {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        /* At this point xTaskToNotify should not be NULL as a transmission was
        in progress. */
        configASSERT( xTaskToNotify != NULL ); → ここを以下にすれば割り込みが複数回発生しても誤動作しない
      if (xTaskToNotify != NULL)
      {

        /* Notify the task that the transmission is complete. */
        vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );

        /* There are no transmissions in progress, so no tasks to notify. */
        xTaskToNotify = NULL;

        /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
        should be performed to ensure the interrupt returns directly to the highest
        priority task.  The macro used for this purpose is dependent on the port in
        use and may be called portEND_SWITCHING_ISR(). */
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

      }
    }

    [参考]

    FreeRTOSとOpenRTOS/SafeRTOS
    www.freertos.org/a00114.html#commercial

    RTOS Resources and Tutorials
    www.highintegritysystems.com/rtos/rtos-tutorials/

    [追記]

    ちなみに、タスク通知が動作しなかった件の、このスレッドのポートレイヤの問題ですが、当面は、以下のようにすることで対処しようと考えています。ただ、RTOSのカーネル/ポートレイヤで、このようなループ終了条件が自明とはちょっと言いにくいようなループは嫌われるのかも、とも感じていますので、何とか改善したい気持ちはあります。(RTOSのドキュメントで、deterministic、とか、決定論的、とか、そういう言葉を見掛けますので、そういうものなのかも、と感じているのです。)

    src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h
    src/FreeRTOS/Source/portable/GCC/RL78/portmacro.h

    #define portEXIT_CRITICAL()                                                     \
    {                                                                               \
    extern volatile uint16_t usCriticalNesting;                                     \
                                                                                    \
        if( usCriticalNesting > portNO_CRITICAL_SECTION_NESTING )                   \
        {                                                                           \
            /* Decrement the nesting count as we are leaving a critical section. */ \
            usCriticalNesting--;                                                    \
                                                                                    \
            /* If the nesting level has reached zero then interrupts should be */   \
            /* re-enabled. */                                                       \
            if( usCriticalNesting == portNO_CRITICAL_SECTION_NESTING )              \
            {                                                                       \
                portENABLE_INTERRUPTS();                                            \
                /* This NOP prevents infinite DTC pending caused by BT WDTIIF $$. */\
                /* (Moreover, such DTC pending causes infinite interrupt pending. */\
                /* Furthermore, such interrupt pending prevents task switching. */  \
                /* So, in case of without this NOP, task switching never occur */   \
                /* if DTC occurs just here.)(This is in case of CC-RL. In case of */\
                /* GNURL78, more codes are generated so this situation does not */  \
                /* happen but NOP is inserted to be the same operation as CC-RL.) */\

                do{ portNOP(); }while (WDTIIF != 0);                                \
            }                                                                       \
        }                                                                           \
    }
     
Reply
  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知ですが、なにか手軽そうですね。先日投稿したSample Programではセマフォを使用していたのですが、タスク通知を使用する方法に書き換え中です。ただ、この機能はFreeRTOSや同じコードベースの商用版のOpenRTOSやAPI互換の商用機能安全版のSafeRTOSに特有なものなのかな、みたいな感じがしますので、いわゆるOS Abstraction layerとかでは対応されないものなのかも、という気もします。

    また、このスレッドの前に投稿していた以下のスレッドで、RL78/G13ですが、UART受信をDMAで行う際に受信エラーを返せるようにしたFreeRTOSサンプルプログラムを投稿した際に遭遇していた、予測外の割り込みによる誤動作に対しては、以下のように似たような小技で対処が出来るような感じになっていました。

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「ダミーのブロック解除待ちを入れることでも対処することが出来ました」
    japan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32254#32254

    タスク通知 - AWS » ドキュメント » FreeRTOS Kernel » 開発者ガイド »
    周辺機器デバイスドライバで使用されるタスク通知: UARTの例 ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#task-notifications-used-in-peripheral-device-drivers-uart-example

    BaseType_t xUART_Send( xUART *pxUARTInstance, uint8_t *pucDataSource, size_t uxLength )
    {
        BaseType_t xReturn;

        /* Save the handle of the task that called this function. The book text
        contains notes as to whether the following line needs to be protected
        by a critical section or not. */
        pxUARTInstance->xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Ensure the calling task does not already have a notification pending
        by calling  ulTaskNotifyTake() with the xClearCountOnExit parameter set
        to pdTRUE, and a block time of 0  (don't block). */
        ulTaskNotifyTake( pdTRUE, 0 );

        /* Start the transmission. */
        UART_low_level_send( pxUARTInstance, pucDataSource, uxLength );

        /* Block until notified that the transmission is complete.
        If the notification is received, then xReturn will be set to 1
        because the ISR will have incremented this task's notification value to 1 (pdTRUE).
        If the operation times out, then xReturn will be 0 (pdFALSE)
        because this task's notification value will not have been changed
        since it was cleared to 0 above.
        If the ISR executes between the calls to UART_low_level_send()
        and the call to ulTaskNotifyTake(), then the event will be latched
        in the task's notification value, and the call to ulTaskNotifyTake()
        will return immediately.*/
        xReturn = ( BaseType_t ) ulTaskNotifyTake( pdTRUE, pxUARTInstance->xTxTimeout );

        return xReturn;
    }

    Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた
    「受信エラー割り込みが複数回発生しないようにするやり方でも対処してみました」
    apan.renesasrulz.com/cafe_rene/f/forum21/5790/amazon-aws-freertos-kernel-developer-guide-renesas-rx-simulator-debug-console/32271#32271

    www.freertos.org » Kernel » Developer Docs » Direct To Task Notifications » ...As Binary Semaphore
    RTOS Task Notifications
    Used As Light Weight Binary Semaphores ← なお送信の例なので関数名やコメントは受信の例へ脳内変換が必要です、、、
    www.freertos.org/RTOS_Task_Notification_As_Binary_Semaphore.html

    void StartTransmission( uint8_t *pcData, size_t xDataLength )
    {
        /* At this point xTaskToNotify should be NULL as no transmission
        is in progress.  A mutex can be used to guard access to the
        peripheral if necessary. */
        configASSERT( xTaskToNotify == NULL );

        /* Store the handle of the calling task. */
        xTaskToNotify = xTaskGetCurrentTaskHandle();

        /* Start the transmission - an interrupt is generated when the
        transmission is complete. */
        vStartTransmit( pcData, xDatalength );
    }
    void vTransmitEndISR( void )
    {
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

        /* At this point xTaskToNotify should not be NULL as a transmission was
        in progress. */
        configASSERT( xTaskToNotify != NULL ); → ここを以下にすれば割り込みが複数回発生しても誤動作しない
      if (xTaskToNotify != NULL)
      {

        /* Notify the task that the transmission is complete. */
        vTaskNotifyGiveFromISR( xTaskToNotify, &xHigherPriorityTaskWoken );

        /* There are no transmissions in progress, so no tasks to notify. */
        xTaskToNotify = NULL;

        /* If xHigherPriorityTaskWoken is now set to pdTRUE then a context switch
        should be performed to ensure the interrupt returns directly to the highest
        priority task.  The macro used for this purpose is dependent on the port in
        use and may be called portEND_SWITCHING_ISR(). */
        portYIELD_FROM_ISR( xHigherPriorityTaskWoken );

      }
    }

    [参考]

    FreeRTOSとOpenRTOS/SafeRTOS
    www.freertos.org/a00114.html#commercial

    RTOS Resources and Tutorials
    www.highintegritysystems.com/rtos/rtos-tutorials/

    [追記]

    ちなみに、タスク通知が動作しなかった件の、このスレッドのポートレイヤの問題ですが、当面は、以下のようにすることで対処しようと考えています。ただ、RTOSのカーネル/ポートレイヤで、このようなループ終了条件が自明とはちょっと言いにくいようなループは嫌われるのかも、とも感じていますので、何とか改善したい気持ちはあります。(RTOSのドキュメントで、deterministic、とか、決定論的、とか、そういう言葉を見掛けますので、そういうものなのかも、と感じているのです。)

    src/FreeRTOS/Source/portable/Renesas/RL78/portmacro.h
    src/FreeRTOS/Source/portable/GCC/RL78/portmacro.h

    #define portEXIT_CRITICAL()                                                     \
    {                                                                               \
    extern volatile uint16_t usCriticalNesting;                                     \
                                                                                    \
        if( usCriticalNesting > portNO_CRITICAL_SECTION_NESTING )                   \
        {                                                                           \
            /* Decrement the nesting count as we are leaving a critical section. */ \
            usCriticalNesting--;                                                    \
                                                                                    \
            /* If the nesting level has reached zero then interrupts should be */   \
            /* re-enabled. */                                                       \
            if( usCriticalNesting == portNO_CRITICAL_SECTION_NESTING )              \
            {                                                                       \
                portENABLE_INTERRUPTS();                                            \
                /* This NOP prevents infinite DTC pending caused by BT WDTIIF $$. */\
                /* (Moreover, such DTC pending causes infinite interrupt pending. */\
                /* Furthermore, such interrupt pending prevents task switching. */  \
                /* So, in case of without this NOP, task switching never occur */   \
                /* if DTC occurs just here.)(This is in case of CC-RL. In case of */\
                /* GNURL78, more codes are generated so this situation does not */  \
                /* happen but NOP is inserted to be the same operation as CC-RL.) */\

                do{ portNOP(); }while (WDTIIF != 0);                                \
            }                                                                       \
        }                                                                           \
    }
     
Children
No Data