Skip to content

Add support for the configUSE_TASK_FPU_SUPPORT constant in the GCC/ARM_CR5 port #584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 14, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 79 additions & 14 deletions portable/GCC/ARM_CR5/port.c
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@

/* Standard includes. */
#include <stdlib.h>
#include <string.h>

/* Scheduler includes. */
#include "FreeRTOS.h"
Expand Down Expand Up @@ -148,6 +149,19 @@
#define portTASK_RETURN_ADDRESS prvTaskExitError
#endif

/*
* The space on the stack required to hold the FPU registers.
*
* The ARM Cortex R5 processor implements the VFPv3-D16 FPU
* architecture. This includes only 16 double-precision registers,
* instead of 32 as is in VFPv3. The register bank can be viewed
* either as sixteen 64-bit double-word registers (D0-D15) or
* thirty-two 32-bit single-word registers (S0-S31), in both cases
* the size of the bank remains the same. The FPU has also a 32-bit
* status register.
*/
#define portFPU_REGISTER_WORDS ( ( 16 * 2 ) + 1 )

/*-----------------------------------------------------------*/

/*
Expand All @@ -161,6 +175,27 @@ extern void vPortRestoreTaskContext( void );
*/
static void prvTaskExitError( void );

/*
* If the application provides an implementation of vApplicationIRQHandler(),
* then it will get called directly without saving the FPU registers on
* interrupt entry, and this weak implementation of
* vApplicationFPUSafeIRQHandler() is just provided to remove linkage errors -
* it should never actually get called so its implementation contains a
* call to configASSERT() that will always fail.
*
* If the application provides its own implementation of
* vApplicationFPUSafeIRQHandler() then the implementation of
* vApplicationIRQHandler() provided in portASM.S will save the FPU registers
* before calling it.
*
* Therefore, if the application writer wants FPU registers to be saved on
* interrupt entry their IRQ handler must be called
* vApplicationFPUSafeIRQHandler(), and if the application writer does not want
* FPU registers to be saved on interrupt entry their IRQ handler must be
* called vApplicationIRQHandler().
*/
void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR ) __attribute__((weak) );

/*-----------------------------------------------------------*/

/* A variable is used to keep track of the critical section nesting. This
Expand Down Expand Up @@ -255,12 +290,31 @@ StackType_t * pxPortInitialiseStack( StackType_t * pxTopOfStack,
/* The task will start with a critical nesting count of 0 as interrupts are
* enabled. */
*pxTopOfStack = portNO_CRITICAL_NESTING;
pxTopOfStack--;

/* 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 = portNO_FLOATING_POINT_CONTEXT;
#if( configUSE_TASK_FPU_SUPPORT == 1 )
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is possible that this port is being compiled for a target without an FPU, in which case configUSE_TASK_FPU_SUPPORT may not be defined. This is probably an issue in the original Cortex-A port as well. This code as-is does not compile when configUSE_TASK_FPU_SUPPORT is left undefined or set to 0.

It might be preferable to use the configENABLE_FPU option from the ARMv8M ports and make FPU management on by default for each task rather than opt-in.

@RichardBarry and @aggarg Do you have a preference here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I see what you, @paulbartell , mean. I did take a look on the configENABLE_FPU option, and I strongly agree with you. Let me know, if you want the configENABLE_FPU-option implemented on both ports (Cortex-R5 and A9), I would gladly do it.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should merge this PR as is and then create a separate PR which should allow setting configUSE_TASK_FPU_SUPPORT to 0. The configUSE_TASK_FPU_SUPPORT would then have the following meaning -

  • 0 - No FPU support.
  • 1 - Tasks individually register their intent to have a FPU context.
  • 2 - All tasks have FPU context.

{
/* 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 initialized to 0. */
pxTopOfStack -= portFPU_REGISTER_WORDS;
memset( pxTopOfStack, 0x00, portFPU_REGISTER_WORDS * sizeof( StackType_t ) );

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

return pxTopOfStack;
}
Expand All @@ -283,6 +337,13 @@ static void prvTaskExitError( void )
}
/*-----------------------------------------------------------*/

void vApplicationFPUSafeIRQHandler( uint32_t ulICCIAR )
{
( void ) ulICCIAR;
configASSERT( ( volatile void * ) NULL );
}
/*-----------------------------------------------------------*/

BaseType_t xPortStartScheduler( void )
{
uint32_t ulAPSR, ulCycles = 8; /* 8 bits per byte. */
Expand Down Expand Up @@ -444,17 +505,21 @@ void FreeRTOS_Tick_Handler( void )
}
/*-----------------------------------------------------------*/

