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

FreeRTOS kernelのRXv3-DPFPU port layerの作り方を考えてみようと思います

こんにちは。NoMaYです。

まだQiitaに掲題の投稿は見当たらなかったので考えてみようと思いました。RXv3コア搭載の品種の中でも、RX72M, RX72N, RX66Nには倍精度浮動小数点演算命令がありますが、命令の操作対象となるレジスタは、通常の汎用レジスタが使用される単精度浮動小数点演算の場合とは異なり、倍精度浮動小数点演算専用のレジスタが用意されています。また、命令の演算結果のフラグなどが格納されるレジスタも専用のものが用意されていますし、例外の発生制御を行ったり発生状況が格納されたりするレジスタにも専用のものが用意されています。ですが、既存のRXv2コア向けのFreeRTOSポートレイヤは、当然ながら、これらの倍精度浮動小数点演算専用のレジスタのことは考慮されておらず、タスク切り替え時にこれらのレジスタの内容の退避/復帰を行っていない為、タスク間で倍精度浮動小数点レジスタの値がぐちゃぐちゃに入り乱れることになってしまうので、倍精度浮動小数点演算命令の使用を諦めざるを得ないことになります。

これに対処する箇所は以下の4箇所(赤字箇所)かと思います。(まだ今は概念コードのレベルですが。)

port.c

#pragma inline_asm prvYieldHandler
static void prvYieldHandler( void )
{
    /* Re-enable interrupts. */
    SETPSW  I

    /* Move the data that was automatically pushed onto the interrupt stack when
    the interrupt occurred from the interrupt stack to the user stack.

    R15 is saved before it is clobbered. */
    PUSH.L  R15

    /* Read the user stack pointer. */
    MVFC    USP, R15

    /* Move the address down to the data being moved. */
    SUB     #12, R15
    MVTC    R15, USP

    /* Copy the data across. */
    MOV.L   [ R0 ], [ R15 ] ; R15
    MOV.L   4[ R0 ], 4[ R15 ]  ; PC
    MOV.L   8[ R0 ], 8[ R15 ]  ; PSW

    /* Move the interrupt stack pointer to its new correct position. */
    ADD #12, R0

    /* All the rest of the registers are saved directly to the user stack. */
    SETPSW  U

    /* Save the rest of the general registers (R15 has been saved already). */
    PUSHM   R1-R14
    /* Save the FPSW and accumulators. */
    MVFC    FPSW, R15
    PUSH.L  R15
    MVFACGU #0, A1, R15
    PUSH.L  R15
    MVFACHI #0, A1, R15
    PUSH.L  R15
    MVFACLO #0, A1, R15 ; Low order word.
    PUSH.L  R15
    MVFACGU #0, A0, R15
    PUSH.L  R15
    MVFACHI #0, A0, R15
    PUSH.L  R15
    MVFACLO #0, A0, R15 ; Low order word.
    PUSH.L  R15
    ここでタスク切り替え前の倍精度浮動小数点演算専用レジスタの値をスタックへ退避する
    /* Save the stack pointer to the TCB. */
    MOV.L   #_pxCurrentTCB, R15
    MOV.L   [ R15 ], R15
    MOV.L   R0, [ R15 ]

    /* Ensure the interrupt mask is set to the syscall priority while the kernel
    structures are being accessed. */
    MVTIPL  #configMAX_SYSCALL_INTERRUPT_PRIORITY

    /* Select the next task to run. */
    BSR.A   _vTaskSwitchContext

    /* Reset the interrupt mask as no more data structure access is required. */
    MVTIPL  #configKERNEL_INTERRUPT_PRIORITY

    /* Load the stack pointer of the task that is now selected as the Running
    state task from its TCB. */
    MOV.L   #_pxCurrentTCB,R15
    MOV.L   [ R15 ], R15
    MOV.L   [ R15 ], R0
    ここでタスク切り替え後の倍精度浮動小数点演算専用レジスタの値をスタックから復帰する
    /* Restore the context of the new task.  The PSW (Program Status Word) and
    PC will be popped by the RTE instruction. */
    POP     R15
    MVTACLO R15, A0     /* Accumulator low 32 bits. */
    POP     R15
    MVTACHI R15, A0     /* Accumulator high 32 bits. */
    POP     R15
    MVTACGU R15, A0     /* Accumulator guard. */
    POP     R15
    MVTACLO R15, A1     /* Accumulator low 32 bits. */
    POP     R15
    MVTACHI R15, A1     /* Accumulator high 32 bits. */
    POP     R15
    MVTACGU R15, A1     /* Accumulator guard. */
    POP     R15
    MVTC    R15,FPSW
    POPM    R1-R15
    RTE
    NOP
    NOP
}
#pragma inline_asm prvStartFirstTask
static void prvStartFirstTask( void )
{
    /* When starting the scheduler there is nothing that needs moving to the
    interrupt stack because the function is not called from an interrupt.
    Just ensure the current stack is the user stack. */
    SETPSW  U

    /* Obtain the location of the stack associated with which ever task
    pxCurrentTCB is currently pointing to. */
    MOV.L   #_pxCurrentTCB, R15
    MOV.L   [R15], R15
    MOV.L   [R15], R0
    ここでタスクのスタート時の倍精度浮動小数点演算専用レジスタの値をスタックから復帰する
    /* Restore the registers from the stack of the task pointed to by
    pxCurrentTCB. */
    POP     R15
    MVTACLO R15, A0     /* Accumulator low 32 bits. */
    POP     R15
    MVTACHI R15, A0     /* Accumulator high 32 bits. */
    POP     R15
    MVTACGU R15, A0     /* Accumulator guard. */
    POP     R15
    MVTACLO R15, A1     /* Accumulator low 32 bits. */
    POP     R15
    MVTACHI R15, A1     /* Accumulator high 32 bits. */
    POP     R15
    MVTACGU R15, A1     /* Accumulator guard. */
    POP     R15
    MVTC    R15,FPSW    /* Floating point status word. */
    POPM    R1-R15      /* R1 to R15 - R0 is not included as it is the SP. */
    RTE                 /* This pops the remaining registers. */
    NOP
    NOP
}
StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
    /* R0 is not included as it is the stack pointer. */

    *pxTopOfStack = 0x00;
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_PSW;
    pxTopOfStack--;
    *pxTopOfStack = ( StackType_t ) pxCode;
    /* When debugging it can be useful if every register is set to a known
    value.  Otherwise code space can be saved by just setting the registers
    that need to be set. */
    #ifdef USE_FULL_REGISTER_INITIALISATION
    {
        pxTopOfStack--;
        *pxTopOfStack = 0xffffffff; /* r15. */
        pxTopOfStack--;
        *pxTopOfStack = 0xeeeeeeee;
        pxTopOfStack--;
        *pxTopOfStack = 0xdddddddd;
        pxTopOfStack--;
        *pxTopOfStack = 0xcccccccc;
        pxTopOfStack--;
        *pxTopOfStack = 0xbbbbbbbb;
        pxTopOfStack--;
        *pxTopOfStack = 0xaaaaaaaa;
        pxTopOfStack--;
        *pxTopOfStack = 0x99999999;
        pxTopOfStack--;
        *pxTopOfStack = 0x88888888;
        pxTopOfStack--;
        *pxTopOfStack = 0x77777777;
        pxTopOfStack--;
        *pxTopOfStack = 0x66666666;
        pxTopOfStack--;
        *pxTopOfStack = 0x55555555;
        pxTopOfStack--;
        *pxTopOfStack = 0x44444444;
        pxTopOfStack--;
        *pxTopOfStack = 0x33333333;
        pxTopOfStack--;
        *pxTopOfStack = 0x22222222;
        pxTopOfStack--;
    }
    #else
    {
        pxTopOfStack -= 15;
    }
    #endif
    ここでタスクのスタート時の倍精度浮動小数点演算専用レジスタの値をスタックに書き込む
    *pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
    pxTopOfStack--;
    *pxTopOfStack = portINITIAL_FPSW;
    pxTopOfStack--;
    *pxTopOfStack = 0x11111111; /* Accumulator 0. */
    pxTopOfStack--;
    *pxTopOfStack = 0x22222222; /* Accumulator 0. */
    pxTopOfStack--;
    *pxTopOfStack = 0x33333333; /* Accumulator 0. */
    pxTopOfStack--;
    *pxTopOfStack = 0x44444444; /* Accumulator 1. */
    pxTopOfStack--;
    *pxTopOfStack = 0x55555555; /* Accumulator 1. */
    pxTopOfStack--;
    *pxTopOfStack = 0x66666666; /* Accumulator 1. */
    return pxTopOfStack;
}

