Merge branch 'feature/freertos_fpu_isr' into 'master'

feature/fpu: Enable usage of FPU inside of a ISR

Closes IDF-100

See merge request espressif/esp-idf!7348
This commit is contained in:
Angus Gratton
2020-01-30 13:38:37 +08:00
5 changed files with 116 additions and 5 deletions

View File

@@ -420,4 +420,12 @@ menu "FreeRTOS"
help help
Hidden option, gets selected by CONFIG_ESPxx_DEBUG_OCDAWARE Hidden option, gets selected by CONFIG_ESPxx_DEBUG_OCDAWARE
config FREERTOS_FPU_IN_ISR
bool "Allow use of float inside Level 1 ISR (EXPERIMENTAL)"
depends on IDF_TARGET_ESP32
default n
help
When enabled, the usage of float type is allowed inside Level 1
ISRs.
endmenu endmenu

View File

@@ -0,0 +1,67 @@
#include <esp_types.h>
#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "esp_intr_alloc.h"
#include "xtensa/hal.h"
#include "unity.h"
#include "soc/cpu.h"
#include "test_utils.h"
#include "math.h"
#define SW_ISR_LEVEL_1 7
#ifdef CONFIG_FREERTOS_FPU_IN_ISR
struct fp_test_context {
SemaphoreHandle_t sync;
float expected;
};
static void software_isr(void *arg) {
(void)arg;
BaseType_t yield;
xt_set_intclear(1 << SW_ISR_LEVEL_1);
struct fp_test_context *ctx = (struct fp_test_context *)arg;
for(int i = 0; i < 16; i++) {
ctx->expected = ctx->expected * 2.0f * cosf(0.0f);
}
xSemaphoreGiveFromISR(ctx->sync, &yield);
if(yield) {
portYIELD_FROM_ISR();
}
}
TEST_CASE("Floating point usage in ISR test", "[freertos]" "[fp]")
{
struct fp_test_context ctx;
float fp_math_operation_result = 0.0f;
intr_handle_t handle;
esp_err_t err = esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, &software_isr, &ctx, &handle);
TEST_ASSERT_EQUAL_HEX32(ESP_OK, err);
ctx.sync = xSemaphoreCreateBinary();
TEST_ASSERT(ctx.sync != NULL);
ctx.expected = 1.0f;
fp_math_operation_result = cosf(0.0f);
xt_set_intset(1 << SW_ISR_LEVEL_1);
xSemaphoreTake(ctx.sync, portMAX_DELAY);
esp_intr_free(handle);
vSemaphoreDelete(ctx.sync);
printf("FP math isr result: %f \n", ctx.expected);
TEST_ASSERT_FLOAT_WITHIN(0.1f, ctx.expected, fp_math_operation_result * 65536.0f);
}
#endif

View File

@@ -138,8 +138,24 @@ _frxt_int_enter:
mull a2, a4, a2 mull a2, a4, a2
add a1, a1, a2 /* for current proc */ add a1, a1, a2 /* for current proc */
#ifdef CONFIG_FREERTOS_FPU_IN_ISR
#if XCHAL_CP_NUM > 0
rsr a3, CPENABLE /* Restore thread scope CPENABLE */
addi sp, sp,-4 /* ISR will manage FPU coprocessor by forcing */
s32i a3, a1, 0 /* its trigger */
#endif
#endif
.Lnested: .Lnested:
1: 1:
#ifdef CONFIG_FREERTOS_FPU_IN_ISR
#if XCHAL_CP_NUM > 0
movi a3, 0 /* whilst ISRs pending keep CPENABLE exception active */
wsr a3, CPENABLE
rsync
#endif
#endif
mov a0, a12 /* restore return addr and return */ mov a0, a12 /* restore return addr and return */
ret ret
@@ -176,6 +192,15 @@ _frxt_int_exit:
s32i a2, a3, 0 /* save nesting count */ s32i a2, a3, 0 /* save nesting count */
bnez a2, .Lnesting /* !=0 after decr so still nested */ bnez a2, .Lnesting /* !=0 after decr so still nested */
#ifdef CONFIG_FREERTOS_FPU_IN_ISR
#if XCHAL_CP_NUM > 0
l32i a3, sp, 0 /* Grab last CPENABLE before leave ISR */
addi sp, sp, 4
wsr a3, CPENABLE
rsync /* ensure CPENABLE was modified */
#endif
#endif
movi a2, pxCurrentTCB movi a2, pxCurrentTCB
addx4 a2, a4, a2 addx4 a2, a4, a2
l32i a2, a2, 0 /* a2 = current TCB */ l32i a2, a2, 0 /* a2 = current TCB */
@@ -642,7 +667,6 @@ _frxt_task_coproc_state:
addx4 a15, a3, a15 addx4 a15, a3, a15
l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */ l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
beqz a15, 2f beqz a15, 2f
l32i a15, a15, CP_TOPOFSTACK_OFFS l32i a15, a15, CP_TOPOFSTACK_OFFS
ret ret

