こんにちは。NoMaYです。まだQiitaに掲題の投稿は見当たらなかったので考えてみようと思いました。RXv3コア搭載の品種の中でも、RX72M, RX72N, RX66Nには倍精度浮動小数点演算命令がありますが、命令の操作対象となるレジスタは、通常の汎用レジスタが使用される単精度浮動小数点演算の場合とは異なり、倍精度浮動小数点演算専用のレジスタが用意されています。また、命令の演算結果のフラグなどが格納されるレジスタも専用のものが用意されていますし、例外の発生制御を行ったり発生状況が格納されたりするレジスタにも専用のものが用意されています。ですが、既存のRXv2コア向けのFreeRTOSポートレイヤは、当然ながら、これらの倍精度浮動小数点演算専用のレジスタのことは考慮されておらず、タスク切り替え時にこれらのレジスタの内容の退避/復帰を行っていない為、タスク間で倍精度浮動小数点レジスタの値がぐちゃぐちゃに入り乱れることになってしまうので、倍精度浮動小数点演算命令の使用を諦めざるを得ないことになります。これに対処する箇所は以下の4箇所(赤字箇所)かと思います。(まだ今は概念コードのレベルですが。)port.c
#pragma inline_asm prvYieldHandlerstatic 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 prvStartFirstTaskstatic 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です。> (1) 通常のタスク処理内か、それとも割り込み処理内か、を真/偽で返すポートレイヤ関数の追加以下のコードにしました。CC-RXの場合portmacro.h
/* Checks whether the current execution context is interrupt. * Return pdTRUE if the current execution context is interrupt, * pdFALSE otherwise. */#pragma inline xPortIsInsideInterruptstatic BaseType_t xPortIsInsideInterrupt( void ){ /* When the user stack pointer is used, the context is not interrupt. * When the interrupt stack pointer is used, the context is interrupt. * Don't call this function before the scheduler has started because * this function always returns pdTRUE before the timing. */ return ( __get_psw() & 0x00020000 /* PSW.U */ ) != 0 ? pdFALSE : pdTRUE;}
ICCRXの場合portmacro.h
/* Checks whether the current execution context is interrupt. * Return pdTRUE if the current execution context is interrupt, * pdFALSE otherwise. */#pragma inline = forcedstatic __inline BaseType_t xPortIsInsideInterrupt( void ){ /* When the user stack pointer is used, the context is not interrupt. * When the interrupt stack pointer is used, the context is interrupt. * Don't call this function before the scheduler has started because * this function always returns pdTRUE before the timing. */ return ( __get_PSW_register() & 0x00020000 /* PSW.U */ ) != 0 ? pdFALSE : pdTRUE;}
GNURXの場合portmacro.h
/* Checks whether the current execution context is interrupt. * Return pdTRUE if the current execution context is interrupt, * pdFALSE otherwise. */__inline __attribute__( ( always_inline ) ) static BaseType_t xPortIsInsideInterrupt( void ){ /* When the user stack pointer is used, the context is not interrupt. * When the interrupt stack pointer is used, the context is interrupt. * Don't call this function before the scheduler has started because * this function always returns pdTRUE before the timing. */ return ( __builtin_rx_mvfc( 0x0 /* PSW */ ) & 0x00020000 /* PSW.U */ ) != 0 ? pdFALSE : pdTRUE;}
CC-RL/ICCRL78/GNURL78/LLVM-RL78の場合portmacro.h
/* Checks whether the current execution context is interrupt. * Return pdTRUE if the current execution context is interrupt, * pdFALSE otherwise. */extern BaseType_t xPortIsInsideInterrupt( void );
port.c
BaseType_t xPortIsInsideInterrupt( void ){ BaseType_t xReturn; /* Check the value of ISP bits of PSW of the currently executing context. * When ISP value is equal to the initial value, the context is not interrupt. * When ISP value is not equal to the initial value, the context is interrupt. */ if( portGET_PSW_ISP() == portPSW_ISP ) { xReturn = pdFALSE; } else { xReturn = pdTRUE; } return xReturn;}