;/*************************************************************************** ; * Copyright (c) 2024 Microsoft Corporation ; * ; * This program and the accompanying materials are made available under the ; * terms of the MIT License which is available at ; * https://opensource.org/licenses/MIT. ; * ; * SPDX-License-Identifier: MIT ; **************************************************************************/ ; ; ;/**************************************************************************/ ;/**************************************************************************/ ;/** */ ;/** ThreadX Component */ ;/** */ ;/** Timer */ ;/** */ ;/**************************************************************************/ ;/**************************************************************************/ ; ; EXTERN _tx_timer_time_slice EXTERN _tx_timer_system_clock EXTERN _tx_timer_current_ptr EXTERN _tx_timer_list_start EXTERN _tx_timer_list_end EXTERN _tx_timer_expired_time_slice EXTERN _tx_timer_expired EXTERN _tx_thread_time_slice EXTERN _tx_timer_expiration_process EXTERN _tx_thread_current_ptr EXTERN _tx_thread_execute_ptr EXTERN _tx_thread_preempt_disable ; ; SECTION `.text`:CODE:NOROOT(2) THUMB ;/**************************************************************************/ ;/* */ ;/* FUNCTION RELEASE */ ;/* */ ;/* _tx_timer_interrupt Cortex-M0/IAR */ ;/* 6.1 */ ;/* AUTHOR */ ;/* */ ;/* William E. Lamie, Microsoft Corporation */ ;/* */ ;/* DESCRIPTION */ ;/* */ ;/* This function processes the hardware timer interrupt. This */ ;/* processing includes incrementing the system clock and checking for */ ;/* time slice and/or timer expiration. If either is found, the */ ;/* interrupt context save/restore functions are called along with the */ ;/* expiration functions. */ ;/* */ ;/* INPUT */ ;/* */ ;/* None */ ;/* */ ;/* OUTPUT */ ;/* */ ;/* None */ ;/* */ ;/* CALLS */ ;/* */ ;/* _tx_timer_expiration_process Timer expiration processing */ ;/* _tx_thread_time_slice Time slice interrupted thread */ ;/* */ ;/* CALLED BY */ ;/* */ ;/* interrupt vector */ ;/* */ ;/**************************************************************************/ ;VOID _tx_timer_interrupt(VOID) ;{ PUBLIC _tx_timer_interrupt _tx_timer_interrupt: ; ; /* Upon entry to this routine, it is assumed that the compiler scratch registers are available ; for use. */ ; ; /* Increment the system clock. */ ; _tx_timer_system_clock++; ; LDR r1, =_tx_timer_system_clock ; Pickup address of system clock LDR r0, [r1, #0] ; Pickup system clock ADDS r0, r0, #1 ; Increment system clock STR r0, [r1, #0] ; Store new system clock ; ; /* Test for time-slice expiration. */ ; if (_tx_timer_time_slice) ; { ; LDR r3, =_tx_timer_time_slice ; Pickup address of time-slice LDR r2, [r3, #0] ; Pickup time-slice CMP r2, #0 ; Is it non-active? BEQ __tx_timer_no_time_slice ; Yes, skip time-slice processing ; ; /* Decrement the time_slice. */ ; _tx_timer_time_slice--; ; SUBS r2, r2, #1 ; Decrement the time-slice STR r2, [r3, #0] ; Store new time-slice value ; ; /* Check for expiration. */ ; if (__tx_timer_time_slice == 0) ; CMP r2, #0 ; Has it expired? BNE __tx_timer_no_time_slice ; No, skip expiration processing ; ; /* Set the time-slice expired flag. */ ; _tx_timer_expired_time_slice = TX_TRUE; ; LDR r3, =_tx_timer_expired_time_slice ; Pickup address of expired flag MOVS r0, #1 ; Build expired value STR r0, [r3, #0] ; Set time-slice expiration flag ; ; } ; __tx_timer_no_time_slice: ; ; /* Test for timer expiration. */ ; if (*_tx_timer_current_ptr) ; { ; LDR r1, =_tx_timer_current_ptr ; Pickup current timer pointer address LDR r0, [r1, #0] ; Pickup current timer LDR r2, [r0, #0] ; Pickup timer list entry CMP r2, #0 ; Is there anything in the list? BEQ __tx_timer_no_timer ; No, just increment the timer ; ; /* Set expiration flag. */ ; _tx_timer_expired = TX_TRUE; ; LDR r3, =_tx_timer_expired ; Pickup expiration flag address MOVS r2, #1 ; Build expired value STR r2, [r3, #0] ; Set expired flag B __tx_timer_done ; Finished timer processing ; ; } ; else ; { __tx_timer_no_timer: ; ; /* No timer expired, increment the timer pointer. */ ; _tx_timer_current_ptr++; ; ADDS r0, r0, #4 ; Move to next timer ; ; /* Check for wrap-around. */ ; if (_tx_timer_current_ptr == _tx_timer_list_end) ; LDR r3, =_tx_timer_list_end ; Pickup addr of timer list end LDR r2, [r3, #0] ; Pickup list end CMP r0, r2 ; Are we at list end? BNE __tx_timer_skip_wrap ; No, skip wrap-around logic ; ; /* Wrap to beginning of list. */ ; _tx_timer_current_ptr = _tx_timer_list_start; ; LDR r3, =_tx_timer_list_start ; Pickup addr of timer list start LDR r0, [r3, #0] ; Set current pointer to list start ; __tx_timer_skip_wrap: ; STR r0, [r1, #0] ; Store new current timer pointer ; } ; __tx_timer_done: ; ; ; /* See if anything has expired. */ ; if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) ; { ; LDR r3, =_tx_timer_expired_time_slice ; Pickup addr of expired flag LDR r2, [r3, #0] ; Pickup time-slice expired flag CMP r2, #0 ; Did a time-slice expire? BNE __tx_something_expired ; If non-zero, time-slice expired LDR r1, =_tx_timer_expired ; Pickup addr of other expired flag LDR r0, [r1, #0] ; Pickup timer expired flag CMP r0, #0 ; Did a timer expire? BEQ __tx_timer_nothing_expired ; No, nothing expired ; __tx_something_expired: ; ; PUSH {r0, lr} ; Save the lr register on the stack ; and save r0 just to keep 8-byte alignment ; ; /* Did a timer expire? */ ; if (_tx_timer_expired) ; { ; LDR r1, =_tx_timer_expired ; Pickup addr of expired flag LDR r0, [r1, #0] ; Pickup timer expired flag CMP r0, #0 ; Check for timer expiration BEQ __tx_timer_dont_activate ; If not set, skip timer activation ; ; /* Process timer expiration. */ ; _tx_timer_expiration_process(); ; BL _tx_timer_expiration_process ; Call the timer expiration handling routine ; ; } __tx_timer_dont_activate: ; ; /* Did time slice expire? */ ; if (_tx_timer_expired_time_slice) ; { ; LDR r3, =_tx_timer_expired_time_slice ; Pickup addr of time-slice expired LDR r2, [r3, #0] ; Pickup the actual flag CMP r2, #0 ; See if the flag is set BEQ __tx_timer_not_ts_expiration ; No, skip time-slice processing ; ; /* Time slice interrupted thread. */ ; _tx_thread_time_slice(); BL _tx_thread_time_slice ; Call time-slice processing LDR r0, =_tx_thread_preempt_disable ; Build address of preempt disable flag LDR r1, [r0] ; Is the preempt disable flag set? CMP r1, #0 ; BNE __tx_timer_skip_time_slice ; Yes, skip the PendSV logic LDR r0, =_tx_thread_current_ptr ; Build current thread pointer address LDR r1, [r0] ; Pickup the current thread pointer LDR r2, =_tx_thread_execute_ptr ; Build execute thread pointer address LDR r3, [r2] ; Pickup the execute thread pointer LDR r0, =0xE000ED04 ; Build address of control register LDR r2, =0x10000000 ; Build value for PendSV bit CMP r1, r3 ; Are they the same? BEQ __tx_timer_skip_time_slice ; If the same, there was no time-slice performed STR r2, [r0] ; Not the same, issue the PendSV for preemption __tx_timer_skip_time_slice: ; ; } ; __tx_timer_not_ts_expiration: ; POP {r0, r1} ; Recover lr register (r0 is just there for MOV lr, r1 ; the 8-byte stack alignment ; ; } ; __tx_timer_nothing_expired: DSB ; Complete all memory access BX lr ; Return to caller ; ;} END