os_tick.c 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506
  1. /*
  2. ***********************************************************************************************************************
  3. * uC/OS-III
  4. * The Real-Time Kernel
  5. *
  6. * (c) Copyright 2009-2011; Micrium, Inc.; Weston, FL
  7. * All rights reserved. Protected by international copyright laws.
  8. *
  9. * TICK MANAGEMENT
  10. *
  11. * File : OS_TICK.C
  12. * By : JJL
  13. * Version : V3.02.00
  14. *
  15. * LICENSING TERMS:
  16. * ---------------
  17. * uC/OS-III is provided in source form for FREE short-term evaluation, for educational use or
  18. * for peaceful research. If you plan or intend to use uC/OS-III in a commercial application/
  19. * product then, you need to contact Micrium to properly license uC/OS-III for its use in your
  20. * application/product. We provide ALL the source code for your convenience and to help you
  21. * experience uC/OS-III. The fact that the source is provided does NOT mean that you can use
  22. * it commercially without paying a licensing fee.
  23. *
  24. * Knowledge of the source code may NOT be used to develop a similar product.
  25. *
  26. * Please help us continue to provide the embedded community with the finest software available.
  27. * Your honesty is greatly appreciated.
  28. *
  29. * You can contact us at www.micrium.com, or by phone at +1 (954) 217-2036.
  30. ************************************************************************************************************************
  31. */
  32. #include <os.h>
  33. #ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
  34. const CPU_CHAR *os_tick__c = "$Id: $";
  35. #endif
  36. /*
  37. ************************************************************************************************************************
  38. * LOCAL PROTOTYPES
  39. ************************************************************************************************************************
  40. */
  41. /*
  42. ************************************************************************************************************************
  43. * TICK TASK
  44. *
  45. * Description: This task is internal to uC/OS-III and is triggered by the tick interrupt.
  46. *
  47. * Arguments : p_arg is an argument passed to the task when the task is created (unused).
  48. *
  49. * Returns : none
  50. *
  51. * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
  52. ************************************************************************************************************************
  53. */
  54. void OS_TickTask (void *p_arg)
  55. {
  56. OS_ERR err;
  57. CPU_TS ts;
  58. p_arg = p_arg; /* Prevent compiler warning */
  59. while (DEF_ON) {
  60. (void)OSTaskSemPend((OS_TICK )0,
  61. (OS_OPT )OS_OPT_PEND_BLOCKING,
  62. (CPU_TS *)&ts,
  63. (OS_ERR *)&err); /* Wait for signal from tick interrupt */
  64. if (err == OS_ERR_NONE) {
  65. if (OSRunning == OS_STATE_OS_RUNNING) {
  66. OS_TickListUpdate(); /* Update all tasks waiting for time */
  67. }
  68. }
  69. }
  70. }
  71. /*$PAGE*/
  72. /*
  73. ************************************************************************************************************************
  74. * INITIALIZE TICK TASK
  75. *
  76. * Description: This function is called by OSInit() to create the tick task.
  77. *
  78. * Arguments : p_err is a pointer to a variable that will hold the value of an error code:
  79. *
  80. * OS_ERR_TICK_STK_INVALID if the pointer to the tick task stack is a NULL pointer
  81. * OS_ERR_TICK_STK_SIZE indicates that the specified stack size
  82. * OS_ERR_PRIO_INVALID if the priority you specified in the configuration is invalid
  83. * (There could be only one task at the Idle Task priority)
  84. * (Maybe the priority you specified is higher than OS_CFG_PRIO_MAX-1
  85. * OS_ERR_?? other error code returned by OSTaskCreate()
  86. *
  87. * Returns : none
  88. *
  89. * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
  90. ************************************************************************************************************************
  91. */
  92. void OS_TickTaskInit (OS_ERR *p_err)
  93. {
  94. #ifdef OS_SAFETY_CRITICAL
  95. if (p_err == (OS_ERR *)0) {
  96. OS_SAFETY_CRITICAL_EXCEPTION();
  97. return;
  98. }
  99. #endif
  100. OSTickCtr = (OS_TICK)0u; /* Clear the tick counter */
  101. OSTickTaskTimeMax = (CPU_TS)0u;
  102. OS_TickListInit(); /* Initialize the tick list data structures */
  103. /* ---------------- CREATE THE TICK TASK ---------------- */
  104. if (OSCfg_TickTaskPrio >= (OS_CFG_PRIO_MAX - 1u)) { /* Only one task at the 'Idle Task' priority */
  105. *p_err = OS_ERR_PRIO_INVALID;
  106. return;
  107. }
  108. OSTaskCreate((OS_TCB *)&OSTickTaskTCB,
  109. (CPU_CHAR *)((void *)"uC/OS-III Tick Task"),
  110. (OS_TASK_PTR )OS_TickTask,
  111. (void *)0,
  112. (OS_PRIO )OSCfg_TickTaskPrio,
  113. (CPU_STK *)OSCfg_TickTaskStkBasePtr,
  114. (CPU_STK_SIZE)OSCfg_TickTaskStkLimit,
  115. (CPU_STK_SIZE)OSCfg_TickTaskStkSize,
  116. (OS_MSG_QTY )0u,
  117. (OS_TICK )0u,
  118. (void *)0,
  119. (OS_OPT )(OS_OPT_TASK_STK_CHK | OS_OPT_TASK_STK_CLR),
  120. (OS_ERR *)p_err);
  121. }
  122. /*$PAGE*/
  123. /*
  124. ************************************************************************************************************************
  125. * INITIALIZE THE TICK LIST
  126. *
  127. * Description: This function initializes the tick handling data structures of uC/OS-III.
  128. *
  129. * Arguments : none
  130. *
  131. * Returns : None
  132. *
  133. * Note(s) : This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  134. ************************************************************************************************************************
  135. */
  136. void OS_TickListInit (void)
  137. {
  138. OS_TICK_SPOKE_IX i;
  139. OS_TICK_SPOKE *p_spoke;
  140. for (i = 0u; i < OSCfg_TickWheelSize; i++) {
  141. p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
  142. p_spoke->FirstPtr = (OS_TCB *)0;
  143. p_spoke->NbrEntries = (OS_OBJ_QTY )0u;
  144. p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
  145. }
  146. }
  147. /*$PAGE*/
  148. /*
  149. ************************************************************************************************************************
  150. * ADD TASK TO TICK LIST
  151. *
  152. * Description: This function is called to place a task in a list of task waiting for either time to expire or waiting to
  153. * timeout on a pend call.
  154. *
  155. * Arguments : p_tcb is a pointer to the OS_TCB of the task to add to the tick list
  156. * -----
  157. *
  158. * time represents either the 'match' value of OSTickCtr or a relative time from the current
  159. * value of OSTickCtr as specified by the 'opt' argument..
  160. *
  161. * relative when 'opt' is set to OS_OPT_TIME_DLY
  162. * relative when 'opt' is set to OS_OPT_TIME_TIMEOUT
  163. * match when 'opt' is set to OS_OPT_TIME_MATCH
  164. * periodic when 'opt' is set to OS_OPT_TIME_PERIODIC
  165. *
  166. * opt is an option specifying how to calculate time. The valid values are:
  167. * ---
  168. * OS_OPT_TIME_DLY
  169. * OS_OPT_TIME_TIMEOUT
  170. * OS_OPT_TIME_PERIODIC
  171. * OS_OPT_TIME_MATCH
  172. *
  173. * p_err is a pointer to a variable that will contain an error code returned by this function.
  174. * -----
  175. * OS_ERR_NONE the call was successful and the time delay was scheduled.
  176. * OS_ERR_TIME_ZERO_DLY if delay is zero or already occurred.
  177. *
  178. * Returns : None
  179. *
  180. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  181. *
  182. * 2) This function is assumed to be called with interrupts disabled.
  183. ************************************************************************************************************************
  184. */
  185. void OS_TickListInsert (OS_TCB *p_tcb,
  186. OS_TICK time,
  187. OS_OPT opt,
  188. OS_ERR *p_err)
  189. {
  190. OS_TICK tick_delta;
  191. OS_TICK tick_next;
  192. OS_TICK_SPOKE *p_spoke;
  193. OS_TCB *p_tcb0;
  194. OS_TCB *p_tcb1;
  195. OS_TICK_SPOKE_IX spoke;
  196. if (opt == OS_OPT_TIME_MATCH) { /* Task time is absolute. */
  197. tick_delta = time - OSTickCtr - 1u;
  198. if (tick_delta > OS_TICK_TH_RDY) { /* If delay already occurred, ... */
  199. p_tcb->TickCtrMatch = (OS_TICK )0u;
  200. p_tcb->TickRemain = (OS_TICK )0u;
  201. p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
  202. *p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
  203. return;
  204. }
  205. p_tcb->TickCtrMatch = time;
  206. p_tcb->TickRemain = tick_delta + 1u;
  207. } else if (time > (OS_TICK)0u) {
  208. if (opt == OS_OPT_TIME_PERIODIC) { /* Task time is periodic. */
  209. tick_next = p_tcb->TickCtrPrev + time;
  210. tick_delta = tick_next - OSTickCtr - 1u;
  211. if (tick_delta < time) { /* If next periodic delay did NOT already occur, ... */
  212. p_tcb->TickCtrMatch = tick_next; /* ... set next periodic delay; ... */
  213. } else {
  214. p_tcb->TickCtrMatch = OSTickCtr + time; /* ... else reset periodic delay. */
  215. }
  216. p_tcb->TickRemain = p_tcb->TickCtrMatch - OSTickCtr;
  217. p_tcb->TickCtrPrev = p_tcb->TickCtrMatch;
  218. } else { /* Task time is relative to current. */
  219. p_tcb->TickCtrMatch = OSTickCtr + time;
  220. p_tcb->TickRemain = time;
  221. }
  222. } else { /* Zero time delay; ... */
  223. p_tcb->TickCtrMatch = (OS_TICK )0u;
  224. p_tcb->TickRemain = (OS_TICK )0u;
  225. p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
  226. *p_err = OS_ERR_TIME_ZERO_DLY; /* ... do NOT delay. */
  227. return;
  228. }
  229. spoke = (OS_TICK_SPOKE_IX)(p_tcb->TickCtrMatch % OSCfg_TickWheelSize);
  230. p_spoke = &OSCfg_TickWheel[spoke];
  231. if (p_spoke->NbrEntries == (OS_OBJ_QTY)0u) { /* First entry in the spoke */
  232. p_tcb->TickNextPtr = (OS_TCB *)0;
  233. p_tcb->TickPrevPtr = (OS_TCB *)0;
  234. p_spoke->FirstPtr = p_tcb;
  235. p_spoke->NbrEntries = (OS_OBJ_QTY)1u;
  236. } else {
  237. p_tcb1 = p_spoke->FirstPtr; /* Point to current first TCB in the list */
  238. while (p_tcb1 != (OS_TCB *)0) {
  239. p_tcb1->TickRemain = p_tcb1->TickCtrMatch /* Compute time remaining of current TCB in list */
  240. - OSTickCtr;
  241. if (p_tcb->TickRemain > p_tcb1->TickRemain) { /* Do we need to insert AFTER current TCB in list? */
  242. if (p_tcb1->TickNextPtr != (OS_TCB *)0) { /* Yes, are we pointing at the last TCB in the list? */
  243. p_tcb1 = p_tcb1->TickNextPtr; /* No, Point to next TCB in the list */
  244. } else {
  245. p_tcb->TickNextPtr = (OS_TCB *)0;
  246. p_tcb->TickPrevPtr = p_tcb1;
  247. p_tcb1->TickNextPtr = p_tcb; /* Yes, TCB to add is now new last entry in the list */
  248. p_tcb1 = (OS_TCB *)0; /* Break loop */
  249. }
  250. } else { /* Insert before the current TCB */
  251. if (p_tcb1->TickPrevPtr == (OS_TCB *)0) { /* Are we inserting before the first TCB? */
  252. p_tcb->TickPrevPtr = (OS_TCB *)0;
  253. p_tcb->TickNextPtr = p_tcb1;
  254. p_tcb1->TickPrevPtr = p_tcb;
  255. p_spoke->FirstPtr = p_tcb;
  256. } else { /* Insert in between 2 TCBs already in the list */
  257. p_tcb0 = p_tcb1->TickPrevPtr;
  258. p_tcb->TickPrevPtr = p_tcb0;
  259. p_tcb->TickNextPtr = p_tcb1;
  260. p_tcb0->TickNextPtr = p_tcb;
  261. p_tcb1->TickPrevPtr = p_tcb;
  262. }
  263. p_tcb1 = (OS_TCB *)0; /* Break loop */
  264. }
  265. }
  266. p_spoke->NbrEntries++;
  267. }
  268. if (p_spoke->NbrEntriesMax < p_spoke->NbrEntries) { /* Keep track of maximum # of entries in each spoke */
  269. p_spoke->NbrEntriesMax = p_spoke->NbrEntries;
  270. }
  271. p_tcb->TickSpokePtr = p_spoke; /* Link back to tick spoke */
  272. *p_err = OS_ERR_NONE;
  273. }
  274. /*$PAGE*/
  275. /*
  276. ************************************************************************************************************************
  277. * REMOVE A TASK FROM THE TICK LIST
  278. *
  279. * Description: This function is called to remove a task from the tick list
  280. *
  281. * Arguments : p_tcb Is a pointer to the OS_TCB to remove.
  282. * -----
  283. *
  284. * Returns : none
  285. *
  286. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  287. *
  288. * 2) This function is assumed to be called with interrupts disabled.
  289. ************************************************************************************************************************
  290. */
  291. void OS_TickListRemove (OS_TCB *p_tcb)
  292. {
  293. OS_TICK_SPOKE *p_spoke;
  294. OS_TCB *p_tcb1;
  295. OS_TCB *p_tcb2;
  296. p_spoke = p_tcb->TickSpokePtr;
  297. if (p_spoke != (OS_TICK_SPOKE *)0) { /* Confirm that task is in tick list */
  298. p_tcb->TickRemain = (OS_TICK)0u;
  299. if (p_spoke->FirstPtr == p_tcb) { /* Is timer to remove at the beginning of list? */
  300. p_tcb1 = (OS_TCB *)p_tcb->TickNextPtr; /* Yes */
  301. p_spoke->FirstPtr = p_tcb1;
  302. if (p_tcb1 != (OS_TCB *)0) {
  303. p_tcb1->TickPrevPtr = (void *)0;
  304. }
  305. } else {
  306. p_tcb1 = p_tcb->TickPrevPtr; /* No, remove timer from somewhere in the list */
  307. p_tcb2 = p_tcb->TickNextPtr;
  308. p_tcb1->TickNextPtr = p_tcb2;
  309. if (p_tcb2 != (OS_TCB *)0) {
  310. p_tcb2->TickPrevPtr = p_tcb1;
  311. }
  312. }
  313. p_tcb->TickNextPtr = (OS_TCB *)0;
  314. p_tcb->TickPrevPtr = (OS_TCB *)0;
  315. p_tcb->TickSpokePtr = (OS_TICK_SPOKE *)0;
  316. p_tcb->TickCtrMatch = (OS_TICK )0u;
  317. p_spoke->NbrEntries--;
  318. }
  319. }
  320. /*$PAGE*/
  321. /*
  322. ************************************************************************************************************************
  323. * RESET TICK LIST PEAK DETECTOR
  324. *
  325. * Description: This function is used to reset the peak detector for the number of entries in each spoke.
  326. *
  327. * Arguments : void
  328. *
  329. * Returns : none
  330. *
  331. * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
  332. ************************************************************************************************************************
  333. */
  334. void OS_TickListResetPeak (void)
  335. {
  336. OS_TICK_SPOKE_IX i;
  337. OS_TICK_SPOKE *p_spoke;
  338. for (i = 0u; i < OSCfg_TickWheelSize; i++) {
  339. p_spoke = (OS_TICK_SPOKE *)&OSCfg_TickWheel[i];
  340. p_spoke->NbrEntriesMax = (OS_OBJ_QTY )0u;
  341. }
  342. }
  343. /*$PAGE*/
  344. /*
  345. ************************************************************************************************************************
  346. * UPDATE THE TICK LIST
  347. *
  348. * Description: This function is called when a tick occurs and determines if the timeout waiting for a kernel object has
  349. * expired or a delay has expired.
  350. *
  351. * Arguments : non
  352. *
  353. * Returns : none
  354. *
  355. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  356. ************************************************************************************************************************
  357. */
  358. void OS_TickListUpdate (void)
  359. {
  360. CPU_BOOLEAN done;
  361. OS_TICK_SPOKE *p_spoke;
  362. OS_TCB *p_tcb;
  363. OS_TCB *p_tcb_next;
  364. OS_TICK_SPOKE_IX spoke;
  365. CPU_TS ts_start;
  366. CPU_TS ts_end;
  367. CPU_SR_ALLOC();
  368. OS_CRITICAL_ENTER();
  369. ts_start = OS_TS_GET();
  370. OSTickCtr++; /* Keep track of the number of ticks */
  371. spoke = (OS_TICK_SPOKE_IX)(OSTickCtr % OSCfg_TickWheelSize);
  372. p_spoke = &OSCfg_TickWheel[spoke];
  373. p_tcb = p_spoke->FirstPtr;
  374. done = DEF_FALSE;
  375. while (done == DEF_FALSE) {
  376. if (p_tcb != (OS_TCB *)0) {
  377. p_tcb_next = p_tcb->TickNextPtr; /* Point to next TCB to update */
  378. switch (p_tcb->TaskState) {
  379. case OS_TASK_STATE_RDY:
  380. case OS_TASK_STATE_PEND:
  381. case OS_TASK_STATE_SUSPENDED:
  382. case OS_TASK_STATE_PEND_SUSPENDED:
  383. break;
  384. case OS_TASK_STATE_DLY:
  385. p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
  386. - OSTickCtr;
  387. if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
  388. p_tcb->TaskState = OS_TASK_STATE_RDY;
  389. OS_TaskRdy(p_tcb); /* Make task ready to run */
  390. } else {
  391. done = DEF_TRUE; /* Don't find a match, we're done! */
  392. }
  393. break;
  394. case OS_TASK_STATE_PEND_TIMEOUT:
  395. p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
  396. - OSTickCtr;
  397. if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
  398. #if (OS_MSG_EN > 0u)
  399. p_tcb->MsgPtr = (void *)0;
  400. p_tcb->MsgSize = (OS_MSG_SIZE)0u;
  401. #endif
  402. p_tcb->TS = OS_TS_GET();
  403. OS_PendListRemove(p_tcb); /* Remove from wait list */
  404. OS_TaskRdy(p_tcb);
  405. p_tcb->TaskState = OS_TASK_STATE_RDY;
  406. p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
  407. p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
  408. } else {
  409. done = DEF_TRUE; /* Don't find a match, we're done! */
  410. }
  411. break;
  412. case OS_TASK_STATE_DLY_SUSPENDED:
  413. p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
  414. - OSTickCtr;
  415. if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
  416. p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
  417. OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
  418. } else {
  419. done = DEF_TRUE; /* Don't find a match, we're done! */
  420. }
  421. break;
  422. case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
  423. p_tcb->TickRemain = p_tcb->TickCtrMatch /* Compute time remaining of current TCB */
  424. - OSTickCtr;
  425. if (OSTickCtr == p_tcb->TickCtrMatch) { /* Process each TCB that expires */
  426. #if (OS_MSG_EN > 0u)
  427. p_tcb->MsgPtr = (void *)0;
  428. p_tcb->MsgSize = (OS_MSG_SIZE)0u;
  429. #endif
  430. p_tcb->TS = OS_TS_GET();
  431. OS_PendListRemove(p_tcb); /* Remove from wait list */
  432. OS_TickListRemove(p_tcb); /* Remove from current wheel spoke */
  433. p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
  434. p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
  435. p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
  436. } else {
  437. done = DEF_TRUE; /* Don't find a match, we're done! */
  438. }
  439. break;
  440. default:
  441. break;
  442. }
  443. p_tcb = p_tcb_next;
  444. } else {
  445. done = DEF_TRUE;
  446. }
  447. }
  448. ts_end = OS_TS_GET() - ts_start; /* Measure execution time of tick task */
  449. if (ts_end > OSTickTaskTimeMax) {
  450. OSTickTaskTimeMax = ts_end;
  451. }
  452. OS_CRITICAL_EXIT();
  453. }