QP/C  8.0.3
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
40Q_DEFINE_THIS_MODULE("qf_time")
41
42//............................................................................
43QTimeEvt QTimeEvt_timeEvtHead_[QF_MAX_TICK_RATE];
44
45//............................................................................
46//! @public @memberof QTimeEvt
47void QTimeEvt_ctorX(QTimeEvt * const me,
48 QActive * const act,
49 enum_t const sig,
50 uint_fast8_t const tickRate)
51{
54 Q_REQUIRE_INCRIT(300, (sig != 0)
55 && (tickRate < QF_MAX_TICK_RATE));
57
58 QEvt_ctor(&me->super, sig);
59
60 me->next = (QTimeEvt *)0;
61 me->act = act;
62 me->ctr = 0U;
63 me->interval = 0U;
64 me->tickRate = (uint8_t)tickRate;
65 me->flags = 0U;
66
67 me->super.refCtr_ = 0U; // adjust from the QEvt_ctor((sig) ctor
68}
69
70//............................................................................
71//! @public @memberof QTimeEvt
72void QTimeEvt_armX(QTimeEvt * const me,
73 uint32_t const nTicks,
74 uint32_t const interval)
75{
78
79 // dynamic range checks
80#if (QF_TIMEEVT_CTR_SIZE == 1U)
81 Q_REQUIRE_INCRIT(400, (nTicks < 0xFFU) && (interval < 0xFFU));
82#elif (QF_TIMEEVT_CTR_SIZE == 2U)
83 Q_REQUIRE_INCRIT(400, (nTicks < 0xFFFFU) && (interval < 0xFFFFU));
84#endif
85
86 QTimeEvtCtr const ctr = me->ctr;
87 uint8_t const tickRate = me->tickRate;
88#ifdef Q_SPY
89 uint_fast8_t const qsId = ((QActive *)(me->act))->prio;
90#endif // def Q_SPY
91
93 (nTicks != 0U)
94 && (ctr == 0U)
95 && (me->act != (void *)0)
96 && (tickRate < (uint_fast8_t)QF_MAX_TICK_RATE));
97
98#ifdef Q_UNSAFE
100#endif // ndef Q_UNSAFE
101
102 me->ctr = (QTimeEvtCtr)nTicks;
104
105 // is the time event unlinked?
106 // NOTE: For the duration of a single clock tick of the specified tick
107 // rate a time event can be disarmed and yet still linked into the list
108 // because un-linking is performed exclusively in QTimeEvt_tick_().
109 if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) {
110 me->flags |= QTE_FLAG_IS_LINKED; // mark as linked
111
112 // The time event is initially inserted into the separate
113 // "freshly armed" list based on timeEvtHead_[tickRate].act.
114 // Only later, inside QTimeEvt_tick_(), the "freshly armed"
115 // list is appended to the main list of armed time events based on
116 // timeEvtHead_[tickRate].next. Again, this is to keep any
117 // changes to the main list exclusively inside QTimeEvt_tick_().
120 }
121
122 QS_BEGIN_PRE(QS_QF_TIMEEVT_ARM, qsId)
123 QS_TIME_PRE(); // timestamp
124 QS_OBJ_PRE(me); // this time event object
125 QS_OBJ_PRE(me->act); // the active object
126 QS_TEC_PRE(nTicks); // the # ticks
127 QS_TEC_PRE(interval); // the interval
128 QS_U8_PRE(tickRate); // tick rate
129 QS_END_PRE()
130
131 QF_CRIT_EXIT();
132}
133
134//............................................................................
135//! @public @memberof QTimeEvt
136bool QTimeEvt_disarm(QTimeEvt * const me) {
139
140 QTimeEvtCtr const ctr = me->ctr;
141
142#ifdef Q_SPY
143 uint_fast8_t const qsId = QACTIVE_CAST_(me->act)->prio;
144#endif
145
146 // was the time event actually armed?
147 bool wasArmed = false;
148 if (ctr != 0U) {
149 wasArmed = true;
151 me->ctr = 0U; // schedule removal from the list
152
153 QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM, qsId)
154 QS_TIME_PRE(); // timestamp
155 QS_OBJ_PRE(me); // this time event object
156 QS_OBJ_PRE(me->act); // the target AO
157 QS_TEC_PRE(ctr); // the # ticks
158 QS_TEC_PRE(me->interval); // the interval
159 QS_U8_PRE(me->tickRate); // tick rate
160 QS_END_PRE()
161 }
162 else { // the time event was already disarmed automatically
163 me->flags &= (uint8_t)(~QTE_FLAG_WAS_DISARMED & 0xFFU);
164
165 QS_BEGIN_PRE(QS_QF_TIMEEVT_DISARM_ATTEMPT, qsId)
166 QS_TIME_PRE(); // timestamp
167 QS_OBJ_PRE(me); // this time event object
168 QS_OBJ_PRE(me->act); // the target AO
169 QS_U8_PRE(me->tickRate); // tick rate
170 QS_END_PRE()
171 }
172
173 QF_CRIT_EXIT();
174
175 return wasArmed;
176}
177
178//............................................................................
179//! @public @memberof QTimeEvt
180bool QTimeEvt_rearm(QTimeEvt * const me,
181 uint32_t const nTicks)
182{
185
186 // dynamic range checks
187#if (QF_TIMEEVT_CTR_SIZE == 1U)
188 Q_REQUIRE_INCRIT(600, nTicks < 0xFFU);
189#elif (QF_TIMEEVT_CTR_SIZE == 2U)
190 Q_REQUIRE_INCRIT(600, nTicks < 0xFFFFU);
191#endif
192
193 uint8_t const tickRate = me->tickRate;
194 QTimeEvtCtr const ctr = me->ctr;
195
197 (nTicks != 0U)
198 && (me->act != (void *)0)
200
201#ifdef Q_SPY
202 uint_fast8_t const qsId = ((QActive *)(me->act))->prio;
203#endif
204
205 me->ctr = (QTimeEvtCtr)nTicks;
206
207 // was the time evt not running?
208 bool wasArmed = false;
209 if (ctr == 0U) {
210 // NOTE: For the duration of a single clock tick of the specified
211 // tick rate a time event can be disarmed and yet still linked into
212 // the list, because unlinking is performed exclusively in the
213 // QTimeEvt_tick_() function.
214
215 // is the time event unlinked?
216 if ((me->flags & QTE_FLAG_IS_LINKED) == 0U) {
217 me->flags |= QTE_FLAG_IS_LINKED; // mark as linked
218
219 // The time event is initially inserted into the separate
220 // "freshly armed" list based on timeEvtHead_[tickRate].act.
221 // Only later, inside QTimeEvt_tick_(), the "freshly armed"
222 // list is appended to the main list of armed time events based on
223 // timeEvtHead_[tickRate].next. Again, this is to keep any
224 // changes to the main list exclusively inside QTimeEvt_tick()_.
227 }
228 }
229 else { // the time event was armed
230 wasArmed = true;
231 }
232
233 QS_BEGIN_PRE(QS_QF_TIMEEVT_REARM, qsId)
234 QS_TIME_PRE(); // timestamp
235 QS_OBJ_PRE(me); // this time event object
236 QS_OBJ_PRE(me->act); // the target AO
237 QS_TEC_PRE(nTicks); // the # ticks
238 QS_TEC_PRE(me->interval); // the interval
239 QS_2U8_PRE(tickRate, (wasArmed ? 1U : 0U));
240 QS_END_PRE()
241
242 QF_CRIT_EXIT();
243
244 return wasArmed;
245}
246
247//............................................................................
248//! @public @memberof QTimeEvt
252
253 bool const wasDisarmed = (me->flags & QTE_FLAG_WAS_DISARMED) != 0U;
254 me->flags |= QTE_FLAG_WAS_DISARMED; // mark as disarmed
255
256 QF_CRIT_EXIT();
257
258 return wasDisarmed;
259}
260
261//............................................................................
262//! @public @memberof QTimeEvt
266 QTimeEvtCtr const ctr = me->ctr;
267 QF_CRIT_EXIT();
268
269 return ctr;
270}
271
272//............................................................................
273//! @static @private @memberof QTimeEvt
274void QTimeEvt_init(void) {
275 for (uint_fast8_t tickRate = 0U;
277 ++tickRate)
278 {
281 }
282}
283
284//............................................................................
285//! @static @private @memberof QTimeEvt
287 uint_fast8_t const tickRate,
288 void const * const sender)
289{
290#ifndef Q_SPY
291 Q_UNUSED_PAR(sender);
292#endif
293
296
298
300
301#ifdef Q_SPY
302 QS_BEGIN_PRE(QS_QF_TICK, 0U)
303 ++prev->ctr;
304 QS_TEC_PRE(prev->ctr); // tick ctr
305 QS_U8_PRE(tickRate); // tick rate
306 QS_END_PRE()
307#endif // def Q_SPY
308
309 // scan the linked-list of time events at this rate...
310 while (true) {
311 Q_ASSERT_INCRIT(810, prev != (QTimeEvt *)0); // sanity check
312
313 QTimeEvt *te = prev->next; // advance down the time evt. list
314
315 if (te == (QTimeEvt *)0) { // end of the list?
316 // NO any new time events armed since the last QTimeEvt_tick_()?
317 if (QTimeEvt_timeEvtHead_[tickRate].act == (void *)0) {
318 break; // terminate the while-loop
319 }
320
322 QTimeEvt_timeEvtHead_[tickRate].act = (void *)0;
323
324 te = prev->next; // switch to the new list
325 }
326
327 // the time event 'te' must be valid
328 Q_ASSERT_INCRIT(840, te != (QTimeEvt *)0);
329
330 QTimeEvtCtr ctr = te->ctr; // move volatile into temporary
331
332 if (ctr == 0U) { // time event scheduled for removal?
333 prev->next = te->next;
334
335 // mark time event 'te' as NOT linked
336 te->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU);
337 // do NOT advance the prev pointer
338 QF_CRIT_EXIT(); // exit crit. section to reduce latency
339 }
340 else if (ctr == 1U) { // is time event about to expire?
341 QActive * const act = (QActive *)te->act;
342 prev = QTimeEvt_expire_(te, prev, act, tickRate);
343
344#ifdef QXK_H_
345 if ((enum_t)te->super.sig < Q_USER_SIG) {
346 QXThread_timeout_(act);
347 QF_CRIT_EXIT();
348 }
349 else {
350 QF_CRIT_EXIT(); // exit crit. section before posting
351
352 // QACTIVE_POST() asserts if the queue overflows
353 QACTIVE_POST(act, &te->super, sender);
354 }
355#else // not QXK
356 QF_CRIT_EXIT(); // exit crit. section before posting
357
358 // QACTIVE_POST() asserts if the queue overflows
359 QACTIVE_POST(act, &te->super, sender);
360#endif
361 }
362 else { // time event keeps timing out
363 --ctr; // decrement the tick counter
364 te->ctr = ctr; // update the original
365
366 prev = te; // advance to this time event
367 QF_CRIT_EXIT(); // exit crit. section to reduce latency
368 }
369 QF_CRIT_ENTRY(); // re-enter crit. section to continue the loop
370 }
371 QF_CRIT_EXIT();
372}
373
374//............................................................................
375//! @static @public @memberof QTimeEvt
376bool QTimeEvt_noActive(uint_fast8_t const tickRate) {
377 // NOTE: this function must be called *inside* critical section
379
380 bool inactive = false;
381
383 // empty
384 }
385 else if ((QTimeEvt_timeEvtHead_[tickRate].act != (void *)0)) {
386 // empty
387 }
388 else {
389 inactive = true;
390 }
391
392 return inactive;
393}
394
395//............................................................................
396//! @private @memberof QTimeEvt
398 QTimeEvt * const prev_link,
399 QActive const * const act,
400 uint_fast8_t const tickRate)
401{
402 // NOTE: this helper function is called *inside* critical section
403#ifndef Q_SPY
406#endif
407
408 QTimeEvt *prev = prev_link;
409 if (me->interval != 0U) { // periodic time evt?
410 me->ctr = me->interval; // rearm the time event
411 prev = me; // advance to this time event
412 }
413 else { // one-shot time event: automatically disarm
414 me->ctr = 0U;
415 prev->next = me->next;
416
417 // mark this time event as NOT linked
418 me->flags &= (uint8_t)(~QTE_FLAG_IS_LINKED & 0xFFU);
419 // do NOT advance the prev pointer
420
421 QS_BEGIN_PRE(QS_QF_TIMEEVT_AUTO_DISARM, act->prio)
422 QS_OBJ_PRE(me); // this time event object
423 QS_OBJ_PRE(act); // the target AO
424 QS_U8_PRE(tickRate); // tick rate
425 QS_END_PRE()
426 }
427
428 QS_BEGIN_PRE(QS_QF_TIMEEVT_POST, act->prio)
429 QS_TIME_PRE(); // timestamp
430 QS_OBJ_PRE(me); // the time event object
431 QS_SIG_PRE(me->super.sig);// signal of this time event
432 QS_OBJ_PRE(act); // the target AO
433 QS_U8_PRE(tickRate); // tick rate
434 QS_END_PRE()
435
436 return prev;
437}
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:94
#define Q_USER_SIG
offset for the user signals (QP Application))
Definition qp.h:212
uint32_t QTimeEvtCtr
Data type to store the block-size defined based on the macro QF_TIMEEVT_CTR_SIZE.
Definition qp.h:389
#define QACTIVE_POST(me_, e_, sender_)
Invoke the direct event posting facility QActive_post_().
Definition qp.h:806
int enum_t
Definition qp.h:92
#define Q_DIM(array_)
Definition qp.h:95
#define QF_MAX_TICK_RATE
Maximum # clock tick rates in the system (0..15)
Definition qp_config.h:143
Internal (package scope) QP/C interface.
#define QACTIVE_CAST_(ptr_)
Definition qp_pkg.h:33
#define QTE_FLAG_WAS_DISARMED
Definition qp_pkg.h:56
#define QTE_FLAG_IS_LINKED
Definition qp_pkg.h:55
Sample QP/C port.
QS/C dummy public interface.
#define QS_OBJ_PRE(obj_)
Definition qs_dummy.h:149
#define QS_TEC_PRE(ctr_)
Definition qs_dummy.h:154
#define QS_SIG_PRE(sig_)
Definition qs_dummy.h:147
#define QS_TIME_PRE()
Definition qs_dummy.h:146
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.h:143
#define QS_U8_PRE(data_)
Definition qs_dummy.h:142
#define QS_END_PRE()
Definition qs_dummy.h:141
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.h:140
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:39
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section)
Definition qsafe.h:49
#define QF_CRIT_EXIT()
Definition qsafe.h:43
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section)
Definition qsafe.h:86
#define QF_CRIT_STAT
Definition qsafe.h:35
Active object class (based on the QHsm implementation strategy)
Definition qp.h:504
QSignal sig
Signal of the event (see Event Signal)
Definition qp.h:113
Time Event class.
Definition qp.h:629
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.
struct QTimeEvt *volatile next
Link to the next time event in the list.
Definition qp.h:632
QTimeEvtCtr volatile ctr
Down-counter of the time event.
Definition qp.h:634
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:376
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:72
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:286
QTimeEvt * QTimeEvt_expire_(QTimeEvt *const me, QTimeEvt *const prev_link, QActive const *const act, uint_fast8_t const tickRate)
Definition qf_time.c:397
QTimeEvtCtr interval
Interval for periodic time event (zero for one-shot time event)
Definition qp.h:635
QTimeEvtCtr QTimeEvt_currCtr(QTimeEvt const *const me)
Get the current value of the down-counter of a time event.
Definition qf_time.c:263
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.h:689
bool QTimeEvt_rearm(QTimeEvt *const me, uint32_t const nTicks)
Rearm a time event.
Definition qf_time.c:180
void QTimeEvt_init(void)
Definition qf_time.c:274
bool QTimeEvt_wasDisarmed(QTimeEvt *const me)
Check the "was disarmed" status of a time event.
Definition qf_time.c:249
bool QTimeEvt_disarm(QTimeEvt *const me)
Disarm a time event.
Definition qf_time.c:136
uint8_t flags
Definition qp.h:637
QEvt super
Definition qp.h:630
void * act
Active object that receives the time events.
Definition qp.h:633
uint8_t tickRate
Definition qp.h:636