Amazon AWSのFreeRTOS Kernel Developer GuideのサンプルコードをRenesas RX SimulatorのDebug Consoleで試せるようにしてみた

こんにちは。NoMaYです。

別スレッドで、AWSドキュメントサイト内にFreeRTOS kernelの開発者ガイドの日本語版が載っていることに気付いたのですが、その中に含まれているCコード例をルネサスRXシミュレータで試せたら面白そうだと思い、やってみました。まずはCC-RX(CS+/e2 studio)版です。後日GNURX(e2 studio)版もやろうと思います。以下、プロジェクトのファイル一式です。(CS+ V8.01+CC-RX V2.03でビルド)(e2 studio用.project/.cproject等を同梱(zipファイルをe2 studioに直接インポート可能))

sim_rx65n_freertos_ccrx_c_csplus_20190526.zip

やったこと(主なもの)

(1) FreeRTOS kernelがルネサスRXシミュレータの未対応機能を使っていたので無理矢理に対応機能のみ使うように小細工
(2) Full DemoからルネサスRXシミュレータの未対応機能を使っている項目を除外
(3) ルネサスRXシミュレータではBSPモジュール内で無限ループしてしまう点を回避
(4) 開発者ガイドのCコード例で使われているvPrintString()をDebug Consoleへ出力するように作成
(5) Full Demo/Simple Blinky Demo/Cコード例でvAssertCalled()呼び出し時にDebug Consoleへメッセージを出力
(6) Full Demo/Simple Blinky DemoでLEDのOn/OffのタイミングでDebug ConsoleへOn/Offのメッセージを出力
(7) 開発者ガイドのCコード例が動くようにルネサスRXシミュレータの起動設定を変更(私の個人的な好みを含む)
(8) 開発者ガイドのCコード例をビルドする時には不要なデモソースをビルドから除外したプロジェクトを作成(CS+のみ)
(9) 開発者ガイドのCコード例をビルドするプロジェクトではコンパイル時の最適化レベルを0に変更(CS+のみ)

以下、FreeRTOS kernelの開発者ガイドのCコード例をルネサスRXシミュレータで動かした時の画面コピーです。(上記の(8)のプロジェクトです。)

FreeRTOS kernelの開発者ガイドではFreeRTOS Windowsシミュレータで実行
docs.aws.amazon.com/ja_jp/freertos-kernel/latest/dg/task-management.html


ルネサスRXシミュレータで実行


ビルドした時のビルド設定とビルド結果


zipファイルには、開発者ガイドのCコード例をビルドするプロジェクト(CS+のみ)の他に、別スレッドに投稿した、RX65N TBボードで動く(といってもデバッグ中ではありますが)Full Demo/Simple Blinky Demoをビルドするプロジェクトも含まれています。(上記の(2)で除外している項目はありますが。) 以下、それらの画面コピーです。

CS+


e2 studio

