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;
}

続く

Parents
  • こんにちは。NoMaYです。

    ちなみに、RX72N搭載の三角関数演算器を扱うコードはBSPモジュールの中でも公開されていましたね。それを見て思ったのは、入力レジスタと出力レジスタが同じアドレスに割り付けられていましたけれど、もし別々になっていて且つ入力レジスタにライトした値がそのままリード出来るようになっていたら、タスク切り替え処理の中で素朴に入力レジスタ値を退避/復帰すれば出来たのかもなぁ、と思いました。

    それで、排他制御ですが、どのライブラリ関数が三角関数演算器を使っているかヘルプから分かりますので、排他制御の中でも軽い部類の、PUSH PSW,CLR PSW.I,POP PSW、あるいは、PUSH PSW,15⇒PSW.IPL,POP PSW、といったところでガードするのがやり易いでしょうかね。

  • こんにちは、hirakuni45です。

    TFU の平均的なパフォーマンスを計測して、意外と短いようなら、単純なロックビットを使う方法で行い、

    そこそこ時間がかかるようなら、セマフォを使った方法を行うのが良いと思います。

    ※TFU はそこそこ高速だと思うので、単純なロックビットで良さそうですね・・

    ※1ms以下だと、タスクを切り替える方がコストが大きそうなので・・


    GNU-RX では、TFU が関係する API は、以下のもので、CC-RX とかだと異なるのでしょうか?

    // -mtfu=intrinsic
    void __init_tfu(void);
    void __builtin_rx_sincosf(float, float*, float*);
    void __builtin_rx_atan2hypotf(float, float, float*, float*);
    // -mtfu=intrinsic,mathlib
    float __builtin_rx_sinf(float);
    float __builtin_rx_cosf(float);
    float __builtin_rx_atan2f(float, float);
    float __builtin_rx_hypotf(float, float);
  • hirakuni45さん、こんにちは。NoMaYです。

    > GNU-RX では、TFU が関係する API は、以下のもので、CC-RX とかだと異なるのでしょうか?

    もしnewlibを使うのであれば、その中でどのように三角関数演算器が使われているのか気になります。CC-RXでは、以下の画面コピーの通りになっていました。(newlibではありませんが。)



    中略





    中略








     

  • 最新のGNU-RX(gcc-8.3.0.202102-GNURX-ELF)のリリースノートに

    「2. [Improvement] - Using the -tfu option will now expand libcalls to instrinsic functions.」

    とあります・・・

    実際は、標準関数との精度の問題もあるし、勝手に呼ばれるのは、良くないと思います。

    ※初期化関数は自分で呼ばなくてはならないし・・

    スレッドセーフでもないし、呼びたい人が個別に対応する方が良いように思います。

    なので、mathlib は使わず、使いたい人がスレッドセーフにして呼び出すようにすれば良いように思います。

Reply
  • 最新のGNU-RX(gcc-8.3.0.202102-GNURX-ELF)のリリースノートに

    「2. [Improvement] - Using the -tfu option will now expand libcalls to instrinsic functions.」

    とあります・・・

    実際は、標準関数との精度の問題もあるし、勝手に呼ばれるのは、良くないと思います。

    ※初期化関数は自分で呼ばなくてはならないし・・

    スレッドセーフでもないし、呼びたい人が個別に対応する方が良いように思います。

    なので、mathlib は使わず、使いたい人がスレッドセーフにして呼び出すようにすれば良いように思います。

Children
No Data