続く

  • こんにちは。NoMaYです。

    RXv3命令セットを調べてみたところDPUSHMとDPOPMという便利な命令がありましたので、ユーザーズマニュアルの該当頁の画面コピーを取ってみました。(あと、倍精度浮動小数点演算専用レジスタの図の画面コピーも。) また、RX72Nの上記命令実行サイクル数の表の画面コピーも取ってみました。

    これらの命令を使うとスタックとの間の退避/復帰は以下の記述で済みそうです。命令実行サイクル数は退避と復帰のそれぞれで20クロック程度ですね。(ただ、64ビット幅のレジスタを単純に1つと数えて良いものか(ひょっとして2つとして数えるべきなのか)ちょっと気になりますけれど、、、)

    スタックへの退避

    DPUSHM.D DR0-DR15
    DPUSHM.L DPSW-DEPC

    スタックからの復帰

    DPOPM.L DPSW-DEPC
    DPOPM.D DR0-DR15

    RXファミリ RXv3命令セットアーキテクチャ ユーザーズマニュアル ソフトウェア編
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01us0316
    r01us0316jj0100-rxv3sm.pdf





    RX72Nグループ ユーザーズマニュアル ハードウェア編
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01uh0824
    r01uh0824jj0100-rx72n.pdf



    続く

  • こんにちは。NoMaYです。

    Cortex-A9、Cortex-M4F、SH2A-FPU、などのポートレイヤを見ていて気付いたのですが、タスク毎にFPUを使うかどうかをAPI関数で申告して(申告しなければFPUを使わないものとして)、FPUを使わないタスクではFPUレジスタの退避/復帰を省略して、スタック使用量(RXv3なら8×16+4×4=144byte)と実行サイクル(RXv3なら20クロック程度×2)を節約出来る機構があるようです。申告する場合のソース例として、FreeRTOS kernelのFreeRTOS Demoプロジェクトに以下のものがありました。赤文字箇所が申告する場合のおまじないのAPI関数です。ちゃんとやる時は、ここまで実装しておきたいですね。

    Demo/Common/Minimal/flop.c

    static portTASK_FUNCTION( vCompetingMathTask1, pvParameters )
    {
    volatile portDOUBLE d1, d2, d3, d4;
    volatile uint16_t *pusTaskCheckVariable;
    volatile portDOUBLE dAnswer;
    short sError = pdFALSE;

        /* Some ports require that tasks that use a hardware floating point unit
        tell the kernel that they require a floating point context before any
        floating point instructions are executed. */
        portTASK_USES_FLOATING_POINT();

        d1 = 123.4567;
        d2 = 2345.6789;
        d3 = -918.222;

        dAnswer = ( d1 + d2 ) * d3;

        /* The variable this task increments to show it is still running is passed in
        as the parameter. */
        pusTaskCheckVariable = ( volatile uint16_t * ) pvParameters;

        /* Keep performing a calculation and checking the result against a constant. */
        for(;;)
        {
            d1 = 123.4567;
            d2 = 2345.6789;
            d3 = -918.222;

            d4 = ( d1 + d2 ) * d3;

            #if configUSE_PREEMPTION == 0
                taskYIELD();
            #endif

            /* If the calculation does not match the expected constant, stop the
            increment of the check variable. */
            if( fabs( d4 - dAnswer ) > 0.001 )
            {
                sError = pdTRUE;
            }

            if( sError == pdFALSE )
            {
                /* If the calculation has always been correct then set set the check
                variable.  The check variable will get set to pdFALSE each time
                xAreMathsTaskStillRunning() is executed. */
                ( *pusTaskCheckVariable ) = pdTRUE;
            }

            #if configUSE_PREEMPTION == 0
                taskYIELD();
            #endif

        }
    }

    続く

  • こんにちは。NoMaYです。

    > タスク毎にFPUを使うかどうかをAPI関数で申告して(申告しなければFPUを使わないものとして)、FPUを使わないタスクではFPUレジスタの退避/復帰を省略して、スタック使用量(RXv3なら8×16+4×4=144byte)と実行サイクル(RXv3なら20クロック程度×2)を節約出来る機構があるようです。…略… ちゃんとやる時は、ここまで実装しておきたいですね。

    この機構も実装出来ました。なお、デバッグしていて気付いたのですが、倍精度浮動小数点演算専用レジスタの内のDEPCは、以下のソフトウェアマニュアルの画面コピーの通り、リードオンリレジスタでしたので、退避/復帰をしないようにしました。(というか出来ません。) GNURX版(開発環境で一番トラブルが多い) > ICCRX版 > CC-RX版(開発環境で一番トラブルが少ない) という優先順位で作業をしていますが、今日時点のGNURX版のポートレイヤを添付しておきます。

    RXファミリ RXv3命令セットアーキテクチャ ユーザーズマニュアル ソフトウェア編 画面コピー
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01us0316
    r01us0316jj0100-rxv3sm.pdf


    FreeRTOS/Source/portable/GCC/RX700v3_DPFPU/port.c

    5732.port.c.20200624.txt
    /*
     * FreeRTOS Kernel V10.3.1
     * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     * the Software, and to permit persons to whom the Software is furnished to do so,
     * subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
     * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
     * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * http://www.FreeRTOS.org
     * http://aws.amazon.com/freertos
     *
     * 1 tab == 4 spaces!
     */
    
    /*-----------------------------------------------------------
     * Implementation of functions defined in portable.h for the RX700 DPFPU port.
     *----------------------------------------------------------*/
    
    /* Scheduler includes. */
    #include "FreeRTOS.h"
    #include "task.h"
    
    /* Library includes. */
    #include "string.h"
    
    /* Hardware specifics. */
    #if( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H == 1 )
    	#include "platform.h"
    #else
    	#include "iodefine.h"
    #endif
    
    /*-----------------------------------------------------------*/
    
    /* Tasks should start with interrupts enabled and in Supervisor mode, therefore
    PSW is set with U and I set, and PM and IPL clear. */
    #define portINITIAL_PSW     ( ( StackType_t ) 0x00030000 )
    #define portINITIAL_FPSW    ( ( StackType_t ) 0x00000100 )
    #define portINITIAL_DPSW    ( ( StackType_t ) 0x00000100 )
    #define portINITIAL_DCMR    ( ( StackType_t ) 0x00000000 )
    #define portINITIAL_DECNT   ( ( StackType_t ) 0x00000001 )
    
    /* Tasks are not created with a DPFPU context, but can be given a DPFPU context
    after they have been created.  A variable is stored as part of the tasks context
    that holds portNO_DPFPU_CONTEXT if the task does not have an DPFPU context, or
    any other value if the task does have an DPFPU context. */
    #define portNO_DPFPU_CONTEXT	( ( StackType_t ) 0 )
    
    /* These macros allow a critical section to be added around the call to
    xTaskIncrementTick(), which is only ever called from interrupts at the kernel
    priority - ie a known priority.  Therefore these local macros are a slight
    optimisation compared to calling the global SET/CLEAR_INTERRUPT_MASK macros,
    which would require the old IPL to be read first and stored in a local variable. */
    #define portMASK_INTERRUPTS_FROM_KERNEL_ISR() 	__asm volatile ( "MVTIPL	%0" ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) )
    #define portUNMASK_INTERRUPTS_FROM_KERNEL_ISR() 	__asm volatile ( "MVTIPL	%0" ::"i"(configKERNEL_INTERRUPT_PRIORITY) )
    
    /*-----------------------------------------------------------*/
    
    /*
     * Function to start the first task executing - written in asm code as direct
     * access to registers is required.
     */
    static void prvStartFirstTask( void ) __attribute__((naked));
    
    /*
     * Software interrupt handler.  Performs the actual context switch (saving and
     * restoring of registers).  Written in asm code as direct register access is
     * required.
     */
    #if( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H == 1 )
    void vSoftwareInterruptISR( void ) __attribute__((naked, vector( R_BSP_SECNAME_INTVECTTBL, VECT_ICU_SWINT )));
    #else
    void vSoftwareInterruptISR( void ) __attribute__((naked));
    #endif
    
    /*
     * The tick interrupt handler.
     */
    #if( configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H == 1 )
    void vTickISR( void ) __attribute__((interrupt( R_BSP_SECNAME_INTVECTTBL, _VECT( configTICK_VECTOR ) )));
    #else
    void vTickISR( void ) __attribute__((interrupt));
    #endif
    
    /*-----------------------------------------------------------*/
    
    extern void *pxCurrentTCB;
    
    StackType_t ulPortTaskHasDPFPUContext;
    
    /*-----------------------------------------------------------*/
    
    /*
     * See header file for description.
     */
    StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
    {
    	/* R0 is not included as it is the stack pointer. */
    
    	*pxTopOfStack = 0x00;
    	pxTopOfStack--;
     	*pxTopOfStack = portINITIAL_PSW;
    	pxTopOfStack--;
    	*pxTopOfStack = ( StackType_t ) pxCode;
    
    	/* When debugging it can be useful if every register is set to a known
    	value.  Otherwise code space can be saved by just setting the registers
    	that need to be set. */
    	#ifdef USE_FULL_REGISTER_INITIALISATION
    	{
    		pxTopOfStack--;
    		*pxTopOfStack = 0xffffffff;	/* r15. */
    		pxTopOfStack--;
    		*pxTopOfStack = 0xeeeeeeee;
    		pxTopOfStack--;
    		*pxTopOfStack = 0xdddddddd;
    		pxTopOfStack--;
    		*pxTopOfStack = 0xcccccccc;
    		pxTopOfStack--;
    		*pxTopOfStack = 0xbbbbbbbb;
    		pxTopOfStack--;
    		*pxTopOfStack = 0xaaaaaaaa;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x99999999;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x88888888;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x77777777;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x66666666;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x55555555;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x44444444;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x33333333;
    		pxTopOfStack--;
    		*pxTopOfStack = 0x22222222;
    		pxTopOfStack--;
    	}
    	#else
    	{
    		pxTopOfStack -= 15;
    	}
    	#endif
    
    	*pxTopOfStack = ( StackType_t ) pvParameters; /* R1 */
    	pxTopOfStack--;
    	*pxTopOfStack = portINITIAL_FPSW;
    	pxTopOfStack--;
    	*pxTopOfStack = 0x11111111; /* Accumulator 1. */
    	pxTopOfStack--;
    	*pxTopOfStack = 0x22222222; /* Accumulator 1. */
    	pxTopOfStack--;
    	*pxTopOfStack = 0x33333333; /* Accumulator 1. */
    	pxTopOfStack--;
    	*pxTopOfStack = 0x44444444; /* Accumulator 0. */
    	pxTopOfStack--;
    	*pxTopOfStack = 0x55555555; /* Accumulator 0. */
    	pxTopOfStack--;
    	*pxTopOfStack = 0x66666666; /* Accumulator 0. */
    
    	#if( configUSE_TASK_DPFPU_SUPPORT == 1 )
    	{
    		/* The task will start without a DPFPU context.  A task that
    		uses the DPFPU hardware must call vPortTaskUsesDPFPU() before
    		executing any floating point instructions. */
    		pxTopOfStack--;
    		*pxTopOfStack = portNO_DPFPU_CONTEXT;
    	}
    	#elif( configUSE_TASK_DPFPU_SUPPORT == 2 )
    	{
    		/* The task will start with a DPFPU context.  Leave enough
    		space for the registers - and ensure they are initialised if desired. */
    		#ifdef USE_FULL_REGISTER_INITIALISATION
    		{
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1515.1515; /* DR15. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1414.1414; /* DR14. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1313.1313; /* DR13. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1212.1212; /* DR12. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1111.1111; /* DR11. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 1010.1010; /* DR10. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  909.0909; /* DR9. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  808.0808; /* DR8. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  707.0707; /* DR7. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  606.0606; /* DR6. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  505.0505; /* DR5. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  404.0404; /* DR4. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  303.0303; /* DR3. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  202.0202; /* DR2. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack =  101.0101; /* DR1. */
    			pxTopOfStack -= 2;
    			*(double *)pxTopOfStack = 9876.54321;/* DR0. */
    		}
    		#else
    		{
    			pxTopOfStack -= 32;
    		}
    		#endif
    		pxTopOfStack--;
    		*pxTopOfStack = portINITIAL_DECNT; /* DECNT. */
    		pxTopOfStack--;
    		*pxTopOfStack = portINITIAL_DCMR;  /* DCMR. */
    		pxTopOfStack--;
    		*pxTopOfStack = portINITIAL_DPSW;  /* DPSW. */
    
    		pxTopOfStack--;
    		*pxTopOfStack = pdTRUE;
    		////????////ulPortTaskHasDPFPUContext = pdTRUE;
    	}
    	#elif( configUSE_TASK_DPFPU_SUPPORT == 0 )
    	{
    		/* Omit DPFPU support. */
    	}
    	#else
    	{
    		#error Invalid configUSE_TASK_DPFPU_SUPPORT setting - configUSE_TASK_DPFPU_SUPPORT must be set to 0, 1, 2, or left undefined.
    	}
    	#endif
    
    	return pxTopOfStack;
    }
    /*-----------------------------------------------------------*/
    
    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();
    
    		/* 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;
    
    		/* Start the first task. */
    		prvStartFirstTask();
    	}
    
    	/* Should not get here. */
    	return pdFAIL;
    }
    /*-----------------------------------------------------------*/
    
    void vPortEndScheduler( void )
    {
    	/* Not implemented in ports where there is nothing to return to.
    	Artificially force an assert. */
    	configASSERT( pxCurrentTCB == NULL );
    }
    /*-----------------------------------------------------------*/
    
    static void prvStartFirstTask( void )
    {
    	__asm volatile
    	(
    		/* When starting the scheduler there is nothing that needs moving to the
    		interrupt stack because the function is not called from an interrupt.
    		Just ensure the current stack is the user stack. */
    		"SETPSW		U						\n" \
    
    		/* Obtain the location of the stack associated with which ever task
    		pxCurrentTCB is currently pointing to. */
    		"MOV.L		#_pxCurrentTCB, R15		\n" \
    		"MOV.L		[R15], R15				\n" \
    		"MOV.L		[R15], R0				\n" \
    
    		/* Restore the registers from the stack of the task pointed to by
    		pxCurrentTCB. */
    
    		//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    		#if( configUSE_TASK_DPFPU_SUPPORT != 0 )
    
    			/* Is there a DPFPU context to restore?  If the restored
    			ulPortTaskHasDPFPUContext is zero then no. */
    			"POP		R15									\n" \
    			"MOV.L		#_ulPortTaskHasDPFPUContext, R14	\n" \
    			"MOV.L		R15, [R14]							\n" \
    			"CMP		#0, R15								\n" \
    
    			/* Restore the DPFPU context, if any. */
    			"BEQ.B		?+						\n" \
    			"DPOPM.L	DPSW-DECNT				\n" \
    			"DPOPM.D	DR0-DR15				\n" \
    			"?:									\n" \
    
    		#endif
    
    		/* Accumulator low 32 bits. */
    		"POP		R15						\n" \
    		"MVTACLO	R15, A0					\n" \
    
    		/* Accumulator high 32 bits. */
    		"POP		R15						\n" \
    		"MVTACHI	R15, A0					\n" \
    
    		/* Accumulator guard. */
    		"POP		R15						\n" \
    		"MVTACGU	R15, A0					\n" \
    
    		/* Accumulator low 32 bits. */
    		"POP		R15						\n" \
    		"MVTACLO	R15, A1					\n" \
    
    		/* Accumulator high 32 bits. */
    		"POP		R15						\n" \
    		"MVTACHI	R15, A1					\n" \
    
    		/* Accumulator guard. */
    		"POP		R15						\n" \
    		"MVTACGU	R15, A1					\n" \
    
    		/* Floating point status word. */
    		"POP		R15						\n" \
    		"MVTC		R15, FPSW 				\n" \
    
    		/* R1 to R15 - R0 is not included as it is the SP. */
    		"POPM		R1-R15 					\n" \
    
    		/* This pops the remaining registers. */
    		"RTE								\n" \
    		"NOP								\n" \
    		"NOP								\n"
    	);
    }
    /*-----------------------------------------------------------*/
    
    void vSoftwareInterruptISR( void )
    {
    	__asm volatile
    	(
    		/* Re-enable interrupts. */
    		"SETPSW		I							\n" \
    
    		/* Move the data that was automatically pushed onto the interrupt stack when
    		the interrupt occurred from the interrupt stack to the user stack.
    
    		R15 is saved before it is clobbered. */
    		"PUSH.L		R15							\n" \
    
    		/* Read the user stack pointer. */
    		"MVFC		USP, R15					\n" \
    
    		/* Move the address down to the data being moved. */
    		"SUB		#12, R15					\n" \
    		"MVTC		R15, USP					\n" \
    
    		/* Copy the data across, R15, then PC, then PSW. */
    		"MOV.L		[ R0 ], [ R15 ]				\n" \
    		"MOV.L 		4[ R0 ], 4[ R15 ]			\n" \
    		"MOV.L		8[ R0 ], 8[ R15 ]			\n" \
    
    		/* Move the interrupt stack pointer to its new correct position. */
    		"ADD		#12, R0						\n" \
    
    		/* All the rest of the registers are saved directly to the user stack. */
    		"SETPSW		U							\n" \
    
    		/* Save the rest of the general registers (R15 has been saved already). */
    		"PUSHM		R1-R14						\n" \
    
    		/* Floating point status word. */
    		"MVFC		FPSW, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator guard. */
    		"MVFACGU	#0, A1, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator high 32 bits. */
    		"MVFACHI	#0, A1, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator low 32 bits. */
    		"MVFACLO	#0, A1, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator guard. */
    		"MVFACGU	#0, A0, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator high 32 bits. */
    		"MVFACHI	#0, A0, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		/* Accumulator low 32 bits. */
    		"MVFACLO	#0, A0, R15					\n" \
    		"PUSH.L		R15							\n" \
    
    		//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    		#if( configUSE_TASK_DPFPU_SUPPORT != 0 )
    
    			/* Does the task have a DPFPU context that needs saving?  If
    			ulPortTaskHasDPFPUContext is 0 then no. */
    			"MOV.L		#_ulPortTaskHasDPFPUContext, R15	\n" \
    			"MOV.L		[R15], R15							\n" \
    			"CMP		#0, R15								\n" \
    
    			/* Save the DPFPU context, if any. */
    			"BEQ.B		?+							\n" \
    			"DPUSHM.D	DR0-DR15					\n" \
    			"DPUSHM.L	DPSW-DECNT					\n" \
    			"?:										\n" \
    
    			/* Save ulPortTaskHasDPFPUContext itself. */
    			"PUSH		R15							\n" \
    
    		#endif
    
    		/* Save the stack pointer to the TCB. */
    		"MOV.L		#_pxCurrentTCB, R15			\n" \
    		"MOV.L		[ R15 ], R15				\n" \
    		"MOV.L		R0, [ R15 ]					\n" \
    
    		/* Ensure the interrupt mask is set to the syscall priority while the kernel
    		structures are being accessed. */
    		"MVTIPL		%0 							\n" \
    
    		/* Select the next task to run. */
    		"BSR.A		_vTaskSwitchContext			\n" \
    
    		/* Reset the interrupt mask as no more data structure access is required. */
    		"MVTIPL		%1							\n" \
    
    		/* Load the stack pointer of the task that is now selected as the Running
    		state task from its TCB. */
    		"MOV.L		#_pxCurrentTCB,R15			\n" \
    		"MOV.L		[ R15 ], R15				\n" \
    		"MOV.L		[ R15 ], R0					\n" \
    
    		/* Restore the context of the new task.  The PSW (Program Status Word) and
    		PC will be popped by the RTE instruction. */
    
    		//////////////////////////////////////////////////////////////////////////////////////////////////////////////
    		#if( configUSE_TASK_DPFPU_SUPPORT != 0 )
    
    			/* Is there a DPFPU context to restore?  If the restored
    			ulPortTaskHasDPFPUContext is zero then no. */
    			"POP		R15									\n" \
    			"MOV.L		#_ulPortTaskHasDPFPUContext, R14	\n" \
    			"MOV.L		R15, [R14]							\n" \
    			"CMP		#0, R15								\n" \
    
    			/* Restore the DPFPU context, if any. */
    			"BEQ.B		?+							\n" \
    			"DPOPM.L	DPSW-DECNT					\n" \
    			"DPOPM.D	DR0-DR15					\n" \
    			"?:										\n" \
    
    		#endif
    
    		/* Accumulator low 32 bits. */
    		"POP		R15							\n" \
    		"MVTACLO	R15, A0						\n" \
    
    		/* Accumulator high 32 bits. */
    		"POP		R15							\n" \
    		"MVTACHI	R15, A0						\n" \
    
    		/* Accumulator guard. */
    		"POP		R15							\n" \
    		"MVTACGU	R15, A0						\n" \
    
    		/* Accumulator low 32 bits. */
    		"POP		R15							\n" \
    		"MVTACLO	R15, A1						\n" \
    
    		/* Accumulator high 32 bits. */
    		"POP		R15							\n" \
    		"MVTACHI	R15, A1						\n" \
    
    		/* Accumulator guard. */
    		"POP		R15							\n" \
    		"MVTACGU	R15, A1						\n" \
    
    		/* Floating point status word. */
    		"POP		R15							\n" \
    		"MVTC		R15, FPSW					\n" \
    
    		/* R1 to R15 - R0 is not included as it is the SP. */
    		"POPM		R1-R15						\n" \
    
    		/* This pops the remaining registers. */
    		"RTE									\n" \
    		"NOP									\n" \
    		"NOP									  "
    		:: "i"(configMAX_SYSCALL_INTERRUPT_PRIORITY), "i"(configKERNEL_INTERRUPT_PRIORITY)
    	);
    }
    /*-----------------------------------------------------------*/
    
    void vTickISR( void )
    {
    	/* Re-enabled interrupts. */
    	__asm volatile( "SETPSW	I" );
    
    	/* Increment the tick, and perform any processing the new tick value
    	necessitates.  Ensure IPL is at the max syscall value first. */
    	portMASK_INTERRUPTS_FROM_KERNEL_ISR();
    	{
    		if( xTaskIncrementTick() != pdFALSE )
    		{
    			taskYIELD();
    		}
    	}
    	portUNMASK_INTERRUPTS_FROM_KERNEL_ISR();
    }
    /*-----------------------------------------------------------*/
    
    uint32_t ulPortGetIPL( void )
    {
    	__asm volatile
    	(
    		"MVFC	PSW, R1			\n"	\
    		"SHLR	#24, R1			\n"	\
    		"RTS					  "
    	);
    
    	/* This will never get executed, but keeps the compiler from complaining. */
    	return 0;
    }
    /*-----------------------------------------------------------*/
    
    void vPortSetIPL( uint32_t ulNewIPL )
    {
    	/* Avoid compiler warning about unreferenced parameter. */
    	( void ) ulNewIPL;
    
    	__asm volatile
    	(
    		"PUSH	R5				\n" \
    		"MVFC	PSW, R5			\n"	\
    		"SHLL	#24, R1			\n" \
    		"AND	#-0F000001H, R5 \n" \
    		"OR		R1, R5			\n" \
    		"MVTC	R5, PSW			\n" \
    		"POP	R5				\n" \
    		"RTS					  "
    	 );
    }
    /*-----------------------------------------------------------*/
    
    #if( configUSE_TASK_DPFPU_SUPPORT == 1 )
    
    	void vPortTaskUsesDPFPU( void )
    	{
    		/* A task is registering the fact that it needs a DPFPU context.  Set the
    		DPFPU flag (which is saved as part of the task context). */
    		ulPortTaskHasDPFPUContext = pdTRUE;
    	}
    
    #endif /* configUSE_TASK_DPFPU_SUPPORT */
    /*-----------------------------------------------------------*/
    
    


    FreeRTOS/Source/portable/GCC/RX700v3_DPFPU/portmacro.h
    3554.portmacro.h.20200624.txt
    /*
     * FreeRTOS Kernel V10.3.1
     * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
     *
     * Permission is hereby granted, free of charge, to any person obtaining a copy of
     * this software and associated documentation files (the "Software"), to deal in
     * the Software without restriction, including without limitation the rights to
     * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
     * the Software, and to permit persons to whom the Software is furnished to do so,
     * subject to the following conditions:
     *
     * The above copyright notice and this permission notice shall be included in all
     * copies or substantial portions of the Software.
     *
     * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
     * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
     * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
     * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
     * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
     * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
     *
     * http://www.FreeRTOS.org
     * http://aws.amazon.com/freertos
     *
     * 1 tab == 4 spaces!
     */
    
    
    #ifndef PORTMACRO_H
    #define PORTMACRO_H
    
    #ifdef __cplusplus
    extern "C" {
    #endif
    
    /*-----------------------------------------------------------
     * Port specific definitions.
     *
     * The settings in this file configure FreeRTOS correctly for the
     * given hardware and compiler.
     *
     * These settings should not be altered.
     *-----------------------------------------------------------
     */
    
    /* Set configUSE_TASK_DPFPU_SUPPORT to 0 to omit DPFPU support even if DPFPU
    hardware is otherwise supported by the FreeRTOS port in use. This constant is
    supported by only FreeRTOS Renesas RX ports that include DPFPU support. */
    
    /* If configUSE_TASK_DPFPU_SUPPORT is set to 1 (or undefined) then each task will
    be created without a DPFPU context, and a task must call vTaskUsesDPFPU() before
    making use of any DPFPU registers.  If configUSE_TASK_DPFPU_SUPPORT is set to 2 then
    tasks are created with a DPFPU context by default, and calling vTaskUsesDPFPU() has
    no effect.  If configUSE_TASK_DPFPU_SUPPORT is set to 0 then tasks never take care
    of any DPFPU context (even if registers DPFPU are used). */
    #ifndef configUSE_TASK_DPFPU_SUPPORT
    	#define configUSE_TASK_DPFPU_SUPPORT 1
    #endif
    
    /* When the FIT configurator or the Smart Configurator is used, platform.h has to be 
    used. */
    #ifndef configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H
    	#define configINCLUDE_PLATFORM_H_INSTEAD_OF_IODEFINE_H 0
    #endif
    
    /*-----------------------------------------------------------*/
    
    /* Type definitions - these are a bit legacy and not really used now, other than
    portSTACK_TYPE and portBASE_TYPE. */
    #define portCHAR		char
    #define portFLOAT		float
    #define portDOUBLE		double
    #define portLONG		long
    #define portSHORT		short
    #define portSTACK_TYPE	uint32_t
    #define portBASE_TYPE	long
    
    typedef portSTACK_TYPE StackType_t;
    typedef long BaseType_t;
    typedef unsigned long UBaseType_t;
    
    #if( configUSE_16_BIT_TICKS == 1 )
    	typedef uint16_t TickType_t;
    	#define portMAX_DELAY ( TickType_t ) 0xffff
    #else
    	typedef uint32_t TickType_t;
    	#define portMAX_DELAY ( TickType_t ) 0xffffffffUL
    
    	/* 32-bit tick type on a 32-bit architecture, so reads of the tick count do
    	not need to be guarded with a critical section. */
    	#define portTICK_TYPE_IS_ATOMIC 1
    #endif
    /*-----------------------------------------------------------*/
    
    /* Hardware specifics. */
    #define portBYTE_ALIGNMENT			8	/* Could make four, according to manual. */
    #define portSTACK_GROWTH			-1
    #define portTICK_PERIOD_MS			( ( TickType_t ) 1000 / configTICK_RATE_HZ )
    #define portNOP()					__asm volatile( "NOP" )
    
    /* 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"		\
    		"CMP	[R10].UB, R10		\n"		\
    		"POP	R10					\n"		\
    	)
    
    #define portYIELD_FROM_ISR( x )	if( ( x ) != pdFALSE ) portYIELD()
    
    /* These macros should not be called directly, but through the
    taskENTER_CRITICAL() and taskEXIT_CRITICAL() macros.  An extra check is
    performed if configASSERT() is defined to ensure an assertion handler does not
    inadvertently attempt to lower the IPL when the call to assert was triggered
    because the IPL value was found to be above	configMAX_SYSCALL_INTERRUPT_PRIORITY
    when an ISR safe FreeRTOS API function was executed.  ISR safe FreeRTOS API
    functions are those that end in FromISR.  FreeRTOS maintains a separate
    interrupt API to ensure API function and interrupt entry is as fast and as
    simple as possible. */
    #define portENABLE_INTERRUPTS() 	__asm volatile ( "MVTIPL	#0" )
    #ifdef configASSERT
    	#define portASSERT_IF_INTERRUPT_PRIORITY_INVALID() configASSERT( ( ulPortGetIPL() <= configMAX_SYSCALL_INTERRUPT_PRIORITY ) )
    	#define portDISABLE_INTERRUPTS() 	if( ulPortGetIPL() < configMAX_SYSCALL_INTERRUPT_PRIORITY ) __asm volatile ( "MVTIPL	%0" ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) )
    #else
    	#define portDISABLE_INTERRUPTS() 	__asm volatile ( "MVTIPL	%0" ::"i"(configMAX_SYSCALL_INTERRUPT_PRIORITY) )
    #endif
    
    /* Critical nesting counts are stored in the TCB. */
    #define portCRITICAL_NESTING_IN_TCB ( 1 )
    
    /* The critical nesting functions defined within tasks.c. */
    extern void vTaskEnterCritical( void );
    extern void vTaskExitCritical( void );
    #define portENTER_CRITICAL()	vTaskEnterCritical()
    #define portEXIT_CRITICAL()		vTaskExitCritical()
    
    /* As this port allows interrupt nesting... */
    uint32_t ulPortGetIPL( void ) __attribute__((naked));
    void vPortSetIPL( uint32_t ulNewIPL ) __attribute__((naked));
    #define portSET_INTERRUPT_MASK_FROM_ISR() ulPortGetIPL(); portDISABLE_INTERRUPTS()
    #define portCLEAR_INTERRUPT_MASK_FROM_ISR( uxSavedInterruptStatus ) vPortSetIPL( uxSavedInterruptStatus )
    
    /*-----------------------------------------------------------*/
    
    /* Task function macros as described on the FreeRTOS.org WEB site. */
    #define portTASK_FUNCTION_PROTO( vFunction, pvParameters ) void vFunction( void *pvParameters )
    #define portTASK_FUNCTION( vFunction, pvParameters ) void vFunction( void *pvParameters )
    
    /* If configUSE_TASK_DPFPU_SUPPORT is set to 1 (or left undefined) then tasks are
    created without a DPFPU context and must call vPortTaskUsesDPFPU() to give
    themselves a DPFPU context before using any DPFPU instructions.  If
    configUSE_TASK_DPFPU_SUPPORT is set to 2 then all tasks will have an DPFPU context
    by default. */
    #if( configUSE_TASK_DPFPU_SUPPORT == 1 )
    	void vPortTaskUsesDPFPU( void );
    #else
    	/* Each task has a DPFPU context already, so define this function away to
    	nothing to prevent it being called accidentally. */
    	#define vPortTaskUsesDPFPU()
    #endif
    #define portTASK_USES_DPFPU() vPortTaskUsesDPFPU()
    /* Definition to allow compatibility with existing FreeRTOS Demo using flop.c. */
    #define portTASK_USES_FLOATING_POINT() vPortTaskUsesDPFPU()
    
    #ifdef __cplusplus
    }
    #endif
    
    #endif /* PORTMACRO_H */
    
    

     

  • こんにちは。NoMaYです。

    こんな感じになりました。zipファイルには、作成したRXv3-DPFPUポートレイヤと、比較用に移植元のRXv2ポートレイヤを、一緒に固めました。(なお、RTOSDemoプログラムの方は微調整作業の最中です。)

    RX700v3-DPFPU-portlayer-rc1-20200701.zip

    ├─org-FreeRTOS-Kernel-10.3.1 (比較用の移植元のRXv2ポートレイヤ)
    │  └─portable
    │      ├─GCC
    │      │  └─RX600v2
    │      │      ├─portmacro.h
    │      │      └─port.c
    │      ├─IAR
    │      │  └─RXv2 (なぜかフォルダ名が他と違います)
    │      │      ├─portmacro.h
    │      │      ├─port.c
    │      │      └─port_asm.s
    │      └─Renesas
    │          └─RX600v2
    │              ├─portmacro.h
    │              ├─port.c
    │              └─port_asm.src
    └─rc1-20200701 (作成したRXv3-DPFPUポートレイヤ)
       └─portable
           ├─GCC
           │  └─RX700v3_DPFPU
           │      ├─portmacro.h
           │      └─port.c
           ├─IAR
           │  └─RX700v3_DPFPU
           │      ├─portmacro.h
           │      └─port.c
           └─Renesas
               └─RX700v3_DPFPU
                   ├─portmacro.h
                   ├─port.c
                   └─port_asm.src (変更無し)

     

  • こんにちは。NoMaYです。

    RX72N搭載の三角関数演算器(RXv3アーキテクチャ構成要素では無いですけれども)ですが、レジスタの仕様が公開されておらず自分が最初から諦めていたので問題意識として脇へ追いやっていたのですが、CC-RXで組み込み関数から普通に使えますし、コンパイルオプションを指定すれば数学ライブラリ内で使われるようにもなりますので、タスク切り替え時に退避/復帰することが出来るようにする何らかの実装があった方が良いとは思っているのですが、仕様が公開されていないので何も出来ていないです。

    RX72Nグループ ユーザーズマニュアル ハードウェア編
    www.renesas.com/jp/ja/search/keyword-search.html#genre=document&q=r01uh0824
    r01uh0824jj0100-rx72n.pdf
    三角関数演算器についての記載はこれだけです、、、

     

  • In reply to NoMaY:

    NoMaYさん

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

    本件、とても気になります。
    hirakuni45さんもご自身のブログ記事でこの点言及されてました。

    FreeRTOSのタスク切り替え時にどう処理すべきかを軸に少し調査してみます。
    また何かわかりましたらこの場で相談させてください。

    以上です

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