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です。#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