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です。#4連投の3つ目です。

    以下、各タスクのソースの通りです。(ヘッダ等は省略しています。また、ソース中に/* Start user code …略… */や/* End user code …略… */の記述がありますが、移植元のRX-TBの時にRXスマートコンフィグレータで生成されたタスクのスケルトンのソースに出力されていたものです。RL78コード生成機能では、タスクのスケルトンのソースは生成されません。)

    src/frtos_skeleton/task_LED0.c

    2705.task_LED0_c_20200106.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "rl78g14fpbdef.h"
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define LED0_BLINK_FREQUENCY_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 500 : 50 )
    #else
    /* Hardware */
    #define LED0_BLINK_FREQUENCY_MS pdMS_TO_TICKS( 500 )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_LED0(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        INTERNAL_NOT_USED( pvParameters );
    
        LED0 = LED_ON;
        for (;;)
        {
            vTaskDelay( LED0_BLINK_FREQUENCY_MS );
            LED0 = ~LED0;
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    src/frtos_skeleton/task_LED1.c
    4331.task_LED1_c_20200106.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "r_cg_intc.h"
    #include "rl78g14fpbdef.h"
    
    extern volatile bool g_task_CONIO_error;
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define SW1_REMOVE_CHATTERING_PERIOD_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 10 : 0 )
    #else
    /* Hardware */
    #define SW1_REMOVE_CHATTERING_PERIOD_MS pdMS_TO_TICKS( 10 )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_LED1(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        INTERNAL_NOT_USED( pvParameters );
    
        LED1 = LED_OFF;
        for (;;)
        {
            R_INTC0_Start();
            xSemaphoreTake( semaphore_handle_SW1_PUSH, portMAX_DELAY );
    
            /* Remove mechanical switch chattering */
            vTaskDelay( SW1_REMOVE_CHATTERING_PERIOD_MS );
    
            if (true == g_task_CONIO_error)
            {
                /* Clear error */
                g_task_CONIO_error = false;
            }
            else
            {
                /* Toggle LED1 */
                if (SW1_PUSH == SW1)
                {
                    LED1 = ~LED1;
                }
            }
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    src/frtos_skeleton/task_CONIO.c
    2514.task_CONIO_c_20200106.txt.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "r_cg_serial.h"
    #include "rl78g14fpbdef.h"
    
    volatile bool g_task_CONIO_error = false;
    
    #define CON_RECV_DEMO_SIZE 3
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define LED1_ERROR_BLINK_FREQUENCY_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 200 : 20 )
    #define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : 600L )
    //#define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : portMAX_DELAY ) /* for debug */
    //#define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : 0 ) /* for debug */
    #else
    /* Hardware */
    #define LED1_ERROR_BLINK_FREQUENCY_MS pdMS_TO_TICKS( 200 )
    #define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( 60 * 1000UL )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_CONIO(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        BaseType_t ret;
    
        INTERNAL_NOT_USED( pvParameters );
    
        U_UART3_ASYNC_Start( semaphore_handle_CON_TX_READY, semaphore_handle_CON_RX_READY );
    
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );
    
            U_UART3_DTC_Receive( recv_buff, CON_RECV_DEMO_SIZE, &err_type );
            ret = xSemaphoreTake( semaphore_handle_CON_RX_READY, CON_RECV_TIMEOUT_MS );
    
            nop();  /* for breakpoint, check timing chart on Simulator GUI */
    
            if (pdPASS == ret && 0 == err_type)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (pdFAIL == ret)
                {
                    U_UART3_DTC_Receive_Abort(); /* or Retry U_UART3_DTC_Receive() */
                    vPrintString( "Timeout Error\n" );
                }
                else
                {
                    if (SCI_EVT_FRAMING_ERR & err_type)
                    {
                        vPrintString( "Framing Error\n" );
                    }
                    if (SCI_EVT_PARITY_ERR & err_type)
                    {
                        vPrintString( "Parity Error\n" );
                    }
                    if (SCI_EVT_OVFL_ERR & err_type)
                    {
                        vPrintString( "Overrun Error\n" );
                    }
                }
    
                g_task_CONIO_error = true;
                LED1 = LED_ON;
                while (true == g_task_CONIO_error)
                {
                    vTaskDelay( LED1_ERROR_BLINK_FREQUENCY_MS );
                    LED1 = ~LED1;
                }
                LED1 = LED_OFF;
    
                continue;
            }
    
            U_UART3_DTC_Send( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );
            ret = xSemaphoreTake( semaphore_handle_CON_TX_READY, portMAX_DELAY );
    
            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    以下、Renesas RL78 Simulatorでシリアル送信データのファイルを使った実行例の画面コピーです。

    CC-RL+CS+の場合

    sim_rl78_serial_data_ok_2.ser(今回追加)を使用した例





    sim_rl78_serial_data_err_uart_format.serを使用した例





    GNURL78+e2 studioの場合 (CC-RL+e2 studioの場合も同様です)

    sim_rl78_serial_data_ok_2.ser(今回追加)を使用した例



    sim_rl78_serial_data_err_uart_format.serを使用した例


     

