Any plan of Renesas RXv3 port layer supporting RXv3's double precision FPU?

Hello Richard,

Is there any plan of such RXv3 port layer? If so, is there any rough schedule? Because…

Recently Renesas ships new RX MCUs which have new RXv3 CPU core. Some of them, RX72M, RX72N and RX66N, have a double precision FPU (DPFPU) and this DPFPU has dedicated data registers and controle registers. I think that these dedicated registers have to be saved/restored in the task switching sequence but the RXv2 port layer, of course, does not take care of them.

RX Family RXv3 Instruction Set Architecture User’s Manual: Software
https://www.renesas.com/us/en/search/keyword-search.html#genre=document&q=r01us0316
r01us0316ej0100-rxv3sm.pdf
Page 38 / 396
1.10 Double-Precision Floating-Point Coprocessor

Last week I started the following Japanese thread regarding to this issue. But now I think that asking you about any plan of new port layer supporting the DPFPU is better to know than not asking, regardless of whether proceeding the thread or not. And I’d like to post a report about your answer to the thread.

A consideration of developing a FreeRTOS kernel RXv3-DPFPU port layer (Please note that original title and all contents are Japanese.)
http://japan.renesasrulz.com/cafe_rene/f/forum21/6329/freertos-kernel-rxv3-dpfpu-port-layer

Best regards,
NoMaY

Yes it would be good to have upstream support for this. I would have to get the hardware first. We also accept pull requests https://github.com/FreeRTOS/FreeRTOS-Kernel if somebody needed this sooner - we would still need to get hardware but the task would be faster as we would just need to verify the testing.

Hello Richard,

Thank you for your reply. I’d like to proceed with the Japanese thread as possible as I can. And I’d like to post a pull request after I finish it (if I can finish it).

By the way, I will use the following hardware i.e. I will not be able to use “Starter Kit”.

https://www.renesas.com/us/en/products/software-tools/boards-and-kits/eval-kits/rx72n-envision-kit.html

https://github.com/renesas/rx72n-envision-kit/wiki

As of todya, Renesas uses the RX600v2 port layer for its firmware.

https://github.com/renesas/rx72n-envision-kit/blob/master/freertos_kernel/portable/Renesas/RX600v2/port.c

Best regards,
NoMaY

Hello Richard,

Two weeks have passed since my last reply but two more weeks seem to be necessary to post a pull request. When I’ll post a pull request, I’d like to include four RTOSDemo projects for the following environments (if any serious bugs of compilers were not found).

  • GNURX/e2 studio
  • ICCRX/EWRX
  • CC-RX/e2 studio
  • CC-RX/CS+

These projects will be derived from the RX71M RSK’s RTOSDemo projects. Additionally, these projects will be composed according to the e2 studio’s Renese RX FreeRTOS project style. (Latest e2 studio can generate the Renese RX FreeRTOS project for CC-RX and GNURX in case of recent RX MCUs such as RX130, RX231, RX64M, RX65N and so on like the following screen copy.)

Best regards,
NoMaY

1 Like

Looks good - we can communicate back and forth in the pull requests if any updates are needed to the project.

Note the FreeRTOS kernel is now in its own repo https://github.com/FreeRTOS/FreeRTOS-Kernel so that is where the port layer pull requests can go. The kernel git repo is then sub-moduled into the repo that contains the full download https://github.com/FreeRTOS/FreeRTOS so that is where the demos go.

In the mean time I purchased two RX72N dev kits and can send one to one of my colleagues.

Hello Richard,

I am finishing to develop the port layers and the RTOS Demo projects. I’ll start a pull request soon. By the way, I added one more environment as following.

GNURX/e2 studio
ICCRX/e2 studio <---- I added this environment.
ICCRX/EWRX
CC-RX/e2 studio
CC-RX/CS+

Best regards,
NoMaY

Hello Richard,

I’m sorry for bothering you but I need your help to finish to develop the port layers and the RTOS Demo projects. I have the following questions.


