os_cpu_port.c 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384
  1. #include <stdio.h>
  2. #include "ucos_ii.h"
  3. #include "nuclei_sdk_soc.h"
  4. #include "os_cpu_port.h"
  5. //#define ENABLE_KERNEL_DEBUG
  6. #ifdef ENABLE_KERNEL_DEBUG
  7. #define UCOSII_PORT_DEBUG(...) printf(__VA_ARGS__)
  8. #else
  9. #define UCOSII_PORT_DEBUG(...)
  10. #endif
  11. #ifndef configASSERT
  12. #define configASSERT( x )
  13. #define configASSERT_DEFINED 0
  14. #else
  15. #define configASSERT_DEFINED 1
  16. #endif
  17. #ifndef configSYSTICK_CLOCK_HZ
  18. #define configSYSTICK_CLOCK_HZ SOC_TIMER_FREQ
  19. #endif
  20. #ifndef configKERNEL_INTERRUPT_PRIORITY
  21. #define configKERNEL_INTERRUPT_PRIORITY 0
  22. #endif
  23. #define SYSTICK_TICK_CONST (configSYSTICK_CLOCK_HZ / configTICK_RATE_HZ)
  24. /* Constants required to set up the initial stack. */
  25. #define portINITIAL_MSTATUS ( MSTATUS_MPP | MSTATUS_MPIE | MSTATUS_FS_INITIAL | MSTATUS_VS_INITIAL)
  26. #define portINITIAL_EXC_RETURN ( 0xfffffffd )
  27. /* Let the user override the pre-loading of the initial LR with the address of
  28. prvTaskExitError() in case it messes up unwinding of the stack in the
  29. debugger. */
  30. #ifdef configTASK_RETURN_ADDRESS
  31. #define portTASK_RETURN_ADDRESS configTASK_RETURN_ADDRESS
  32. #else
  33. #define portTASK_RETURN_ADDRESS prvTaskExitError
  34. #endif
  35. /*
  36. * Setup the timer to generate the tick interrupts. The implementation in this
  37. * file is weak to allow application writers to change the timer used to
  38. * generate the tick interrupt.
  39. */
  40. void vPortSetupTimerInterrupt(void);
  41. /*
  42. * Exception handlers.
  43. */
  44. #define xPortSysTickHandler irqc_mtip_handler
  45. /*
  46. * Start first task is a separate function so it can be tested in isolation.
  47. */
  48. extern void prvPortStartFirstTask(void) __attribute__((naked));
  49. /*
  50. * Used to catch tasks that attempt to return from their implementing function.
  51. */
  52. static void prvTaskExitError(void);
  53. /*-----------------------------------------------------------*/
  54. /* Each task maintains its own interrupt status in the critical nesting
  55. variable. */
  56. static UBaseType_t uxCriticalNesting = 0xaaaaaaaa;
  57. /*
  58. * Record the real MTH calculated by the configMAX_SYSCALL_INTERRUPT_PRIORITY
  59. * The configMAX_SYSCALL_INTERRUPT_PRIORITY is not the left-aligned level value,
  60. * See equations below:
  61. * Level Bits number: lvlbits = min(nlbits, CLICINTCTLBITS)
  62. * Left align Bits number: lfabits = 8-lvlbits
  63. * 0 < configMAX_SYSCALL_INTERRUPT_PRIORITY <= (2^lvlbits-1)
  64. * uxMaxSysCallMTH = (configMAX_SYSCALL_INTERRUPT_PRIORITY << lfabits) | ((2^lfabits)-1)
  65. * If nlbits = 3, CLICINTCTLBITS=3, then lvlbits = 3, lfabits = 5
  66. * Set configMAX_SYSCALL_INTERRUPT_PRIORITY to 6
  67. * Then uxMaxSysCallMTH = (6<<5) | (2^5 - 1) = 223
  68. *
  69. * See function prvCheckMaxSysCallPrio and prvCalcMaxSysCallMTH
  70. */
  71. uint8_t uxMaxSysCallMTH = 255;
  72. /*-----------------------------------------------------------*/
  73. /*
  74. *********************************************************************************************************
  75. * INITIALIZE A TASK'S STACK
  76. *
  77. * Description: This function is called by either OSTaskCreate() or OSTaskCreateExt() to initialize the
  78. * stack frame of the task being created. This function is highly processor specific.
  79. *
  80. * Arguments : task is a pointer to the task code
  81. *
  82. * p_arg is a pointer to a user supplied data area that will be passed to the task
  83. * when the task first executes.
  84. *
  85. * p_tos is a pointer to the top of stack. It is assumed that 'ptos' points to
  86. * a 'free' entry on the task stack. If OS_STK_GROWTH is set to 1 then
  87. * 'ptos' will contain the HIGHEST valid address of the stack. Similarly, if
  88. * OS_STK_GROWTH is set to 0, the 'ptos' will contains the LOWEST valid address
  89. * of the stack.
  90. *
  91. * opt specifies options that can be used to alter the behavior of OSTaskStkInit().
  92. * (see uCOS_II.H for OS_TASK_OPT_xxx).
  93. *
  94. * Returns : Always returns the location of the new top-of-stack once the processor registers have
  95. * been placed on the stack in the proper order.
  96. *
  97. * Note(s) : (1) Interrupts are enabled when task starts executing.
  98. *
  99. * (2) There is no need to save register x0 since it is a hard-wired zero.
  100. *
  101. * (3) RISC-V calling convention register usage:
  102. *
  103. * +-------------+-------------+----------------------------------+
  104. * | Register | ABI Name | Description |
  105. * +-------------+-------------+----------------------------------+
  106. * | x31 - x28 | t6 - t3 | Temporaries |
  107. * +-------------+-------------+----------------------------------+
  108. * | x27 - x18 | s11 - s2 | Saved registers |
  109. * +-------------+-------------+----------------------------------+
  110. * | x17 - x12 | a7 - a2 | Function arguments |
  111. * +-------------+-------------+----------------------------------+
  112. * | x11 - x10 | a1 - a0 | Function arguments/return values |
  113. * +-------------+-------------+----------------------------------+
  114. * | x9 | s1 | Saved register |
  115. * +-------------+-------------+----------------------------------+
  116. * | x8 | s0/fp | Saved register/frame pointer |
  117. * +-------------+-------------+----------------------------------+
  118. * | x7 - x5 | t2 - t0 | Temporaries |
  119. * +-------------+-------------+----------------------------------+
  120. * | x4 | tp | Thread pointer |
  121. * +-------------+-------------+----------------------------------+
  122. * | x3 | gp | Global pointer |
  123. * +-------------+-------------+----------------------------------+
  124. * | x2 | sp | Stack pointer |
  125. * +-------------+-------------+----------------------------------+
  126. * | x1 | ra | return address |
  127. * +-------------+-------------+----------------------------------+
  128. * | x0 | zero | Hard-wired zero |
  129. * +-------------+-------------+----------------------------------+
  130. *
  131. * As per the standard RISC-V ABI pxTopcOfStack is passed in in a0, pxCode in
  132. * a1, and pvParameters in a2. The new top of stack is passed out in a0.
  133. *
  134. * The RISC-V context is saved rtos tasks in the following stack frame,
  135. * where the global and thread pointers are currently assumed to be constant so
  136. * are not saved:
  137. *
  138. * mstatus
  139. * #ifndef __riscv_32e
  140. * rsv1
  141. * rsv0
  142. * x31
  143. * x30
  144. * x29
  145. * x28
  146. * x27
  147. * x26
  148. * x25
  149. * x24
  150. * x23
  151. * x22
  152. * x21
  153. * x20
  154. * x19
  155. * x18
  156. * x17
  157. * x16
  158. * #endif
  159. * x15
  160. * x14
  161. * x13
  162. * x12
  163. * x11
  164. * pvParameters
  165. * x9
  166. * x8
  167. * x7
  168. * x6
  169. * x5
  170. * portTASK_RETURN_ADDRESS
  171. * pxCode
  172. */
  173. OS_STK* OSTaskStkInit(void (*task)(void* pd), void* pdata, OS_STK* ptos, INT16U opt)
  174. {
  175. /* Simulate the stack frame as it would be created by a context switch
  176. interrupt. */
  177. // Force stack 8byte align for double floating point case
  178. OS_STK* pxTopOfStack = (OS_STK*)(((unsigned long)ptos) & (~(unsigned long)(portBYTE_ALIGNMENT - 1)));
  179. /* Offset added to account for the way the MCU uses the stack on entry/exit
  180. of interrupts, and to ensure alignment. */
  181. pxTopOfStack--;
  182. *pxTopOfStack = portINITIAL_MSTATUS; /* MSTATUS */
  183. /* Save code space by skipping register initialisation. */
  184. #ifndef __riscv_32e
  185. pxTopOfStack -= 24; /* X11 - X31. + 2 reserved */
  186. #else
  187. pxTopOfStack -= 6; /* X11 - X15. */
  188. #endif
  189. *pxTopOfStack = (StackType_t) pdata; /* X10/A0 */
  190. pxTopOfStack -= 6; /* X5 - X9 */
  191. *pxTopOfStack = (StackType_t) portTASK_RETURN_ADDRESS; /* RA, X1 */
  192. pxTopOfStack --;
  193. *pxTopOfStack = ((StackType_t) task) ; /* PC */
  194. return pxTopOfStack;
  195. }
  196. static void prvTaskExitError(void)
  197. {
  198. volatile uint32_t ulDummy = 0;
  199. /* A function that implements a task must not exit or attempt to return to
  200. its caller as there is nothing to return to. If a task wants to exit it
  201. should instead call vTaskDelete( NULL ).
  202. Artificially force an assert() to be triggered if configASSERT() is
  203. defined, then stop here so application writers can catch the error. */
  204. configASSERT(uxCriticalNesting == ~0UL);
  205. portDISABLE_INTERRUPTS();
  206. while (ulDummy == 0) {
  207. /* This file calls prvTaskExitError() after the scheduler has been
  208. started to remove a compiler warning about the function being defined
  209. but never called. ulDummy is used purely to quieten other warnings
  210. about code appearing after this function is called - making ulDummy
  211. volatile makes the compiler think the function could return and
  212. therefore not output an 'unreachable code' warning for code that appears
  213. after it. */
  214. /* Sleep and wait for interrupt */
  215. __WFI();
  216. }
  217. }
  218. /*
  219. * See header file for description.
  220. */
  221. void OSStartHighRdy(void)
  222. {
  223. __disable_irq();
  224. /* Start the timer that generates the tick ISR. Interrupts are disabled
  225. here already. */
  226. vPortSetupTimerInterrupt();
  227. /* Initialise the critical nesting count ready for the first task. */
  228. uxCriticalNesting = 0;
  229. /* Mark OSRunning to True */
  230. OSRunning = OS_TRUE;
  231. #if (OS_CPU_HOOKS_EN > 0) && (OS_TASK_SW_HOOK_EN > 0)
  232. /* Call OSTaskSwHook */
  233. OSTaskSwHook();
  234. #endif
  235. /* Start the first task. */
  236. prvPortStartFirstTask();
  237. /* Should never get here as the tasks will now be executing! Call the task
  238. exit error function to prevent compiler warnings about a static function
  239. not being called in the case that the application writer overrides this
  240. functionality by defining configTASK_RETURN_ADDRESS. */
  241. prvTaskExitError();
  242. /* Should not get here! */
  243. return;
  244. }
  245. /*-----------------------------------------------------------*/
  246. void vPortEndScheduler(void)
  247. {
  248. /* Not implemented in ports where there is nothing to return to.
  249. Artificially force an assert. */
  250. configASSERT(uxCriticalNesting == 1000UL);
  251. }
  252. /*-----------------------------------------------------------*/
  253. void vPortEnterCritical(void)
  254. {
  255. portDISABLE_INTERRUPTS();
  256. uxCriticalNesting++;
  257. }
  258. /*-----------------------------------------------------------*/
  259. void vPortExitCritical(void)
  260. {
  261. configASSERT(uxCriticalNesting);
  262. uxCriticalNesting--;
  263. if (uxCriticalNesting == 0) {
  264. portENABLE_INTERRUPTS();
  265. }
  266. }
  267. /*-----------------------------------------------------------*/
  268. void vPortAssert(int32_t x)
  269. {
  270. if ((x) == 0) {
  271. portDISABLE_INTERRUPTS();
  272. while (1) {
  273. /* Sleep and wait for interrupt */
  274. __WFI();
  275. };
  276. }
  277. }
  278. /*-----------------------------------------------------------*/
  279. void xPortTaskSwitch(void)
  280. {
  281. portDISABLE_INTERRUPTS();
  282. /* Clear Software IRQ, A MUST */
  283. SysTimer_ClearSWIRQ();
  284. #if (OS_CPU_HOOKS_EN > 0) && (OS_TASK_SW_HOOK_EN > 0)
  285. /* Call OSTaskSwHook */
  286. OSTaskSwHook();
  287. #endif
  288. OSPrioCur = OSPrioHighRdy;
  289. OSTCBCur = OSTCBHighRdy;
  290. portENABLE_INTERRUPTS();
  291. }
  292. /*-----------------------------------------------------------*/
  293. /*
  294. *********************************************************************************************************
  295. * SYS TICK HANDLER
  296. *
  297. * Description: Handle the system tick (SysTick) interrupt, which is used to generate the uC/OS-II tick
  298. * interrupt.
  299. *
  300. * Arguments : None.
  301. *
  302. * Note(s) : This function is defined with weak linking in 'riscv_hal_stubs.c' so that it can be
  303. * overridden by the kernel port with same prototype
  304. *********************************************************************************************************
  305. */
  306. __INTERRUPT void xPortSysTickHandler(void)
  307. {
  308. #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */
  309. OS_CPU_SR cpu_sr;
  310. #endif
  311. /* The SysTick runs at the lowest interrupt priority, so when this interrupt
  312. executes all interrupts must be unmasked. There is therefore no need to
  313. save and then restore the interrupt mask value as its value is already
  314. known. */
  315. OS_ENTER_CRITICAL();
  316. SysTick_Reload(SYSTICK_TICK_CONST);
  317. OSIntEnter(); /* Tell uC/OS-II that we are starting an ISR */
  318. OS_EXIT_CRITICAL();
  319. OSTimeTick(); /* Call uC/OS-II's OSTimeTick() */
  320. OSIntExit(); /* Tell uC/OS-II that we are leaving the ISR */
  321. }
  322. /*-----------------------------------------------------------*/
  323. /*
  324. * Setup the systick timer to generate the tick interrupts at the required
  325. * frequency.
  326. */
  327. __attribute__((weak)) void vPortSetupTimerInterrupt(void)
  328. {
  329. TickType_t ticks = SYSTICK_TICK_CONST;
  330. /* Make SWI and SysTick the lowest priority interrupts. */
  331. /* Stop and clear the SysTimer. SysTimer as Vector Interrupt */
  332. SysTick_Config(ticks);
  333. IRQC_EnableIRQ(SysTimer_IRQn);
  334. IRQC_EnableIRQ(SysTimerSW_IRQn);
  335. }
  336. /*-----------------------------------------------------------*/