Reply
  • こんにちは。NoMaYです。#4連投の3つ目です。

    以下、各タスクのソースの通りです。(ヘッダ等は省略しています。また、ソース中に/* Start user code …略… */や/* End user code …略… */の記述がありますが、移植元のRX-TBの時にRXスマートコンフィグレータで生成されたタスクのスケルトンのソースに出力されていたものです。RL78コード生成機能では、タスクのスケルトンのソースは生成されません。)

    src/frtos_skeleton/task_LED0.c

    2705.task_LED0_c_20200106.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "rl78g14fpbdef.h"
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define LED0_BLINK_FREQUENCY_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 500 : 50 )
    #else
    /* Hardware */
    #define LED0_BLINK_FREQUENCY_MS pdMS_TO_TICKS( 500 )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_LED0(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        INTERNAL_NOT_USED( pvParameters );
    
        LED0 = LED_ON;
        for (;;)
        {
            vTaskDelay( LED0_BLINK_FREQUENCY_MS );
            LED0 = ~LED0;
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    src/frtos_skeleton/task_LED1.c
    4331.task_LED1_c_20200106.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "r_cg_intc.h"
    #include "rl78g14fpbdef.h"
    
    extern volatile bool g_task_CONIO_error;
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define SW1_REMOVE_CHATTERING_PERIOD_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 10 : 0 )
    #else
    /* Hardware */
    #define SW1_REMOVE_CHATTERING_PERIOD_MS pdMS_TO_TICKS( 10 )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_LED1(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        INTERNAL_NOT_USED( pvParameters );
    
        LED1 = LED_OFF;
        for (;;)
        {
            R_INTC0_Start();
            xSemaphoreTake( semaphore_handle_SW1_PUSH, portMAX_DELAY );
    
            /* Remove mechanical switch chattering */
            vTaskDelay( SW1_REMOVE_CHATTERING_PERIOD_MS );
    
            if (true == g_task_CONIO_error)
            {
                /* Clear error */
                g_task_CONIO_error = false;
            }
            else
            {
                /* Toggle LED1 */
                if (SW1_PUSH == SW1)
                {
                    LED1 = ~LED1;
                }
            }
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    src/frtos_skeleton/task_CONIO.c
    2514.task_CONIO_c_20200106.txt.txt
    #include "task_function.h"
    /* Start user code for import. Do not edit comment generated here */
    #include "freertos_start.h"
    #include "platform.h"
    #include "r_cg_serial.h"
    #include "rl78g14fpbdef.h"
    
    volatile bool g_task_CONIO_error = false;
    
    #define CON_RECV_DEMO_SIZE 3
    
    #if defined(RENESAS_SIMULATOR_DEBUGGING)
    /* Hardware or Renesas RL78 Simulator */
    #define LED1_ERROR_BLINK_FREQUENCY_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 200 : 20 )
    #define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : 600L )
    //#define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : portMAX_DELAY ) /* for debug */
    //#define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( !IsRenesasSimDebugMode() ? 60 * 1000UL : 0 ) /* for debug */
    #else
    /* Hardware */
    #define LED1_ERROR_BLINK_FREQUENCY_MS pdMS_TO_TICKS( 200 )
    #define CON_RECV_TIMEOUT_MS pdMS_TO_TICKS( 60 * 1000UL )
    #endif
    /* End user code. Do not edit comment generated here */
    
    void task_CONIO(void * pvParameters)
    {
    /* Start user code for function. Do not edit comment generated here */
    
        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        BaseType_t ret;
    
        INTERNAL_NOT_USED( pvParameters );
    
        U_UART3_ASYNC_Start( semaphore_handle_CON_TX_READY, semaphore_handle_CON_RX_READY );
    
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );
    
            U_UART3_DTC_Receive( recv_buff, CON_RECV_DEMO_SIZE, &err_type );
            ret = xSemaphoreTake( semaphore_handle_CON_RX_READY, CON_RECV_TIMEOUT_MS );
    
            nop();  /* for breakpoint, check timing chart on Simulator GUI */
    
            if (pdPASS == ret && 0 == err_type)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (pdFAIL == ret)
                {
                    U_UART3_DTC_Receive_Abort(); /* or Retry U_UART3_DTC_Receive() */
                    vPrintString( "Timeout Error\n" );
                }
                else
                {
                    if (SCI_EVT_FRAMING_ERR & err_type)
                    {
                        vPrintString( "Framing Error\n" );
                    }
                    if (SCI_EVT_PARITY_ERR & err_type)
                    {
                        vPrintString( "Parity Error\n" );
                    }
                    if (SCI_EVT_OVFL_ERR & err_type)
                    {
                        vPrintString( "Overrun Error\n" );
                    }
                }
    
                g_task_CONIO_error = true;
                LED1 = LED_ON;
                while (true == g_task_CONIO_error)
                {
                    vTaskDelay( LED1_ERROR_BLINK_FREQUENCY_MS );
                    LED1 = ~LED1;
                }
                LED1 = LED_OFF;
    
                continue;
            }
    
            U_UART3_DTC_Send( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );
            ret = xSemaphoreTake( semaphore_handle_CON_TX_READY, portMAX_DELAY );
    
            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }
    
    /* End user code. Do not edit comment generated here */
    }
    


    以下、Renesas RL78 Simulatorでシリアル送信データのファイルを使った実行例の画面コピーです。

    CC-RL+CS+の場合

    sim_rl78_serial_data_ok_2.ser(今回追加)を使用した例





    sim_rl78_serial_data_err_uart_format.serを使用した例





    GNURL78+e2 studioの場合 (CC-RL+e2 studioの場合も同様です)

    sim_rl78_serial_data_ok_2.ser(今回追加)を使用した例



    sim_rl78_serial_data_err_uart_format.serを使用した例


     

Children
No Data