(1) Based on the Cortex-A9 GCC port layer’s FPU support code, I added the double precision FPU support code to Renesas RX600v2 GCC/IAR/Renesas port layers and made RX700v3 DPFPU GCC/IAR/Renesas port layers. But I am confused due to the following original code.

port.c
https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CA9/port.c

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
...
    #if( configUSE_TASK_FPU_SUPPORT == 1 )
    {
        /* The task will start without a floating point context.  A task that
        uses the floating point hardware must call vPortTaskUsesFPU() before
        executing any floating point instructions. */
        pxTopOfStack--;
        *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
    }
    #elif( configUSE_TASK_FPU_SUPPORT == 2 )
    {
        /* The task will start with a floating point context.  Leave enough
        space for the registers - and ensure they are initialised to 0. */
        pxTopOfStack -= portFPU_REGISTER_WORDS;
        memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );

        pxTopOfStack--;
        *pxTopOfStack = pdTRUE; <---- This means (newly created) TASK HAS FLOATING POINT CONTEXT.
        ulPortTaskHasFPUContext = pdTRUE; <---- What does this code mean?
    }
    #else
...
}

(1-#) By the way, today I noticed that the Cortex-A9 IAR and RVDS port layers do not have ‘configUSE_TASK_FPU_SUPPORT’ and they support only the case of ‘configUSE_TASK_FPU_SUPPORT == 1’.

(1-a) The ulPortTaskHasFPUContext is a task context. It belongs to each task and the value in a task is always non-zero (i.e. ‘pdTRUE’) in case of ‘configUSE_TASK_FPU_SUPPORT == 2’.

(1-b) The pxPortInitialiseStack() is executed in either Non-task (i.e. before the first task starts) or Task (i.e. after the first task starts) which is goint to create new task.

My question is the following. ****************

(1-Q) When a task starts, the value of ulPortTaskHasFPUContext is restored from the stack which was set by the pxPortInitialiseStack() to ‘pdTRUE’. This means that the value of ulPortTaskHasFPUContext of all Task is already ‘pdTRUE’ so that ‘ulPortTaskHasFPUContext = pdTRUE;’ is not necessary. On the other hand, Non-task does not need the ulPortTaskHasFPUContext.
Is there any reason for this statement?


(2) I modified RTOSDemo programs of RX700_RX71M_RSK_GCC_e2studio_IAR and RX700_RX71M_RSK_Renesas_e2studio for RX72N Envision Kit and made RTOSDemo programs of RX700_RX72N_EnvisionKit_GCC_e2studio, RX700_RX72N_EnvisionKit_IAR_e2studio_EWRX and RX700_RX72N_EnvisionKit_Renesas_e2studio_CS+. But I wonder why the original two programs have the following difference.

(2-a) The former (i.e. for GNURX and ICCRX) has the following definition.

IntQueueTimer.c
https://github.com/FreeRTOS/FreeRTOS/blob/master/FreeRTOS/Demo/RX700_RX71M_RSK_GCC_e2studio_IAR/src/Full_Demo/IntQueueTimer.c

#define tmrTIMER_0_1_FREQUENCY  ( 2000UL )
#define tmrTIMER_2_3_FREQUENCY  ( 2301UL ) <---- HERE

(2-b) The latter (i.e. CC-RX) has the following definition.

IntQueueTimer.c
https://github.com/FreeRTOS/FreeRTOS/blob/master/FreeRTOS/Demo/RX700_RX71M_RSK_Renesas_e2studio/src/Full_Demo/IntQueueTimer.c

#define tmrTIMER_0_1_FREQUENCY  ( 2000UL )
#define tmrTIMER_2_3_FREQUENCY  ( 2407UL ) <---- HERE

My question is the following. ****************

(2-Q) Why the definision of tmrTIMER_2_3_FREQUENCY is different from each other? Moreover, tmrTIMER_2_3_FREQUENCY is not used and only tmrTIMER_0_1_FREQUENCY is used in the both IntQueueTimer.c as following. Why is it not used?

/* Set the compare match value. */
TMR01.TCORA = ( unsigned short ) ( ( ( configPERIPHERAL_CLOCK_HZ / tmrTIMER_0_1_FREQUENCY ) -1 ) / 8 );
TMR23.TCORA = ( unsigned short ) ( ( ( configPERIPHERAL_CLOCK_HZ / tmrTIMER_0_1_FREQUENCY ) -1 ) / 8 );

Best Regards,
NoMaY

The A9 is a more complex port because the floating point registers are separate from the integer registers - and the code sequence basically differentiates whether you want all tasks to be given a floating point context by default, or if you want to select which tasks do and don’t have a floating point context. ulPortTaskHasFPUContext is saved as part of the task’s context to let the scheduler know if it has to save and restore the floating point registers for that task. As I recall, the RX does not have separate floating point and integer registers, and saving a floating point context involves saving just one additional register, namely the floating point status word.

That is because those compilers do not use floating point registers unexpectedly, so you don’t need the option to give every task a floating point context. Whereas the GCC compiler, depending on version and optimisation level, can use the wide floating point registers to optimise C standard library functions such as memcpy() - in which case it is safer to give all tasks a floating point context.

Are you using this RXv2 file, which already has floating point support, as a reference? https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/RX600v2/port.c

I don’t think you need to worry about the ulPortTaskHasFPUContext variable.

Hmm, now here you have me, I don’t know why there is a difference, however from your next comment:

It looks like it is not used anyway. These timers are used to test the interrupt nesting, and it looks like it may have been copied from an example that had two timers, but for some reason in the RX demo only one was used.

Hello Richard,

Thank you for your reply and I’m sorry for confusing you. RXv3 double precision FPU has also separeted registers from integer registers (and interger registers are used as single precision floating point registers as you said). Therefore I picked up FPU support code from Cortex-A9 GCC port layer, then I modified it for RXv3 DPFPU, then I incorporated it into RX600v2 port layers and finally I made up them as RX700v3 DPFPU port layers. So, the following my question leads to RXv3 double precision FPU but does not lead to RXv2/v3 single precision FPU.

So, if Cortex-A9 GCC port layer does not need the statement, I will not incorporate such statement into RX700v3 DPFPU port layers. But if there is any reason that Cortex-A9 GCC port layer should have the statement, I will incorporate such statement into RX700v3 DPFPU port layers. (But I have no idea about the reason.) So, I still need your answer.

Is there any reason for that statement?

I don’t want to bother you so long, if you don’t have any idea (i.e. neither YES nor NO), please let me know. If so, I will not incorporate such statement into RX700v3 DPFPU port layers because I have an impression that this statement does not cause any problem but putting this statement here is strange code.


Regarding to the question (2-Q), my understanding is “there are no reason, somehow these are different, somehow only one difinition is used”. My conclusion is that the following code might be better. I will modify RX72N Envision Kit RTOSDemo programs to use this code.

#define tmrTIMER_0_1_FREQUENCY  ( 2000UL )
#define tmrTIMER_2_3_FREQUENCY  ( 2000UL ) <---- I will use 2000UL

/* Set the compare match value. */
TMR01.TCORA = ( ...omit... ) ( ( ( ...omit... / tmrTIMER_0_1_FREQUENCY ) -1 ) / 8 );
TMR23.TCORA = ( ...omit... ) ( ( ( ...omit... / tmrTIMER_2_3_FREQUENCY ) -1 ) / 8 ); <---- I will use tmrTIMER_2_3_FREQUENCY

Best Regards,
NoMaY

ulPortTaskHasFPUContext is initialised to zero. https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CA9/port.c#L199

In the case where configUSE_TASK_FPU_SUPPORT is 1, a task can give itself an FPU context by calling vPortTaskUsesFPU() https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CA9/port.c#L484 but that can only be done AFTER the scheduler has started, so the scheduler always starts without any tasks having a floating point context. ulPortTaskHasFPUContext is zero, so when the value of the variable is tested as the context of the first task to run it popped of that task’s stack it is correctly determined that the task does not have a floating point context - it can’t do yet because it hasn’t executed so can’t have called vPortTaskUsesFPU(). https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CA9/portASM.S#L102

In the case where configUSE_TASK_FPU_SUPPORT is 2 then all tasks are given an FPU context when they are created. That means ulPortTaskHasFPUContext must be 1 when the context of the first task to run is popped of that task’s stack - otherwise the floating point context of that task would not be set to its proper initial state when the task starts running and would instead just contain random values. Hence when configUSE_TASK_FPU_SUPPORT is 2 ulPortTaskHasFPUContext is set to 1.

Now the interesting question is, why is the variable set to 1 here: https://github.com/FreeRTOS/FreeRTOS-Kernel/blob/master/portable/GCC/ARM_CA9/port.c#L300? If xTaskCreate() is called after the scheduler has started then ulPortTaskHasFPUContext is part of the running task’s context, not part of the context of the task being created, but setting the variable to 1 is perfectly safe because every task has an FPU context, so the variable can never be anything other than 1 already, and setting it to 1 again therefore has no effect in any case other than when the first task starts. Arguable ulPortTaskHasFPUContext could just be initialised to 1 when configUSE_TASK_FPU_SUPPORT is 2, or set to 1 just once when the scheduler is started, but that would require an additional #if / #endif block, so a little less tidy.

Hello Richard,

Thank you very much for your reply. After I have read your replay, I can get three source code outline like the following. I think that you said about TYPE 1 and TYPE2. Is my understanding right? (There is one more souce code outline of TYPE3, but this outline does not seem to be what you said about.)

If so, but… is it necessary for ulPortTaskHasFPUContext to be either initialised to 1 (TYPE 1) or set to 1 (TYPE 2)? Anyhow ulPortTaskHasFPUContext is always poped to 1 when each task just starts…

I know, I know, missing such initialization/setting makes programmers (like me) confused… I’m sorry that I am Japanese and I cannot get good words to say about my new impression… i.e. You did not want to cause such confusion and also you did not want to add additional #if / #endif block. So, you placed the statment “ulPortTaskHasFPUContext = pdTRUE;” there. Is my new impression right?

TYPE 1)

