QP/C++  7.0.1
Real-Time Embedded Framework
qf_time.cpp
Go to the documentation of this file.
1//$file${src::qf::qf_time.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2//
3// Model: qpcpp.qm
4// File: ${src::qf::qf_time.cpp}
5//
6// This code has been generated by QM 5.2.1 <www.state-machine.com/qm>.
7// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
8//
9// This code is covered by the following QP license:
10// License # : LicenseRef-QL-dual
11// Issued to : Any user of the QP/C++ real-time embedded framework
12// Framework(s) : qpcpp
13// Support ends : 2023-12-31
14// License scope:
15//
16// Copyright (C) 2005 Quantum Leaps, LLC <state-machine.com>.
17//
18// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
19//
20// This software is dual-licensed under the terms of the open source GNU
21// General Public License version 3 (or any later version), or alternatively,
22// under the terms of one of the closed source Quantum Leaps commercial
23// licenses.
24//
25// The terms of the open source GNU General Public License version 3
26// can be found at: <www.gnu.org/licenses/gpl-3.0>
27//
28// The terms of the closed source Quantum Leaps commercial licenses
29// can be found at: <www.state-machine.com/licensing>
30//
31// Redistributions in source code must retain this top-level comment block.
32// Plagiarizing this software to sidestep the license obligations is illegal.
33//
34// Contact information:
35// <www.state-machine.com/licensing>
36// <info@state-machine.com>
37//
38//$endhead${src::qf::qf_time.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
44
45#define QP_IMPL // this is QP implementation
46#include "qf_port.hpp" // QF port
47#include "qf_pkg.hpp" // QF package-scope interface
48#include "qassert.h" // QP embedded systems-friendly assertions
49#ifdef Q_SPY // QS software tracing enabled?
50 #include "qs_port.hpp" // QS port
51 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
52#else
53 #include "qs_dummy.hpp" // disable the QS software tracing
54#endif // Q_SPY
55
56// unnamed namespace for local definitions with internal linkage
57namespace {
58Q_DEFINE_THIS_MODULE("qf_time")
59} // unnamed namespace
60
61//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
62// Check for the minimum required QP version
63#if (QP_VERSION < 690U) || (QP_VERSION != ((QP_RELEASE^4294967295U) % 0x3E8U))
64#error qpcpp version 6.9.0 or higher required
65#endif
66//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67
68//$define${QF::QTimeEvt} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
69namespace QP {
70
71//${QF::QTimeEvt} ............................................................
73
74//${QF::QTimeEvt::QTimeEvt} ..................................................
76 QActive * const act,
77 enum_t const sgnl,
78 std::uint_fast8_t const tickRate)
79:
80#ifndef Q_EVT_CTOR
81 QEvt(),
82#else
83 QEvt(static_cast<QSignal>(sgnl), 0U),
84#endif
85 m_next(nullptr),
86 m_act(act),
87 m_ctr(0U),
88 m_interval(0U)
89{
91 Q_REQUIRE_ID(300, (sgnl >= Q_USER_SIG)
92 && (tickRate < QF_MAX_TICK_RATE));
93
94 #ifndef Q_EVT_CTOR
95 sig = static_cast<QSignal>(sgnl); // set QEvt::sig of this time event
96 #endif
97
98 // Setting the POOL_ID event attribute to zero is correct only for
99 // events not allocated from event pools, which must be the case
100 // for Time Events.
101 //
102 poolId_ = 0U;
103
104 // The refCtr_ attribute is not used in time events, so it is
105 // reused to hold the tickRate as well as other information
106 //
107 refCtr_ = static_cast<std::uint8_t>(tickRate);
108}
109
110//${QF::QTimeEvt::armX} ......................................................
112 QTimeEvtCtr const nTicks,
113 QTimeEvtCtr const interval) noexcept
114{
115 std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE;
116 QTimeEvtCtr const ctr = m_ctr; // temporary to hold volatile
117
120 Q_REQUIRE_ID(400, (m_act != nullptr)
121 && (ctr == 0U)
122 && (nTicks != 0U)
123 && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE))
124 && (static_cast<enum_t>(sig) >= Q_USER_SIG));
125 #ifdef Q_NASSERT
126 (void)ctr; // avoid compiler warning about unused variable
127 #endif
128
130 QF_CRIT_E_();
131 m_ctr = nTicks;
132 m_interval = interval;
133
134 // is the time event unlinked?
135 // NOTE: For the duration of a single clock tick of the specified tick
136 // rate a time event can be disarmed and yet still linked into the list,
137 // because un-linking is performed exclusively in the QF_tickX() function.
138 //
139 if (static_cast<std::uint_fast8_t>(
140 static_cast<std::uint_fast8_t>(refCtr_) & TE_IS_LINKED) == 0U)
141 {
142 // mark as linked
143 refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED);
144
145 // The time event is initially inserted into the separate
146 // "freshly armed" list based on timeEvtHead_[tickRate].act.
147 // Only later, inside QTimeEvt::tick_(), the "freshly armed"
148 // list is appended to the main list of armed time events based on
149 // timeEvtHead_[tickRate].next. Again, this is to keep any
150 // changes to the main list exclusively inside QTimeEvt::tick_().
151 m_next = timeEvtHead_[tickRate].toTimeEvt();
152 timeEvtHead_[tickRate].m_act = this;
153 }
154
155 #ifdef Q_SPY
156 std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
157 #endif
159 QS_TIME_PRE_(); // timestamp
160 QS_OBJ_PRE_(this); // this time event object
161 QS_OBJ_PRE_(m_act); // the active object
162 QS_TEC_PRE_(nTicks); // the number of ticks
163 QS_TEC_PRE_(interval); // the interval
164 QS_U8_PRE_(tickRate); // tick rate
166
167 QF_CRIT_X_();
168}
169
170//${QF::QTimeEvt::disarm} ....................................................
171bool QTimeEvt::disarm() noexcept {
173 QF_CRIT_E_();
174 #ifdef Q_SPY
175 std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
176 #endif
177
178 // is the time event actually armed?
179 bool wasArmed;
180 if (m_ctr != 0U) {
181 wasArmed = true;
182 refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED);
183
185 QS_TIME_PRE_(); // timestamp
186 QS_OBJ_PRE_(this); // this time event object
187 QS_OBJ_PRE_(m_act); // the target AO
188 QS_TEC_PRE_(m_ctr); // the number of ticks
189 QS_TEC_PRE_(m_interval); // the interval
192
193 m_ctr = 0U; // schedule removal from the list
194 }
195 else { // the time event was already disarmed automatically
196 wasArmed = false;
197 refCtr_ = static_cast<std::uint8_t>(refCtr_
198 & static_cast<std::uint8_t>(~TE_WAS_DISARMED));
199
201 QS_TIME_PRE_(); // timestamp
202 QS_OBJ_PRE_(this); // this time event object
203 QS_OBJ_PRE_(m_act); // the target AO
204 QS_U8_PRE_(refCtr_& TE_TICK_RATE); // tick rate
206
207 }
208 QF_CRIT_X_();
209
210 return wasArmed;
211}
212
213//${QF::QTimeEvt::rearm} .....................................................
214bool QTimeEvt::rearm(QTimeEvtCtr const nTicks) noexcept {
215 std::uint8_t const tickRate = refCtr_ & TE_TICK_RATE;
216
219 Q_REQUIRE_ID(600, (m_act != nullptr)
220 && (tickRate < static_cast<std::uint8_t>(QF_MAX_TICK_RATE))
221 && (nTicks != 0U)
222 && (static_cast<enum_t>(sig) >= Q_USER_SIG));
223
225 QF_CRIT_E_();
226
227 // is the time evt not running?
228 bool wasArmed;
229 if (m_ctr == 0U) {
230 wasArmed = false;
231
232 // is the time event unlinked?
233 // NOTE: For a duration of a single clock tick of the specified
234 // tick rate a time event can be disarmed and yet still linked into
235 // the list, because unlinking is performed exclusively in the
236 // QTimeEvt::tickX() function.
237 if (static_cast<std::uint8_t>(refCtr_ & TE_IS_LINKED) == 0U) {
238 // mark as linked
239 refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_IS_LINKED);
240
241 // The time event is initially inserted into the separate
242 // "freshly armed" list based on timeEvtHead_[tickRate].act.
243 // Only later, inside QTimeEvt::tick_(), the "freshly armed"
244 // list is appended to the main list of armed time events based on
245 // timeEvtHead_[tickRate].next. Again, this is to keep any
246 // changes to the main list exclusively inside QTimeEvt::tick_().
247 m_next = timeEvtHead_[tickRate].toTimeEvt();
248 timeEvtHead_[tickRate].m_act = this;
249 }
250 }
251 else { // the time event is being disarmed
252 wasArmed = true;
253 }
254 m_ctr = nTicks; // re-load the tick counter (shift the phasing)
255
256 #ifdef Q_SPY
257 std::uint_fast8_t const qs_id = static_cast<QActive *>(m_act)->m_prio;
258 #endif
260 QS_TIME_PRE_(); // timestamp
261 QS_OBJ_PRE_(this); // this time event object
262 QS_OBJ_PRE_(m_act); // the target AO
263 QS_TEC_PRE_(m_ctr); // the number of ticks
264 QS_TEC_PRE_(m_interval); // the interval
265 QS_2U8_PRE_(tickRate, (wasArmed ? 1U : 0U));
267
268 QF_CRIT_X_();
269
270 return wasArmed;
271}
272
273//${QF::QTimeEvt::wasDisarmed} ...............................................
274bool QTimeEvt::wasDisarmed() noexcept {
275 std::uint8_t const isDisarmed = refCtr_ & TE_WAS_DISARMED;
276 // mark as disarmed
277 refCtr_ = static_cast<std::uint8_t>(refCtr_ | TE_WAS_DISARMED);
278 return isDisarmed != 0U;
279}
280
281//${QF::QTimeEvt::tick_} .....................................................
283 std::uint_fast8_t const tickRate,
284 void const * const sender) noexcept
285{
286 Q_UNUSED_PAR(sender); // when Q_SPY not defined
287
288 QTimeEvt *prev = &timeEvtHead_[tickRate];
289
291 QF_CRIT_E_();
292
294 prev->m_ctr = (prev->m_ctr + 1U);
295 QS_TEC_PRE_(prev->m_ctr); // tick ctr
296 QS_U8_PRE_(tickRate); // tick rate
298
299 // scan the linked-list of time events at this rate...
300 for (;;) {
301 QTimeEvt *t = prev->m_next; // advance down the time evt. list
302
303 // end of the list?
304 if (t == nullptr) {
305
306 // any new time events armed since the last run of tick_()?
307 if (timeEvtHead_[tickRate].m_act != nullptr) {
308
309 // sanity check
310 Q_ASSERT_CRIT_(110, prev != nullptr);
311 prev->m_next = timeEvtHead_[tickRate].toTimeEvt();
312 timeEvtHead_[tickRate].m_act = nullptr;
313 t = prev->m_next; // switch to the new list
314 }
315 else {
316 break; // all currently armed time evts. processed
317 }
318 }
319
320 // time event scheduled for removal?
321 if (t->m_ctr == 0U) {
322 prev->m_next = t->m_next;
323 // mark time event 't' as NOT linked
324 t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_
325 & static_cast<std::uint8_t>(~TE_IS_LINKED));
326 // do NOT advance the prev pointer
327 QF_CRIT_X_(); // exit crit. section to reduce latency
328
329 // prevent merging critical sections, see NOTE1 below
331 }
332 else {
333 t->m_ctr = (t->m_ctr - 1U);
334
335 // is time evt about to expire?
336 if (t->m_ctr == 0U) {
337 QActive * const act = t->toActive(); // temp for volatile
338
339 // periodic time evt?
340 if (t->m_interval != 0U) {
341 t->m_ctr = t->m_interval; // rearm the time event
342 prev = t; // advance to this time event
343 }
344 // one-shot time event: automatically disarm
345 else {
346 prev->m_next = t->m_next;
347
348 // mark time event 't' as NOT linked
349 t->refCtr_ = static_cast<std::uint8_t>(t->refCtr_
350 & static_cast<std::uint8_t>(~TE_IS_LINKED));
351 // do NOT advance the prev pointer
352
354 act->m_prio)
355 QS_OBJ_PRE_(t); // this time event object
356 QS_OBJ_PRE_(act); // the target AO
357 QS_U8_PRE_(tickRate); // tick rate
359 }
360
362 QS_TIME_PRE_(); // timestamp
363 QS_OBJ_PRE_(t); // the time event object
364 QS_SIG_PRE_(t->sig); // signal of this time event
365 QS_OBJ_PRE_(act); // the target AO
366 QS_U8_PRE_(tickRate); // tick rate
368
369 QF_CRIT_X_(); // exit crit. section before posting
370
371 // asserts if queue overflows
372 act->POST(t, sender);
373 }
374 else {
375 prev = t; // advance to this time event
376 QF_CRIT_X_(); // exit crit. section to reduce latency
377
378 // prevent merging critical sections
379 // In some QF ports the critical section exit takes effect only
380 // on the next machine instruction. If this case, the next
381 // instruction is another entry to a critical section, the
382 // critical section won't be really exited, but rather the
383 // two adjacent critical sections would be merged. The
384 // QF_CRIT_EXIT_NOP() macro contains minimal code required
385 // to prevent such merging of critical sections in QF ports,
386 // in which it can occur.
388 }
389 }
390 QF_CRIT_E_(); // re-enter crit. section to continue
391 }
392 QF_CRIT_X_();
393}
394
395//${QF::QTimeEvt::noActive} ..................................................
396bool QTimeEvt::noActive(std::uint_fast8_t const tickRate) noexcept {
398 Q_REQUIRE_ID(200, tickRate < QF_MAX_TICK_RATE);
399
400 bool inactive;
401 if (timeEvtHead_[tickRate].m_next != nullptr) {
402 inactive = false;
403 }
404 else if (timeEvtHead_[tickRate].m_act != nullptr) {
405 inactive = false;
406 }
407 else {
408 inactive = true;
409 }
410 return inactive;
411}
412
413//${QF::QTimeEvt::QTimeEvt} ..................................................
415 :
416#ifdef Q_EVT_CTOR
417 QEvt(0U, 0U),
418#else
419 QEvt(),
420#endif // Q_EVT_CTOR
421 m_next(nullptr),
422 m_act(nullptr),
423 m_ctr(0U),
424 m_interval(0U)
425{
426 #ifndef Q_EVT_CTOR
427 sig = 0U;
428
429 // Setting the POOL_ID event attribute to zero is correct only for
430 // events not allocated from event pools, which must be the case
431 // for Time Events.
432 //
433 poolId_ = 0U; // not from any event pool
434
435 // The refCtr_ attribute is not used in time events, so it is
436 // reused to hold the tickRate as well as other information
437 //
438 refCtr_ = 0U; // default rate 0
439 #endif // Q_EVT_CTOR
440}
441
442} // namespace QP
443//$enddef${QF::QTimeEvt} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
QP::QActive active object class (based on the QP::QHsm-style implementation strategy)
Definition: qf.hpp:322
std::uint8_t m_prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.hpp:367
Event class.
Definition: qep.hpp:168
QSignal sig
signal of the event instance
Definition: qep.hpp:173
std::uint8_t volatile refCtr_
reference counter (only used for dynamic, mutable events)
Definition: qep.hpp:181
std::uint8_t poolId_
pool ID (0 for static, immutable event)
Definition: qep.hpp:177
Time Event class (inherits QP:QEvt)
Definition: qf.hpp:930
void * m_act
the active object that receives the time events
Definition: qf.hpp:941
QTimeEvtCtr volatile m_ctr
the internal down-counter of the time event
Definition: qf.hpp:949
QTimeEvt()
private default constructor only for friends
Definition: qf_time.cpp:414
QTimeEvt * toTimeEvt() noexcept
encapsulate the cast the QTimeEvt.m_act attribute
Definition: qf.hpp:1144
bool wasDisarmed() noexcept
Check the "was disarmed" status of a time event.
Definition: qf_time.cpp:274
QTimeEvtCtr m_interval
the interval for the periodic time event (zero for the one-shot time event)
Definition: qf.hpp:958
QActive * toActive() noexcept
encapsulate the cast the m_act attribute to QActive*
Definition: qf.hpp:1139
QTimeEvt *volatile m_next
link to the next time event in the list
Definition: qf.hpp:934
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]
heads of linked lists of time events, one for every clock tick rate
Definition: qf.hpp:963
void armX(QTimeEvtCtr const nTicks, QTimeEvtCtr const interval=0U) noexcept
Arm a time event (one shot or periodic) for event posting.
Definition: qf_time.cpp:111
static void tick_(std::uint_fast8_t const tickRate, void const *const sender) noexcept
Processes all armed time events at every clock tick.
Definition: qf_time.cpp:282
static bool noActive(std::uint_fast8_t const tickRate) noexcept
Returns true if all time events are inactive and false any time event is active.
Definition: qf_time.cpp:396
bool rearm(QTimeEvtCtr const nTicks) noexcept
Rearm a time event.
Definition: qf_time.cpp:214
bool disarm() noexcept
Disarm a time event.
Definition: qf_time.cpp:171
#define QF_MAX_TICK_RATE
The maximum number of clock tick rates in the application.
Definition: config.hpp:48
#define Q_EVT_CTOR
The preprocessor switch to activate the constructor in QP::QEvt.
Definition: config.hpp:251
QP/C++ framework.
Definition: exa_native.dox:1
constexpr std::uint8_t TE_WAS_DISARMED
Definition: qf_pkg.hpp:107
@ QS_QF_TIMEEVT_AUTO_DISARM
a time event expired and was disarmed
Definition: qs.hpp:180
@ QS_QF_TIMEEVT_DISARM
true disarming of an armed time event
Definition: qs.hpp:182
@ QS_QF_TIMEEVT_REARM
rearming of a time event
Definition: qs.hpp:183
@ QS_QF_TIMEEVT_POST
a time event posted itself directly to an AO
Definition: qs.hpp:184
@ QS_QF_TIMEEVT_DISARM_ATTEMPT
attempt to disarm a disarmed QTimeEvt
Definition: qs.hpp:181
@ QS_QF_TICK
QTimeEvt::tick_() was called.
Definition: qs.hpp:176
@ QS_QF_TIMEEVT_ARM
a time event was armed
Definition: qs.hpp:179
constexpr std::uint8_t TE_TICK_RATE
Definition: qf_pkg.hpp:108
constexpr std::uint8_t TE_IS_LINKED
Definition: qf_pkg.hpp:106
constexpr enum_t Q_USER_SIG
Type returned from state-handler functions.
Definition: qep.hpp:278
std::uint32_t QTimeEvtCtr
Data type to store the block-size defined based on the macro QF_TIMEEVT_CTR_SIZE.
Definition: qf.hpp:155
std::uint16_t QSignal
QSignal represents the signal of an event.
Definition: qep.hpp:139
Customizable and memory-efficient Design by Contract (DbC) for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:141
#define Q_REQUIRE_ID(id_, expr_)
Definition: qassert.h:243
int enum_t
alias for enumerations used for event signals
Definition: qep.hpp:84
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition: qep.hpp:965
#define QF_CRIT_EXIT_NOP()
No-operation for exiting a critical section.
Definition: qf.hpp:1517
Internal (package scope) QF/C++ interface.
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.hpp:147
#define Q_ASSERT_CRIT_(id_, test_)
Definition: qf_pkg.hpp:187
#define QF_CRIT_X_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.hpp:170
#define QF_CRIT_E_()
This is an internal macro for entering a critical section.
Definition: qf_pkg.hpp:158
#define QS_TIME_PRE_()
Output time stamp to a QS record (used in predefined and application-specific trace records)
Definition: qs.hpp:1082
Internal (package scope) QS/C++ interface.
#define QS_U8_PRE_(data_)
Internal QS macro to output an unformatted uint8_t data element.
Definition: qs_pkg.hpp:172
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Internal QS macro to begin a predefined QS record without critical section.
Definition: qs_pkg.hpp:144
#define QS_OBJ_PRE_(obj_)
Internal QS macro to output object pointer data element.
Definition: qs_pkg.hpp:193
#define QS_TEC_PRE_(ctr_)
Definition: qs_pkg.hpp:296
#define QS_END_NOCRIT_PRE_()
Internal QS macro to end a predefiend QS record without critical section.
Definition: qs_pkg.hpp:153
#define QS_2U8_PRE_(data1_, data2_)
Internal QS macro to output 2 unformatted uint8_t data elements.
Definition: qs_pkg.hpp:176