/***************************************************************************
 * 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                                                     */ 
/**                                                                       */
/**   Thread                                                              */
/**                                                                       */
/**************************************************************************/
/**************************************************************************/


/* #define TX_SOURCE_CODE  */


/* Include necessary system files.  */

/*  #include "tx_api.h"
    #include "tx_thread.h"
    #include "tx_timer.h"  */


    EXTERN      _tx_thread_execute_ptr
    EXTERN      _tx_thread_current_ptr
    EXTERN      _tx_timer_time_slice
#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
    EXTERN      _tx_execution_thread_enter
#endif


    SECTION `.text`:CODE:REORDER:NOROOT(2)
    CODE
/**************************************************************************/ 
/*                                                                        */ 
/*  FUNCTION                                               RELEASE        */ 
/*                                                                        */ 
/*    _tx_thread_schedule                                RISC-V32/IAR     */
/*                                                           6.1          */
/*  AUTHOR                                                                */ 
/*                                                                        */ 
/*    William E. Lamie, Microsoft Corporation                             */ 
/*    Tom van Leeuwen, Technolution B.V.                                  */
/*                                                                        */ 
/*  DESCRIPTION                                                           */ 
/*                                                                        */ 
/*    This function waits for a thread control block pointer to appear in */ 
/*    the _tx_thread_execute_ptr variable.  Once a thread pointer appears */ 
/*    in the variable, the corresponding thread is resumed.               */ 
/*                                                                        */ 
/*  INPUT                                                                 */ 
/*                                                                        */ 
/*    None                                                                */ 
/*                                                                        */ 
/*  OUTPUT                                                                */ 
/*                                                                        */ 
/*    None                                                                */
/*                                                                        */ 
/*  CALLS                                                                 */ 
/*                                                                        */ 
/*    None                                                                */
/*                                                                        */ 
/*  CALLED BY                                                             */ 
/*                                                                        */ 
/*    _tx_initialize_kernel_enter          ThreadX entry function         */ 
/*    _tx_thread_system_return             Return to system from thread   */ 
/*    _tx_thread_context_restore           Restore thread's context       */ 
/*                                                                        */ 
/*  RELEASE HISTORY                                                       */ 
/*                                                                        */ 
/*    DATE              NAME                      DESCRIPTION             */ 
/*                                                                        */ 
/*  09-30-2020     William E. Lamie         Initial Version 6.1           */ 
/*                                                                        */ 
/**************************************************************************/ 
/* VOID   _tx_thread_schedule(VOID)
{  */
    PUBLIC  _tx_thread_schedule
_tx_thread_schedule:

    /* Enable interrupts.  */
    csrsi   mstatus, 0x08                               ; Enable interrupts

    /* Wait for a thread to execute.  */
    /* do
    {  */

    la      t0, _tx_thread_execute_ptr                  ; Pickup address of execute ptr
_tx_thread_schedule_loop:
    lw      t1, 0(t0)                                   ; Pickup next thread to execute
    beqz    t1, _tx_thread_schedule_loop                ; If NULL, wait for thread to execute

    /* }
    while(_tx_thread_execute_ptr == TX_NULL);  */
    
    /* Yes! We have a thread to execute.  Lockout interrupts and
       transfer control to it.  */
    csrci   mstatus, 0x08                               ; Lockout interrupts

    /* Setup the current thread pointer.  */
    /* _tx_thread_current_ptr =  _tx_thread_execute_ptr;  */

    la      t0, _tx_thread_current_ptr                  ; Pickup current thread pointer address
    sw      t1, 0(t0)                                   ; Set current thread pointer

    /* Increment the run count for this thread.  */
    /* _tx_thread_current_ptr -> tx_thread_run_count++;  */

    lw      t2, 4(t1)                                   ; Pickup run count
    lw      t3, 24(t1)                                  ; Pickup time slice value
    addi    t2, t2, 1                                   ; Increment run count
    sw      t2, 4(t1)                                   ; Store new run count

    /* Setup time-slice, if present.  */
    /* _tx_timer_time_slice =  _tx_thread_current_ptr -> tx_thread_time_slice;  */

    la      t2, _tx_timer_time_slice                    ; Pickup time-slice variable address

    /* Switch to the thread's stack.  */
    /* SP =  _tx_thread_execute_ptr -> tx_thread_stack_ptr;  */

    lw      sp, 8(t1)                                   ; Switch to thread's stack
    sw      t3, 0(t2)                                   ; Store new time-slice*/

#ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY

    call    _tx_execution_thread_enter                  ; Call the thread execution enter function
#endif

    /* Determine if an interrupt frame or a synchronous task suspension frame
       is present.  */

    lw      t2, 0(sp)                                   ; Pickup stack type
    beqz    t2, _tx_thread_synch_return                 ; If 0, solicited thread return

    /* Determine if floating point registers need to be recovered.  */

#if __iar_riscv_base_isa == rv32e 
    flw     f0, 0x7C(sp)                                ; Recover ft0
    flw     f1, 0x80(sp)                                ; Recover ft1
    flw     f2, 0x84(sp)                                ; Recover ft2
    flw     f3, 0x88(sp)                                ; Recover ft3
    flw     f4, 0x8C(sp)                                ; Recover ft4
    flw     f5, 0x90(sp)                                ; Recover ft5
    flw     f6, 0x94(sp)                                ; Recover ft6
    flw     f7, 0x98(sp)                                ; Recover ft7
    flw     f8, 0x9C(sp)                                ; Recover fs0
    flw     f9, 0xA0(sp)                                ; Recover fs1
    flw     f10,0xA4(sp)                                ; Recover fa0
    flw     f11,0xA8(sp)                                ; Recover fa1
    flw     f12,0xAC(sp)                                ; Recover fa2
    flw     f13,0xB0(sp)                                ; Recover fa3
    flw     f14,0xB4(sp)                                ; Recover fa4
    flw     f15,0xB8(sp)                                ; Recover fa5
    flw     f16,0xBC(sp)                                ; Recover fa6
    flw     f17,0xC0(sp)                                ; Recover fa7
    flw     f18,0xC4(sp)                                ; Recover fs2
    flw     f19,0xC8(sp)                                ; Recover fs3
    flw     f20,0xCC(sp)                                ; Recover fs4
    flw     f21,0xD0(sp)                                ; Recover fs5
    flw     f22,0xD4(sp)                                ; Recover fs6
    flw     f23,0xD8(sp)                                ; Recover fs7
    flw     f24,0xDC(sp)                                ; Recover fs8
    flw     f25,0xE0(sp)                                ; Recover fs9
    flw     f26,0xE4(sp)                                ; Recover fs10
    flw     f27,0xE8(sp)                                ; Recover fs11
    flw     f28,0xEC(sp)                                ; Recover ft8
    flw     f29,0xF0(sp)                                ; Recover ft9
    flw     f30,0xF4(sp)                                ; Recover ft10
    flw     f31,0xF8(sp)                                ; Recover ft11
    lw      t0, 0xFC(sp)                                ; Recover fcsr
    csrw    fcsr, t0                                    ; 