portable/GCC/ARM_CA9/port.c

**#if( configUSE_TASK_FPU_SUPPORT == 1 )**

**ulPortTaskHasFPUContext = portNO_FLOATING_POINT_CONTEXT;**

**#elif( configUSE_TASK_FPU_SUPPORT == 2 )**

**ulPortTaskHasFPUContext = pdTRUE;**

**#else /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */**

**#error ...omit...**

**#endif /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */**

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
...
    #if( configUSE_TASK_FPU_SUPPORT == 1 )
    {
        /* The task will start without a floating point context.  A task that
        uses the floating point hardware must call vPortTaskUsesFPU() before
        executing any floating point instructions. */
        pxTopOfStack--;
        *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
    }
    #elif( configUSE_TASK_FPU_SUPPORT == 2 )
    {
        /* The task will start with a floating point context.  Leave enough
        space for the registers - and ensure they are initialised to 0. */
        pxTopOfStack -= portFPU_REGISTER_WORDS;
        memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );

        pxTopOfStack--;
        *pxTopOfStack = pdTRUE;
**////////        ulPortTaskHasFPUContext = pdTRUE;**
    }
    #else /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */
    {
        #error Invalid configUSE_TASK_FPU_SUPPORT setting - configUSE_TASK_FPU_SUPPORT must be set to 1, 2, or left undefined.
    }
    #endif /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */
