Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page

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追加

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

    割り込みのみ版のCONIOタスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。)

    freertos_sampleprog2_gnurl78_c/src/frtos_skeleton/task_CONIO.c

    void task_CONIO(void * pvParameters)
    {
    /* ...略... */

        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        MD_STATUS status;
        uint32_t cnt;

        INTERNAL_NOT_USED( pvParameters );

        U_UART3_Start();

        cnt = 0;
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );

            if (0 == cnt)
            {
                status = U_UART3_Send_Receive_Wait(
                             (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE),
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
           }
            else
            {
                status = U_UART3_Receive_Wait(
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );

            }
            cnt++;

            nop();  /* for breakpoint, check timing chart on Simulator GUI */

            if (MD_OK == status)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (MD_RECV_TIMEOUT == status)
                {
                    vPrintString( "Recv Timeout Error\n" );
                }
                else if (MD_RECV_ERROR == status)
                {
                    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" );
                    }
                }
                else if (MD_SEND_ERROR == status)
                {
                    vPrintString( "Send 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;

                U_UART3_Receive_ClearError(); /* if not called, will be called internally */

                cnt = 0;
                continue;
            }

            status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );

            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }

    /* ...略... */
    }

    コード生成されたシリアル処理のユーザ記述部(抜粋)のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。) (なお、受信完了で受信割り込みを止めるようにしています。)

    freertos_sampleprog2_gnurl78_c/src/r_cg_serial.c

    /* Start user code for adding. Do not edit comment generated here */
    ...略...
    void U_UART3_Start(void)
    {
        R_UART3_Start();
        U_UART3_Receive_Stop();
    }
    MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, 
        volatile uint8_t * p_err_type, TickType_t rx_wait)
    {
        MD_STATUS status = MD_OK;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( rx_wait );

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }
        }

        return status;
    }

    static void U_UART3_Receive(volatile uint8_t * rx_buf, uint16_t rx_num)
    {
        /* This function should be called in SRMK3==1 */
        g_uart3_rx_ready_flag = false;
        R_UART3_Receive( (uint8_t *)rx_buf, rx_num );
        SRMK3 = 0U;        /* enable INTSR3 interrupt */
    }
    MD_STATUS U_UART3_Send_Wait(uint8_t * tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;

        if (tx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_tx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( portMAX_DELAY );[1
        }

        return status;
    }
    MD_STATUS U_UART3_Send_Receive_Wait(uint8_t * tx_buf, uint16_t tx_num, 
        volatile uint8_t * rx_buf, uint16_t rx_num,
        volatile uint8_t * p_err_type, TickType_t txrx_wait)
    {
        MD_STATUS status;

        if (tx_num < 1U || rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            g_uart3_tx_task = NULL;
            R_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( txrx_wait );

            if (false == g_uart3_tx_ready_flag)
            {
                U_UART3_Send_Stop();
            }

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }

            if (false == g_uart3_tx_ready_flag)
            {
                *p_err_type = 0U;
                status = MD_SEND_ERROR;
            }
        }

        return status;
    }
    /* End user code. Do not edit comment generated here */

    freertos_sampleprog2_gnurl78_c/src/r_cg_serial_user.c (以下では関数の順番を入れ替えています)

    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */

        if (0U == g_uart3_rx_error_type)
        {
            U_UART3_Receive_Stop();
            g_uart3_rx_ready_flag = true;

            vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );
        }

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_sendend(void)
    {
        /* Start user code. Do not edit comment generated here */
    #if 0 /* for debugging about MD_SEND_ERROR */
        return;
    #endif

        U_UART3_Send_Stop();
        g_uart3_tx_ready_flag = true;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_tx_task );

        /* End user code. Do not edit comment generated here */
    }

     

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

    DTC版のCONIOタスクのソースは以下の通りです。今回はFreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。また、割り込みのみ版に対してDTC版に特有の部分を赤文字にしています。)

    freertos_sampleprog1_gnurl78_c/src/frtos_skeleton/task_CONIO.c

    void task_CONIO(void * pvParameters)
    {
    /* ...略... */

        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        MD_STATUS status;
        uint32_t cnt;

        INTERNAL_NOT_USED( pvParameters );

        U_UART3_Using_DTC_Start();

        cnt = 0;
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );

            if (0 == cnt)
            {
                status = U_UART3_Send_Receive_Wait(
                             (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE),
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );

            }
            else
            {
                status = U_UART3_Receive_Wait(
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
            }
            cnt++;

            nop();  /* for breakpoint, check timing chart on Simulator GUI */

            if (MD_OK == status)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (MD_RECV_TIMEOUT == status)
                {
                    vPrintString( "Recv Timeout Error\n" );
                }
                else if (MD_RECV_ERROR == status)
                {
                    if (SCI_EVT_RXDTC_SWOR & err_type)
                    {
                        vPrintString( "DTC Software Overrun Error\n" );
                    }
                    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" );
                    }
                }
                else if (MD_SEND_ERROR == status)
                {
                    vPrintString( "Send 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;

                U_UART3_Receive_ClearError(); /* if not called, will be called internally */

                cnt = 0;
                continue;
            }

            status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );

            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }

    /* ...略... */
    }

    コード生成されたシリアル処理のユーザ記述部(抜粋)のソースは以下の通りです。(FreeRTOSのAPI関数(のラッパー関数)を呼び出している箇所を赤文字にしています。) (なお、受信完了で受信割り込みを止めるようにしています。)

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial.c

    /* Start user code for adding. Do not edit comment generated here */
    ...略...
    void U_UART3_Using_DTC_Start(void)
    {
        R_UART3_Start();
        U_UART3_Receive_Stop();
    }
    MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, 
        volatile uint8_t * p_err_type, TickType_t rx_wait)
    {
        MD_STATUS status = MD_OK;

        if (rx_num < 1U || 255U < rx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( rx_wait );

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }
        }

        return status;
    }

    static void U_UART3_Receive(volatile uint8_t * rx_buf, uint16_t rx_num)
    {
        /* This function should be called in SRMK3==1 */
        g_uart3_rx_ready_flag = false;
        gp_uart3_rx_address = &g_uart3_rx_error_data;
        U_DTCD0_UserInit( rx_buf, rx_num );
        SRMK3 = 0U;        /* enable INTSR3 interrupt */
        SREMK3 = 0U;       /* enable INTSRE3 interrupt */
    #if 0 /* for debugging about SCI_EVT_RXDTC_SWOR */
        SRIF3 = 1U; /* ATTENTION: signal from SAU to DTC cannot be masked */
    #endif
        R_DTCD0_Start();
    }
    MD_STATUS U_UART3_Send_Wait(uint8_t * tx_buf, uint16_t tx_num)
    {
        MD_STATUS status = MD_OK;

        if (tx_num < 1U || 256U < tx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_tx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( portMAX_DELAY );
        }

        return status;
    }
    MD_STATUS U_UART3_Send_Receive_Wait(uint8_t * tx_buf, uint16_t tx_num, 
        volatile uint8_t * rx_buf, uint16_t rx_num,
        volatile uint8_t * p_err_type, TickType_t txrx_wait)
    {
        MD_STATUS status;

        if (tx_num < 1U || 256U < tx_num || rx_num < 1U || 255U < rx_num)
        {
            status = MD_ARGERROR;
        }
        else
        {
            if (0U != g_uart3_rx_error_type)
            {
                U_UART3_Receive_ClearError();
            }

            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            g_uart3_tx_task = NULL;
            U_UART3_Send( tx_buf, tx_num );

            /* Wait for a notification from the interrupt/callback */
            ulTaskNotifyTake_R_Helper( txrx_wait );

            if (false == g_uart3_tx_ready_flag)
            {
                U_UART3_Send_Stop();
            }

            status = U_UART3_Receive_Complete( p_err_type );

            if (MD_RECV_TIMEOUT == status)
            {
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Clear an unhandled notification from timeout till stop */
                ulTaskNotifyTake_R_Helper( 0 );
            }

            if (false == g_uart3_tx_ready_flag && SCI_EVT_RXDTC_SWOR != g_uart3_rx_error_type)
            {
                *p_err_type = 0U;
                status = MD_SEND_ERROR;
            }
        }

        return status;
    }
    /* End user code. Do not edit comment generated here */

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c (以下では関数の順番を入れ替えています)

    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */

        if (0U == g_uart3_rx_error_type)
        {
            U_UART3_Receive_Stop();
            g_uart3_rx_ready_flag = true;

            vTaskNotifyGiveFromISR_R_Helper( &g_uart3_rx_task );
        }

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_sendend(void)
    {
        /* Start user code. Do not edit comment generated here */
    #if 0 /* for debugging about MD_SEND_ERROR */
        return;
    #endif

        U_UART3_Send_Stop();
        g_uart3_tx_ready_flag = true;

        vTaskNotifyGiveFromISR_R_Helper( &g_uart3_tx_task );

        /* End user code. Do not edit comment generated here */
    }

    なお、DTC版では、以前の投稿と同様に、CC-RL/GNURL78の最適化オプションで未使用の変数/関数を削除する最適化を実施することを前提にして、マクロの小技を使用して、割り込み処理ルーチンを差し替えています。(以下では以前の投稿には無かった処理を追加した部分を赤文字にしています。)

    freertos_sampleprog1_gnurl78_c/src/r_cg_serial_user.c

    /* Start user code for global. Do not edit comment generated here */
    ...略...
    R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_receive)
    R_CG_DEFAULT_ISR_UNUSED(r_uart3_interrupt_send)
    #define r_uart3_interrupt_receive r_uart3_interrupt_receive_UNUSED
    #define r_uart3_interrupt_send r_uart3_interrupt_send_UNUSED
    /* End user code. Do not edit comment generated here */

    void r_uart3_interrupt_receive(void) ← コード生成機能が生成した割り込み関数(使用しない)
    {
    ...略...
    }

    void r_uart3_interrupt_send(void) ← コード生成機能が生成した割り込み関数(使用しない)
    {
    ...略...
    }

    ...略...

    /* Start user code for adding. Do not edit comment generated here */
    #undef r_uart3_interrupt_receive
    #undef r_uart3_interrupt_send
    R_CG_REDEFINE_ISR(r_uart3_interrupt_receive)
    R_CG_REDEFINE_ISR(r_uart3_interrupt_send)

    void r_uart3_interrupt_receive(void)
    {
        uint8_t err_type;

        /* For the case that INTSR3 is accepted prior to INTSRE3. */
        err_type = (uint8_t)(SSR13 & 0x0007U);
        SIR13 = (uint16_t)err_type;

        if (U_DTCD0_Is_Operating() || (0U < U_DTCD0_Get_Count()))
        {
            /* Unfortunately INTSR3 is accepted before DTC0 is enabled. */
            err_type = SCI_EVT_RXDTC_SWOR;
        }

        if (err_type != 0U)
        {
            r_uart3_callback_error(err_type);
        }

        r_uart3_callback_receiveend();
    }

    void r_uart3_interrupt_send(void)
    {
        /* DTC has finished data transfer operation. (i.e. The last data has been
         * written to SDR register.) But the data may stay in either SDR register
         * or shift register.
         */

        /* It is preferred that next interrupt will be a transmission end interrupt
         * (i.e. not only SDR register but also shift register become empty) than
         * a SDR register empty interrupt.
         */
        SMR12 &= ~_0001_SAU_BUFFER_EMPTY;

        /* In normal case of interrupt, UART has just finished data transmission
         * operation at 2nd interrupt. But in abnormal case of after long or very
         * long time pending of data transfer completion interrupt, though at 1st
         * interrupt, SDR register may already become empty or, moreover, shift
         * register may already have finished data transmission operation. In the
         * latter case, it is not clear whether data transmission end interrupt
         * will occur or not, so the following check is useful.
         */
        if ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U)
        {
            /* UART has finished data transmission operation. Note that one more
             * interrupt may occur by above change of SMR register setting unless
             * transmission interrupt becomes disabled. (In this code, it becomes
             * disabled in the following callback function.)
             */
            r_uart3_callback_sendend();
        }
        else
        {
        /* If ((SSR12 & _0040_SAU_UNDER_EXECUTE) == 0U) changes from false to true
         * just here, changing SMR register setting at the following timing may
         * lose data transmission end interrupt.
         *
         *     SMR12 &= ~_0001_SAU_BUFFER_EMPTY;
         */
        }
    }
    /* End user code. Do not edit comment generated here */

    [追記]

    なお、4連投の1つ目は、こちらです。

  • こんにちは。NoMaYです。

    以下、別スレッド『e2 studio v7.5.0のFreeRTOS ProjectでVisual Expression+Renesas RX Simulator/TB-RX65Nで試せるSample Programを作ってみた』へ投稿した内容の転記です。そもそもは、こちらのスレッドが発端でしたので、、、

    RL78/G14 Fast Prototyping Boardで、同じようなSample Programを、チョコさんのリングバッファのソースコードを使って作ろうとしていたところ、RTOS使用時は、そのソースの通常処理側と割り込み処理側の排他制御のやり方を変えないと拙い、ということに気付きました。そして、それはFITでも同様な筈なのでは、と気付きました。(FITには、以前からFreeRTOSパッケージがありますが、その時点から対策されていなかったのでは無いでしょうか、、、)

    RTOS未使用時は大丈夫だけれどもRTOS使用時は拙い排他制御のやり方(通常処理側と割り込み処理側の排他制御)

    ・個別の割り込みマスクフラグを使用して割り込み一旦禁止と割り込み再度許可を行う

    理由

    ・RTOSによるタスク切り替えが上記による割り込み禁止期間中に発生すると禁止期間がミリ秒オーダーになってしまう(タスク切り替えにより暫くの間は他タスクに行ってしまうから)

    対応

    ・RTOS側で用意されているクリティカルセクションへの出入り関数(FreeRTOSならtaskENTER_CRITICAL()とtaskEXIT_CRITICAL()など)を使用するように手を加える

    具体的には、チョコさんのリングバッファのソースコードですと、以下のような置き換えをする必要があります。(以下の場所以外にもあります。なお、RL78のFreeRTOSでは両出入り関数はDIとEIに帰着されるので、以下のように単純化してあります。他マイコンなどでは、可能性として、割り込み優先順位を操作することで、クリティカルセクション内でも通信割り込みを受け付けられるようにする実装も有り得ると思います。その場合は、以下のような単なる単なる置き換えにならないこともあるかと思います。)

    RL78のUARTへのリングバッファ追加/UART_3/UART0.c

    /*******************************************************************************
    * Function Name: get_blk
    * Description  : リングバッファから指定されたデータを読み出す(転送)
    * Arguments    : 格納ポインタ,データ数
    * Return Value : 残りデータ数
    *******************************************************************************/
    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */

        if ( ( 0 != number ) && ( 0 != g_rx_dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

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

        転送データ数を算出

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

            if ( number < g_rx_dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = g_rx_dtno;                    /* 転送数はバッファ内の全て */
                work = number - g_rx_dtno;          /* 残りデータ数を算出       */
            }

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

        データを転送

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

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                taskENTER_CRITICAL();
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= 0x0F;
                g_rx_dtno--;                        /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                taskEXIT_CRITICAL();
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    また、FITのR_SCI_RXモジュールにも同様な場所があります。

    。。。以後省略。。。

  • こんにちは。NoMaYです。

    UART受信にチョコさんのリングバッファのソースコードを使用する版をデバッグしていて、不具合が存在していたことに気付きました。以下のg_rx_dtnoという変数は割り込み処理で値が更新される変数なのですが、そのことは以下の4箇所で読み出された値が同じ値とは限らないということになります。このg_rx_dtnoという変数はリングバッファ内の受信済みデータ量を示す変数ですが、青文字のところで、要求データ数 number >= リングバッファ内データ量 g_rx_dtno が成立していても、赤文字のところに来るまでに、もしデータを受信してしまっていると number < g_rx_dtno となってしまっている場合があります。その場合には、要求データ数よりも多くのデータを 読み出しバッファ buff へ書き込んでしまいますので、(読み出しバッファを余分に確保していなかったのであれば)、バッファオーバーランが起きてしまいます。実際、以下の画面コピーの通り、起きてしまっているようでした。(表現が微妙なのは、アクセスブレークポイント機能やトレース機能を使おうとすると再現しなくなってしまうのみならず、読み出しバッファの値をチェックするようなif文を追加することでも再現しなくなってしまうという、トホホな状況に陥ってしまっていたからです。(ですので、この部分を対処したことで現象は起きなくなったのですが、デバッグしているプログラムには本当の原因が別にある、という可能性は残っています。))

    なお、先日投稿したRL78のコード生成機能で生成されたUART受信処理のコードと同様に、この区間でタスク切り替えが発生してしまった場合には他タスクへミリ秒オーダーで行ってしまうかもしれず、そうなると、ずっと先までバッファオーバーランしてしまうこともあるかもしれないと思いました。

    異常時(時々発生)の画面コピー


    正常時(期待値)の画面コピー


    RL78コード生成へのリングバッファ追加
    japan.renesasrulz.com/cafe_rene/m/sample_program/306

    RL78のUARTへのリングバッファ追加/UART_3/UART0.c

    対処前:
    ・ g_rx_dtnoは割り込み処理で値が更新される変数なので、以下の4箇所(太字箇所(黒/青/赤/黒))で読み出された値が同じ値とは限らない
    ・ 青文字で、要求データ数 number >= リングバッファ内データ量 g_rx_dtno でも、赤文字で、number < g_rx_dtno の場合が有り得る
    ・ その場合、要求データ数よりも多くのデータを 読み出しバッファ buff へ書き込んでしまうのでバッファオーバーランが起きてしまう

    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */

        if ( ( 0 != number ) && ( 0 != g_rx_dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

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

        転送データ数を算出

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

            if ( number < g_rx_dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = g_rx_dtno;                    /* 転送数はバッファ内の全て */
                work = number - g_rx_dtno;          /* 残りデータ数を算出       */
            }

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

        データを転送

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

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= 0x0F;
                g_rx_dtno--; ←ここは問題は無いです /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    対処案: (追加箇所/変更箇所を赤字にしています)

    uint8_t get_blk(uint8_t * const buff, uint8_t number)
    {

        uint8_t work;
        uint8_t dtno;                               /* リングバッファ内データ量 */
        uint8_t cnt;                                /* 転送用カウンタ           */
        uint8_t * gp_buff;                          /* 転送用ポインタ           */

        work = number;                              /* 要求データ数をセット     */
        dtno = g_rx_dtno;                           /* 変数がvolatileなので保持 */

        if ( ( 0 != number ) && ( 0 != dtno ) )
        {                               /* 転送データがある場合                 */

            gp_buff = buff;                         /* 転送先ポインタを設定     */

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

        転送データ数を算出

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

            if ( number < dtno )
            {                           /* 要求数がデータ量以下の場合           */
                cnt = number;                       /* 転送データ数をセット     */
                work = 0;                           /* 残りデータ数は0にする    */
            }
            else
            {                           /* 要求数がデータ量以上の場合           */
                cnt = dtno;                         /* 転送数はバッファ内の全て */
                work = number - dtno;               /* 残りデータ数を算出       */
            }

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

        データを転送

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

            for (  ; cnt > 0 ; cnt-- )
            {
                *gp_buff = g_rx_buff[g_rx_rdpt];    /* リングバッファから転送   */
                SRMK0 = 1;                          /* INTSR0との排他制御       */
                g_rx_rdpt++;                        /* 読み出しポインタを更新   */
                g_rx_rdpt &= RX_BUFF_SIZE_BITS;
                g_rx_dtno--; ←ここはそのままです   /* データ数を-1            */
                SRMK0 = 0;                          /* INTSR0との排他制御終了   */
                gp_buff++;                          /* 転送ポインタを更新       */

            }
        

        }

        return( work );

    }

    以下、デバッグ時の画面コピーです。(いつものようにRL78シミュレータでデバッグしていました。) なお、初めの方で書いた「読み出しバッファの値をチェックするようなif文」とは、以下のことです。(ちなみに、ワーニングが出ますが、今回は放置しました。(CC-RL V1.02ですが。))

    if ((*(volatile __near uint8_t *)0xf500e) != 0)
    {
        nop();
    }

    CS+


    シミュレータGUI


    アクセスブレークポイント機能を使おうとしたら再現しなくなってしまいました(以下は受信オーバーランエラーになった画面)


    トレース機能を使おうとしたら再現しなくなってしまいました(以下は正常動作している画面)

     

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

    このスレッドで以前に投稿した、RX-TBの自作のFreeRTOS Sample ProgramをRL78/G14 Fast Prototyping Boardへ移植したSample Programですが、今回、UART受信にチョコさんのリングバッファのソースコードを使用する版を作成してみました。(なお、UART送信はDTCで行う方ではなく割り込みのみで行う方です。)

    思い立ってから随分と時間が経ってしまったのですが、自分の感覚的には、少し凝って(?)以下の動作を目指したのが拙かったかなぁ、と思っているところです。(といっても、たかが数十行のことですので、自分のコーディング能力の無さによるところが大きいですけど、、、)

    ● 受信エラーやリングバッファオーバーフローが発生した時点で即それ以降のリングバッファからの(正常に)受信済みのデータの読み出しが出来なくなるようなエラー処理では無くて、それらが発生していても読み出すデータの数が(正常に)受信済みのデータの数より多くなければ読み出しが出来るようにしてみた。

    以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.03/e2 studio v7.6.0+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱)(当方特有の事情でCC-RL V1.02を使用))(GNURL78版はe2 studio v7.6.0+GNURL78 2019q4(4.9.2.201904)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX-TBの自作のFreeRTOSプロジェクトもどきSample Programに似せてあります。

    rl78g14fpb_freertos_sampleprog3_ccrl_c_csplus_20200220.zip (*1)    459KB
    rl78g14fpb_freertos_sampleprog3_gnurl78_c_e2v760_20200220.zip (*1,*2)   425KB

    含まれるプロジェクト
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

    *1:実機用のデバッガプロパティ設定(CS+)や.launchファイル設定(e2 studio)は当方特有の事情でオンボードエミュレータが使えない為に未確認です。(ひと通りは設定してあります。)

    *2:リンカスクリプトには別スレッド「GNURL78でconst領域/Mirror領域をちょっと安全に使えるようにlinker scriptのASSERT()で小技(TIPS)を考えてみた」に投稿した内容を反映してあります。

    この後、2投目、3投目は以下の通りです。

    2投目

    ・受信リングバッファ版のCONIOタスクのソース
    (コード生成されたシリアル処理のユーザ記述部のソースは省略します。)
    (なお、LED0タスクとLED1タスクにのソースに関しては今までと同じです。)

    ・チョコさんのリングバッファのソースの変更点(ソース比較ツールの画面コピー)

    もともとのソースがUART番号依存していないことを利用してUART[番号].cに以下の#defineを記述してUART3対応にしてみました。(なお、もともとはr_cg_serial_user.cのr_uart[番号]_callback_softwareoverrun()に記述されていた処理を、UART[番号].cにuartx_callback_receivedata()を作成して、そちらへ移しています。)

    #define init_bf u_uart3_init_bf
    #define chk_status u_uart3_chk_status
    #define get_data u_uart3_get_data
    #define get_blk u_uart3_get_blk
    #define g_rx_buff g_uart3_rx_buff
    #define g_rx_rdpt g_uart3_rx_rdpt
    #define g_rx_dtno g_uart3_rx_dtno
    #define g_rx_status g_uart3_rx_status
    #define uartx_callback_receivedata u_uart3_callback_receivedata
    #define RX_BUFF_SIZE UART3_RX_BUFF_SIZE
    #define RX_DTPT_MASK (UART3_RX_BUFF_SIZE - 1)

    3投目

    ・Renesas RL78 Simulatorでの実行例(画面コピー: CC-RL+CS+ 及び GNURL78+e2 studio)

    正常系 (シリアル送信データファイルはsim_rl78_serial_data_ok_3.serを使用)
    異常系 (シリアル送信データファイルはsim_rl78_serial_data_err_overrun.serを使用)

    ・Renesas RL78 Simulatorのトレース機能で調べた割り込み禁止時間の例(画面コピー: CC-RL+CS+ のみ)

    タスク切り替え処理用割り込み(割り込み禁止)で4μs強の時間が掛かっている。(1バイトの転送時間が5.5μsなのですが、残念ながら、連続転送させるとオーバーランエラーが発生します。)

    ・Renesas RL78 Simulatorのカバレッジ機能でソースウィンドウに未実行箇所を表示させた例(画面コピー: CC-RL+CS+ のみ)

    以下、実機で実行させた時のTeraTermの画面コピーと送信したデータのファイルです。(割り込み禁止時間との兼ね合いで1msの送信遅延を挿入しています。さすがに1msも必要無いのですが、TeraTermで設定出来る最小時間が1msですので、そのように設定しています。)



    4061.teraterm_serial_data_send_with_1ms_interval_20200224.txt
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ123456abcdefUVWXYZ_
    END_
    

     

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

    受信リングバッファ版のCONIOタスクのソースは以下の通りです。今回も前回同様、FreeRTOSのAPI関数(のラッパー関数)をタスク側ソースではなくコード生成側ソースで呼ぶようにしています。(コード生成側ソースの関数を呼び出している箇所を青文字にしています。また、割り込みのみ版やDTC版に対して受信リングバッファ版に特有の部分を赤文字にしています。)

    freertos_sampleprog3_gnurl78_c/src/frtos_skeleton/task_CONIO.c

    void task_CONIO(void * pvParameters)
    {
    /* ...略... */

        volatile uint8_t recv_buff[CON_RECV_DEMO_SIZE + 1];
        volatile uint8_t err_type;
        MD_STATUS status;
        uint32_t cnt;

        INTERNAL_NOT_USED( pvParameters );

        U_UART3_Using_RecvRingBuff_Start();

        cnt = 0;
        for (;;)
        {
            memset( (char *)recv_buff, 0, CON_RECV_DEMO_SIZE + 1 );

            if (0 == cnt)
            {
                status = U_UART3_Send_Receive_Wait(
                             (uint8_t *)CON_START_MESSAGE, sizeof(CON_START_MESSAGE),
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
            }
            else
            {
                status = U_UART3_Receive_Wait(
                             recv_buff, CON_RECV_DEMO_SIZE, &err_type, CON_RECV_TIMEOUT_MS
                         );
            }
            cnt++;

            nop();  /* for breakpoint, check timing chart on Simulator GUI */

            if (MD_OK == status)
            {
                taskENTER_CRITICAL();
                vPrintString( "Received Message: " );
                vPrintString( (char *)recv_buff );
                vPrintString( "\n" );
                taskEXIT_CRITICAL();
            }
            else
            {
                if (MD_RECV_TIMEOUT == status)
                {
                    vPrintString( "Recv Timeout Error\n" );
                }
                else if (MD_RECV_ERROR == status)
                {
                    if (SCI_EVT_RXBUF_OVFL & err_type)
                    {
                        vPrintString( "Ring Buffer Overflow Error\n" );
                    }
                    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;

                U_UART3_Receive_ClearError(); /* if not called, will be called internally */

                cnt = 0;
                continue;
            }

            status = U_UART3_Send_Wait( (uint8_t *)recv_buff, CON_RECV_DEMO_SIZE );

            nop();  /* for breakpoint, check timing chart on Simulator GUI */
        }

    /* ...略... */
    }

    チョコさんのリングバッファのソースの変更点は、以下のソース比較ツールの画面コピーの通りです。もともとのソースがUART番号依存していないことを利用してUART[番号].cに以下の#defineを記述してUART3対応にしてみました。(なお、もともとはr_cg_serial_user.cのr_uart[番号]_callback_softwareoverrun()に記述されていた処理を、UART[番号].cにuartx_callback_receivedata()を作成して、そちらへ移しています。)

    #define init_bf u_uart3_init_bf
    #define chk_status u_uart3_chk_status
    #define get_data u_uart3_get_data
    #define get_blk u_uart3_get_blk
    #define g_rx_buff g_uart3_rx_buff
    #define g_rx_rdpt g_uart3_rx_rdpt
    #define g_rx_dtno g_uart3_rx_dtno
    #define g_rx_status g_uart3_rx_status
    #define uartx_callback_receivedata u_uart3_callback_receivedata
    #define RX_BUFF_SIZE UART3_RX_BUFF_SIZE
    #define RX_DTPT_MASK (UART3_RX_BUFF_SIZE - 1)






     

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

    以下、Renesas RL78 Simulatorでの実行例の画面コピーです。

    正常系 (CC-RL+CS+の場合) (シリアル送信データファイルはsim_rl78_serial_data_ok_3.serを使用)



    正常系 (GNURL78+e2 studioの場合) (シリアル送信データファイルはsim_rl78_serial_data_ok_3.serを使用)



    異常系 (CC-RL+CS+場合) (シリアル送信データファイルはsim_rl78_serial_data_err_overrun.serを使用)



    異常系 (GNURL78+e2 studioの場合) (シリアル送信データファイルはsim_rl78_serial_data_err_overrun.serを使用)



    なお、今回、Renesas RL78 Simulatorのトレース機能を使用して、もっとも割り込み禁止時間が長くなっていると思われる、ソフトウェア割り込み(もどき)でのタスク切り替え処理用割り込みの時間を調べてみたところ、4μs強の時間が掛かっていました。

    今回のUART通信では、ボーレートが2Mbpsで1バイトが11ビット(start:1,data:8,parity:1,stop1)ですので、1バイトの転送時間が5.5μsなのですが、残念ながら、連続転送させるとオーバーランエラーが発生します。先ほどの割り込み禁止時間が4μs強ですので間に合っても良さそうですが、今になって拙かったかなと思ったのは、多重割り込みは使用していないものの割り込み禁止期間中に割り込みが複数発生することはありますので、今回は割り込み優先順位が以下の通りになっている為に受信割り込みの受付がもっとも後回しにされてしまっている筈であり、そのせいでオーバーランエラーが発生してしまうのかな、と思い始めています。

    割り込み優先順位: 役割表記(上側)と割り込み要求名表記(下側)

    ソフトウェア割り込み(もどき)でのタスク切り替え処理用割り込み > ティック割り込み > 送信割り込み > 受信割り込み

    INTWDTI > INTIT > INTST3 > INTSR3

    以下、トレース表示の画面コピーです。

    019ms153us406ns - 019ms149us031ns = 4us375ns



    また、今回、Renesas RL78 Simulatorのカバレッジ機能を使用してみましたが、ソースウィンドウで未実行箇所が容易に分かるのはちょっといいかも、と思いました。(ちなみに、コンパイラの最適化オプションが最適化ありのままですと、ソース行とコンパイルされたコードの対応関係が崩れて訳が分からなくなりますので、コンパイラの最適化オプションをデバッグ優先に変更しておく必要があります。)

    以下、カバレッジ表示の画面コピーです。

    その1:

    sim_rl78_serial_data_ok_3.ser ⇒ if文内に未実行箇所あり


    sim_rl78_serial_data_ok_3.ser + sim_rl78_serial_data_err_overrun.ser ⇒ if文内にまだ未実行箇所あり


    sim_rl78_serial_data_ok_3.ser + sim_rl78_serial_data_err_overrun.ser + sim_rl78_serial_data_err_timeout.ser ⇒ 全て実行


    その2:

    sim_rl78_serial_data_ok_3.ser ⇒ if文内に未実行箇所あり


    sim_rl78_serial_data_ok_3.ser + sim_rl78_serial_data_err_timeout.ser ⇒ if文内にまだ未実行箇所あり


    sim_rl78_serial_data_ok_3.ser + sim_rl78_serial_data_err_timeout.ser + sim_rl78_serial_data_err_uart_format.ser ⇒ 全て実行


    その他:

    コンパイラの最適化オプションで未使用の変数/関数を削除する最適化を実施しているのでコードの全く無い関数もあります


    CS+のヘルプの画面コピー


    [追記]

    なお、3連投の1つ目は、こちらです。

  • こんにちは。NoMaYです。

    FreeRTOSのタスク通知のAPIには、0以外の32ビット値を割り込み処理側から通常処理側(タスク処理側)へ通知することが出来る以下の関数があるのですが、それを使って先日投稿したSample Programをちょっと書き換えてみました。

    割り込み処理側(送り側)(今回から使用)

    BaseType_t xTaskNotifyFromISR( TaskHandle_t xTaskToNotify, uint32_t ulValue, eNotifyAction eAction, BaseType_t *pxHigherPriorityTaskWoken );

    通常処理側(タスク処理側)(受け側)(以前から使用)

    uint32_t ulTaskNotifyTake( BaseType_t xClearCountOnExit, TickType_t xTicksToWait );

    以下、プロジェクトのファイル一式です。(CC-RL版はCS+ V8.03/e2 studio v7.6.0+CC-RL V1.02でビルド(e2 studio用.project/.cproject等を同梱)(当方特有の事情でCC-RL V1.02を使用))(GNURL78版はe2 studio v7.6.0+GNURL78 2019q4(4.9.2.201904)でビルド)(共にzipファイルをe2 studioに直接インポート可能) プロジェクト構造は極力RX-TBの自作のFreeRTOSプロジェクトもどきSample Programに似せてあります。(なお、タスク通知に絡んだ変更の他にも、FreeRTOS kernelのソースをv10.2.1→v10.3.1へ更新したり、UART送信の転送モードやUART受信割り込み/UART受信エラー割り込みの優先順位、あれこれ細かな部分、等の変更も行っています。)

    rl78g14fpb_freertos_sampleprog1_ccrl_c_csplus_20200407.zip (*1)    456KB
    rl78g14fpb_freertos_sampleprog2_ccrl_c_csplus_20200407.zip (*1)    447KB
    rl78g14fpb_freertos_sampleprog3_ccrl_c_csplus_20200407.zip (*1)    507KB
    rl78g14fpb_freertos_sampleprog1_gnurl78_c_e2v760_20200407.zip (*1,*2)   422KB
    rl78g14fpb_freertos_sampleprog2_gnurl78_c_e2v760_20200407.zip (*1,*2)   413KB
    rl78g14fpb_freertos_sampleprog3_gnurl78_c_e2v760_20200407.zip (*1,*2)   474KB

    sampleprog1: UART送受信をDTCで行う版
    sampleprog2: UART送受信を割り込みのみで行う版
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

    *1:実機用のデバッガプロパティ設定(CS+)や.launchファイル設定(e2 studio)は当方特有の事情でオンボードエミュレータが使えない為に未確認です。(ひと通りは設定してあります。)

    *2:リンカスクリプトには別スレッド『GNURL78でconst領域/Mirror領域をちょっと安全に使えるようにlinker scriptのASSERT()で小技(TIPS)を考えてみた』に投稿した内容を反映してあります。

    今回使ったAPI関数の日本語ドキュメントは以下にあります。

    xTaskNotifyFromISR() (今回から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#xtasknotify-and-xtasknotifyfromisr-api-functions


    ulTaskNotifyTake() (以前から使用)
    docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-notification.html#ultasknotifytake-api-function


    これを以下のように使ってみました。(直接は使わずにラッパー関数を作って使うようにしました。)

    freertos_sampleprog2_ccrl_c/src/frtos_startup/freertos_start.c (sampleprog1,3も同じ)

    BaseType_t xTaskNotifyFromISR_R_Helper(TaskHandle_t *pxTask, uint32_t ulValue)
    {
        if (NULL != *pxTask)
        {
            BaseType_t sHigherPriorityTaskWoken = pdFALSE;

            /* Notify the task that the interrupt/callback is complete. */
            xTaskNotifyFromISR( *pxTask, ulValue, eSetValueWithOverwrite, &sHigherPriorityTaskWoken );

            /* There are no interrupt/callback in progress, so no tasks to notify. */
            *pxTask = NULL;

            portYIELD_FROM_ISR( sHigherPriorityTaskWoken );
        }

        return pdTRUE;

    } /* End of function xTaskNotifyFromISR_R_Helper() */
    uint32_t ulTaskNotifyTake_R_Helper(TickType_t xTicksToWait)
    {
        /* Wait to be notified that the interrupt/callback is complete. */
        return ulTaskNotifyTake( pdTRUE, xTicksToWait );

    } /* End of function ulTaskNotifyTake_R_Helper() */

    freertos_sampleprog2_ccrl_c/src/r_cg_serial_user.c (sampleprog1,3も同様)

    static void r_uart3_callback_receiveend(void)
    {
        /* Start user code. Do not edit comment generated here */

        if (0U == g_uart3_rx_error_type)
        {
            U_UART3_Receive_Stop();
            g_uart3_rx_ready_flag = true;

            xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | MD_OK );
        }

        /* End user code. Do not edit comment generated here */
    }
    static void r_uart3_callback_error(uint8_t err_type)
    {
        /* Start user code. Do not edit comment generated here */

        U_UART3_Receive_Stop();
        g_uart3_rx_error_type = err_type;

        xTaskNotifyFromISR_R_Helper( &g_uart3_rx_task, 0x10000 | (err_type << 8) | MD_RECV_ERROR );

        /* End user code. Do not edit comment generated here */
    }

    freertos_sampleprog2_ccrl_c/src/r_cg_serial.c (sampleprog1,3も同様)

    MD_STATUS U_UART3_Receive_Wait(volatile uint8_t * rx_buf, uint16_t rx_num, 
        volatile uint8_t * p_err_type, TickType_t rx_wait)
    {
        MD_STATUS status = MD_OK;
        uint32_t value;

        if (rx_num < 1U)
        {
            status = MD_ARGERROR;
        }
        else
        {
            g_uart3_rx_task = xTaskGetCurrentTaskHandle_R_Helper();
            U_UART3_Receive( rx_buf, rx_num );

            /* Wait for a notification from the interrupt/callback */
            value = ulTaskNotifyTake_R_Helper( rx_wait );

            if (0U == value)
            {
                /* Timeout */
                U_UART3_Receive_Stop();
                g_uart3_rx_task = NULL;

                /* Check an unhandled notification from timeout till stop */
                value = ulTaskNotifyTake_R_Helper( 0 );
            }

            if (0U != value)
            {
                /* Normal receive end or receive error */
                *p_err_type = (value >> 8) & 0xFFU;
                status = value & 0xFFU;
            }
            else
            {
                /* Timeout */
                *p_err_type = SCI_EVT_RXWAIT_TMOT;
                status = MD_RECV_TIMEOUT;
            }
        }

        return status;
    }

    なお、以下の点を鑑み、UART受信割り込み/UART受信エラー割り込みの優先順位変更(sampleprog1,2,3)とUART送信の転送モード変更(sampleprog2,3)を行っています。

    本スレッド
    japan.renesasrulz.com/cafe_rene/f/forum21/5845/rl78-freertos-api-cc-rl-gnurl78/34294#34294

    今になって拙かったかなと思ったのは、多重割り込みは使用していないものの割り込み禁止期間中に割り込みが複数発生することはありますので、今回は割り込み優先順位が以下の通りになっている為に受信割り込みの受付がもっとも後回しにされてしまっている筈であり、そのせいでオーバーランエラーが発生してしまうのかな、と思い始めています。

    割り込み優先順位: 役割表記(上側)と割り込み要求名表記(下側)

    ソフトウェア割り込み(もどき)でのタスク切り替え処理用割り込み > ティック割り込み > 送信割り込み > 受信割り込み

    INTWDTI > INTIT > INTST3 > INTSR3


    チョコさんが以前に指摘されていたRL78コード生成機能によるCSI連続送信の終了の割り込みコードの問題を調べています
    japan.renesasrulz.com/cafe_rene/f/forum18/6217/rl78-csi/34513#34513

    今回の割り込みコードの問題はCSI連続送信でしたが、他のタイプの連続転送に関しても、コードを生成させて、生成コードを確認してみました。不具合原因のコードから推測すると、問題点の(2)の該当/非該当は以下の表のようになると思います。

    ...略...

    SAU UART連続送信モード(繰り返し送信モード)        該当?

    ...略...


    以下、上記に関してコード生成機能の設定を変更した箇所の画面コピーです。(以下の画面コピーはCC-RL+CS+のものですが、CC-RL+e2 studioでもGNURL78+e2 studioでも、同様です。)

    sampleprog1(UART送受信をDTCで行う版)


    sampleprog2(UART送受信を割り込みのみで行う版)とsample3(UART受信をリングバッファで行いUART送信を割り込みのみで行う版)


     

  • こんにちは。NoMaYです。

    これまで投稿してきたSample Programでは、コンパイラの最適化オプションで未使用の変数/関数を削除する最適化を実施してデータサイズ/コードサイズを削減していたのですが、今回、FreeRTOSConfig.hを以下のファイル比較ツールの画面コピーの通りに変更してみたところ、更に削減することが出来ました。

    例えば、sampleprog2(UART送受信を割り込みのみで行う版)では、CC-RLで表示されるPROGRAM SECTIONのサイズが8247(2037H)→4274(10b2H)へ、GNURL78で表示されるtextセクションのサイズが11240→5898へ、となりました。(ちなみに、CC-RLとGNURL78では集計の発想が異なる為、双方を直接は比較出来ません。)

      CC-RL V1.02 GNURL78 2019q4(4.9.2.201904)  
      PROGRAM
    (Bytes)
    ROMDATA
    (Bytes)
    RAMDATA
    (Bytes)
    text
    (Bytes)
    data
    (Bytes)
    bss
    (Bytes)
     
     
      今まで 今回 今まで 今回 今まで 今回 今まで 今回 今まで 今回 今まで 今回  
    sampleprog1 8475 4502 725 665 9246 9198 11578 6236 188 188 9198 9150  
    sampleprog2 8247 4274 697 637 9197 9149 11240 5898 132 132 9196 9148  
    sampleprog3 8658 4685 713 653 9198 9170 11730 6386 132 132 9218 9170  

    sampleprog1: UART送受信をDTCで行う版
    sampleprog2: UART送受信を割り込みのみで行う版
    sampleprog3: UART受信をリングバッファで行いUART送信を割り込みのみで行う版

    なお、今回試したFreeRTOSConfig.hとビルドした結果のmapファイル等を以下のzipファイルに固めました。

    rl78g14fpb_freertos_sampleprogs_issue_20200413.zip

    以下、画面コピーです。

    zipファイル内のファイル一覧


    FreeRTOSConfig.hの変更内容: 今まで(左側ペイン)と今回(右側ペイン)


    サイズ比較結果例: sampleprog2: CC-RL: 今まで(左側ペイン)と今回(右側ペイン)


    サイズ比較結果例: sampleprog2: GNURL78: 今まで(左側ペイン)と今回(右側ペイン)


     

  • こんにちは。NoMaYです。

    FreeRTOSConfig.hを変更して、FreeRTOS kernel内で動的メモリ確保が行われないようにしてみました。(ちなみに、FreeRTOS kernelのメモリマネージャのheap_1.cをビルドから除外したり、xTaskCreate()の代わりにxTaskCreateStatic()を使用したり、vApplicationGetIdleTaskMemory()を実装したり、といった変更もする必要があります。)

    なお、今回試したFreeRTOSConfig.h等のソースとビルドした結果のmapファイル等を以下のzipファイルに固めました。

    rl78g14fpb_freertos_sampleprogs_issue_20200414.zip

    ソースの主な変更箇所は以下の通りです。

    src/frtos_config/FreeRTOSConfig.h (以下の設定を追加)

    #define configSUPPORT_DYNAMIC_ALLOCATION    0
    #define configSUPPORT_STATIC_ALLOCATION     1

    src/frtos_startup/freertos_start.h (xTaskCreateStatic()を呼び出すラッパーマクロを用意)

    #define xTaskCreateStatic_R_Helper(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask) \
    do{ \
        static StaticTask_t xTCBBuffer; \
        static StackType_t  xStackBuffer[usStackDepth]; \
        TaskHandle_t xCreatedTask; \
        xCreatedTask = xTaskCreateStatic( pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, xStackBuffer, &xTCBBuffer ); \
        if (NULL != (TaskHandle_t *)pxCreatedTask) \
        { \
            *(TaskHandle_t *)pxCreatedTask = xCreatedTask; \
        } \
        (void) &xCreatedTask; \
    }while (0)

    src/frtos_startup/freertos_object_init.c (TaskCreate()の代わりにxTaskCreateStatic()を上記ラッパーマクロ経由で使用)

    void Kernel_Object_init (void)
    {
        /************** task creation ****************************/

        xTaskCreateStatic_R_Helper(main_task, "MAIN_TASK", 512, NULL, 1, NULL);

        xTaskCreateStatic_R_Helper(task_LED0, "task_LED0", 512, NULL, 2, NULL);

        xTaskCreateStatic_R_Helper(task_LED1, "task_LED1", 512, NULL, 2, NULL);

        xTaskCreateStatic_R_Helper(task_CONIO, "task_CONIO", 512, NULL, 3, NULL);

        /************** semaphore creation ***********************/

        /************** queue creation ***************************/

        /************** software time creation **************************/

        /************** event groups creation ********************/

        /************** stream buffer creation *************************/

        /************** message buffer creation *********************/

    } /* End of function Kernel_Object_init()*/

    src/frtos_startup/freertos_start.c (vApplicationGetIdleTaskMemory()を実装)

    #if( configSUPPORT_STATIC_ALLOCATION != 0 )
    void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer, StackType_t **ppxIdleTaskStackBuffer, uint32_t *pulIdleTaskStackSize )
    {
        static StaticTask_t xIdleTaskTCBBuffer;
        static StackType_t  xIdleTaskStackBuffer[configMINIMAL_STACK_SIZE];

        *ppxIdleTaskTCBBuffer = &xIdleTaskTCBBuffer;
        *ppxIdleTaskStackBuffer = xIdleTaskStackBuffer;
        *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;

    } /* End of vApplicationGetIdleTaskMemory() */
    #endif /* configSUPPORT_STATIC_ALLOCATION != 0 */

    なお、上記ソースでスタックサイズをconfigMINIMAL_STACK_SIZEとしたのは、以下のFreeRTOS kernelのソースでconfigMINIMAL_STACK_SIZEとなっていたので、単にそれを踏襲したからです。

    src/FreeRTOS/Source/tasks.c

    void vTaskStartScheduler( void )
    {
    BaseType_t xReturn;

        /* Add the idle task at the lowest priority. */
        #if( configSUPPORT_STATIC_ALLOCATION == 1 )
        {
            StaticTask_t *pxIdleTaskTCBBuffer = NULL;
            StackType_t *pxIdleTaskStackBuffer = NULL;
            uint32_t ulIdleTaskStackSize;

            /* The Idle task is created using user provided RAM - obtain the
            address of the RAM then create the idle task. */
            vApplicationGetIdleTaskMemory( &pxIdleTaskTCBBuffer, &pxIdleTaskStackBuffer, &ulIdleTaskStackSize );
            xIdleTaskHandle = xTaskCreateStatic(    prvIdleTask,
                                                    configIDLE_TASK_NAME,
                                                    ulIdleTaskStackSize,
                                                    ( void * ) NULL, /*lint !e961.  The cast is not redundant for all compilers. */
                                                    portPRIVILEGE_BIT, /* In effect ( tskIDLE_PRIORITY | portPRIVILEGE_BIT ), but tskIDLE_PRIORITY is zero. */
                                                    pxIdleTaskStackBuffer,
                                                    pxIdleTaskTCBBuffer ); /*lint !e961 MISRA exception, justified as it is not a redundant explicit cast to all supported compilers. */
            ...略...
        }
        #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 */
        ...略...
    }

    以下、画面コピーです。

    FreeRTOS kernelのメモリマネージャのheap_1.cをビルドから除外



    zipファイル内のファイル一覧


    FreeRTOSConfig.hの変更内容: 前回(左側ペイン)と今回(右側ペイン)


    freertos_object_init.cの変更内容: 前回(左側ペイン)と今回(右側ペイン)


    サイズ比較結果例: sampleprog2: CC-RL: 前回(左側ペイン)と今回(右側ペイン)


    サイズ比較結果例: sampleprog2: GNURL78: 前回(左側ペイン)と今回(右側ペイン)

     

  • こんにちは。NoMaYです。

    昨日のラッパーマクロですが、mapファイルの内容が少し分かり易くなるかなと思い、ちょっと変更してみました。あと、FreeRTOS kernelのAPI関数のスタックサイズの尺度は4バイト単位(RX)だったり2バイト単位(RL78)だったりするのですが、何ヶ月経っても慣れません(違和感が無くなりません)ので、1バイト単位の数値をFreeRTOS kernelの流儀の数値へ変換するマクロを考えてみました。

    src/frtos_startup/freertos_start.h (ラッパーマクロに以下の赤文字部分を追加)

    #define xTaskCreateStatic_R_Helper(pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask) \
    do{ \
        static StaticTask_t pxTaskCode##_xTCBBuffer; \
        static StackType_t  pxTaskCode##_xStackBuffer[usStackDepth]; \
        TaskHandle_t xCreatedTask; \
        xCreatedTask = xTaskCreateStatic( pxTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxTaskCode##_xStackBuffer, &pxTaskCode##_xTCBBuffer ); \
        if (NULL != (TaskHandle_t *)pxCreatedTask) \
        { \
            *(TaskHandle_t *)pxCreatedTask = xCreatedTask; \
        } \
        (void) &xCreatedTask; \ [追記] コンパイル時のワーニング除去の為に作成途中では必要でしたが最終的には不要でした
    }while (0)

    src/frtos_config/FreeRTOSConfig.h (以下の変換マクロを追加)

    #define pdBYTES_TO_STACK_DEPTH( ulBytes ) ( ( ( uint32_t ) ( ulBytes ) + ( sizeof( StackType_t ) - 1 ) ) / sizeof( StackType_t ) )

    src/frtos_startup/freertos_object_init.c (以下のように変更)

    void Kernel_Object_init (void)
    {
        /************** task creation ****************************/

        xTaskCreateStatic_R_Helper( main_task, "MAIN_TASK", pdBYTES_TO_STACK_DEPTH(1024), NULL, 1, NULL );

        xTaskCreateStatic_R_Helper( task_LED0, "task_LED0", pdBYTES_TO_STACK_DEPTH(1024), NULL, 2, NULL );

        xTaskCreateStatic_R_Helper( task_LED1, "task_LED1", pdBYTES_TO_STACK_DEPTH(1024), NULL, 2, NULL );

        xTaskCreateStatic_R_Helper( task_CONIO, "task_CONIO", pdBYTES_TO_STACK_DEPTH(1024), NULL, 3, NULL);

        /************** semaphore creation ***********************/

        /************** queue creation ***************************/

        /************** software time creation **************************/

        /************** event groups creation ********************/

        /************** stream buffer creation *************************/

        /************** message buffer creation *********************/

    } /* End of function Kernel_Object_init()*/

    昨日のラッパーマクロでのmapファイル(CC-RLの場合)

    FILE=DefaultBuild\freertos_object_init.obj
                                      000f3f5a  000f5011      10b8
      _xStackBuffer@1@Kernel_Object_init
                                      000f3f5a       400   data ,l         1
      _xTCBBuffer@2@Kernel_Object_init
                                      000f435a        2e   data ,l         1
      _xStackBuffer@3@Kernel_Object_init
                                      000f4388       400   data ,l         1
      _xTCBBuffer@4@Kernel_Object_init
                                      000f4788        2e   data ,l         1
      _xStackBuffer@5@Kernel_Object_init
                                      000f47b6       400   data ,l         1
      _xTCBBuffer@6@Kernel_Object_init
                                      000f4bb6        2e   data ,l         1
      _xStackBuffer@7@Kernel_Object_init
                                      000f4be4       400   data ,l         1
      _xTCBBuffer@8@Kernel_Object_init
                                      000f4fe4        2e   data ,l         1
    FILE=DefaultBuild\freertos_start.obj
                                      000f5012  000f50d5        c4
      _xIdleTaskTCBBuffer@1@vApplicationGetIdleTaskMemory
                                      000f5012        2e   data ,l         1
      _xIdleTaskStackBuffer@2@vApplicationGetIdleTaskMemory
                                      000f5040        96   data ,l         1

    今回のラッパーマクロでのmapファイル(CC-RLの場合)

    FILE=DefaultBuild\freertos_object_init.obj
                                      000f3f5a  000f5011      10b8
      _main_task_xStackBuffer@1@Kernel_Object_init
                                      000f3f5a       400   data ,l         1
      _main_task_xTCBBuffer@2@Kernel_Object_init
                                      000f435a        2e   data ,l         1
      _task_LED0_xStackBuffer@3@Kernel_Object_init
                                      000f4388       400   data ,l         1
      _task_LED0_xTCBBuffer@4@Kernel_Object_init
                                      000f4788        2e   data ,l         1
      _task_LED1_xStackBuffer@5@Kernel_Object_init
                                      000f47b6       400   data ,l         1
      _task_LED1_xTCBBuffer@6@Kernel_Object_init
                                      000f4bb6        2e   data ,l         1
      _task_CONIO_xStackBuffer@7@Kernel_Object_init
                                      000f4be4       400   data ,l         1
      _task_CONIO_xTCBBuffer@8@Kernel_Object_init
                                      000f4fe4        2e   data ,l         1
    FILE=DefaultBuild\freertos_start.obj
                                      000f5012  000f50d5        c4
      _xIdleTaskTCBBuffer@1@vApplicationGetIdleTaskMemory
                                      000f5012        2e   data ,l         1
      _xIdleTaskStackBuffer@2@vApplicationGetIdleTaskMemory
                                      000f5040        96   data ,l         1

    昨日のラッパーマクロでのmapファイル(GNURL78の場合)

     .bss.xTCBBuffer.3825
                    0x000f3f04       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.xStackBuffer.3826
                    0x000f3f32      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.xTCBBuffer.3822
                    0x000f4332       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.xStackBuffer.3823
                    0x000f4360      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.xTCBBuffer.3819
                    0x000f4760       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.xStackBuffer.3820
                    0x000f478e      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.xTCBBuffer.3816
                    0x000f4b8e       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.xStackBuffer.3817
                    0x000f4bbc      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.xIdleTaskStackBuffer.3828
                    0x000f4fbc       0x96 ./src/frtos_startup/freertos_start.o
     .bss.xIdleTaskTCBBuffer.3827
                    0x000f5052       0x2e ./src/frtos_startup/freertos_start.o

    今回のラッパーマクロでのmapファイル(GNURL78の場合)

     .bss.task_CONIO_xTCBBuffer.3825
                    0x000f3f04       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.task_CONIO_xStackBuffer.3826
                    0x000f3f32      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.task_LED1_xTCBBuffer.3822
                    0x000f4332       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.task_LED1_xStackBuffer.3823
                    0x000f4360      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.task_LED0_xTCBBuffer.3819
                    0x000f4760       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.task_LED0_xStackBuffer.3820
                    0x000f478e      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.main_task_xTCBBuffer.3816
                    0x000f4b8e       0x2e ./src/frtos_startup/freertos_object_init.o
     .bss.main_task_xStackBuffer.3817
                    0x000f4bbc      0x400 ./src/frtos_startup/freertos_object_init.o
     .bss.xIdleTaskStackBuffer.3832
                    0x000f4fbc       0x96 ./src/frtos_startup/freertos_start.o
     .bss.xIdleTaskTCBBuffer.3831
                    0x000f5052       0x2e ./src/frtos_startup/freertos_start.o

     

  • こんにちは。NoMaYです。

    Call Walker (CC-RL+CS+)でガチにスタック使用量を見積もってみました。(なお、見積もりの妥当性の検証は今からやろうとしているところです。) Call Walkerには以下の使用制限がありますので、Call Walkerを起動した後に、Call WalkerのGUI上で、手作業で、関数/サブルーチンの情報を修正しました。

    ・ 関数ポインタで関数呼び出しする箇所では、その箇所から呼ばれる関数/サブルーチンの追跡をしない
    ・ アセンブラソースのサブルーチン(Cスタートアップルーチンも含む)に関しては、スタック使用量を不明としている
    ・ アセンブラソースのサブルーチン(Cスタートアップルーチンも含む)に関しては、関数/サブルーチンの追跡をしない
    ・ 上記の影響で、アセンブラソースのみから呼ばれる関数/サブルーチンが、どこからも呼ばれないものとして扱われる
    ・ アセンブラソースのサブルーチン(Cスタートアップルーチンも含む)の単なる分岐先ラベルがサブルーチン扱いされる

    以下、修正前と修正後のCall Walkerのcalファイルと画面コピーです。

    修正前
    freertos_sampleprog2_ccrl_c.cal


    修正後
    freertos_sampleprog2_ccrl_c.Editing.cal


    関数/サブルーチンの情報を修正するには、Call WalkerのGUIの「Add...」「Modify...」「Delete」、および、ドラッグアンドドロップ操作による移動、の機能を使用しました。ただ、困ったことに、Undo機能がありませんので、操作を間違えた時、そのあとの操作でうまく修復できないケースもあり、最初から修正をやり直すことを何度か繰り返す必要がありました。加えて、一度修正しても、プログラムはどんどん変更されていきますので、何度も何度も修正を繰り返さないといけないかも知れません。その手間を減らす為のMerge機能があるようなのですが、今のところ、使い方のコツを修得出来ていません。(ちなみに、修正を行う時、表示ツリーの状態が強制的に全展開状態になってしまうのですが、これには大変閉口しています、、、毎回、展開レベルを1に戻すことで対処していますが、、、)

    「Add...」「Modify...」「Delete」のメニュー/ツールバー/ダイアログ





    展開レベルを1に戻す小技



    なお、スタック使用量が不明となっていた(スタック使用量が0となっていた)アセンブラソースのサブルーチン(Cスタートアップルーチンも含む)で値を設定(値を修正)したのは以下の通りです。





    以下、Call Walkerのヘルプ画面の幾つかです。





     

  • こんにちは。NoMaYです。

    Call Walker (CC-RL+CS+)で見積もったスタック使用量の妥当性を検証してみました。なお、そもそも(RTOS未使用時においても)Call Walker (CC-RL+CS+)によるスタック使用量の見積もりは適切ではないですよね。あまりに当たり前過ぎて、皆さん不問に付しているのだとは思うのですが、(少なくとも多重割り込みを使用していない場合において、)必要スタックサイズの値は以下ではないかと思うのですが、その点が考慮されてませんよね、、、

    本来の必要スタックサイズの見積もり(多重割り込みを使用していない場合):

    必要サイズ = Cスタートアップルーチンから数えて通常処理で最も深くなるサイズ + 割り込み処理の内で最も深くなるサイズ

    Call Walker (CC-RL+CS+)での必要スタックサイズの見積もり:

    必要サイズ = 通常処理も割り込み処理も区別することなく、単に最も深くなるサイズ

    ですので、私も、そうした点は手計算するものとして検証しました。ですが、検証といっても、大したことをする訳でもなく、デバッガ上で、ブレークポイントを設定してブレークさせたり、ステップ実行させたりして、最も深くなる位置でのSP(スタックポインタ)の値をウォッチウィンドウで確認してみただけです。また、しらみつぶしに確認した訳ではなく、事例を絞って確認しました。あと、ルネサスRL78シミュレータで確認しました。

    確認結果:

    (A) 見積もりと実動作は概ね一致

    ただし

    (B) 関数/サブルーチンの最後のCALL命令をBR命令に置き換える最適化が行われた箇所(および、CALL命令をBR命令で置き換えた後の分岐先が連続していたのでBR命令さえ削除された箇所)の影響により実動作の方がサイズが小さかった(SPの値としては大きかった)という不一致あり(実害は無い)

    また

    (C) 所望の実行経路を今回のサンプルプログラムですぐに実現することが出来なかったので断念したものあり

    さらに

    (D) スタック渡し引数で使用されるスタックが、呼び出し元の関数に計上されるのか、呼び出し先の関数に計上されるのか、調べてみたところ、(少なくともCC-RL V1.02では)呼び出し元の関数に計上されていたので、このことは見積もりの方がサイズが大きくなる方向に作用すると思います

    確認事例:

    (1) main_task(レジスタ渡し引数有り+戻り番地+自動変数無し、関数/サブルーチン呼び出し無し)での確認 ⇒ 一致
    (2) r_intc0_interrupt(割り込み処理)から数えて最も深くなる場所での確認 ⇒ 一致
    (3) _start(Cスタートアップルーチンのアセンブラ)から数えて通常処理で最も深くなる場所での確認 ⇒ 不一致(実害無し)
    (4) _vPortYield(FreeRTOSポートレイヤのアセンブラの割り込み処理の1つ)から数えて最も深くなる場所での確認 ⇒ 一致
    (5) _vPortTickISR(FreeRTOSポートレイヤのアセンブラの割り込み処理の1つ)から数えて最も深くなる場所での確認 ⇒ 断念
    (6) スタック渡し引数がある関数を記述してmain_taskから呼び出してみた時の例

    また、今回、確認がやり易くなるかと考えて、各タスクの優先順位を以下の通りに変更してみました。(実質、main_task実行中にr_intc0_interruptが実行されるケースが多くなる、ようにしてみました。)

    src/frtos_startup/freertos_object_init.c (赤文字箇所を変更)

    void Kernel_Object_init (void)
    {
        ...略...

        xTaskCreateStatic_R_Helper( main_task, "MAIN_TASK", pdBYTES_TO_STACK_DEPTH(1024), NULL, 1, NULL );

        xTaskCreateStatic_R_Helper( task_LED0, "task_LED0", pdBYTES_TO_STACK_DEPTH(1024), NULL, 0/*2*/, NULL );

        xTaskCreateStatic_R_Helper( task_LED1, "task_LED1", pdBYTES_TO_STACK_DEPTH(1024), NULL, 2, NULL );

        xTaskCreateStatic_R_Helper( task_CONIO, "task_CONIO", pdBYTES_TO_STACK_DEPTH(1024), NULL, 0/*3*/, NULL);

        ...略...

    } /* End of function Kernel_Object_init()*/

    ちなみに、mapファイルで調べたmain_taskのスタック領域は 0x0f3f5a ~ 0x0f435a となっていました。あと、_startのスタック領域は 0x0f510e ~ 0x0ffe20 となっていました。

    DefaultBuild/freertos_sampleprog2_ccrl_c.map

    FILE=DefaultBuild\freertos_object_init.obj
                                      000f3f5a  000f5011      10b8
      _main_task_xStackBuffer@1@Kernel_Object_init
                                      000f3f5a       400   data ,l         1
      _main_task_xTCBBuffer@2@Kernel_Object_init
                                      000f435a        2e   data ,l         1
    Absolute value symbols
    FILE=rlink_generates_05
    ...
      __STACK_ADDR_START
                                      000ffe20         0   none ,g         1
      __STACK_ADDR_END
                                      000f510e         0   none ,g         1

    以下、それぞれの場合のCS+のルネサスRL78シミュレータでの実行結果の画面コピーです。なお、今回、Call Walkerの表示設定は以下の通りに変更しています。あと、CS+のビルドツールのプロパティを変更して、アセンブラソースを出力させるようにしています。

    [View]→[Show Used Stack]をチェック
    [View]→[Show All Symbols]をチェック


    [アセンブリソースファイルにコメントを出力する]を[いいえ]に設定(デフォルト設定) (画面コピーの大きさを少し小さくする為)
    [アセンブリソースファイルを出力する]を[はい(-asm_path)]に設定


    (1) main_task(レジスタ渡し引数有り+戻り番地+自動変数無し、関数/サブルーチン呼び出し無し)での確認

    SP期待値: 0x0f4356 (0x0f435a - 4)
    SP確認値: 0x0f4356 ⇒ OK(一致)



    (2) r_intc0_interrupt(割り込み処理)から数えて最も深くなる場所での確認

    SP期待値: 0x0f432e (0x0f435a - 4 - 40) : 4 = main_taskでの使用量、40 = r_intc0_interruptでの使用量
    SP確認値: 0x0f432e ⇒ OK(一致)



    (3) _start(Cスタートアップルーチンのアセンブラ)から数えて通常処理で最も深くなる場所での確認

    SP期待値: 0x0ffe10 (0x0ffe20 - 16)
    SP確認値: 0x0ffe18 ⇒ △(不一致だが実害無し)



    (4) _vPortYield(FreeRTOSポートレイヤのアセンブラの割り込み処理の1つ)から数えて最も深くなる場所での確認

    SP期待値: 0x0f4344 (0x0f435a - 4 - 22) : 4 = main_taskでの使用量、22 = _vPortYield割り込みでの使用量
    SP確認値: 0x0f4340 ⇒ OK(一致)



    (5) _vPortTickISR(FreeRTOSポートレイヤのアセンブラの割り込み処理の1つ)から数えて最も深くなる場所での確認

    SP期待値: 0x0f4330 (0x0f435a - 4 - 38) : 4 = main_taskでの使用量、38 = _vPortTickISR割り込みでの使用量
    SP確認値: Unknown(所望の実行経路を今回のサンプルプログラムですぐに実現することが出来なかったので断念)



    (6) スタック渡し引数がある関数を記述してmain_taskから呼び出してみた時の例

    スタック渡し引数で使用されるスタックは呼び出し元の関数のスタックサイズに計上されていました。このことは見積もり値の方が大きくなる方向に作用すると思います。

    main_task.c

    uint16_t foo3(uint16_t a, uint16_t b, uint16_t c);
    uint16_t foo4(uint16_t a, uint16_t b, uint16_t c, uint16_t d);

    uint16_t foo3(uint16_t a, uint16_t b, uint16_t c)
    {
        return a + b + c;
    }

    uint16_t foo4(uint16_t a, uint16_t b, uint16_t c, uint16_t d)
    {
        return a + b + c + d;
    }

    uint16_t a, b, c, d;
    volatile uint16_t r;

    void main_task(void *pvParameters)
    {
        INTERNAL_NOT_USED(pvParameters);
        while(1)
        {
           r = foo3( a, b, c );
           r = foo4( a, b, c, d );
        }
    }

    main_task.asm

    _foo3:
        .STACK _foo3 = 4
        addw ax, bc
        addw ax, de
        ret

    _foo4:
        .STACK _foo4 = 4
        movw hl, ax
        movw ax, [sp+0x04]
        addw ax, de
        movw de, ax
        movw ax, hl
        addw ax, bc
        addw ax, de
        ret

    _main_task:
        .STACK _main_task = 6
    .BB@LABEL@3_1:  ; bb11
        movw de, !LOWW(_c)
        movw bc, !LOWW(_b)
        movw ax, !LOWW(_a)
        call $!_foo3 ⇒ ここから先の呼び出しが深くなった時にスタック使用量に +2 のゲタが発生する
        movw !LOWW(_r), ax
        movw ax, !LOWW(_d)
        push ax
        movw de, !LOWW(_c)
        movw bc, !LOWW(_b)
        movw ax, !LOWW(_a)
        call $!_foo4 ⇒ ここから先の呼び出しではそのようなゲタは発生しない
        pop hl
        movw !LOWW(_r), ax
        br $.BB@LABEL@3_1

     

  • こんにちは。NoMaYです。

    Call Walker (CC-RL+CS+)でのスタック使用量の見積もりは妥当だと感じましたので、最終的に、必要スタックサイズが幾つなのか計算してみました。ただ、以前に別スレッド『e2 studio v7.5.0のFreeRTOS ProjectでVisual Expression+Renesas RX Simulator/TB-RX65Nで試せるSample Programを作ってみた』で思ったことですが、ソースレベルデバッグ時には最適化無しにすると思いますが、そのことはスタックは最適化無しの場合でも足りるサイズを確保しておかざるを得ない、ことになるのだと思います、、、

    必要スタックサイズを見積もる為のCall Walker (CC-RL+CS+)によるスタック使用量の見積もり値(元データ)

      最適化無し デフォルト最適化 左記の何れか大きい方
    通常処理(Cスタートアップルーチン & タスク)  
    _start 156 68 156
    main_task 6 4 6
    task_LED0 66 44 66
    task_LED1 76 48 76
    task_CONIO 124 74 124
    prvIdleTask 10 4 10
    割り込み処理  
    r_uart3_interrupt_receive 78 58  
    r_uart3_interrupt_send 50 44  
    r_intc0_interrupt 46 40  
    _vPortYield 26 22  
    _vPortTickISR 48 38  
    割り込み処理の最大値 78 58 78

    最終的な必要スタックサイズの見積もり値

      通常処理 割り込み処理の最大値 左記の合計
    _start 156 割り込み発生せず 156
    main_task 6 78 84
    task_LED0 66 78 144
    task_LED1 76 78 154
    task_CONIO 124 78 202
    prvIdleTask 10 78 88

    以下、最適化無しの場合の修正前と修正後のCall Walkerのcalファイルと画面コピーです。

    修正前
    freertos_sampleprog2_ccrl_c.Onothing.cal


    修正後
    freertos_sampleprog2_ccrl_c.Onothing.Editing.cal

     

  • こんにちは。NoMaYです。

    今回、Call Walker (CC-RL+CS+)上で、PowerON_Reset_PC、Tasks、TaskExitError、Interruptsの4つのダミーのシンボルを定義して、以下の画面コピーのように、それぞれの下に関数/サブルーチンの情報をグループ化してみました。(どう運用するのが良いのか、試行錯誤している内の、思い付きの1つです。) なお、今まで意識していませんでしたが、e2 studioのスタック解析ビューには、関数/サブルーチンの情報を修正する機能が無いですね、、、あと、以前に別スレッド『e2 studio v7.5.0のStack Analysis ViewはGNURXでは1箇所でも関数ポインタで関数呼び出しするとERRORになって使えませんね』で思ったことでもあるのですが、今回のサンプルプログラムのGNURL78版でも、main_taskのスタック使用量の表示が0ですので、(少なくとも)戻り番地の分は計上されていないみたいですね、、、

    最適化無しの場合の今回修正後のcalファイルとCall Walkerの画面コピー
    freertos_sampleprog2_ccrl_c.Onothing.Editing.cal


    デフォルト最適化の場合の今回修正後のcalファイルとCall Walkerの画面コピー
    freertos_sampleprog2_ccrl_c.Editing.cal


    ダミーのシンボルは以下の画面コピーの通りに定義しました。(なお、アドレスは単なる仮の値ですが、値が重複してはいけないようです。)

    PowerON_Reset_PC


    Tasks


    TaskExitError


    Interrupts


    以下、e2 studio v7.8.0のスタック解析ビューの画面コピーです。

    CC-RLで最適化無し(-Onothing)


    CC-RLでデフォルト最適化(デフォルト)


    GNURL78でデバッグ優先最適化(-Og)


    GNURL78でサイズ優先最適化(-Os)


    ちなみに、e2 studio v7.8.0のバグリストでも、スタック使用量の表示の件はStack Analysis Viewのバグとして挙がっていないみたいです。

    e²studio 7.8 Known Issues List
    List generated on 30/03/2020 15:14:31
    www2.renesas.eu/_custom/software/ree_eclipse/e2studio7/docs/releasenotes/7.8.0/openissues.htm
     

Top Page [◀◀]  2   3   4   5   6   7   8   9   ... [▶▶Last Page