QP/C  7.0.0
Real-Time Embedded Framework
qf_time.c
Go to the documentation of this file.
1/*============================================================================
2* QP/C Real-Time Embedded Framework (RTEF)
3* Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
4*
5* SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
6*
7* This software is dual-licensed under the terms of the open source GNU
8* General Public License version 3 (or any later version), or alternatively,
9* under the terms of one of the closed source Quantum Leaps commercial
10* licenses.
11*
12* The terms of the open source GNU General Public License version 3
13* can be found at: <www.gnu.org/licenses/gpl-3.0>
14*
15* The terms of the closed source Quantum Leaps commercial licenses
16* can be found at: <www.state-machine.com/licensing>
17*
18* Redistributions in source code must retain this top-level comment block.
19* Plagiarizing this software to sidestep the license obligations is illegal.
20*
21* Contact information:
22* <www.state-machine.com>
23* <info@state-machine.com>
24============================================================================*/
32#define QP_IMPL /* this is QP implementation */
33#include "qf_port.h" /* QF port */
34#include "qf_pkg.h" /* QF package-scope interface */
35#include "qassert.h" /* QP embedded systems-friendly assertions */
36#ifdef Q_SPY /* QS software tracing enabled? */
37 #include "qs_port.h" /* QS port */
38 #include "qs_pkg.h" /* QS facilities for pre-defined trace records */
39#else
40 #include "qs_dummy.h" /* disable the QS software tracing */
41#endif /* Q_SPY */
42
43Q_DEFINE_THIS_MODULE("qf_time")
44
45/* Package-scope objects ****************************************************/
47QTimeEvt QF_timeEvtHead_[QF_MAX_TICK_RATE]; /* heads of time event lists */
48
49#ifdef Q_SPY
56 #define QACTIVE_CAST_(ptr_) ((QActive *)(ptr_))
57#endif
58
59/*==========================================================================*/
60#ifdef Q_SPY
86void QF_tickX_(uint_fast8_t const tickRate, void const * const sender)
87#else
88void QF_tickX_(uint_fast8_t const tickRate)
89#endif
90{
91 QTimeEvt *prev = &QF_timeEvtHead_[tickRate];
92
94 QF_CRIT_E_();
95
97 ++prev->ctr;
98 QS_TEC_PRE_(prev->ctr); /* tick ctr */
99 QS_U8_PRE_(tickRate); /* tick rate */
101
102 /* scan the linked-list of time events at this rate... */
103 for (;;) {
104 QTimeEvt *t = prev->next; /* advance down the time evt. list */
105
106 /* end of the list? */
107 if (t == (QTimeEvt *)0) {
108
109 /* any new time events armed since the last run of QF_tickX_()? */
110 if (QF_timeEvtHead_[tickRate].act != (void *)0) {
111
112 /* sanity check */
113 Q_ASSERT_CRIT_(110, prev != (QTimeEvt *)0);
114 prev->next = (QTimeEvt *)QF_timeEvtHead_[tickRate].act;
115 QF_timeEvtHead_[tickRate].act = (void *)0;
116 t = prev->next; /* switch to the new list */
117 }
118 else {
119 break; /* all currently armed time evts. processed */
120 }
121 }
122
123 /* time event scheduled for removal? */
124 if (t->ctr == 0U) {
125 prev->next = t->next;
126 /* mark time event 't' as NOT linked */
127 t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU);
128 /* do NOT advance the prev pointer */
129 QF_CRIT_X_(); /* exit crit. section to reduce latency */
130
131 /* prevent merging critical sections, see NOTE1 below */
133 }
134 else {
135 --t->ctr;
136
137 /* is time event about to expire? */
138 if (t->ctr == 0U) {
139 /* temporary for volatile */
140 QActive * const act = (QActive *)t->act;
141
142 /* periodic time evt? */
143 if (t->interval != 0U) {
144 t->ctr = t->interval; /* rearm the time event */
145 prev = t; /* advance to this time event */
146 }
147 /* one-shot time event: automatically disarm */
148 else {
149 prev->next = t->next;
150 /* mark time event 't' as NOT linked */
151 t->super.refCtr_ &= (uint8_t)(~QTE_IS_LINKED & 0xFFU);
152 /* do NOT advance the prev pointer */
153
155 QS_OBJ_PRE_(t); /* this time event object */
156 QS_OBJ_PRE_(act); /* the target AO */
157 QS_U8_PRE_(tickRate); /* tick rate */
159 }
160
162 QS_TIME_PRE_(); /* timestamp */
163 QS_OBJ_PRE_(t); /* the time event object */
164 QS_SIG_PRE_(t->super.sig); /* signal of this time event */
165 QS_OBJ_PRE_(act); /* the target AO */
166 QS_U8_PRE_(tickRate); /* tick rate */
168
169 QF_CRIT_X_(); /* exit critical section before posting */
170
171 /* QACTIVE_POST() asserts internally if the queue overflows */
172 QACTIVE_POST(act, &t->super, sender);
173 }
174 else {
175 prev = t; /* advance to this time event */
176 QF_CRIT_X_(); /* exit crit. section to reduce latency */
177
178 /* prevent merging critical sections, see NOTE1 below */
180 }
181 }
182 QF_CRIT_E_(); /* re-enter crit. section to continue */
183 }
184 QF_CRIT_X_();
185}
186
187/*==========================================================================*/
202bool QF_noTimeEvtsActiveX(uint_fast8_t const tickRate) {
203
205 Q_REQUIRE_ID(200, tickRate < QF_MAX_TICK_RATE);
206
207 bool inactive;
208 if (QF_timeEvtHead_[tickRate].next != (QTimeEvt *)0) {
209 inactive = false;
210 }
211 else if ((QF_timeEvtHead_[tickRate].act != (void *)0)) {
212 inactive = false;
213 }
214 else {
215 inactive = true;
216 }
217 return inactive;
218}
219
220/*==========================================================================*/
239void QTimeEvt_ctorX(QTimeEvt * const me, QActive * const act,
240 enum_t const sig, uint_fast8_t tickRate)
241{
243 Q_REQUIRE_ID(300, (sig >= (enum_t)Q_USER_SIG)
244 && (tickRate < QF_MAX_TICK_RATE));
245
246 me->next = (QTimeEvt *)0;
247 me->ctr = 0U;
248 me->interval = 0U;
249 me->super.sig = (QSignal)sig;
250
251 /* For backwards compatibility with QTimeEvt_ctor(), the active object
252 * pointer can be uninitialized (NULL) and is NOT validated in the
253 * precondition. The active object pointer is validated in preconditions
254 * to QTimeEvt_arm_() and QTimeEvt_rearm().
255 */
256 me->act = act;
257
258 /* Setting the POOL_ID event attribute to zero is correct only for
259 * events not allocated from event pools, which must be the case
260 * for Time Events.
261 */
262 me->super.poolId_ = 0U;
263
264 /* The refCtr_ attribute is not used in time events, so it is
265 * reused to hold the tickRate as well as other information
266 */
267 me->super.refCtr_ = (uint8_t)tickRate;
268}
269
270/*==========================================================================*/
301void QTimeEvt_armX(QTimeEvt * const me,
302 QTimeEvtCtr const nTicks, QTimeEvtCtr const interval)
303{
304 uint_fast8_t const tickRate
305 = ((uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE);
306 QTimeEvtCtr const ctr = me->ctr;
307#ifdef Q_SPY
308 uint_fast8_t const qs_id = ((QActive *)(me->act))->prio;
309#endif
310
314 Q_REQUIRE_ID(400, (me->act != (void *)0)
315 && (ctr == 0U)
316 && (nTicks != 0U)
317 && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE)
318 && (me->super.sig >= (QSignal)Q_USER_SIG));
319#ifdef Q_NASSERT
320 (void)ctr; /* avoid compiler warning about unused variable */
321#endif
322
324 QF_CRIT_E_();
325 me->ctr = nTicks;
326 me->interval = interval;
327
328 /* is the time event unlinked?
329 * NOTE: For the duration of a single clock tick of the specified tick
330 * rate a time event can be disarmed and yet still linked into the list,
331 * because un-linking is performed exclusively in the QF_tickX() function.
332 */
333 if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) {
334 me->super.refCtr_ |= QTE_IS_LINKED; /* mark as linked */
335
336 /* The time event is initially inserted into the separate
337 * "freshly armed" link list based on QF_timeEvtHead_[tickRate].act.
338 * Only later, inside the QF_tickX() function, the "freshly armed"
339 * list is appended to the main list of armed time events based on
340 * QF_timeEvtHead_[tickRate].next. Again, this is to keep any
341 * changes to the main list exclusively inside the QF_tickX()
342 * function.
343 */
344 me->next = (QTimeEvt *)QF_timeEvtHead_[tickRate].act;
345 QF_timeEvtHead_[tickRate].act = me;
346 }
347
349 QS_TIME_PRE_(); /* timestamp */
350 QS_OBJ_PRE_(me); /* this time event object */
351 QS_OBJ_PRE_(me->act); /* the active object */
352 QS_TEC_PRE_(nTicks); /* the number of ticks */
353 QS_TEC_PRE_(interval); /* the interval */
354 QS_U8_PRE_(tickRate); /* tick rate */
356
357 QF_CRIT_X_();
358}
359
360/*==========================================================================*/
380bool QTimeEvt_disarm(QTimeEvt * const me) {
381#ifdef Q_SPY
382 uint_fast8_t const qs_id = QACTIVE_CAST_(me->act)->prio;
383#endif
384
386 QF_CRIT_E_();
387
388 /* is the time event actually armed? */
389 bool wasArmed;
390 if (me->ctr != 0U) {
391 wasArmed = true;
393
395 QS_TIME_PRE_(); /* timestamp */
396 QS_OBJ_PRE_(me); /* this time event object */
397 QS_OBJ_PRE_(me->act); /* the target AO */
398 QS_TEC_PRE_(me->ctr); /* the number of ticks */
399 QS_TEC_PRE_(me->interval); /* the interval */
402
403 me->ctr = 0U; /* schedule removal from the list */
404 }
405 else { /* the time event was already disarmed automatically */
406 wasArmed = false;
407 me->super.refCtr_ &= (uint8_t)(~QTE_WAS_DISARMED & 0xFFU);
408
410 QS_TIME_PRE_(); /* timestamp */
411 QS_OBJ_PRE_(me); /* this time event object */
412 QS_OBJ_PRE_(me->act); /* the target AO */
415
416 }
417 QF_CRIT_X_();
418
419 return wasArmed;
420}
421
422/*==========================================================================*/
444bool QTimeEvt_rearm(QTimeEvt * const me, QTimeEvtCtr const nTicks) {
445 uint_fast8_t const tickRate
446 = (uint_fast8_t)me->super.refCtr_ & QTE_TICK_RATE;
447#ifdef Q_SPY
448 uint_fast8_t const qs_id = ((QActive *)(me->act))->prio;
449#endif
450
454 Q_REQUIRE_ID(600, (me->act != (void *)0)
455 && (tickRate < QF_MAX_TICK_RATE)
456 && (nTicks != 0U)
457 && (me->super.sig >= (QSignal)Q_USER_SIG));
458
460 QF_CRIT_E_();
461
462 /* is the time evt not running? */
463 bool wasArmed;
464 if (me->ctr == 0U) {
465 wasArmed = false;
466
467 /* NOTE: For the duration of a single clock tick of the specified
468 * tick rate a time event can be disarmed and yet still linked into
469 * the list, because unlinking is performed exclusively in the
470 * QF_tickX() function.
471 */
472 /* is the time event linked yet? */
473 if ((me->super.refCtr_ & QTE_IS_LINKED) == 0U) {
474 me->super.refCtr_ |= QTE_IS_LINKED; /* mark as linked */
475
476 /* The time event is initially inserted into the separate
477 * "freshly armed" list based on QF_timeEvtHead_[tickRate].act.
478 * Only later, inside the QF_tickX() function, the "freshly armed"
479 * list is appended to the main list of armed time events based on
480 * QF_timeEvtHead_[tickRate].next. Again, this is to keep any
481 * changes to the main list exclusively inside the QF_tickX()
482 * function.
483 */
484 me->next = (QTimeEvt *)QF_timeEvtHead_[tickRate].act;
485 QF_timeEvtHead_[tickRate].act = me;
486 }
487 }
488 else { /* the time event was armed */
489 wasArmed = true;
490 }
491 me->ctr = nTicks; /* re-load the tick counter (shift the phasing) */
492
494 QS_TIME_PRE_(); /* timestamp */
495 QS_OBJ_PRE_(me); /* this time event object */
496 QS_OBJ_PRE_(me->act); /* the target AO */
497 QS_TEC_PRE_(me->ctr); /* the number of ticks */
498 QS_TEC_PRE_(me->interval); /* the interval */
499 QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U));
501
502 QF_CRIT_X_();
503
504 return wasArmed;
505}
506
507/*==========================================================================*/
531 uint8_t const wasDisarmed = (me->super.refCtr_ & QTE_WAS_DISARMED);
532 me->super.refCtr_ |= QTE_WAS_DISARMED; /* mark as disarmed */
533 return wasDisarmed != 0U;
534}
535
536/*==========================================================================*/
555 QF_CRIT_E_();
556 QTimeEvtCtr const ret = me->ctr;
557 QF_CRIT_X_();
558
559 return ret;
560}
561
562/*============================================================================
563* NOTE1:
564* In some QF ports the critical section exit takes effect only on the next
565* machine instruction. If this case, the next instruction is another entry
566* to a critical section, the critical section won't be really exited, but
567* rather the two adjacent critical sections would be merged.
568*
569* The QF_CRIT_EXIT_NOP() macro contains minimal code required
570* to prevent such merging of critical sections in QF ports,
571* in which it can occur.
572*/
#define Q_SPY
When defined, Q_SPY activates the QS software tracing instrumentation.
Definition: macros.h:27
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:102
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:252
@ Q_USER_SIG
Definition: qep.h:691
uint16_t QSignal
Definition: qep.h:97
signed int enum_t
Definition: qep.h:60
uint8_t QTimeEvtCtr
Definition: qf.h:397
#define QACTIVE_POST(me_, e_, sender_)
Definition: qf.h:238
#define QF_CRIT_EXIT_NOP()
Definition: qf.h:871
Internal (package scope) QF/C interface.
#define QF_CRIT_STAT_
Definition: qf_pkg.h:54
#define QTE_WAS_DISARMED
Definition: qf_pkg.h:158
#define Q_ASSERT_CRIT_(id_, test_)
Definition: qf_pkg.h:97
#define QTE_IS_LINKED
Definition: qf_pkg.h:157
#define QF_CRIT_X_()
Definition: qf_pkg.h:78
#define QTE_TICK_RATE
Definition: qf_pkg.h:159
#define QF_CRIT_E_()
Definition: qf_pkg.h:66
#define QACTIVE_CAST_(ptr_)
Definition: qf_time.c:56
#define QS_TIME_PRE_()
Definition: qs.h:220
@ QS_QF_TIMEEVT_AUTO_DISARM
Definition: qs.h:102
@ QS_QF_TIMEEVT_DISARM
Definition: qs.h:104
@ QS_QF_TIMEEVT_REARM
Definition: qs.h:105
@ QS_QF_TIMEEVT_POST
Definition: qs.h:106
@ QS_QF_TIMEEVT_DISARM_ATTEMPT
Definition: qs.h:103
@ QS_QF_TICK
Definition: qs.h:98
@ QS_QF_TIMEEVT_ARM
Definition: qs.h:101
Internal (package scope) QS/C interface.
#define QS_U8_PRE_(data_)
Definition: qs_pkg.h:151
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Definition: qs_pkg.h:138
#define QS_OBJ_PRE_(obj_)
Definition: qs_pkg.h:179
#define QS_TEC_PRE_(ctr_)
Definition: qs_pkg.h:264
#define QS_END_NOCRIT_PRE_()
Definition: qs_pkg.h:148
#define QS_2U8_PRE_(data1_, data2_)
Definition: qs_pkg.h:154
#define QS_SIG_PRE_(sig_)
Definition: qs_pkg.h:172
#define QF_MAX_TICK_RATE
Definition: qf.h:107
uint8_t prio
Definition: qf.h:152
QSignal sig
Definition: qep.h:123
uint8_t poolId_
Definition: qep.h:128
uint8_t volatile refCtr_
Definition: qep.h:133
bool QF_noTimeEvtsActiveX(uint_fast8_t const tickRate)
Definition: qf_time.c:202
void QF_tickX_(uint_fast8_t const tickRate, void const *const sender)
Definition: qf_time.c:86
QTimeEvt QF_timeEvtHead_[QF_MAX_TICK_RATE]
Definition: qf_time.c:47
Definition: qf.h:456
struct QTimeEvt *volatile next
Definition: qf.h:460
void *volatile act
Definition: qf.h:463
QTimeEvtCtr volatile ctr
Definition: qf.h:471
bool QTimeEvt_rearm(QTimeEvt *const me, QTimeEvtCtr const nTicks)
Definition: qf_time.c:444
QTimeEvtCtr interval
Definition: qf.h:480
QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const *const me)
Definition: qf_time.c:553
void QTimeEvt_ctorX(QTimeEvt *const me, QActive *const act, enum_t const sig, uint_fast8_t tickRate)
Definition: qf_time.c:239
bool QTimeEvt_wasDisarmed(QTimeEvt *const me)
Definition: qf_time.c:530
void QTimeEvt_armX(QTimeEvt *const me, QTimeEvtCtr const nTicks, QTimeEvtCtr const interval)
Definition: qf_time.c:301
bool QTimeEvt_disarm(QTimeEvt *const me)
Definition: qf_time.c:380
QEvt super
Definition: qf.h:457