...
}

TYPE 2)

portable/GCC/ARM_CA9/port.c

BaseType_t xPortStartScheduler( void )
{
    uint32_t ulAPSR;

    **#if( configUSE_TASK_FPU_SUPPORT == 1 )**
    **{**
        **ulPortTaskHasFPUContext = portNO_FLOATING_POINT_CONTEXT;**
    **}**
    **#elif( configUSE_TASK_FPU_SUPPORT == 2 )**
    **{**
        **ulPortTaskHasFPUContext = pdTRUE;**
    **}**
    **#else /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */**
    **{**
        **#error ...omit...**
    **}**
    **#endif /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */**
...

StackType_t *pxPortInitialiseStack( StackType_t *pxTopOfStack, TaskFunction_t pxCode, void *pvParameters )
{
...
    #if( configUSE_TASK_FPU_SUPPORT == 1 )
    {
        /* The task will start without a floating point context.  A task that
        uses the floating point hardware must call vPortTaskUsesFPU() before
        executing any floating point instructions. */
        pxTopOfStack--;
        *pxTopOfStack = portNO_FLOATING_POINT_CONTEXT;
    }
    #elif( configUSE_TASK_FPU_SUPPORT == 2 )
    {
        /* The task will start with a floating point context.  Leave enough
        space for the registers - and ensure they are initialised to 0. */
        pxTopOfStack -= portFPU_REGISTER_WORDS;
        memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );

        pxTopOfStack--;
        *pxTopOfStack = pdTRUE;
**////////        ulPortTaskHasFPUContext = pdTRUE;**
    }
    #else /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */
    {
        #error Invalid configUSE_TASK_FPU_SUPPORT setting - configUSE_TASK_FPU_SUPPORT must be set to 1, 2, or left undefined.
    }
    #endif /* if ( configUSE_TASK_FPU_SUPPORT == 1 ) */
...
}

