os_tick.c 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576
  1. /*
  2. *********************************************************************************************************
  3. * uC/OS-III
  4. * The Real-Time Kernel
  5. *
  6. * Copyright 2009-2022 Silicon Laboratories Inc. www.silabs.com
  7. *
  8. * SPDX-License-Identifier: APACHE-2.0
  9. *
  10. * This software is subject to an open source license and is distributed by
  11. * Silicon Laboratories Inc. pursuant to the terms of the Apache License,
  12. * Version 2.0 available at www.apache.org/licenses/LICENSE-2.0.
  13. *
  14. *********************************************************************************************************
  15. */
  16. /*
  17. *********************************************************************************************************
  18. * TICK MANAGEMENT
  19. *
  20. * File : os_tick.c
  21. * Version : V3.08.02
  22. *********************************************************************************************************
  23. */
  24. #define MICRIUM_SOURCE
  25. #include "os.h"
  26. #ifdef VSC_INCLUDE_SOURCE_FILE_NAMES
  27. const CPU_CHAR *os_tick__c = "$Id: $";
  28. #endif
  29. #if (OS_CFG_TICK_EN > 0u)
  30. /*
  31. ************************************************************************************************************************
  32. * FUNCTION PROTOTYPES
  33. ************************************************************************************************************************
  34. */
  35. static void OS_TickListUpdate (OS_TICK ticks);
  36. /*
  37. ************************************************************************************************************************
  38. * TICK INIT
  39. *
  40. * Description: This function initializes the variables related to the tick handler.
  41. * The function is internal to uC/OS-III.
  42. *
  43. * Arguments : p_err is a pointer to a variable that will contain an error code returned by this function.
  44. * -----
  45. * OS_ERR_NONE the tick variables were initialized successfully
  46. *
  47. * Returns : none
  48. *
  49. * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
  50. ************************************************************************************************************************
  51. */
  52. void OS_TickInit (OS_ERR *p_err)
  53. {
  54. *p_err = OS_ERR_NONE;
  55. OSTickCtr = 0u; /* Clear the tick counter */
  56. #if (OS_CFG_DYN_TICK_EN > 0u)
  57. OSTickCtrStep = 0u;
  58. #endif
  59. OSTickList.TCB_Ptr = (OS_TCB *)0;
  60. #if (OS_CFG_DBG_EN > 0u)
  61. OSTickList.NbrEntries = 0u;
  62. OSTickList.NbrUpdated = 0u;
  63. #endif
  64. }
  65. /*
  66. ************************************************************************************************************************
  67. * TICK UPDATE
  68. *
  69. * Description: This function updates the list of task either delayed pending with timeout.
  70. * The function is internal to uC/OS-III.
  71. *
  72. * Arguments : ticks the number of ticks which have elapsed
  73. * -----
  74. *
  75. * Returns : none
  76. *
  77. * Note(s) : This function is INTERNAL to uC/OS-III and your application should not call it.
  78. ************************************************************************************************************************
  79. */
  80. void OS_TickUpdate (OS_TICK ticks)
  81. {
  82. #if (OS_CFG_TS_EN > 0u)
  83. CPU_TS ts_start;
  84. #endif
  85. CPU_SR_ALLOC();
  86. CPU_CRITICAL_ENTER();
  87. OSTickCtr += ticks; /* Keep track of the number of ticks */
  88. OS_TRACE_TICK_INCREMENT(OSTickCtr);
  89. #if (OS_CFG_TS_EN > 0u)
  90. ts_start = OS_TS_GET();
  91. OS_TickListUpdate(ticks);
  92. OSTickTime = OS_TS_GET() - ts_start;
  93. if (OSTickTimeMax < OSTickTime) {
  94. OSTickTimeMax = OSTickTime;
  95. }
  96. #else
  97. OS_TickListUpdate(ticks);
  98. #endif
  99. #if (OS_CFG_DYN_TICK_EN > 0u)
  100. if (OSTickList.TCB_Ptr != (OS_TCB *)0) {
  101. OSTickCtrStep = OSTickList.TCB_Ptr->TickRemain;
  102. } else {
  103. OSTickCtrStep = 0u;
  104. }
  105. OS_DynTickSet(OSTickCtrStep);
  106. #endif
  107. CPU_CRITICAL_EXIT();
  108. }
  109. /*
  110. ************************************************************************************************************************
  111. * INSERT
  112. *
  113. * Description: This task is internal to uC/OS-III and allows the insertion of a task in a tick list.
  114. *
  115. * Arguments : p_tcb is a pointer to the TCB to insert in the list
  116. *
  117. * elapsed is the number of elapsed ticks since the last tick interrupt
  118. *
  119. * tick_base is value of OSTickCtr from which time is offset
  120. *
  121. * time is the amount of time remaining (in ticks) for the task to become ready
  122. *
  123. * Returns : OS_TRUE if time is valid for the given tick base
  124. *
  125. * OS_FALSE if time is invalid (i.e. zero delay)
  126. *
  127. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application should not call it.
  128. *
  129. * 2) This function supports both Periodic Tick Mode (PTM) and Dynamic Tick Mode (DTM).
  130. *
  131. * 3) PTM should always call this function with elapsed == 0u.
  132. ************************************************************************************************************************
  133. */
  134. CPU_BOOLEAN OS_TickListInsert (OS_TCB *p_tcb,
  135. OS_TICK elapsed,
  136. OS_TICK tick_base,
  137. OS_TICK time)
  138. {
  139. OS_TCB *p_tcb1;
  140. OS_TCB *p_tcb2;
  141. OS_TICK_LIST *p_list;
  142. OS_TICK delta;
  143. OS_TICK remain;
  144. delta = (time + tick_base) - (OSTickCtr + elapsed); /* How many ticks until our delay expires? */
  145. if (delta == 0u) {
  146. p_tcb->TickRemain = 0u;
  147. return (OS_FALSE);
  148. }
  149. OS_TRACE_TASK_DLY(delta);
  150. p_list = &OSTickList;
  151. if (p_list->TCB_Ptr == (OS_TCB *)0) { /* Is the list empty? */
  152. p_tcb->TickRemain = delta; /* Yes, Store time in TCB */
  153. p_tcb->TickNextPtr = (OS_TCB *)0;
  154. p_tcb->TickPrevPtr = (OS_TCB *)0;
  155. p_list->TCB_Ptr = p_tcb; /* Point to TCB of task to place in the list */
  156. #if (OS_CFG_DYN_TICK_EN > 0u)
  157. if (elapsed != 0u) {
  158. OSTickCtr += elapsed; /* Update OSTickCtr before we set a new tick step. */
  159. OS_TRACE_TICK_INCREMENT(OSTickCtr);
  160. }
  161. OSTickCtrStep = delta;
  162. OS_DynTickSet(OSTickCtrStep);
  163. #endif
  164. #if (OS_CFG_DBG_EN > 0u)
  165. p_list->NbrEntries = 1u; /* List contains 1 entry */
  166. #endif
  167. return (OS_TRUE);
  168. }
  169. #if (OS_CFG_DBG_EN > 0u)
  170. p_list->NbrEntries++; /* Update debug counter to reflect the new entry. */
  171. #endif
  172. p_tcb2 = p_list->TCB_Ptr;
  173. remain = p_tcb2->TickRemain - elapsed; /* How many ticks until the head's delay expires? */
  174. if ((delta < remain) && /* If our entry is the new head of the tick list ... */
  175. (p_tcb2->TickPrevPtr == (OS_TCB *)0)) {
  176. p_tcb->TickRemain = delta; /* ... the delta is equivalent to the full delay ... */
  177. p_tcb2->TickRemain = remain - delta; /* ... the previous head's delta is now relative to it. */
  178. p_tcb->TickPrevPtr = (OS_TCB *)0;
  179. p_tcb->TickNextPtr = p_tcb2;
  180. p_tcb2->TickPrevPtr = p_tcb;
  181. p_list->TCB_Ptr = p_tcb;
  182. #if (OS_CFG_DYN_TICK_EN > 0u)
  183. if (elapsed != 0u) {
  184. OSTickCtr += elapsed; /* Update OSTickCtr before we set a new tick step. */
  185. OS_TRACE_TICK_INCREMENT(OSTickCtr);
  186. }
  187. /* In DTM, a new list head must update the tick ... */
  188. OSTickCtrStep = delta; /* ... timer to interrupt at the new delay value. */
  189. OS_DynTickSet(OSTickCtrStep);
  190. #endif
  191. return (OS_TRUE);
  192. }
  193. /* Our entry comes after the current list head. */
  194. delta -= remain; /* Make delta relative to the head. */
  195. p_tcb1 = p_tcb2;
  196. p_tcb2 = p_tcb1->TickNextPtr;
  197. while ((p_tcb2 != (OS_TCB *)0) && /* Find the appropriate position in the delta list. */
  198. (delta >= p_tcb2->TickRemain)) {
  199. delta -= p_tcb2->TickRemain;
  200. p_tcb1 = p_tcb2;
  201. p_tcb2 = p_tcb2->TickNextPtr;
  202. }
  203. if (p_tcb2 != (OS_TCB *)0) { /* Our entry is not the last element in the list. */
  204. p_tcb1 = p_tcb2->TickPrevPtr;
  205. p_tcb->TickRemain = delta; /* Store remaining time */
  206. p_tcb->TickPrevPtr = p_tcb1;
  207. p_tcb->TickNextPtr = p_tcb2;
  208. p_tcb2->TickRemain -= delta; /* Reduce time of next entry in the list */
  209. p_tcb2->TickPrevPtr = p_tcb;
  210. p_tcb1->TickNextPtr = p_tcb;
  211. } else { /* Our entry belongs at the end of the list. */
  212. p_tcb->TickRemain = delta;
  213. p_tcb->TickPrevPtr = p_tcb1;
  214. p_tcb->TickNextPtr = (OS_TCB *)0;
  215. p_tcb1->TickNextPtr = p_tcb;
  216. }
  217. return (OS_TRUE);
  218. }
  219. /*
  220. ************************************************************************************************************************
  221. * ADD DELAYED TASK TO TICK LIST
  222. *
  223. * Description: This function is called to place a task in a list of task waiting for time to expire
  224. *
  225. * Arguments : p_tcb is a pointer to the OS_TCB of the task to add to the tick list
  226. * -----
  227. *
  228. * time represents either the 'match' value of OSTickCtr or a relative time from the current
  229. * system time as specified by the 'opt' argument..
  230. *
  231. * relative when 'opt' is set to OS_OPT_TIME_DLY
  232. * relative when 'opt' is set to OS_OPT_TIME_TIMEOUT
  233. * match when 'opt' is set to OS_OPT_TIME_MATCH
  234. * periodic when 'opt' is set to OS_OPT_TIME_PERIODIC
  235. *
  236. * opt is an option specifying how to calculate time. The valid values are:
  237. * ---
  238. * OS_OPT_TIME_DLY
  239. * OS_OPT_TIME_TIMEOUT
  240. * OS_OPT_TIME_PERIODIC
  241. * OS_OPT_TIME_MATCH
  242. *
  243. * p_err is a pointer to a variable that will contain an error code returned by this function.
  244. * -----
  245. * OS_ERR_NONE the call was successful and the time delay was scheduled.
  246. * OS_ERR_TIME_ZERO_DLY if the effective delay is zero
  247. *
  248. * Returns : None
  249. *
  250. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  251. *
  252. * 2) This function is assumed to be called with interrupts disabled.
  253. ************************************************************************************************************************
  254. */
  255. void OS_TickListInsertDly (OS_TCB *p_tcb,
  256. OS_TICK time,
  257. OS_OPT opt,
  258. OS_ERR *p_err)
  259. {
  260. OS_TICK elapsed;
  261. OS_TICK tick_base;
  262. OS_TICK base_offset;
  263. CPU_BOOLEAN valid_dly;
  264. #if (OS_CFG_DYN_TICK_EN > 0u)
  265. elapsed = OS_DynTickGet();
  266. #else
  267. elapsed = 0u;
  268. #endif
  269. if (opt == OS_OPT_TIME_MATCH) { /* MATCH to absolute tick ctr value mode */
  270. tick_base = 0u; /* tick_base + time == time */
  271. } else if (opt == OS_OPT_TIME_PERIODIC) { /* PERIODIC mode. */
  272. if (time == 0u) {
  273. *p_err = OS_ERR_TIME_ZERO_DLY; /* Infinite frequency is invalid. */
  274. return;
  275. }
  276. tick_base = p_tcb->TickCtrPrev;
  277. #if (OS_CFG_DYN_TICK_EN > 0u) /* How far is our tick-base from the system time? */
  278. base_offset = OSTickCtr + elapsed - tick_base;
  279. #else
  280. base_offset = OSTickCtr - tick_base;
  281. #endif
  282. if (base_offset >= time) { /* If our task missed the last period, move ... */
  283. tick_base += time * (base_offset / time); /* ... tick_base up to the next one. */
  284. if ((base_offset % time) != 0u) {
  285. tick_base += time; /* Account for rounding errors with integer division */
  286. }
  287. p_tcb->TickCtrPrev = tick_base; /* Adjust the periodic tick base */
  288. }
  289. p_tcb->TickCtrPrev += time; /* Update for the next time we perform a periodic dly. */
  290. } else { /* RELATIVE time delay mode */
  291. #if (OS_CFG_DYN_TICK_EN > 0u) /* Our base is always the current system time. */
  292. tick_base = OSTickCtr + elapsed;
  293. #else
  294. tick_base = OSTickCtr;
  295. #endif
  296. }
  297. valid_dly = OS_TickListInsert(p_tcb, elapsed, tick_base, time);
  298. if (valid_dly == OS_TRUE) {
  299. p_tcb->TaskState = OS_TASK_STATE_DLY;
  300. *p_err = OS_ERR_NONE;
  301. } else {
  302. *p_err = OS_ERR_TIME_ZERO_DLY;
  303. }
  304. }
  305. /*
  306. ************************************************************************************************************************
  307. * REMOVE A TASK FROM THE TICK LIST
  308. *
  309. * Description: This function is called to remove a task from the tick list
  310. *
  311. * Arguments : p_tcb Is a pointer to the OS_TCB to remove.
  312. * -----
  313. *
  314. * Returns : none
  315. *
  316. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  317. *
  318. * 2) This function is assumed to be called with interrupts disabled.
  319. ************************************************************************************************************************
  320. */
  321. void OS_TickListRemove (OS_TCB *p_tcb)
  322. {
  323. OS_TCB *p_tcb1;
  324. OS_TCB *p_tcb2;
  325. OS_TICK_LIST *p_list;
  326. #if (OS_CFG_DYN_TICK_EN > 0u)
  327. OS_TICK elapsed;
  328. #endif
  329. #if (OS_CFG_DYN_TICK_EN > 0u)
  330. elapsed = OS_DynTickGet();
  331. #endif
  332. p_tcb1 = p_tcb->TickPrevPtr;
  333. p_tcb2 = p_tcb->TickNextPtr;
  334. p_list = &OSTickList;
  335. if (p_tcb1 == (OS_TCB *)0) {
  336. if (p_tcb2 == (OS_TCB *)0) { /* Remove the ONLY entry in the list? */
  337. p_list->TCB_Ptr = (OS_TCB *)0;
  338. #if (OS_CFG_DBG_EN > 0u)
  339. p_list->NbrEntries = 0u;
  340. #endif
  341. p_tcb->TickRemain = 0u;
  342. #if (OS_CFG_DYN_TICK_EN > 0u)
  343. if (elapsed != 0u) {
  344. OSTickCtr += elapsed; /* Keep track of time. */
  345. OS_TRACE_TICK_INCREMENT(OSTickCtr);
  346. }
  347. OSTickCtrStep = 0u;
  348. OS_DynTickSet(OSTickCtrStep);
  349. #endif
  350. } else {
  351. p_tcb2->TickPrevPtr = (OS_TCB *)0;
  352. p_tcb2->TickRemain += p_tcb->TickRemain; /* Add back the ticks to the delta */
  353. p_list->TCB_Ptr = p_tcb2;
  354. #if (OS_CFG_DBG_EN > 0u)
  355. p_list->NbrEntries--;
  356. #endif
  357. #if (OS_CFG_DYN_TICK_EN > 0u)
  358. if (p_tcb2->TickRemain != p_tcb->TickRemain) { /* Only set a new tick if tcb2 had a longer delay. */
  359. if (elapsed != 0u) {
  360. OSTickCtr += elapsed; /* Keep track of time. */
  361. OS_TRACE_TICK_INCREMENT(OSTickCtr);
  362. p_tcb2->TickRemain -= elapsed; /* We must account for any time which has passed. */
  363. }
  364. OSTickCtrStep = p_tcb2->TickRemain;
  365. OS_DynTickSet(OSTickCtrStep);
  366. }
  367. #endif
  368. p_tcb->TickNextPtr = (OS_TCB *)0;
  369. p_tcb->TickRemain = 0u;
  370. }
  371. } else {
  372. p_tcb1->TickNextPtr = p_tcb2;
  373. if (p_tcb2 != (OS_TCB *)0) {
  374. p_tcb2->TickPrevPtr = p_tcb1;
  375. p_tcb2->TickRemain += p_tcb->TickRemain; /* Add back the ticks to the delta list */
  376. }
  377. p_tcb->TickPrevPtr = (OS_TCB *)0;
  378. #if (OS_CFG_DBG_EN > 0u)
  379. p_list->NbrEntries--;
  380. #endif
  381. p_tcb->TickNextPtr = (OS_TCB *)0;
  382. p_tcb->TickRemain = 0u;
  383. }
  384. }
  385. /*
  386. ************************************************************************************************************************
  387. * UPDATE THE LIST OF TASKS DELAYED OR PENDING WITH TIMEOUT
  388. *
  389. * Description: This function updates the delta list which contains tasks that are delayed or pending with a timeout.
  390. *
  391. * Arguments : ticks the number of ticks which have elapsed.
  392. *
  393. * Returns : none
  394. *
  395. * Note(s) : 1) This function is INTERNAL to uC/OS-III and your application MUST NOT call it.
  396. ************************************************************************************************************************
  397. */
  398. static void OS_TickListUpdate (OS_TICK ticks)
  399. {
  400. OS_TCB *p_tcb;
  401. OS_TICK_LIST *p_list;
  402. #if (OS_CFG_DBG_EN > 0u)
  403. OS_OBJ_QTY nbr_updated;
  404. #endif
  405. #if (OS_CFG_MUTEX_EN > 0u)
  406. OS_TCB *p_tcb_owner;
  407. OS_PRIO prio_new;
  408. #endif
  409. #if (OS_CFG_DBG_EN > 0u)
  410. nbr_updated = 0u;
  411. #endif
  412. p_list = &OSTickList;
  413. p_tcb = p_list->TCB_Ptr;
  414. if (p_tcb != (OS_TCB *)0) {
  415. if (p_tcb->TickRemain <= ticks) {
  416. ticks = ticks - p_tcb->TickRemain;
  417. p_tcb->TickRemain = 0u;
  418. } else {
  419. p_tcb->TickRemain -= ticks;
  420. }
  421. while (p_tcb->TickRemain == 0u) {
  422. #if (OS_CFG_DBG_EN > 0u)
  423. nbr_updated++;
  424. #endif
  425. switch (p_tcb->TaskState) {
  426. case OS_TASK_STATE_DLY:
  427. p_tcb->TaskState = OS_TASK_STATE_RDY;
  428. OS_RdyListInsert(p_tcb); /* Insert the task in the ready list */
  429. break;
  430. case OS_TASK_STATE_DLY_SUSPENDED:
  431. p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
  432. break;
  433. default:
  434. #if (OS_CFG_MUTEX_EN > 0u)
  435. p_tcb_owner = (OS_TCB *)0;
  436. if (p_tcb->PendOn == OS_TASK_PEND_ON_MUTEX) {
  437. p_tcb_owner = (OS_TCB *)((OS_MUTEX *)((void *)p_tcb->PendObjPtr))->OwnerTCBPtr;
  438. }
  439. #endif
  440. #if (OS_MSG_EN > 0u)
  441. p_tcb->MsgPtr = (void *)0;
  442. p_tcb->MsgSize = 0u;
  443. #endif
  444. #if (OS_CFG_TS_EN > 0u)
  445. p_tcb->TS = OS_TS_GET();
  446. #endif
  447. OS_PendListRemove(p_tcb); /* Remove task from pend list */
  448. switch (p_tcb->TaskState) {
  449. case OS_TASK_STATE_PEND_TIMEOUT:
  450. OS_RdyListInsert(p_tcb); /* Insert the task in the ready list */
  451. p_tcb->TaskState = OS_TASK_STATE_RDY;
  452. break;
  453. case OS_TASK_STATE_PEND_TIMEOUT_SUSPENDED:
  454. p_tcb->TaskState = OS_TASK_STATE_SUSPENDED;
  455. break;
  456. default:
  457. break;
  458. }
  459. p_tcb->PendStatus = OS_STATUS_PEND_TIMEOUT; /* Indicate pend timed out */
  460. p_tcb->PendOn = OS_TASK_PEND_ON_NOTHING; /* Indicate no longer pending */
  461. #if (OS_CFG_MUTEX_EN > 0u)
  462. if (p_tcb_owner != (OS_TCB *)0) {
  463. if ((p_tcb_owner->Prio != p_tcb_owner->BasePrio) &&
  464. (p_tcb_owner->Prio == p_tcb->Prio)) { /* Has the owner inherited a priority? */
  465. prio_new = OS_MutexGrpPrioFindHighest(p_tcb_owner);
  466. prio_new = (prio_new > p_tcb_owner->BasePrio) ? p_tcb_owner->BasePrio : prio_new;
  467. if (prio_new != p_tcb_owner->Prio) {
  468. OS_TaskChangePrio(p_tcb_owner, prio_new);
  469. OS_TRACE_MUTEX_TASK_PRIO_DISINHERIT(p_tcb_owner, p_tcb_owner->Prio);
  470. }
  471. }
  472. }
  473. #endif
  474. break;
  475. }
  476. p_list->TCB_Ptr = p_tcb->TickNextPtr;
  477. p_tcb = p_list->TCB_Ptr; /* Get 'p_tcb' again for loop */
  478. if (p_tcb == (OS_TCB *)0) {
  479. #if (OS_CFG_DBG_EN > 0u)
  480. p_list->NbrEntries = 0u;
  481. #endif
  482. break;
  483. } else {
  484. #if (OS_CFG_DBG_EN > 0u)
  485. p_list->NbrEntries--;
  486. #endif
  487. p_tcb->TickPrevPtr = (OS_TCB *)0;
  488. if (p_tcb->TickRemain <= ticks) {
  489. ticks = ticks - p_tcb->TickRemain;
  490. p_tcb->TickRemain = 0u;
  491. } else {
  492. p_tcb->TickRemain -= ticks;
  493. }
  494. }
  495. }
  496. }
  497. #if (OS_CFG_DBG_EN > 0u)
  498. p_list->NbrUpdated = nbr_updated;
  499. #endif
  500. }
  501. #endif /* #if OS_CFG_TICK_EN */