void vPortTaskUsesFPU( void )
{
uint32_t ulInitialFPSCR = 0;
#if( configUSE_TASK_FPU_SUPPORT != 2 )

/* A task is registering the fact that it needs an FPU context. Set the
* FPU flag (which is saved as part of the task context). */
ulPortTaskHasFPUContext = pdTRUE;
void vPortTaskUsesFPU( void )
{
uint32_t ulInitialFPSCR = 0;

/* Initialise the floating point status register. */
__asm volatile ( "FMXR FPSCR, %0" ::"r" ( ulInitialFPSCR ) : "memory" );
}
/* A task is registering the fact that it needs an FPU context. Set the
* FPU flag (which is saved as part of the task context). */
ulPortTaskHasFPUContext = pdTRUE;

/* Initialise the floating point status register. */
__asm volatile ( "FMXR FPSCR, %0" ::"r" ( ulInitialFPSCR ) : "memory" );
}

#endif /* configUSE_TASK_FPU_SUPPORT */
/*-----------------------------------------------------------*/

void vPortClearInterruptMask( uint32_t ulNewMaskValue )
Expand Down
37 changes: 37 additions & 0 deletions portable/GCC/ARM_CR5/portASM.S
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,42 @@ switch_before_exit:
next. */
portRESTORE_CONTEXT

/******************************************************************************
* If the application provides an implementation of vApplicationIRQHandler(),
* then it will get called directly without saving the FPU registers on
* interrupt entry, and this weak implementation of
* vApplicationIRQHandler() will not get called.
*
* If the application provides its own implementation of
* vApplicationFPUSafeIRQHandler() then this implementation of
* vApplicationIRQHandler() will be called, save the FPU registers, and then
* call vApplicationFPUSafeIRQHandler().
*
* Therefore, if the application writer wants FPU registers to be saved on
* interrupt entry their IRQ handler must be called
* vApplicationFPUSafeIRQHandler(), and if the application writer does not want
* FPU registers to be saved on interrupt entry their IRQ handler must be
* called vApplicationIRQHandler().
*****************************************************************************/

.align 4
.weak vApplicationIRQHandler
.type vApplicationIRQHandler, %function
vApplicationIRQHandler:
PUSH {LR}
FMRX R1, FPSCR
VPUSH {D0-D15}
PUSH {R1}

LDR r1, vApplicationFPUSafeIRQHandlerConst
BLX r1

POP {R0}
VPOP {D0-D15}
VMSR FPSCR, R0

POP {PC}

ulICCIARConst: .word ulICCIAR
ulICCEOIRConst: .word ulICCEOIR
ulICCPMRConst: .word ulICCPMR
Expand All @@ -272,6 +308,7 @@ ulMaxAPIPriorityMaskConst: .word ulMaxAPIPriorityMask
vTaskSwitchContextConst: .word vTaskSwitchContext
vApplicationIRQHandlerConst: .word vApplicationIRQHandler
ulPortInterruptNestingConst: .word ulPortInterruptNesting
vApplicationFPUSafeIRQHandlerConst: .word vApplicationFPUSafeIRQHandler

.end

Expand Down
13 changes: 11 additions & 2 deletions portable/GCC/ARM_CR5/portmacro.h
Original file line number Diff line number Diff line change
Expand Up @@ -116,9 +116,18 @@
* handler for whichever peripheral is used to generate the RTOS tick. */
void FreeRTOS_Tick_Handler( void );

/* Any task that uses the floating point unit MUST call vPortTaskUsesFPU()
* before any floating point instructions are executed. */
/* If configUSE_TASK_FPU_SUPPORT is set to 1 (or left undefined) then tasks are
created without an FPU context and must call vPortTaskUsesFPU() to give
themselves an FPU context before using any FPU instructions. If
configUSE_TASK_FPU_SUPPORT is set to 2 then all tasks will have an FPU context
by default. */
#if( configUSE_TASK_FPU_SUPPORT != 2 )
void vPortTaskUsesFPU( void );
#else
/* Each task has an FPU context already, so define this function away to
nothing to prevent it being called accidentally. */
#define vPortTaskUsesFPU()
#endif /* configUSE_TASK_FPU_SUPPORT */
#define portTASK_USES_FLOATING_POINT() vPortTaskUsesFPU()

#define portLOWEST_INTERRUPT_PRIORITY ( ( ( uint32_t ) configUNIQUE_INTERRUPT_PRIORITIES ) - 1UL )
Expand Down