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
commit 86034ad049
5 changed files with 116 additions and 5 deletions

View file

@ -420,4 +420,12 @@ menu "FreeRTOS"
help
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

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
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:
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 */
ret
@ -176,6 +192,15 @@ _frxt_int_exit:
s32i a2, a3, 0 /* save nesting count */
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
addx4 a2, a4, a2
l32i a2, a2, 0 /* a2 = current TCB */
@ -642,7 +667,6 @@ _frxt_task_coproc_state:
addx4 a15, a3, a15
l32i a15, a15, 0 /* && pxCurrentTCB != 0) { */
beqz a15, 2f
l32i a15, a15, CP_TOPOFSTACK_OFFS
ret

View file

@ -955,7 +955,12 @@ _xt_coproc_exc:
/* Get co-processor state save area of new owner thread. */
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. */
movi a0, _xt_coproc_mask
@ -997,7 +1002,13 @@ locking.
rsync /* ensure wsr.CPENABLE is complete */
/* 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 */
#endif
/* If no old owner then nothing to save. */
beqz a2, .L_check_new
@ -1039,6 +1050,7 @@ locking.
.L_check_new:
/* 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. */
beqz a15, .L_xt_coproc_done
l16ui a3, a15, XT_CPSTORED /* a3 = new owner's CPSTORED */
movi a4, _xt_coproc_sa_offset
@ -1133,7 +1145,6 @@ _xt_lowint1:
#endif
#endif
/* Save rest of interrupt context and enter RTOS. */
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
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
first use of ``float``. Likewise due to Lazy Context Switching, interrupt service
routines must also not use ``float``.
first use of ``float``. Likewise due to Lazy Context Switching, only interrupt
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
arithmetic (``double``). Instead ``double`` is implemented via software hence the