Parents
  • こんにちは。NoMaYです。#前の投稿の続きです

    今回、CC-RXのC言語拡張仕様からGNURXのC言語拡張仕様への置き換え以外に、以下のことも行っています。

    (1) RXスマートコンフィグレータがGNURX向けに生成したベクタ配列変数に自前で実装した割り込み関数を配置する小細工
    (2) RXスマートコンフィグレータがGNURX向けに生成したコードでRTOSサポートを有効にする為のr_bsp_config.hの小細工
    (3) resetprg.cに追加したrenesas_simulator_debugging_keyがconst volatileだとGNURXでROMに配置されないので変更
    (4) GNURXでのワーニング対策でFreeRTOSConfig.hでconfigENABLE_BACKWARD_COMPATIBILITYを0で定義(未定義時は1)
    (5) FreeRTOS kernelのRX600v2のGNURX版もCC-RX版のconfigINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_Hの切り分けを導入
    (6) FreeRTOS kernelのRX600v2のGNURX版もCC-RX版と同様なルネサスRXシミュレータ向けの無理矢理な代替実装を追加

    以下、詳細です。

    (1) RXスマートコンフィグレータがGNURX向けに生成したベクタ配列変数に自前で実装した割り込み関数を配置する小細工

    CC-RXでは、C/C++ソースで#pragma interrupt XXX(vect=NNN)のように記述してコンパイルすれば、リンカがベクタテーブルにベクタを埋め込んでくれるのですが、RXスマートコンフィグレータがGNURX向けに生成したコードは、生成したソースに記述されたベクタ配列変数にRXスマートコンフィグレータがベクタを配置するようになっています。そこで、この為だけにRXスマートコンフィグレータにソースを生成させて、生成させたソースの幾つかはビルドから除外するようにし、生成されたソースに以下のような記述を追加してみました。(赤文字箇所) (なお、CC-RX/GNURX/ICCRX共通化BSPがリリースされ、RXスマートコンフィグレータがGNURXが持っている、CC-RXと同様のコンパイラ/リンカ機能を使うようになれば、こういうことは不要になる筈です。)

    src/smc_gen/general/r_cg_vector_table.c

    #include "r_cg_macrodriver.h"
    /* Start user code for include. Do not edit comment generated here */
    #define r_Config_CMT3_cmi3_interrupt vTickISR
    #ifndef RENESAS_SIMULATOR_DEBUGGING
    #define r_Config_ICU_software_interrupt vSoftwareInterruptISR
    #define r_Config_CMT1_cmi1_interrupt undefined_interrupt_source_isr
    #else
    #define r_Config_ICU_software_interrupt undefined_interrupt_source_isr
    #define r_Config_CMT1_cmi1_interrupt vSoftwareInterruptISR
    #if FREERTOS_USER_MAIN == 2
    #define r_Config_TPU4_tgi4a_interrupt undefined_interrupt_source_isr
    #define r_Config_TPU5_tgi5a_interrupt undefined_interrupt_source_isr
    #endif
    #endif
    extern void vTickISR( void );
    extern void vSoftwareInterruptISR( void );
    /* End user code. Do not edit comment generated here */
    #include "r_cg_userdefine.h"
    void * const Reserved_Vector[] __attribute((section(".rvectors"))) =
    {
        /* 0x0000 Reserved */
        (void (*)(void))0xFFFFFFFF,
    ...略...
        /* 0x006C ICU SWINT */
        r_Config_ICU_software_interrupt,
    ...略...
        /* 0x0074 CMT1 CMI1 */
        r_Config_CMT1_cmi1_interrupt,
    ...略...
        /* 0x0204 PERIB INTB129 */
        r_Config_CMT3_cmi3_interrupt,
    ...略...
    }

    src/smc_gen/Config_CMT1/Config_CMT1.h

    /* Start user code for function. Do not edit comment generated here */

    /* The src/FreeRTOS/Source/portable/Renesas/ files have its own implementation for the CMT1. */
    #define R_Config_CMT1_Create() do{}while (0)

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

    src/smc_gen/Config_CMT3/Config_CMT3.h

    /* Start user code for function. Do not edit comment generated here */

    /* The src/frtos_startup/freertos_start.c has its own implementation for the CMT3. */
    #define R_Config_CMT3_Create() do{}while (0)

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

    src/smc_gen/Config_TPU4/Config_TPU4.h

    /* Start user code for function. Do not edit comment generated here */

    /* The src/FreeRTOS/Demo/Full_Demo/IntQueueTimer.c has its own implementation for the TPU4. */
    #define R_Config_TPU4_Create() do{}while (0)

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

    src/smc_gen/Config_TPU5/Config_TPU5.h

    /* Start user code for function. Do not edit comment generated here */

    /* The src/FreeRTOS/Demo/Full_Demo/IntQueueTimer.c has its own implementation for the TPU5. */
    #define R_Config_TPU5_Create() do{}while (0)

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

    (2) RXスマートコンフィグレータがGNURX向けに生成したコードでRTOSサポートを有効にする為のr_bsp_config.hの小細工

    CC-RX版でRのTOSサポートを有効にする為のr_bsp_config.hの小細工は、別スレッド「FreeRTOSをFITやCG(や追々PDG2でも?)と一緒に使うサンプルプログラムをCSplusでビルド出来るプロジェクトにしてみた」に投稿したものですが、GNURX版ではそれだけでは足りませんでしたので、以下のように小細工しました。(赤文字箇所)

    src/smc_gen/r_config/r_bsp_config.h

    変更前

    /* This macro lets other modules no if a RTOS is being used.
       0 = RTOS is not used.
       1 = RTOS is used.
    */
    #define BSP_CFG_RTOS_USED               (0) /* Generated value. Do not edit this manually */

    変更後

    /* This macro lets other modules no if a RTOS is being used.
       0 = RTOS is not used.
       1 = FreeRTOS is used.
       2 = embOS is used.(This is not available.)
       3 = MicroC_OS is used.(This is not available.)
       4 = RI600V4 or RI600PX is used.(This is not available.)
    */
    #if !defined(BSP_CFG_RTOS_USED) || (BSP_CFG_RTOS_USED == 0)
    #if defined(BSP_CFG_RTOS_USED)
    #undef BSP_CFG_RTOS_USED
    #endif
    #define BSP_CFG_RTOS_USED               (0) /* Generated value. Do not edit this manually */
    #endif
    /* This macro is used to select which CMT channel used for system timer of RTOS.
     * The setting of this macro is only valid if the macro BSP_CFG_RTOS_USED is set to a value other than 0. */
    #if (BSP_CFG_RTOS_USED != 0)
    /* Setting value.
     * 0      = CMT channel 0 used for system timer of RTOS (recommended to be used for RTOS).
     * 1      = CMT channel 1 used for system timer of RTOS.
     * 2      = CMT channel 2 used for system timer of RTOS.
     * 3      = CMT channel 3 used for system timer of RTOS.
     * Others = Invalid. */
    #define BSP_CFG_RTOS_SYSTEM_TIMER       (3)
    #endif
    /* As of today, the r_bsp.h in the R_BSP module which is generated by the RX Smart Configurator for GNURX project
       does not support RTOS. Therefore RTOS header files are included here. */
    #if BSP_CFG_RTOS_USED == 0      /* Non-OS */
    #elif BSP_CFG_RTOS_USED == 1    /* FreeRTOS */
    #include "FreeRTOS.h"
    #include "task.h"
    #include "semphr.h"
    #include "queue.h"
    #include "croutine.h"
    #include "timers.h"
    #include "event_groups.h"
    #include "freertos_start.h"
    #elif BSP_CFG_RTOS_USED == 2    /* SEGGER embOS */
    #elif BSP_CFG_RTOS_USED == 3    /* Micrium MicroC/OS */
    #elif BSP_CFG_RTOS_USED == 4    /* Renesas RI600V4 & RI600PX */
    #else
    #endif

    (3) resetprg.cに追加したrenesas_simulator_debugging_keyがconst volatileだとGNURXでROMに配置されないので変更

    修飾子の組み合わせがconst volatileの場合、、CC-RXではROMに配置されたのですが、GNURXではROMに配置されずにRAMに配置されたので、以下のように修正しました。(赤文字箇所)

    src/r_bsp_modified/resetprg.c

    前回の版

    /* Switch Key for Renesas Simulator Debugging */
    #ifdef RENESAS_SIMULATOR_DEBUGGING
    const volatile uint32_t renesas_simulator_debugging_key = 0xFFFFFFFF; // non 0x00000001 is emulator, 0x00000001 is simulator
    #endif
    void PowerON_Reset_PC(void)
    {
        ...略...
        /* Switch to high-speed operation */
    #ifndef RENESAS_SIMULATOR_DEBUGGING
        operating_frequency_set();
    #else
        if (renesas_simulator_debugging_key != 0x00000001)
        {
            operating_frequency_set();
        }
    #endif
        ...略...
    }

    今回の版

    /* Switch Key for Renesas Simulator Debugging */
    #ifdef RENESAS_SIMULATOR_DEBUGGING
    const uint32_t renesas_simulator_debugging_key = 0xFFFFFFFF;    // non 0x00000001 is emulator, 0x00000001 is simulator
    #endif
    void PowerON_Reset_PC(void)
    {
        ...略...
        /* Switch to high-speed operation */
    #ifndef RENESAS_SIMULATOR_DEBUGGING
        operating_frequency_set();
    #else
        if (*(volatile uint32_t *)&renesas_simulator_debugging_key != 0x00000001)
        {
            operating_frequency_set();
        }
    #endif
        ...略...
    }

    (4) GNURXでのワーニング対策でFreeRTOSConfig.hでconfigENABLE_BACKWARD_COMPATIBILITYを0で定義(未定義時は1)

    未定義時は以下のワーニングが出ましたので、出ないようにする為に、以下を追加しました。(青文字箇所)

    出たワーニング

    rx-elf-gcc @"src/FreeRTOS/Demo/Common/Minimal/QueueSet.o.in"
    In file included from ../src/FreeRTOS/Demo/Common/Minimal/QueueSet.c:47:0:
    ../src/FreeRTOS/Demo/Common/Minimal/QueueSet.c: In function 'prvTestQueueOverwriteWithQueueSet':
    …略…\src\FreeRTOS\Source\include/FreeRTOS.h:924:23: warning: declaration of 'QueueHandle_t' shadows a global declaration [-Wshadow]
      #define xQueueHandle QueueHandle_t
                           ^
    ../src/FreeRTOS/Demo/Common/Minimal/QueueSet.c:613:15: note: in expansion of macro 'xQueueHandle'
     QueueHandle_t xQueueHandle = NULL, xReceivedHandle = NULL;
                   ^
    In file included from ../src/FreeRTOS/Demo/Common/Minimal/QueueSet.c:49:0:
    …略…\src\FreeRTOS\Source\include/queue.h:48:34: warning: shadowed declaration is here [-Wshadow]
     typedef struct QueueDefinition * QueueHandle_t;
                                      ^

    追加した内容
    src/frtos_config/FreeRTOSConfig.h

    /* When the FIT configurator or the Smart Configurator is used, platform.h has
    to be used. */
    #define configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H  1

    /* Definitions to allow backward compatibility with FreeRTOS versions prior to
    V8 if desired. */
    #define configENABLE_BACKWARD_COMPATIBILITY             0

    (5) FreeRTOS kernelのRX600v2のGNURX版もCC-RX版のconfigINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_Hの切り分けを導入

    RXスマートコンフィグレータでコード生成させた場合に、e2 studioがiodefine.hがあるBSPモジュールのフォルダのパスをインクルードパスリストから削除してしまう点はGNURXでもCC-RXと同様なので、以下のようにFreeRTOS kernelのRX600v2のCC-RX版と同じになるように修正しました。(赤文字箇所)

    src/FreeRTOS/Source/portable/GCC/RX600v2/port.c

    /* Hardware specifics. */
    #if defined( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H ) && ( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H == 1 )
        #include "platform.h"
    #else
        #include "iodefine.h"
    #endif

    (6) FreeRTOS kernelのRX600v2のGNURX版もCC-RX版と同様なルネサスRXシミュレータ向けの無理矢理な代替実装を追加

    以下の通りです。(青文字箇所)

    src/FreeRTOS/Source/portable/GCC/RX600v2/port.c

    BaseType_t xPortStartScheduler( void )
    {
    extern void vApplicationSetupTimerInterrupt( void );

        /* Use pxCurrentTCB just so it does not get optimised away. */
        if( pxCurrentTCB != NULL )
        {
            /* Call an application function to set up the timer that will generate the
            tick interrupt.  This way the application can decide which peripheral to
            use.  A demo application is provided to show a suitable example. */
            vApplicationSetupTimerInterrupt();

    #if !defined(RENESAS_SIMULATOR_DEBUGGING) || RENESAS_SIMULATOR_DEBUGGING == 0
            /* Enable the software interrupt. */
            _IEN( _ICU_SWINT ) = 1;

            /* Ensure the software interrupt is clear. */
            _IR( _ICU_SWINT ) = 0;

            /* Ensure the software interrupt is set to the kernel priority. */
            _IPR( _ICU_SWINT ) = configKERNEL_INTERRUPT_PRIORITY;
    #else
            /* The Renesas Simulator for RX does not support the software interrupt.
            Therefore the CMT channel 1 interrupt is used for the yield interrupt. */

            /* Protect off. */
            SYSTEM.PRCR.WORD = 0xA502;

            /* Enable the compare match timer 1. */
            MSTP( CMT1 ) = 0;

            /* Protect on. */
            SYSTEM.PRCR.WORD = 0xA500;

            /* Stop the timer 1. */
            CMT.CMSTR0.BIT.STR1 = 0;

            /* Set the compare match value. */
            CMT1.CMCOR = 0;

            /* Clear counter. */
            CMT1.CMCNT = 0;

            /* Disable interrupt on compare match. (Divide the PCLK by 8.) */
            CMT1.CMCR.WORD = 0x0080;

            /* Ensure the timer 1 interrupt is set to the kernel priority. */
            IPR(CMT1, CMI1) = configKERNEL_INTERRUPT_PRIORITY;

            /* Ensure the timer 1 interrupt is clear. */
            IR(CMT1, CMI1)  = 0;

            /* Enable the timer 1 interrupt. */
            IEN(CMT1, CMI1) = 1;

            /* Start the timer 1. */
            CMT.CMSTR0.BIT.STR1 = 1;
    #endif

            /* Start the first task. */
            prvStartFirstTask();
        }

        /* Should not get here. */
        return pdFAIL;
    }

    src/FreeRTOS/Source/portable/GCC/RX600v2/portmacro.h

    #if !defined(RENESAS_SIMULATOR_DEBUGGING) || RENESAS_SIMULATOR_DEBUGGING == 0
    /* Yield equivalent to "*portITU_SWINTR = 0x01; ( void ) *portITU_SWINTR;"
    where portITU_SWINTR is the location of the software interrupt register
    (0x000872E0).  Don't rely on the assembler to select a register, so instead
    save and restore clobbered registers manually. */
    #define portYIELD()                         \
        __asm volatile                          \
        (                                       \
            "PUSH.L R10                 \n"     \
            "MOV.L  #0x872E0, R10       \n"     \
            "MOV.B  #0x1, [R10]         \n"     \
            "MOV.L  [R10], R10          \n"     \
            "POP    R10                 \n"     \
        )
    #else
    #define portYIELD()                                                         \
        __asm volatile                                                          \
        (                                                                       \
            "PUSH.L R10                 \n"                                     \
    \
            /* Disable the timer 1 interrupt. */                                \
            /* _IEN( _CMT1_CMI1 ) = 0; */                                       \
            "MOV.L  #0x00087203, R10    \n"                                     \
            "BCLR   #0x05, [R10].B      \n"                                     \
            /* Read back and test to complete a write access here. */           \
            /* if( _IEN( _CMT1_CMI1 ) == 0 ){}else{} */                         \
            "BTST   #0x05, [R10].B      \n"                                     \
    \
            /* Enable interrupt on compare match. (Divide the PCLK by 8.) */    \
            /* CMT1.CMCR.WORD = 0x00C0; */                                      \
            "MOV.L  #0x00088008, R10    \n"                                     \
            "MOV.W  #0x00C0, [R10]      \n"                                     \
    \
            /* Wait for the timer 1 interrupt request. */                       \
            /* while( _IR( _CMT1_CMI1 ) == 0 ){} */                             \
            "MOV.L  #0x0008701D, R10    \n"                                     \
    "?:"    "BTST   #0x00, [R10].B      \n"                                     \
            "BEQ    ?-                  \n"                                     \
    \
            /* Disable interrupt on compare match. (Divide the PCLK by 8.) */   \
            /* CMT1.CMCR.WORD = 0x0080; */                                      \
            "MOV.L  #0x00088008, R10    \n"                                     \
            "MOV.W  #0x0080, [R10]      \n"                                     \
    \
            /* Enable the timer 1 interrupt. */                                 \
            /* _IEN( _CMT1_CMI1 ) = 1; */                                       \
            "MOV.L  #0x00087203, R10    \n"                                     \
            "BSET   #0x05, [R10].B      \n"                                     \
            /* Read back and test to complete a write access here. */           \
            /* if( _IEN( _CMT1_CMI1 ) == 1 ){}else{} */                         \
            "BTST   #0x05, [R10].B      \n"                                     \
    \
            "POP    R10                 \n"                                     \
        )
    #endif

    #define portYIELD_FROM_ISR( x ) if( x != pdFALSE ) portYIELD()

    あとそれから、以下はGNURXでFull Demoをビルドした時のe2 studioの画面コピーです。

  • NoMaYさん

    こんにちは、シェルティです。

    種々ご検討いただきありがとうございます。
    近日中にFITモジュール全般を更新してCC-RX/GCC/IAR対応となります。
    またスマートコンフィグレータとAmazon FreeRTOSの連携もある程度可能になります。
    各開発チームには可能な限りNoMaYさんのレスを確認して取り込めるアイデアを取り込んでほしいと
    依頼はしておりますが、各人温度差があると思いますので、どのくらいうまくAmazon FreeRTOSと
    スマートコンフィグレータが連携して、かつ、コンパイラ違いの環境でもコンフィグレーションが正しく
    働くかが正直なところ未知数です。
    継続して改善を続けていくことになりますので、引き続きご意見などをいただけますと幸いです。

    以上です
Reply
  • NoMaYさん

    こんにちは、シェルティです。

    種々ご検討いただきありがとうございます。
    近日中にFITモジュール全般を更新してCC-RX/GCC/IAR対応となります。
    またスマートコンフィグレータとAmazon FreeRTOSの連携もある程度可能になります。
    各開発チームには可能な限りNoMaYさんのレスを確認して取り込めるアイデアを取り込んでほしいと
    依頼はしておりますが、各人温度差があると思いますので、どのくらいうまくAmazon FreeRTOSと
    スマートコンフィグレータが連携して、かつ、コンパイラ違いの環境でもコンフィグレーションが正しく
    働くかが正直なところ未知数です。
    継続して改善を続けていくことになりますので、引き続きご意見などをいただけますと幸いです。

    以上です
Children
No Data