#endif

    /* Recover standard registers.  */

    lw      t0, 0x78(sp)                                ; Recover mepc
    csrw    mepc, t0                                    ; Store mepc
    li      t0, 0x1880                                  ; Prepare MPIP
    csrw    mstatus, t0                                 ; Enable MPIP

    lw      x1, 0x70(sp)                                ; Recover RA
    lw      x5, 0x4C(sp)                                ; Recover t0
    lw      x6, 0x48(sp)                                ; Recover t1
    lw      x7, 0x44(sp)                                ; Recover t2
    lw      x8, 0x30(sp)                                ; Recover s0
    lw      x9, 0x2C(sp)                                ; Recover s1
    lw      x10, 0x6C(sp)                               ; Recover a0
    lw      x11, 0x68(sp)                               ; Recover a1
    lw      x12, 0x64(sp)                               ; Recover a2
    lw      x13, 0x60(sp)                               ; Recover a3
    lw      x14, 0x5C(sp)                               ; Recover a4
    lw      x15, 0x58(sp)                               ; Recover a5
    lw      x16, 0x54(sp)                               ; Recover a6
    lw      x17, 0x50(sp)                               ; Recover a7
    lw      x18, 0x28(sp)                               ; Recover s2
    lw      x19, 0x24(sp)                               ; Recover s3
    lw      x20, 0x20(sp)                               ; Recover s4
    lw      x21, 0x1C(sp)                               ; Recover s5
    lw      x22, 0x18(sp)                               ; Recover s6
    lw      x23, 0x14(sp)                               ; Recover s7
    lw      x24, 0x10(sp)                               ; Recover s8
    lw      x25, 0x0C(sp)                               ; Recover s9
    lw      x26, 0x08(sp)                               ; Recover s10
    lw      x27, 0x04(sp)                               ; Recover s11
    lw      x28, 0x40(sp)                               ; Recover t3
    lw      x29, 0x3C(sp)                               ; Recover t4
    lw      x30, 0x38(sp)                               ; Recover t5
    lw      x31, 0x34(sp)                               ; Recover t6

#if __iar_riscv_base_isa == rv32e 
    addi    sp, sp, 260                                 ; Recover stack frame - with floating point registers  
#else
    addi    sp, sp, 128                                 ; Recover stack frame - without floating point registers
#endif
    mret                                                ; Return to point of interrupt

_tx_thread_synch_return:

#if __iar_riscv_base_isa == rv32e 
    flw     f8, 0x3C(sp)                                ; Recover fs0
    flw     f9, 0x40(sp)                                ; Recover fs1
    flw     f18,0x44(sp)                                ; Recover fs2
    flw     f19,0x48(sp)                                ; Recover fs3
    flw     f20,0x4C(sp)                                ; Recover fs4
    flw     f21,0x50(sp)                                ; Recover fs5
    flw     f22,0x54(sp)                                ; Recover fs6
    flw     f23,0x58(sp)                                ; Recover fs7
    flw     f24,0x5C(sp)                                ; Recover fs8
    flw     f25,0x60(sp)                                ; Recover fs9
    flw     f26,0x64(sp)                                ; Recover fs10
    flw     f27,0x68(sp)                                ; Recover fs11
    lw      t0, 0x6C(sp)                                ; Recover fcsr
    csrw    fcsr, t0                                    ; 
#endif

    /* Recover standard preserved registers.  */
    /* Recover standard registers.  */

    lw      x1, 0x34(sp)                                ; Recover RA
    lw      x8, 0x30(sp)                                ; Recover s0
    lw      x9, 0x2C(sp)                                ; Recover s1
    lw      x18, 0x28(sp)                               ; Recover s2
    lw      x19, 0x24(sp)                               ; Recover s3
    lw      x20, 0x20(sp)                               ; Recover s4
    lw      x21, 0x1C(sp)                               ; Recover s5
    lw      x22, 0x18(sp)                               ; Recover s6
    lw      x23, 0x14(sp)                               ; Recover s7
    lw      x24, 0x10(sp)                               ; Recover s8
    lw      x25, 0x0C(sp)                               ; Recover s9
    lw      x26, 0x08(sp)                               ; Recover s10
    lw      x27, 0x04(sp)                               ; Recover s11
    lw      t0, 0x38(sp)                                ; Recover mstatus
    csrw    mstatus, t0                                 ; Store mstatus, enables interrupt
#if __iar_riscv_base_isa == rv32e 
    addi    sp, sp, 116                                 ; Recover stack frame
#else
    addi    sp, sp, 64                                  ; Recover stack frame
#endif
    ret                                                 ; Return to thread

/* }  */
    END
    
