QP/C 8.1.2
Real-Time Event Framework
Loading...
Searching...
No Matches
qf_time.c
Go to the documentation of this file.
1//============================================================================
2// QP/C Real-Time Event Framework (RTEF)
3//
4// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
5//
6// Q u a n t u m L e a P s
7// ------------------------
8// Modern Embedded Software
9//
10// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
11//
12// This software is dual-licensed under the terms of the open-source GNU
13// General Public License (GPL) or under the terms of one of the closed-
14// source Quantum Leaps commercial licenses.
15//
16// Redistributions in source code must retain this top-level comment block.
17// Plagiarizing this software to sidestep the license obligations is illegal.
18//
19// NOTE:
20// The GPL does NOT permit the incorporation of this code into proprietary
21// programs. Please contact Quantum Leaps for commercial licensing options,
22// which expressly supersede the GPL and are designed explicitly for
23// closed-source distribution.
24//
25// Quantum Leaps contact information:
26// <www.state-machine.com/licensing>
27// <info@state-machine.com>
28//============================================================================
29#define QP_IMPL // this is QP implementation
30#include "qp_port.h" // QP port
31#include "qp_pkg.h" // QP package-scope interface
32#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
33#ifdef Q_SPY // QS software tracing enabled?
34 #include "qs_port.h" // QS port
35 #include "qs_pkg.h" // QS facilities for pre-defined trace records
36#else
37 #include "qs_dummy.h" // disable the QS software tracing
38#endif // Q_SPY
39
40//============================================================================
41#if (QF_MAX_TICK_RATE > 0U)
42
43Q_DEFINE_THIS_MODULE("qf_time")
44
45//............................................................................
47
48//............................................................................
49//! @public @memberof QTimeEvt
50void QTimeEvt_ctorX(QTimeEvt * const me,
51 QActive * const act,
52 enum_t const sig,
53 uint_fast8_t const tickRate)
54{
57
58 // the signal must be != 0, but other reserved signals are allowed
59 Q_REQUIRE_INCRIT(300, sig != 0);
60
61 // the tick rate must be in the configured range
64
65 QEvt_ctor(&me->super, sig); // the superclass' ctor
66
67 me->super.refCtr_ = 0U; // adjust from QEvt_ctor(sig)
68 me->next = (QTimeEvt *)0;
69 me->act = act; // might be NULL for a time-event head
70 me->ctr = 0U;
71 me->interval = 0U;
72 me->tickRate = (uint8_t)tickRate;
73 me->flags = 0U;
74}
75
76//............................................................................
77//! @public @memberof QTimeEvt
78void QTimeEvt_armX(QTimeEvt * const me,
79 uint32_t const nTicks,
80 uint32_t const interval)
81{
84
85 // nTicks and interval parameters must fit in the configured dynamic range
86#if (QF_TIMEEVT_CTR_SIZE == 1U)
87 Q_REQUIRE_INCRIT(400, nTicks < 0xFFU);
88 Q_REQUIRE_INCRIT(410, interval < 0xFFU);
89#elif (QF_TIMEEVT_CTR_SIZE == 2U)
90 Q_REQUIRE_INCRIT(400, nTicks < 0xFFFFU);
91 Q_REQUIRE_INCRIT(410, interval < 0xFFFFU);
92#endif
93
94#ifndef Q_UNSAFE
95 QTimeEvtCtr const ctr = me->ctr;
96#endif
97
98 uint8_t const tickRate = me->tickRate;
99
100 // nTicks must be != 0 for arming a time event
101 Q_REQUIRE_INCRIT(440, nTicks != 0U);
102
103 // the time event must not be already armed
104 Q_REQUIRE_INCRIT(450, ctr == 0U);
105
106 // the AO associated with this time event must be valid
107 Q_REQUIRE_INCRIT(460, me->act != (void *)0);
108
109 // the tick rate of this time event must be in range
111
112 me->ctr = (QTimeEvtCtr)nTicks;
114
115 // is the time event unlinked?
116 // NOTE: For the duration of a single clock tick of the specified tick
117 // rate a time event can be disarmed and yet still linked into the list
118 // because un-linking is performed exclusively in QTimeEvt_tick_().
119 if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) {
120 // The time event is initially inserted into the separate
121 // "freshly armed" list based on timeEvtHead_[tickRate].act.
122 // Only later, inside QTimeEvt_tick_(), the "freshly armed"
123 // list is appended to the main list of armed time events based on
124 // timeEvtHead_[tickRate].next. Again, this is to keep any
125 // changes to the main list exclusively inside QTimeEvt_tick_().
126
127 me->flags |= QTE_FLAG_IS_LINKED; // mark as linked
130 }
131
132 QS_BEGIN_PRE(QS_QF_TIMEEVT_ARM, ((QActive *)(me->act))->prio)
133 QS_TIME_PRE(); // timestamp
134 QS_OBJ_PRE(me); // this time event object
135 QS_OBJ_PRE(me->act); // the active object
136 QS_TEC_PRE(nTicks); // the # ticks
137 QS_TEC_PRE(interval); // the interval
138 QS_U8_PRE(tickRate); // tick rate
139 QS_END_PRE()
140
141 QF_CRIT_EXIT();
142}
143
144//............................................................................
145//! @public @memberof QTimeEvt
146bool QTimeEvt_disarm(QTimeEvt * const me) {
149
150 QTimeEvtCtr const ctr = me->ctr; // move member into temporary
151
152#ifdef Q_SPY
153 uint_fast8_t const qsId = QACTIVE_CAST_(me->act)->prio;
154#endif
155
156 // was the time event actually armed?
157 bool wasArmed = false;
158 if (ctr != 0U) {
159 wasArmed = true;
161 me->ctr = 0U; // schedule removal from the list
162
164 QS_TIME_PRE(); // timestamp
165 QS_OBJ_PRE(me); // this time event object
166 QS_OBJ_PRE(me->act); // the target AO
167 QS_TEC_PRE(ctr); // the # ticks
168 QS_TEC_PRE(me->interval); // the interval
169 QS_U8_PRE(me->tickRate); // tick rate
170 QS_END_PRE()
171 }
172 else { // the time event was already disarmed automatically
173 me->flags &= (uint8_t)(~QTE_FLAG_WAS_DISARMED & 0xFFU);
174
176 QS_TIME_PRE(); // timestamp
177 QS_OBJ_PRE(me); // this time event object
178 QS_OBJ_PRE(me->act); // the target AO
179 QS_U8_PRE(me->tickRate); // tick rate
180 QS_END_PRE()
181 }
182
183 QF_CRIT_EXIT();
184
185 return wasArmed;
186}
187
188//............................................................................
189//! @public @memberof QTimeEvt
190bool QTimeEvt_rearm(QTimeEvt * const me, uint32_t const nTicks) {
193
194 // nTicks parameter must fit in the configured dynamic range
195#if (QF_TIMEEVT_CTR_SIZE == 1U)
196 Q_REQUIRE_INCRIT(600, nTicks < 0xFFU);
197#elif (QF_TIMEEVT_CTR_SIZE == 2U)
198 Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU);
199#endif
200
201 uint8_t const tickRate = me->tickRate;
202 QTimeEvtCtr const ctr = me->ctr;
203
204 // nTicks must be != 0 for arming a time event
205 Q_REQUIRE_INCRIT(610, nTicks != 0U);
206
207 // the AO associated with this time event must be valid
208 Q_REQUIRE_INCRIT(620, me->act != (void *)0);
209
210 // the tick rate of this time event must be in range
212
213#ifdef Q_SPY
214 uint_fast8_t const qsId = ((QActive *)(me->act))->prio;
215#endif
216
217 me->ctr = (QTimeEvtCtr)nTicks;
218
219 // was the time evt not running?
220 bool wasArmed = false;
221 if (ctr == 0U) {
222 // NOTE: For the duration of a single clock tick of the specified
223 // tick rate a time event can be disarmed and yet still linked into
224 // the list, because unlinking is performed exclusively in the
225 // QTimeEvt_tick_() function.
226
227 // is the time event unlinked?
228 if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) {
229
230 // The time event is initially inserted into the separate
231 // "freshly armed" list based on timeEvtHead_[tickRate].act.
232 // Only later, inside QTimeEvt_tick_(), the "freshly armed"
233 // list is appended to the main list of armed time events based on
234 // timeEvtHead_[tickRate].next. Again, this is to keep any
235 // changes to the main list exclusively inside QTimeEvt_tick()_.
236
237 me->flags |= QTE_FLAG_IS_LINKED; // mark as linked
240 }
241 }
242 else { // the time event was armed
243 wasArmed = true;
244 }
245
247 QS_TIME_PRE(); // timestamp
248 QS_OBJ_PRE(me); // this time event object
249 QS_OBJ_PRE(me->act); // the target AO
250 QS_TEC_PRE(nTicks); // the # ticks
251 QS_TEC_PRE(me->interval); // the interval
252 QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U));
253 QS_END_PRE()
254
255 QF_CRIT_EXIT();
256
257 return wasArmed;
258}
259
260//............................................................................
261//! @public @memberof QTimeEvt
265
266 // was this time-event disarmed automatically upon expiration?
267 bool const wasDisarmed = (me->flags & QTE_FLAG_WAS_DISARMED) != 0U;
268
269 me->flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed (SIDE EFFECT!)
270
271 QF_CRIT_EXIT();
272
273 return wasDisarmed;
274}
275
276//............................................................................
277//! @static @private @memberof QTimeEvt
279 uint_fast8_t const tickRate,
280 void const * const sender)
281{
282#ifndef Q_SPY
283 Q_UNUSED_PAR(sender);
284#endif
285
288
289 // the tick rate of this time event must be in range
291
293
294#ifdef Q_SPY
296 ++prev->ctr;
297 QS_TEC_PRE(prev->ctr); // tick ctr
298 QS_U8_PRE(tickRate); // tick rate
299 QS_END_PRE()
300#endif
301
302 // scan the linked-list of time events at this tick rate...
303 for (;;) {
304 QTimeEvt *te = prev->next; // advance down the time evt. list
305
306 if (te == (QTimeEvt *)0) { // end of the list?
307 // set 'te' to the the newly-armed linked list
309 if (te == (void *)0) { // no newly-armed time events?
310 break; // terminate the loop
311 }
312
313 prev->next = te;
314 QTimeEvt_timeEvtHead_[tickRate].act = (void *)0;
315 }
316
317 QTimeEvtCtr ctr = te->ctr; // move member into temporary
318
319 if (ctr == 0U) { // time event scheduled for removal?
320 prev->next = te->next;
321
322 // mark time event 'te' as NOT linked
323 te->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU);
324 // do NOT advance the prev pointer
325 QF_CRIT_EXIT(); // exit crit. section to reduce latency
326 }
327 else if (ctr == 1U) { // is time event about to expire?
328 QActive * const act = (QActive *)te->act;
329 prev = QTimeEvt_expire_(te, prev, act, tickRate);
330
331#ifdef QXK_H_
332 if ((enum_t)te->super.sig < Q_USER_SIG) {
333 QXThread_timeout_(act);
334 QF_CRIT_EXIT();
335 }
336 else {
337 QF_CRIT_EXIT(); // exit crit. section before posting
338
339 // QACTIVE_POST() asserts if the queue overflows
340 QACTIVE_POST(act, &te->super, sender);
341 }
342#else // not QXK
343 QF_CRIT_EXIT(); // exit crit. section before posting
344
345 // QACTIVE_POST() asserts if the queue overflows
346 QACTIVE_POST(act, &te->super, sender);
347#endif
348 }
349 else { // time event keeps timing out
350 --ctr; // decrement the tick counter
351 te->ctr = ctr; // update the member original
352
353 prev = te; // advance to this time event
354 QF_CRIT_EXIT(); // exit crit. section to reduce latency
355 }
356 QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop
357 }
358 QF_CRIT_EXIT();
359}
360
361//............................................................................
362//! @static @public @memberof QTimeEvt
363bool QTimeEvt_noActive(uint_fast8_t const tickRate) {
364 // NOTE: this function must be called *inside* critical section
366
367 QTimeEvt const * const head = &QTimeEvt_timeEvtHead_[tickRate];
368 bool const noActive =
369 (head->next == (QTimeEvt *)0) && (head->act == (void *)0);
370
371 return noActive;
372}
373
374//............................................................................
375//! @public @memberof QTimeEvt
377 // NOTE: this function does NOT apply critical section, so it can
378 // be safely called from an already established critical section.
379 return me->ctr;
380}
381
382//............................................................................
383//! @static @private @memberof QTimeEvt
384void QTimeEvt_init(void) {
385 // call ctors for time event heads for all configured tick rates
386 for (uint_fast8_t tickRate = 0U;
388 ++tickRate)
389 {
390 // time event head has invalid AO and Q_USER_SIG as signal
393 }
394}
395
396//............................................................................
397//! @private @memberof QTimeEvt
399 QTimeEvt * const prev_link,
400 QActive const * const act,
401 uint_fast8_t const tickRate)
402{
403 // NOTE: this helper function is called *inside* critical section
404#ifndef Q_SPY
407#endif
408
409 QTimeEvt *prev = prev_link;
410 if (me->interval != 0U) { // periodic time evt?
411 me->ctr = me->interval; // rearm the time event
412 prev = me; // advance to this time event
413 }
414 else { // one-shot time event: automatically disarm
415 me->ctr = 0U;
416 prev->next = me->next;
417
418 // mark this time event as NOT linked
419 me->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU);
420 // do NOT advance the prev pointer
421
423 QS_OBJ_PRE(me); // this time event object
424 QS_OBJ_PRE(act); // the target AO
425 QS_U8_PRE(tickRate); // tick rate
426 QS_END_PRE()
427 }
428
430 QS_TIME_PRE(); // timestamp
431 QS_OBJ_PRE(me); // the time event object
432 QS_SIG_PRE(me->super.sig);// signal of this time event
433 QS_OBJ_PRE(act); // the target AO
434 QS_U8_PRE(tickRate); // tick rate
435 QS_END_PRE()
436
437 return prev;
438}
439
440#else // (QF_MAX_TICK_RATE > 0U)
441
442//............................................................................
443void QTimeEvt_init(void) { // dummy init
444}
445
446#endif // (QF_MAX_TICK_RATE > 0U)
QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE]
Definition qf_time.c:46
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.h:90
#define Q_USER_SIG
Offset for the user signals (QP/C Application).
Definition qp.h:170
uint32_t QTimeEvtCtr
Data type to store the block-size defined based on the macro QF_TIMEEVT_CTR_SIZE.
Definition qp.h:390
#define QACTIVE_POST(me_, e_, sender_)
Invoke the direct event posting facility QActive_post_().
Definition qp.h:760
int enum_t
Definition qp.h:88
#define Q_DIM(array_)
Helper macro to calculate static dimension of a 1-dim array.
Definition qp.h:91
#define QF_MAX_TICK_RATE
Maximum # clock tick rates in the system (0..15).
Definition qp_config.h:120
QP/C Framework in C internal (package-scope) interface.
#define QACTIVE_CAST_(ptr_)
Internal helper macro to encapsulate MISRA deviation.
Definition qp_pkg.h:35
#define QTE_FLAG_WAS_DISARMED
Definition qp_pkg.h:53
#define QTE_FLAG_IS_LINKED
Definition qp_pkg.h:52
Sample QP/C port.
#define QS_TIME_PRE()
Definition qs.h:340
@ QS_QF_TIMEEVT_AUTO_DISARM
a time event expired and was disarmed
Definition qs.h:99
@ QS_QF_TIMEEVT_DISARM
true disarming of an armed time event
Definition qs.h:101
@ QS_QF_TIMEEVT_REARM
rearming of a time event
Definition qs.h:102
@ QS_QF_TIMEEVT_POST
a time event posted itself directly to an AO
Definition qs.h:103
@ QS_QF_TIMEEVT_DISARM_ATTEMPT
attempt to disarm a disarmed QTimeEvt
Definition qs.h:100
@ QS_QF_TICK
QTimeEvt tick was called.
Definition qs.h:95
@ QS_QF_TIMEEVT_ARM
a time event was armed
Definition qs.h:98
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_OBJ_PRE(obj_)
Output pre-formatted object pointer element.
Definition qs_pkg.h:46
#define QS_TEC_PRE(ctr_)
Output pre-formatted time event counter data element.
Definition qs_pkg.h:97
#define QS_SIG_PRE(sig_)
Output pre-formatted event signal data element.
Definition qs_pkg.h:47
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.h:41
#define QS_U8_PRE(data_)
Output pre-formatted unsigned 8-bit integer data element.
Definition qs_pkg.h:40
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.h:38
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.h:32
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#define QF_CRIT_EXIT()
Definition qsafe.h:44
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section).
Definition qsafe.h:98
#define QF_CRIT_STAT
Definition qsafe.h:36
Active object class (based on the QHsm implementation strategy).
Definition qp.h:447
void QEvt_ctor(QEvt *const me, enum_t const sig)
QEvt constructor
Definition qf_act.c:47
uint32_t sig
Event signal (see Event Signal).
Definition qp.h:101
Time Event class.
Definition qp.h:573
void QTimeEvt_ctorX(QTimeEvt *const me, QActive *const act, enum_t const sig, uint_fast8_t const tickRate)
The "extended" constructor to initialize a Time Event.
Definition qf_time.c:50
QTimeEvtCtr ctr
Down-counter of the time event.
Definition qp.h:578
bool QTimeEvt_noActive(uint_fast8_t const tickRate)
Check if any time events are active at a given clock tick rate.
Definition qf_time.c:363
void QTimeEvt_armX(QTimeEvt *const me, uint32_t const nTicks, uint32_t const interval)
Arm a time event (extended version for one-shot or periodic time event).
Definition qf_time.c:78
void QTimeEvt_tick_(uint_fast8_t const tickRate, void const *const sender)
Processes all armed time events at every clock tick.
Definition qf_time.c:278
QTimeEvt * QTimeEvt_expire_(QTimeEvt *const me, QTimeEvt *const prev_link, QActive const *const act, uint_fast8_t const tickRate)
Definition qf_time.c:398
QTimeEvtCtr interval
Interval for periodic time event (zero for one-shot time event).
Definition qp.h:579
QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE]
Static array of heads of linked lists of time events (one for every clock tick rate).
Definition qp_pkg.h:48
bool QTimeEvt_rearm(QTimeEvt *const me, uint32_t const nTicks)
Rearm a time event.
Definition qf_time.c:190
QTimeEvtCtr QTimeEvt_getCtr(QTimeEvt const *const me)
Get the current value of the down-counter of a time event.
Definition qf_time.c:376
void QTimeEvt_init(void)
Definition qf_time.c:384
bool QTimeEvt_wasDisarmed(QTimeEvt *const me)
Check the "was disarmed" status of a time event.
Definition qf_time.c:262
bool QTimeEvt_disarm(QTimeEvt *const me)
Disarm a time event.
Definition qf_time.c:146
uint8_t flags
Definition qp.h:581
struct QTimeEvt * next
Link to the next time event in the list.
Definition qp.h:576
QEvt super
Definition qp.h:574
void * act
Active object that receives the time events.
Definition qp.h:577
uint8_t tickRate
Definition qp.h:580