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,28 +186,44 @@ 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 */
201208 lw a1 , RV_COPROC_ENABLE(a0 )
202209 /* To avoid having branches below, set the coprocessor enable flag now */
203210 andi a2 , a1 , 1 << HWLP_COPROC_IDX
204- beqz a2 , _hwlp_restore_never_used
211+ beqz a2 , _hwlp_restore_end
205212 /* Enable bit was set, restore the coprocessor context */
206213 lw a0 , RV_COPROC_SA+HWLP_COPROC_IDX*4 (a0 ) /* a0 = RvCoprocSaveArea->sa_coprocs[HWLP_COPROC_IDX] */
214+ /* This will set the dirty flag for sure */
207215 hwlp_restore_regs a0
208- _hwlp_restore_never_used:
209- /* Clear the context */
216+ #if SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1
217+ /* The hardware doesn't update the HWLP state properly after executing the last instruction,
218+ * as such, we must manually put the state of the HWLP to dirty now if any counter is not 0 */
219+ csrr a0 , CSR_LOOP0_COUNT
220+ bnez a0 , _hwlp_restore_end
221+ csrr a0 , CSR_LOOP1_COUNT
222+ bnez a0 , _hwlp_restore_end
223+ /* The counters are 0, cleaning the state */
224+ #endif /* SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1 */
210225 csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
226+ _hwlp_restore_end:
211227 lw ra, (sp )
212228 addi sp , sp , 16
213229 ret
@@ -458,6 +474,11 @@ rtos_current_tcb:
458474 .global rtos_int_enter
459475 .type rtos_int_enter, @function
460476rtos_int_enter:
477+ #if SOC_CPU_COPROC_NUM > 0
478+ /* Use s2 to store the state of the coprocessors */
479+ li s2, 0
480+ #endif /* SOC_CPU_COPROC_NUM > 0 */
481+
461482#if ( configNUM_CORES > 1 )
462483 csrr s0, mhartid /* s0 = coreID */
463484 slli s0, s0, 2 /* s0 = coreID * 4 */
@@ -483,7 +504,6 @@ rtos_int_enter:
483504 li a0 , 0 /* return 0 in case we are going to branch */
484505 bnez a1 , rtos_int_enter_end /* if (port_uxInterruptNesting[coreID] > 0) jump to rtos_int_enter_end */
485506
486- li s2, 0
487507#if SOC_CPU_COPROC_NUM > 0
488508 /* Disable the coprocessors to forbid the ISR from using it */
489509#if SOC_CPU_HAS_PIE
@@ -525,6 +545,7 @@ rtos_int_enter:
525545 addi a1 , a1 , -HWLP_DIRTY_STATE
526546 bnez a1 , 1f
527547 /* State is dirty! The hardware loop feature was used, save the registers */
548+ ori s2, s2, 1 << HWLP_COPROC_IDX /* Mark the HWLP coprocessor as enabled (dirty) */
528549 li a1 , 1 /* Allocate the save area if not already allocated */
529550 li a2 , HWLP_COPROC_IDX
530551 mv s1, ra
@@ -537,8 +558,6 @@ rtos_int_enter:
537558 /* Get the area where we need to save the HWLP registers */
538559 lw a0 , RV_COPROC_SA+HWLP_COPROC_IDX*4 (a0 ) /* a0 = RvCoprocSaveArea->sa_coprocs[\coproc_idx] */
539560 hwlp_save_regs a0
540- /* Disable the HWLP feature so that ISR cannot use them */
541- csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
5425611 :
543562#endif
544563
@@ -559,9 +578,16 @@ rtos_int_enter:
559578 ESP_HW_STACK_GUARD_MONITOR_START_CUR_CORE a0 a1
560579#endif /* CONFIG_ESP_SYSTEM_HW_STACK_GUARD */
561580
581+ rtos_int_enter_end:
582+ /* Disable the HWLP coprocessor for ISRs */
583+ #if SOC_CPU_HAS_HWLOOP
584+ csrwi CSR_HWLP_STATE_REG, HWLP_OFF_STATE
585+ #endif
586+
587+ #if SOC_CPU_COPROC_NUM > 0
562588 /* Return the coprocessor context from s2 */
563589 mv a0 , s2
564- rtos_int_enter_end:
590+ # endif / * SOC_CPU_COPROC_NUM > 0 */
565591 ret
566592
567593/**
@@ -577,19 +603,19 @@ rtos_int_enter_end:
577603 .type rtos_int_exit, @function
578604rtos_int_exit:
579605 /* 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 */
606+ * handler, let's use callee-saved registers instead of stack space */
607+ mv s10, ra
582608 mv s11, a0
583609#if SOC_CPU_COPROC_NUM > 0
584610 /* Save a1 as it contains the bitmap with the enabled coprocessors */
585611 mv s8, a1
586612#endif
587613
588614#if ( configNUM_CORES > 1 )
589- csrr a1 , mhartid /* a1 = coreID */
590- slli a1 , a1 , 2 /* a1 = a1 * 4 */
615+ csrr s7 , mhartid /* s7 = coreID */
616+ slli s7, s7 , 2 /* s7 = s7 * 4 */
591617 la a0 , port_xSchedulerRunning /* a0 = &port_xSchedulerRunning */
592- add a0 , a0 , a1 /* a0 = &port_xSchedulerRunning[coreID] */
618+ add a0 , a0 , s7 /* a0 = &port_xSchedulerRunning[coreID] */
593619 lw a0 , (a0 ) /* a0 = port_xSchedulerRunning[coreID] */
594620#else
595621 lw a0 , port_xSchedulerRunning /* a0 = port_xSchedulerRunning */
@@ -599,7 +625,7 @@ rtos_int_exit:
599625 /* Update nesting interrupts counter */
600626 la a2 , port_uxInterruptNesting /* a2 = &port_uxInterruptNesting */
601627#if ( configNUM_CORES > 1 )
602- add a2 , a2 , a1 /* a2 = &port_uxInterruptNesting[coreID] // a1 already contains coreID * 4 */
628+ add a2 , a2 , s7 /* a2 = &port_uxInterruptNesting[coreID] // s7 already contains coreID * 4 */
603629#endif /* ( configNUM_CORES > 1 ) */
604630 lw a0 , 0 (a2 ) /* a0 = port_uxInterruptNesting[coreID] */
605631
@@ -611,84 +637,66 @@ rtos_int_exit:
611637 bnez a0 , rtos_int_exit_end
612638
613639isr_skip_decrement:
614- /* If the CPU reached this label, a2 (uxInterruptNesting) is 0 for sure */
640+
641+ #if ( SOC_CPU_COPROC_NUM > 0 )
642+ /* Keep the current TCB in a0 */
643+ call rtos_current_tcb
644+ #endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
615645
616646 /* Schedule the next task if a yield is pending */
617- la s7 , xPortSwitchFlag /* s7 = &xPortSwitchFlag */
647+ la s6 , xPortSwitchFlag /* s6 = &xPortSwitchFlag */
618648#if ( configNUM_CORES > 1 )
619- add s7, s7, a1 /* s7 = &xPortSwitchFlag[coreID] // a1 already contains coreID * 4 */
649+ add s6, s6, s7 /* s6 = &xPortSwitchFlag[coreID] // s7 already contains coreID * 4 */
620650#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 */
651+ lw a1 , 0 (s6 ) /* a1 = xPortSwitchFlag[coreID] */
652+ bnez a1 , context_switch_requested /* if (xPortSwitchFlag[coreID] != 0) jump to context_switch_requested */
623653
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
654+ no_context_switch:
655+ /* No need to do anything on the FPU side, its state is already saved in `s11` */
656+
657+ #if SOC_CPU_HAS_HWLOOP
658+ csrwi CSR_HWLP_STATE_REG, HWLP_CLEAN_STATE
659+ /* If the HWLP coprocessor has a hardware bug with its state, manually set the state to DIRTY
660+ * if it was already dirty before the interrupt, else, keep it to CLEAN */
661+ #if SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1
662+ andi a1 , s8, 1 << HWLP_COPROC_IDX
663+ beqz a1 , 1f
664+ /* To re-enable the HWLP coprocessor, set the status to DIRTY */
665+ csrwi CSR_HWLP_STATE_REG, HWLP_DIRTY_STATE
666+ 1 :
667+ #endif /* SOC_CPU_HAS_HWLOOP_STATE_BUG && ESP32P4_REV_MIN_FULL <= 1 */
668+ #endif /* SOC_CPU_HAS_HWLOOP */
669+
670+ #if SOC_CPU_HAS_PIE
671+ /* Re-enable the PIE coprocessor if it was used */
672+ andi a1 , s8, 1 << PIE_COPROC_IDX
673+ beqz a1 , 1f
674+ pie_enable a1
675+ 1 :
676+ #endif /* SOC_CPU_HAS_PIE */
677+ j restore_stack_pointer
678+
679+ context_switch_requested:
628680#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 */
681+ /* Preserve former TCB in s9 */
634682 mv s9, a0
635- #endif
683+ #endif / * ( SOC_CPU_COPROC_NUM > 0 ) */
636684 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; */
685+ /* Clears the switch pending flag (stored in s6) */
686+ sw zero, 0 (s6) /* xPortSwitchFlag[coreID] = 0; */
642687
643688#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 */
689+ /* If the Task to schedule is NOT the same as the former one (s9), keep the coprocessors disabled. */
690+ /* Check if the new TCB is the same as the previous one */
645691 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-
692+ beq a0 , s9, no_context_switch
665693#endif /* ( SOC_CPU_COPROC_NUM > 0 ) */
666694
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 */
670695#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 */
678696 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 */
682697#endif /* SOC_CPU_HAS_HWLOOP */
683698
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:
699+ restore_stack_pointer:
692700
693701#if CONFIG_ESP_SYSTEM_HW_STACK_GUARD
694702 /* esp_hw_stack_guard_monitor_stop(); pass the scratch registers */
@@ -698,10 +706,8 @@ no_switch_restored:
698706
699707#if ( configNUM_CORES > 1 )
700708 /* Recover the stack of next task and prepare to exit */
701- csrr a1 , mhartid
702- slli a1 , a1 , 2
703709 la a0 , pxCurrentTCBs /* a0 = &pxCurrentTCBs */
704- add a0 , a0 , a1 /* a0 = &pxCurrentTCBs[coreID] */
710+ add a0 , a0 , s7 /* a0 = &pxCurrentTCBs[coreID] */
705711 lw a0 , 0 (a0 ) /* a0 = pxCurrentTCBs[coreID] */
706712 lw sp , 0 (a0 ) /* sp = previous sp */
707713#else
@@ -724,4 +730,5 @@ no_switch_restored:
724730
725731rtos_int_exit_end:
726732 mv a0 , s11 /* a0 = new mstatus */
733+ mv ra, s10
727734 ret
0 commit comments