port.c 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. #include "tx_api.h"
  2. #include "tx_timer.h"
  3. #include "tx_thread.h"
  4. #include "tx_initialize.h"
  5. #include "nuclei_sdk_soc.h"
  6. // SOC_TIMER_FREQ should be provided in <Device>.h of NMSIS. eg. evalsoc.h
  7. // TX_TIMER_TICKS_PER_SECOND defined in tx_user.h which can overwrite the default one in tx_api.h if TX_INCLUDE_USER_DEFINE_FILE defined
  8. #define SYSTICK_TICK_CONST (SOC_TIMER_FREQ / TX_TIMER_TICKS_PER_SECOND)
  9. #define KERNEL_INTERRUPT_PRIORITY 0
  10. // MUST define SysTick_Handler as eclic_mtip_handler, which is registered in vector table
  11. #define SysTick_Handler eclic_mtip_handler
  12. /* This is the timer interrupt service routine. */
  13. void SysTick_Handler(void)
  14. {
  15. // Reload timer
  16. SysTick_Reload(SYSTICK_TICK_CONST);
  17. /* Increment system clock. */
  18. _tx_timer_system_clock++;
  19. /* Test for time-slice expiration. */
  20. if (_tx_timer_time_slice) {
  21. /* Decrement the time_slice. */
  22. _tx_timer_time_slice--;
  23. /* Check for expiration. */
  24. if (_tx_timer_time_slice == 0) {
  25. /* Set the time-slice expired flag. */
  26. _tx_timer_expired_time_slice = TX_TRUE;
  27. }
  28. }
  29. /* Test for timer expiration. */
  30. if (*_tx_timer_current_ptr) {
  31. /* Set expiration flag. */
  32. _tx_timer_expired = TX_TRUE;
  33. } else {
  34. /* No timer expired, increment the timer pointer. */
  35. _tx_timer_current_ptr++;
  36. /* Check for wrap-around. */
  37. if (_tx_timer_current_ptr == _tx_timer_list_end) {
  38. /* Wrap to beginning of list. */
  39. _tx_timer_current_ptr = _tx_timer_list_start;
  40. }
  41. }
  42. /* See if anything has expired. */
  43. if ((_tx_timer_expired_time_slice) || (_tx_timer_expired)) {
  44. /* Did a timer expire? */
  45. if (_tx_timer_expired) {
  46. /* Process timer expiration. */
  47. _tx_timer_expiration_process();
  48. }
  49. /* Did time slice expire? */
  50. if (_tx_timer_expired_time_slice) {
  51. /* Time slice interrupted thread. */
  52. _tx_thread_time_slice();
  53. }
  54. }
  55. }
  56. // Task Switch code called in eclic_msip_handler
  57. void PortThreadSwitch(void)
  58. {
  59. #ifdef TX_ENABLE_EXECUTION_CHANGE_NOTIFY
  60. _tx_execution_thread_exit();
  61. #endif
  62. /*
  63. * Magic idle task emulation for threadx
  64. * ThreadX don't have idle task, so _tx_thread_execute_ptr could be NULL
  65. * If it is NULL, it means it should goto idle state, and wait for interrupt
  66. */
  67. if (!_tx_thread_execute_ptr) {
  68. /* increase the timer interrupt to higher priority to enable interrupt nesting */
  69. ECLIC_SetLevelIRQ(SysTimer_IRQn, KERNEL_INTERRUPT_PRIORITY + 1);
  70. /* swap task stack to interrupt stack to avoid interrupt nesting on task stack */
  71. __ASM volatile("csrrw sp, " STRINGIFY(CSR_MSCRATCHCSWL) ", sp");
  72. /* mcause and msubm must be saved and restore if interrupt nested */
  73. rv_csr_t mcause = __RV_CSR_READ(CSR_MCAUSE);
  74. rv_csr_t msubm = __RV_CSR_READ(CSR_MSUBM);
  75. __enable_irq();
  76. /* If no ready task just go to idle and wait for interrupt */
  77. while (!_tx_thread_execute_ptr) {
  78. __WFI();
  79. }
  80. /* disable interrupt to avoid interrupt nesting since new task handle found */
  81. __disable_irq();
  82. /* restore mcause and msubm which is necessary since interrupt nested manually by us */
  83. __RV_CSR_WRITE(CSR_MSUBM, msubm);
  84. __RV_CSR_WRITE(CSR_MCAUSE, mcause);
  85. /* swap interrupt stack back to task stack */
  86. __ASM volatile("csrrw sp, " STRINGIFY(CSR_MSCRATCHCSWL) ", sp");
  87. /* restore timer interrupt to origin kernel interrupt priority */
  88. ECLIC_SetLevelIRQ(SysTimer_IRQn, KERNEL_INTERRUPT_PRIORITY);
  89. }
  90. /* Determine if the time-slice is active. */
  91. if (_tx_timer_time_slice && _tx_thread_current_ptr) {
  92. /* Preserve current remaining time-slice for the thread and clear the current time-slice. */
  93. _tx_thread_current_ptr -> tx_thread_time_slice = _tx_timer_time_slice;
  94. _tx_timer_time_slice = 0;
  95. }
  96. _tx_thread_current_ptr = _tx_thread_execute_ptr;
  97. /* Increment the run count for this thread. */
  98. _tx_thread_current_ptr -> tx_thread_run_count++;
  99. /* Clear Software IRQ, A MUST */
  100. SysTimer_ClearSWIRQ();
  101. }
  102. void SetupSysTickInterrupt(void)
  103. {
  104. uint64_t ticks = SYSTICK_TICK_CONST;
  105. /* Make SWI and SysTick the lowest priority interrupts. */
  106. /* Stop and clear the SysTimer. SysTimer as Non-Vector Interrupt */
  107. SysTick_Config(ticks);
  108. ECLIC_DisableIRQ(SysTimer_IRQn);
  109. ECLIC_SetLevelIRQ(SysTimer_IRQn, KERNEL_INTERRUPT_PRIORITY);
  110. ECLIC_SetShvIRQ(SysTimer_IRQn, ECLIC_NON_VECTOR_INTERRUPT);
  111. ECLIC_EnableIRQ(SysTimer_IRQn);
  112. /* Set SWI interrupt level to lowest level/priority, SysTimerSW as Vector Interrupt */
  113. ECLIC_SetShvIRQ(SysTimerSW_IRQn, ECLIC_VECTOR_INTERRUPT);
  114. ECLIC_SetLevelIRQ(SysTimerSW_IRQn, KERNEL_INTERRUPT_PRIORITY);
  115. ECLIC_EnableIRQ(SysTimerSW_IRQn);
  116. }
  117. // TODO
  118. // No need to do it here, in void tx_application_define(void *first_unused_memory) function
  119. // You don't need to use this first_unused_memory since it is not available in our port
  120. // You should allocate an memory by your self such as memory_area array in threadx demo
  121. //static uint64_t s_threadx_heap[TX_HEAP_SIZE/sizeof(uint64_t)];
  122. VOID _tx_initialize_low_level(VOID)
  123. {
  124. // _tx_initialize_unused_memory = s_threadx_heap;
  125. _tx_initialize_unused_memory = NULL;
  126. SetupSysTickInterrupt();
  127. _tx_thread_interrupt_control(0);
  128. // Enable interrupt and task sp swap
  129. #if defined(ECLIC_HW_CTX_AUTO) && defined(CFG_HAS_ECLICV2)
  130. __RV_CSR_SET(CSR_MECLIC_CTL, MECLIC_CTL_TSP_EN);
  131. #endif
  132. }
  133. UINT _tx_thread_interrupt_control(UINT new_posture)
  134. {
  135. ULONG temp;
  136. if (new_posture == TX_INT_DISABLE) {
  137. // clear interrupt
  138. temp = __RV_CSR_READ_CLEAR(CSR_MSTATUS, MSTATUS_MIE);
  139. } else {
  140. temp = __RV_CSR_SWAP(CSR_MSTATUS, new_posture);
  141. }
  142. return (UINT)temp;
  143. }
  144. // _tx_thread_schedule function implemented in context.S
  145. // _tx_thread_system_return implemented in tx_port.h
  146. VOID _tx_thread_exit(VOID)
  147. {
  148. while (1) {
  149. __WFI();
  150. }
  151. }
  152. VOID _tx_thread_stack_build(TX_THREAD *thread_ptr, VOID (*function_ptr)(VOID))
  153. {
  154. struct thread_stack_frame *frame;
  155. uint8_t *stk;
  156. int i;
  157. stk = thread_ptr -> tx_thread_stack_end;
  158. /* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc */
  159. /* 32-bit boundary for ilp32e, and 128-bit boundary for others */
  160. #ifndef __riscv_32e
  161. stk = (uint8_t *)(((unsigned long)stk) & (~(unsigned long)(16 - 1)));
  162. #else
  163. stk = (uint8_t *)(((unsigned long)stk) & (~(unsigned long)(4 - 1)));
  164. #endif
  165. stk -= sizeof(struct thread_stack_frame);
  166. frame = (struct thread_stack_frame*)stk;
  167. for (i = 0; i < sizeof(struct thread_stack_frame) / sizeof(unsigned long); i++) {
  168. ((unsigned long*)frame)[i] = 0xdeadbeef;
  169. }
  170. frame->epc = (unsigned long)function_ptr;
  171. frame->ra = (unsigned long)_tx_thread_exit;
  172. frame->mstatus = THREAD_INITIAL_MSTATUS;
  173. thread_ptr -> tx_thread_stack_ptr = stk;
  174. }