View File

@@ -955,7 +955,12 @@ _xt_coproc_exc:
/* Get co-processor state save area of new owner thread. */ /* Get co-processor state save area of new owner thread. */
call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */ call0 XT_RTOS_CP_STATE /* a15 = new owner's save area */
beqz a15, .L_goto_invalid /* not in a thread (invalid) */
#ifndef CONFIG_FREERTOS_FPU_IN_ISR
beqz a15, .L_goto_invalid
#endif
/*When FPU in ISR is enabled we could deal with zeroed a15 */
/* Enable the co-processor's bit in CPENABLE. */ /* Enable the co-processor's bit in CPENABLE. */
movi a0, _xt_coproc_mask movi a0, _xt_coproc_mask
@@ -997,7 +1002,13 @@ locking.
rsync /* ensure wsr.CPENABLE is complete */ rsync /* ensure wsr.CPENABLE is complete */
/* Only need to context switch if new owner != old owner. */ /* Only need to context switch if new owner != old owner. */
/* If float is necessary on ISR, we need to remove this check */
/* below, because on restoring from ISR we may have new == old condition used
* to force cp restore to next thread
*/
#ifndef CONFIG_FREERTOS_FPU_IN_ISR
beq a15, a2, .L_goto_done /* new owner == old, we're done */ beq a15, a2, .L_goto_done /* new owner == old, we're done */
#endif
/* If no old owner then nothing to save. */ /* If no old owner then nothing to save. */
beqz a2, .L_check_new beqz a2, .L_check_new
@@ -1039,6 +1050,7 @@ locking.
.L_check_new: .L_check_new:
/* Check if any state has to be restored for new owner. */ /* Check if any state has to be restored for new owner. */
/* NOTE: a15 = new owner's save area, cannot be zero when we get here. */ /* NOTE: a15 = new owner's save area, cannot be zero when we get here. */
beqz a15, .L_xt_coproc_done
l16ui a3, a15, XT_CPSTORED /* a3 = new owner's CPSTORED */ l16ui a3, a15, XT_CPSTORED /* a3 = new owner's CPSTORED */
movi a4, _xt_coproc_sa_offset movi a4, _xt_coproc_sa_offset
@@ -1133,7 +1145,6 @@ _xt_lowint1:
#endif #endif
#endif #endif
/* Save rest of interrupt context and enter RTOS. */ /* Save rest of interrupt context and enter RTOS. */
call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */ call0 XT_RTOS_INT_ENTER /* common RTOS interrupt entry */

View File

@@ -409,8 +409,9 @@ the state of a core's FPU registers are not immediately saved when a context
switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a switch occurs. Therefore, tasks that utilize ``float`` must be pinned to a
particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin particular core upon creation. If not, ESP-IDF FreeRTOS will automatically pin
the task in question to whichever core the task was running on upon the task's the task in question to whichever core the task was running on upon the task's
first use of ``float``. Likewise due to Lazy Context Switching, interrupt service first use of ``float``. Likewise due to Lazy Context Switching, only interrupt
routines must also not use ``float``. service routines of lowest priority (that is it the Level 1) can use ``float``,
higher priority interrupts do not support FPU usage.
ESP32 does not support hardware acceleration for double precision floating point ESP32 does not support hardware acceleration for double precision floating point
arithmetic (``double``). Instead ``double`` is implemented via software hence the arithmetic (``double``). Instead ``double`` is implemented via software hence the