11/*
2- * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
2+ * SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
33 *
44 * SPDX-License-Identifier: Apache-2.0
55 */
@@ -186,30 +186,47 @@ rtos_save_\name\()_coproc_norestore:
186186
187187 /**
188188 * @brief Restore the HWLP registers contained in the dedicated save area if the given task ever used it.
189- * This routine sets the HWLP context to clean in any case.
189+ * This routine sets the HWLP context to dirty if the task ever used it and any of the loop counter
190+ * is not zero. Else, it sets it to clean.
190191 *
191192 * @param a0 StaticTask address for the newly scheduled task
192193 */
193194hwlp_restore_if_used:
194195 addi sp , sp , -16
195196 sw ra, (sp )
196- /* Check if the HWLP was in use beforehand */
197+ /* Re-enable the HWLP coprocessor */
198+ csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
199+ /* Check if the HWLP was ever used by this task, if yes:
200+ * - Set HWLP state to DIRTY if any of the HWLP counter is != 0.
201+ * Please note that the `hwlp_restore_regs` macro will set the DIRTY bit!
202+ * - Keep the state as CLEAN if both counters are 0.
203+ */
197204 li a1 , 0
198205 li a2 , HWLP_COPROC_IDX
199206 call pxPortGetCoprocArea
200207 /* Get the enable flags from the coprocessor save area */
201- lw a1 , RV_COPROC_ENABLE(a0 )
202- /* To avoid having branches below, set the coprocessor enable flag now */
203- andi a2 , a1 , 1 << HWLP_COPROC_IDX
204- beqz a2 , _hwlp_restore_never_used
208+ lw a2 , RV_COPROC_ENABLE(a0 )
209+ andi a1 , a2 , 1 << HWLP_COPROC_IDX
210+ beqz a1 , _hwlp_restore_end
205211 /* Enable bit was set, restore the coprocessor context */
206- lw a0 , RV_COPROC_SA+HWLP_COPROC_IDX*4 (a0 ) /* a0 = RvCoprocSaveArea->sa_coprocs[HWLP_COPROC_IDX] */
207- hwlp_restore_regs a0
208- _hwlp_restore_never_used:
209- /* Clear the context */
212+ lw a3 , RV_COPROC_SA+HWLP_COPROC_IDX*4 (a0 ) /* a0 = RvCoprocSaveArea->sa_coprocs[HWLP_COPROC_IDX] */
213+ /* This will set the dirty flag for sure, a2 is preserved */
214+ hwlp_restore_regs a3
215+ #if SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1
216+ /* The hardware doesn't update the HWLP state properly after executing the last instruction,
217+ * as such, we must manually put the state of the HWLP to dirty now if any counter is not 0 */
218+ csrr a3 , CSR_LOOP0_COUNT
219+ bnez a3 , _hwlp_restore_end
220+ csrr a3 , CSR_LOOP1_COUNT
221+ bnez a3 , _hwlp_restore_end
222+ /* The counters are 0, mark the HWLP coprocessor as disabled in the enable flag and clean the state */
223+ xori a2 , a2 , 1 << HWLP_COPROC_IDX
224+ sw a2 , RV_COPROC_ENABLE(a0 )
225+ #endif /* SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1 */
210226 csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
211- lw ra, (sp )
212- addi sp , sp , 16
227+ _hwlp_restore_end:
228+ lw ra, (sp )
229+ addi sp , sp , 16
213230 ret
214231
215232#endif /* SOC_CPU_HAS_HWLOOP */
@@ -458,6 +475,11 @@ rtos_current_tcb:
458475 .global rtos_int_enter
459476 .type rtos_int_enter, @function
460477rtos_int_enter:
478+ #if SOC_CPU_COPROC_NUM > 0
479+ /* Use s2 to store the state of the coprocessors */
480+ li s2, 0
481+ #endif /* SOC_CPU_COPROC_NUM > 0 */
482+
461483#if ( configNUM_CORES > 1 )
462484 csrr s0, mhartid /* s0 = coreID */
463485 slli s0, s0, 2 /* s0 = coreID * 4 */
@@ -483,7 +505,6 @@ rtos_int_enter:
483505 li a0 , 0 /* return 0 in case we are going to branch */
484506 bnez a1 , rtos_int_enter_end /* if (port_uxInterruptNesting[coreID] > 0) jump to rtos_int_enter_end */
485507
486- li s2, 0
487508#if SOC_CPU_COPROC_NUM > 0
488509 /* Disable the coprocessors to forbid the ISR from using it */
489510#if SOC_CPU_HAS_PIE
@@ -525,6 +546,7 @@ rtos_int_enter:
525546 addi a1 , a1 , -HWLP_DIRTY_STATE
526547 bnez a1 , 1f
527548 /* State is dirty! The hardware loop feature was used, save the registers */
549+ ori s2, s2, 1 << HWLP_COPROC_IDX /* Mark the HWLP coprocessor as enabled (dirty) */
528550 li a1 , 1 /* Allocate the save area if not already allocated */
529551 li a2 , HWLP_COPROC_IDX
530552 mv s1, ra
@@ -537,8 +559,6 @@ rtos_int_enter:
537559 /* Get the area where we need to save the HWLP registers */
538560 lw a0 , RV_COPROC_SA+HWLP_COPROC_IDX*4 (a0 ) /* a0 = RvCoprocSaveArea->sa_coprocs[\coproc_idx] */
539561 hwlp_save_regs a0
540- /* Disable the HWLP feature so that ISR cannot use them */
541- csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
5425621 :
543563#endif
544564
@@ -559,9 +579,16 @@ rtos_int_enter:
559579 ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
560580#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
561581
582+ rtos_int_enter_end:
583+ /* Disable the HWLP coprocessor for ISRs */
584+ #if SOC_CPU_HAS_HWLOOP
585+ csrwi CSR_HWLP_STATE_REG, HWLP_OFF_STATE
586+ #endif
587+
588+ #if SOC_CPU_COPROC_NUM > 0
562589 /* Return the coprocessor context from s2 */
563590 mv a0 , s2
564- rtos_int_enter_end:
591+ # endif / * SOC_CPU_COPROC_NUM > 0 */
565592 ret
566593
567594/**
@@ -577,19 +604,19 @@ rtos_int_enter_end:
577604 .type rtos_int_exit, @function
578605rtos_int_exit:
579606 /* To speed up this routine and because this current routine is only meant to be called from the interrupt
580- * handler, let's use callee-saved registers instead of stack space. Registers `s5-s11` are not used by
581- * the caller */
607+ * handler, let's use callee-saved registers instead of stack space */
608+ mv s10, ra
582609 mv s11, a0
583610#if SOC_CPU_COPROC_NUM > 0
584611 /* Save a1 as it contains the bitmap with the enabled coprocessors */
585612 mv s8, a1
586613#endif
587614
588615#if ( configNUM_CORES > 1 )
589- csrr a1 , mhartid /* a1 = coreID */
590- slli a1 , a1 , 2 /* a1 = a1 * 4 */
616+ csrr s7 , mhartid /* s7 = coreID */
617+ slli s7, s7 , 2 /* s7 = s7 * 4 */
591618 la a0 , port_xSchedulerRunning /* a0 = &port_xSchedulerRunning */
592- add a0 , a0 , a1 /* a0 = &port_xSchedulerRunning[coreID] */
619+ add a0 , a0 , s7 /* a0 = &port_xSchedulerRunning[coreID] */
593620 lw a0 , (a0 ) /* a0 = port_xSchedulerRunning[coreID] */
594621#else
595622 lw a0 , port_xSchedulerRunning /* a0 = port_xSchedulerRunning */
@@ -599,7 +626,7 @@ rtos_int_exit:
599626 /* Update nesting interrupts counter */
600627 la a2 , port_uxInterruptNesting /* a2 = &port_uxInterruptNesting */
601628#if ( configNUM_CORES > 1 )
602- add a2 , a2 , a1 /* a2 = &port_uxInterruptNesting[coreID] // a1 already contains coreID * 4 */
629+ add a2 , a2 , s7 /* a2 = &port_uxInterruptNesting[coreID] // s7 already contains coreID * 4 */
603630#endif /* ( configNUM_CORES > 1 ) */
604631 lw a0 , 0 (a2 ) /* a0 = port_uxInterruptNesting[coreID] */
605632
@@ -611,84 +638,66 @@ rtos_int_exit:
611638 bnez a0 , rtos_int_exit_end
612639
613640isr_skip_decrement:
614- /* If the CPU reached this label, a2 (uxInterruptNesting) is 0 for sure */
641+
642+ #if ( SOC_CPU_COPROC_NUM > 0 )
643+ /* Keep the current TCB in a0 */
644+ call rtos_current_tcb
645+ #endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
615646
616647 /* Schedule the next task if a yield is pending */
617- la s7 , xPortSwitchFlag /* s7 = &xPortSwitchFlag */
648+ la s6 , xPortSwitchFlag /* s6 = &xPortSwitchFlag */
618649#if ( configNUM_CORES > 1 )
619- add s7, s7, a1 /* s7 = &xPortSwitchFlag[coreID] // a1 already contains coreID * 4 */
650+ add s6, s6, s7 /* s6 = &xPortSwitchFlag[coreID] // s7 already contains coreID * 4 */
620651#endif /* ( configNUM_CORES > 1 ) */
621- lw a0 , 0 (s7 ) /* a0 = xPortSwitchFlag[coreID] */
622- beqz a0 , no_switch_restore_coproc /* if (xPortSwitchFlag[coreID] == 0) jump to no_switch_restore_coproc */
652+ lw a1 , 0 (s6 ) /* a1 = xPortSwitchFlag[coreID] */
653+ bnez a1 , context_switch_requested /* if (xPortSwitchFlag[coreID] != 0) jump to context_switch_requested */
623654
624- /* Preserve return address and schedule next task. To speed up the process, and because this current routine
625- * is only meant to be called from the interrupt handle, let's save some speed and space by using callee-saved
626- * registers instead of stack space. Registers `s3-s11` are not used by the caller */
627- mv s10, ra
655+ no_context_switch:
656+ /* No need to do anything on the FPU side, its state is already saved in `s11` */
657+
658+ #if SOC_CPU_HAS_HWLOOP
659+ csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
660+ /* If the HWLP coprocessor has a hardware bug with its state, manually set the state to DIRTY
661+ * if it was already dirty before the interrupt, else, keep it to CLEAN */
662+ #if SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1
663+ andi a1 , s8, 1 << HWLP_COPROC_IDX
664+ beqz a1 , 1f
665+ /* To re-enable the HWLP coprocessor, set the status to DIRTY */
666+ csrwi CSR_HWLP_STATE_REG, HWLP_DIRTY_STATE
667+ 1 :
668+ #endif /* SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1 */
669+ #endif /* SOC_CPU_HAS_HWLOOP */
670+
671+ #if SOC_CPU_HAS_PIE
672+ /* Re-enable the PIE coprocessor if it was used */
673+ andi a1 , s8, 1 << PIE_COPROC_IDX
674+ beqz a1 , 1f
675+ pie_enable a1
676+ 1 :
677+ #endif /* SOC_CPU_HAS_PIE */
678+ j restore_stack_pointer
679+
680+ context_switch_requested:
628681#if ( SOC_CPU_COPROC_NUM > 0 )
629- /* In the cases where the newly scheduled task is different from the previously running one,
630- * we have to disable the coprocessors to let them trigger an exception on first use.
631- * Else, if the same task is scheduled, restore the former coprocessors state (before the interrupt) */
632- call rtos_current_tcb
633- /* Keep former TCB in s9 */
682+ /* Preserve former TCB in s9 */
634683 mv s9, a0
635- #endif
684+ #endif / * ( SOC_CPU_COPROC_NUM > 0 ) */
636685 call vTaskSwitchContext
637- #if ( SOC_CPU_COPROC_NUM == 0 )
638- mv ra, s10 /* Restore original return address */
639- #endif
640- /* Clears the switch pending flag (stored in s7) */
641- sw zero, 0 (s7) /* xPortSwitchFlag[coreID] = 0; */
686+ /* Clears the switch pending flag (stored in s6) */
687+ sw zero, 0 (s6) /* xPortSwitchFlag[coreID] = 0; */
642688
643689#if ( SOC_CPU_COPROC_NUM > 0 )
644- /* If the Task to schedule is NOT the same as the former one (s9), keep the coprocessors disabled */
690+ /* If the Task to schedule is NOT the same as the former one (s9), keep the coprocessors disabled. */
691+ /* Check if the new TCB is the same as the previous one */
645692 call rtos_current_tcb
646- mv ra, s10 /* Restore original return address */
647- beq a0 , s9, no_switch_restore_coproc
648-
649- #if SOC_CPU_HAS_HWLOOP
650- /* We have to restore the context of the HWLP if the newly scheduled task used it before. In all cases, this
651- * routine will also clean the state and set it to clean */
652- mv s7, ra
653- /* a0 contains the current TCB address */
654- call hwlp_restore_if_used
655- mv ra, s7
656- #endif /* SOC_CPU_HAS_HWLOOP */
657-
658- #if SOC_CPU_HAS_FPU
659- /* Disable the FPU in the `mstatus` value to return */
660- li a1 , ~CSR_MSTATUS_FPU_DISABLE
661- and s11, s11, a1
662- #endif /* SOC_CPU_HAS_FPU */
663- j no_switch_restored
664-
693+ beq a0 , s9, no_context_switch
665694#endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
666695
667- no_switch_restore_coproc:
668- /* We reach here either because there is no switch scheduled or because the TCB that is going to be scheduled
669- * is the same as the one that has been interrupted. In both cases, we need to restore the coprocessors status */
670696#if SOC_CPU_HAS_HWLOOP
671- /* Check if the ISR altered the state of the HWLP */
672- csrr a1 , CSR_HWLP_STATE_REG
673- addi a1 , a1 , -HWLP_DIRTY_STATE
674- bnez a1 , 1f
675- /* ISR used the HWLP, restore the HWLP context! */
676- mv s7, ra
677- /* a0 contains the current TCB address */
678697 call hwlp_restore_if_used
679- mv ra, s7
680- 1 :
681- /* Else, the ISR hasn't touched HWLP registers, we don't need to restore the HWLP registers */
682698#endif /* SOC_CPU_HAS_HWLOOP */
683699
684- #if SOC_CPU_HAS_PIE
685- andi a0 , s8, 1 << PIE_COPROC_IDX
686- beqz a0 , 2f
687- pie_enable a0
688- 2 :
689- #endif /* SOC_CPU_HAS_PIE */
690-
691- no_switch_restored:
700+ restore_stack_pointer:
692701
693702#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
694703 /* esp_hw_stack_guard_monitor_stop(); pass the scratch registers */
@@ -698,10 +707,8 @@ no_switch_restored:
698707
699708#if ( configNUM_CORES > 1 )
700709 /* Recover the stack of next task and prepare to exit */
701- csrr a1 , mhartid
702- slli a1 , a1 , 2
703710 la a0 , pxCurrentTCBs /* a0 = &pxCurrentTCBs */
704- add a0 , a0 , a1 /* a0 = &pxCurrentTCBs[coreID] */
711+ add a0 , a0 , s7 /* a0 = &pxCurrentTCBs[coreID] */
705712 lw a0 , 0 (a0 ) /* a0 = pxCurrentTCBs[coreID] */
706713 lw sp , 0 (a0 ) /* sp = previous sp */
707714#else
@@ -724,4 +731,5 @@ no_switch_restored:
724731
725732rtos_int_exit_end:
726733 mv a0 , s11 /* a0 = new mstatus */
734+ mv ra, s10
727735 ret
0 commit comments