TYPE 3)

portable/GCC/ARM_CA9/port.c

Nothing to do for ulPortTaskHasFPUContext. The value can be 0, 1 and even if random value.

portable/GCC/ARM_CA9/portASM.S

.macro portSAVE_CONTEXT

...omit...

**#if ( configUSE_TASK_FPU_SUPPORT == 2 )**

    **/* Save the floating point context, ALWAYS. */**
    **FMRX    R1,  FPSCR**
    **VPUSH   {D0-D15}**
    **VPUSH   {D16-D31}**
    **PUSH    {R1}**

**#else**

    /* Does the task have a floating point context that needs saving?  If
    ulPortTaskHasFPUContext is 0 then no. */
    LDR     R2, ulPortTaskHasFPUContextConst
    LDR     R3, [R2]
    CMP     R3, #0

    /* Save the floating point context, if any. */
    FMRXNE  R1,  FPSCR
    VPUSHNE {D0-D15}
    VPUSHNE {D16-D31}
    PUSHNE  {R1}

    /* Save ulPortTaskHasFPUContext itself. */
    PUSH    {R3}

**#endif**

...omit...

.macro portRESTORE_CONTEXT

...omit...

**#if ( configUSE_TASK_FPU_SUPPORT == 2 )**

    **/* Restore the floating point context, ALWAYS. */**
    **POP     {R0}**
    **VPOP    {D16-D31}**
    **VPOP    {D0-D15}**
    **VMSR    FPSCR, R0**

**#else**

    /* Is there a floating point context to restore?  If the restored
    ulPortTaskHasFPUContext is zero then no. */
    LDR     R0, ulPortTaskHasFPUContextConst
    POP     {R1}
    STR     R1, [R0]
    CMP     R1, #0

    /* Restore the floating point context, if any. */
    POPNE   {R0}
    VPOPNE  {D16-D31}
    VPOPNE  {D0-D15}
    VMSRNE  FPSCR, R0

**#endif**

...omit...

Am I right in thinking in your third option the variable that says if a task has a floating point context is ignored when all tasks are created with a floating point context? If so, then that is probably better as it is more efficient - no point testing the variable if you know it is going to be set to 1.

Hello Richard,

Now I am going to start modifying the latest code of RX700v3 DPFPU port layer according to the idea of the TYPE 3 because I agree with you that it is more efficient as you said.

I think that the discussion regarding to the ulPortTaskHasFPUContextConst can be closed.

Thank you very much.

(But if I misunderstand your reply, please let me know. The phrase “Am I right” is not easy for me. I am worried about my misunderstanding your reply.)

Best regards,
NoMaY

Hello,

The port layers and the RTOS Demo projects have been merged into master branch of repositories of FreeRTOS-Kernel and FreeRTOS as follows:

Best regards,
NoMaY