QP/C++  8.0.0
Real-Time Embedded Framework
Loading...
Searching...
No Matches
qk.cpp
Go to the documentation of this file.
1//$file${src::qk::qk.cpp} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2//
3// Model: qpcpp.qm
4// File: ${src::qk::qk.cpp}
5//
6// This code has been generated by QM 7.0.0 <www.state-machine.com/qm>.
7// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
8//
9// Copyright (C) 2005 Quantum Leaps, LLC. All rights reserved.
10//
11// Q u a n t u m L e a P s
12// ------------------------
13// Modern Embedded Software
14//
15// SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
16//
17// The QP/C++ software is dual-licensed under the terms of the open-source
18// GNU General Public License (GPL) or under the terms of one of the closed-
19// source Quantum Leaps commercial licenses.
20//
21// Redistributions in source code must retain this top-level comment block.
22// Plagiarizing this software to sidestep the license obligations is illegal.
23//
24// NOTE:
25// The GPL does NOT permit the incorporation of this code into proprietary
26// programs. Please contact Quantum Leaps for commercial licensing options,
27// which expressly supersede the GPL and are designed explicitly for
28// closed-source distribution.
29//
30// Quantum Leaps contact information:
31// <www.state-machine.com/licensing>
32// <info@state-machine.com>
33//
34//$endhead${src::qk::qk.cpp} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
35#define QP_IMPL // this is QP implementation
36#include "qp_port.hpp" // QP port
37#include "qp_pkg.hpp" // QP package-scope interface
38#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
39#ifdef Q_SPY // QS software tracing enabled?
40 #include "qs_port.hpp" // QS port
41 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
42#else
43 #include "qs_dummy.hpp" // disable the QS software tracing
44#endif // Q_SPY
45
46// protection against including this source file in a wrong project
47#ifndef QK_HPP_
48 #error "Source file included in a project NOT based on the QK kernel"
49#endif // QK_HPP_
50
51// unnamed namespace for local definitions with internal linkage
52namespace {
53Q_DEFINE_THIS_MODULE("qk")
54} // unnamed namespace
55
56//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
57// Check for the minimum required QP version
58#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U))
59#error qpcpp version 7.3.0 or higher required
60#endif
61//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
62//$define${QK::QK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
63namespace QP {
64namespace QK {
65
66//${QK::QK-base::schedLock} ..................................................
67QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept {
70 QF_MEM_SYS();
71
74 == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis));
75
76 // first store the previous lock prio
77 QSchedStatus stat;
78 if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling?
79 QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio)
80 QS_TIME_PRE(); // timestamp
81 // the previous lock ceiling & new lock ceiling
82 QS_2U8_PRE(static_cast<std::uint8_t>(QK_priv_.lockCeil),
83 static_cast<std::uint8_t>(ceiling));
85
86 // previous status of the lock
87 stat = static_cast<QSchedStatus>(QK_priv_.lockCeil);
88
89 // new status of the lock
90 QK_priv_.lockCeil = ceiling;
91 #ifndef Q_UNSAFE
92 QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~ceiling);
93 #endif
94 }
95 else {
96 stat = 0xFFU; // scheduler not locked
97 }
98
99 QF_MEM_APP();
100 QF_CRIT_EXIT();
101
102 return stat; // return the status to be saved in a stack variable
103}
104
105//${QK::QK-base::schedUnlock} ................................................
106void schedUnlock(QSchedStatus const prevCeil) noexcept {
107 // has the scheduler been actually locked by the last QK::schedLock()?
108 if (prevCeil != 0xFFU) {
111 QF_MEM_SYS();
112
114 == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis));
116 && (QK_priv_.lockCeil > prevCeil));
117
118 QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio)
119 QS_TIME_PRE(); // timestamp
120 // current lock ceiling (old), previous lock ceiling (new)
121 QS_2U8_PRE(static_cast<std::uint8_t>(QK_priv_.lockCeil),
122 static_cast<std::uint8_t>(prevCeil));
123 QS_END_PRE()
124
125 // restore the previous lock ceiling
126 QK_priv_.lockCeil = prevCeil;
127 #ifndef Q_UNSAFE
128 QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~prevCeil);
129 #endif
130
131 // find if any AOs should be run after unlocking the scheduler
132 if (QK_sched_() != 0U) { // preemption needed?
133 QK_activate_(); // activate any unlocked AOs
134 }
135
136 QF_MEM_APP();
137 QF_CRIT_EXIT();
138 }
139}
140
141} // namespace QK
142} // namespace QP
143//$enddef${QK::QK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
144
145extern "C" {
146//$define${QK-extern-C} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
147
148//${QK-extern-C::QK_priv_} ...................................................
150
151//${QK-extern-C::QK_sched_} ..................................................
152std::uint_fast8_t QK_sched_() noexcept {
153 // NOTE: this function is entered with interrupts DISABLED
154
157
158 std::uint_fast8_t p;
159 if (QK_priv_.readySet.isEmpty()) {
160 p = 0U; // no activation needed
161 }
162 else {
163 // find the highest-prio AO with non-empty event queue
165
167 == static_cast<std::uint_fast8_t>(~QK_priv_.actThre_dis));
168
169 // is the AO's prio. below the active preemption-threshold?
170 if (p <= QK_priv_.actThre) {
171 p = 0U; // no activation needed
172 }
173 else {
175 == static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil_dis));
176
177 // is the AO's prio. below the lock-ceiling?
178 if (p <= QK_priv_.lockCeil) {
179 p = 0U; // no activation needed
180 }
181 else {
183 == static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio_dis));
184 QK_priv_.nextPrio = p; // next AO to run
185 #ifndef Q_UNSAFE
187 = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio);
188 #endif
189 }
190 }
191 }
192
193 return p;
194}
195
196//${QK-extern-C::QK_activate_} ...............................................
197void QK_activate_() noexcept {
198 // NOTE: this function is entered with interrupts DISABLED
199
200 std::uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio.
201 std::uint_fast8_t p = QK_priv_.nextPrio; // next prio to run
202
204 (prio_in == static_cast<std::uint_fast8_t>(~QK_priv_.actPrio_dis))
205 && (p == static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio_dis)));
206 Q_REQUIRE_INCRIT(510, (prio_in <= QF_MAX_ACTIVE)
207 && (0U < p) && (p <= QF_MAX_ACTIVE));
208
209 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
210 std::uint_fast8_t pprev = prio_in;
211 #endif // QF_ON_CONTEXT_SW || Q_SPY
212
213 QK_priv_.nextPrio = 0U; // clear for the next time
214 #ifndef Q_UNSAFE
215 QK_priv_.nextPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio);
216 #endif
217
218 std::uint_fast8_t pthre_in;
219 QP::QActive *a;
220 if (prio_in == 0U) { // preempting the idle thread?
221 pthre_in = 0U;
222 }
223 else {
224 a = QP::QActive::registry_[prio_in];
225 Q_ASSERT_INCRIT(510, a != nullptr);
226
227 pthre_in = static_cast<std::uint_fast8_t>(a->getPThre());
228 Q_INVARIANT_INCRIT(511, pthre_in == static_cast<std::uint_fast8_t>(
229 ~static_cast<std::uint_fast8_t>(a->m_pthre_dis) & 0xFFU));
230 }
231
232 // loop until no more ready-to-run AOs of higher pthre than the initial
233 do {
234 a = QP::QActive::registry_[p]; // obtain the pointer to the AO
235 Q_ASSERT_INCRIT(520, a != nullptr); // the AO must be registered
236 std::uint_fast8_t const pthre
237 = static_cast<std::uint_fast8_t>(a->getPThre());
238 Q_INVARIANT_INCRIT(522, pthre == static_cast<std::uint_fast8_t>(
239 ~static_cast<std::uint_fast8_t>(a->m_pthre_dis) & 0xFFU));
240
241 // set new active prio. and preemption-threshold
242 QK_priv_.actPrio = p;
243 QK_priv_.actThre = pthre;
244 #ifndef Q_UNSAFE
245 QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~p);
246 QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~pthre);
247 #endif
248
249 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
250 if (p != pprev) { // changing threads?
251
252 QS_BEGIN_PRE(QP::QS_SCHED_NEXT, p)
253 QS_TIME_PRE(); // timestamp
254 QS_2U8_PRE(p, // prio. of the scheduled AO
255 pprev); // previous prio.
256 QS_END_PRE()
257
258 #ifdef QF_ON_CONTEXT_SW
260 #endif // QF_ON_CONTEXT_SW
261
262 pprev = p; // update previous prio.
263 }
264 #endif // QF_ON_CONTEXT_SW || Q_SPY
265
266 QF_MEM_APP();
267 QF_INT_ENABLE(); // unconditionally enable interrupts
268
269 QP::QEvt const * const e = a->get_();
270 // NOTE QActive_get_() performs QF_MEM_APP() before return
271
272 // dispatch event (virtual call)
273 a->dispatch(e, a->getPrio());
274 #if (QF_MAX_EPOOL > 0U)
275 QP::QF::gc(e);
276 #endif
277
278 // determine the next highest-prio. AO ready to run...
279 QF_INT_DISABLE(); // unconditionally disable interrupts
280 QF_MEM_SYS();
281
282 // internal integrity check (duplicate inverse storage)
285
286 if (a->getEQueue().isEmpty()) { // empty queue?
288 #ifndef Q_UNSAFE
290 #endif
291 }
292
293 if (QK_priv_.readySet.isEmpty()) {
294 p = 0U; // no activation needed
295 }
296 else {
297 // find new highest-prio AO ready to run...
299
300 // is the new prio. below the initial preemption-threshold?
301 if (p <= pthre_in) {
302 p = 0U; // no activation needed
303 }
304 else {
307
308 // is the AO's prio. below the lock preemption-threshold?
309 if (p <= QK_priv_.lockCeil) {
310 p = 0U; // no activation needed
311 }
312 else {
314 }
315 }
316 }
317 } while (p != 0U);
318
319 // restore the active prio. and preemption-threshold
320 QK_priv_.actPrio = prio_in;
321 QK_priv_.actThre = pthre_in;
322 #ifndef Q_UNSAFE
323 QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actPrio);
324 QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actThre);
325 #endif
326
327 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
328 if (prio_in != 0U) { // resuming an active object?
329 a = QP::QActive::registry_[prio_in]; // pointer to preempted AO
330
331 QS_BEGIN_PRE(QP::QS_SCHED_NEXT, prio_in)
332 QS_TIME_PRE(); // timestamp
333 // prio. of the resumed AO, previous prio.
334 QS_2U8_PRE(prio_in, pprev);
335 QS_END_PRE()
336 }
337 else { // resuming prio.==0 --> idle
338 a = nullptr; // QK idle loop
339
340 QS_BEGIN_PRE(QP::QS_SCHED_IDLE, pprev)
341 QS_TIME_PRE(); // timestamp
342 QS_U8_PRE(pprev); // previous prio.
343 QS_END_PRE()
344 }
345
346 #ifdef QF_ON_CONTEXT_SW
348 #endif // QF_ON_CONTEXT_SW
349
350 #endif // QF_ON_CONTEXT_SW || Q_SPY
351}
352//$enddef${QK-extern-C} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
353} // extern "C"
354
355//$define${QK::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
356namespace QP {
357namespace QF {
358
359//${QK::QF-cust::init} .......................................................
360void init() {
361 bzero_(&QF::priv_, sizeof(QF::priv_));
362 bzero_(&QK_priv_, sizeof(QK_priv_));
364
365 // setup the QK scheduler as initially locked and not running
366 QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
367
368 #ifndef Q_UNSAFE
370 QK_priv_.actPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actPrio);
371 QK_priv_.nextPrio_dis = static_cast<std::uint_fast8_t>(~QK_priv_.nextPrio);
372 QK_priv_.actThre_dis = static_cast<std::uint_fast8_t>(~QK_priv_.actThre);
373 QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil);
374 #endif
375
376 #ifdef QK_INIT
377 QK_INIT(); // port-specific initialization of the QK kernel
378 #endif
379}
380
381//${QK::QF-cust::stop} .......................................................
382void stop() {
383 onCleanup(); // cleanup callback
384 // nothing else to do for the QK preemptive kernel
385}
386
387//${QK::QF-cust::run} ........................................................
388int_t run() {
389 #ifdef Q_SPY
390 // produce the QS_QF_RUN trace record
392 QF_MEM_SYS();
393 QS::beginRec_(QS_REC_NUM_(QS_QF_RUN));
394 QS::endRec_();
395 QF_MEM_APP();
397 #endif // Q_SPY
398
399 onStartup(); // application-specific startup callback
400
402 QF_MEM_SYS();
403
404 #ifdef QK_START
405 QK_START(); // port-specific startup of the QK kernel
406 #endif
407
408 QK_priv_.lockCeil = 0U; // unlock the QK scheduler
409 #ifndef Q_UNSAFE
410 QK_priv_.lockCeil_dis = static_cast<std::uint_fast8_t>(~QK_priv_.lockCeil);
411 #endif
412
413 #ifdef QF_ON_CONTEXT_SW
414 // officially switch to the idle context
416 #endif
417
418 // activate AOs to process events posted so far
419 if (QK_sched_() != 0U) {
420 QK_activate_();
421 }
422
423 QF_MEM_APP();
425
426 for (;;) { // QK idle loop...
427 QK::onIdle(); // application-specific QK on-idle callback
428 }
429
430 #ifdef __GNUC__ // GNU compiler?
431 return 0;
432 #endif
433}
434
435} // namespace QF
436} // namespace QP
437//$enddef${QK::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
438
439//$define${QK::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
440namespace QP {
441
442//${QK::QActive} .............................................................
443
444//${QK::QActive::start} ......................................................
445void QActive::start(
446 QPrioSpec const prioSpec,
447 QEvtPtr * const qSto,
448 std::uint_fast16_t const qLen,
449 void * const stkSto,
450 std::uint_fast16_t const stkSize,
451 void const * const par)
452{
453 Q_UNUSED_PAR(stkSto); // not needed in QK
454 Q_UNUSED_PAR(stkSize); // not needed in QK
455
458 QF_MEM_SYS();
459
461 && (stkSto == nullptr));
462 QF_MEM_APP();
463 QF_CRIT_EXIT();
464
465 m_prio = static_cast<std::uint8_t>(prioSpec & 0xFFU); // QF-prio.
466 m_pthre = static_cast<std::uint8_t>(prioSpec >> 8U); // preemption-thre.
467 register_(); // make QF aware of this AO
468
469 m_eQueue.init(qSto, qLen); // init the built-in queue
470
471 // top-most initial tran. (virtual call)
472 this->init(par, m_prio);
473 QS_FLUSH(); // flush the trace buffer to the host
474
475 // See if this AO needs to be scheduled if QK is already running
477 QF_MEM_SYS();
478 if (QK_sched_() != 0U) { // activation needed?
479 QK_activate_();
480 }
481 QF_MEM_APP();
482 QF_CRIT_EXIT();
483}
484
485} // namespace QP
486//$enddef${QK::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Private attributes of the QK kernel.
Definition qk.hpp:68
std::uint_fast8_t actPrio_dis
Definition qk.hpp:82
std::uint_fast8_t actThre
Definition qk.hpp:73
QP::QPSet readySet
Definition qk.hpp:70
std::uint_fast8_t lockCeil_dis
Definition qk.hpp:94
QP::QPSet readySet_dis
Definition qk.hpp:78
std::uint_fast8_t nextPrio
Definition qk.hpp:72
std::uint_fast8_t actPrio
Definition qk.hpp:71
std::uint_fast8_t nextPrio_dis
Definition qk.hpp:86
std::uint_fast8_t actThre_dis
Definition qk.hpp:90
std::uint_fast8_t lockCeil
Definition qk.hpp:74
Active object class (based on the QHsm implementation strategy)
Definition qp.hpp:699
void start(QPrioSpec const prioSpec, QEvtPtr *const qSto, std::uint_fast16_t const qLen, void *const stkSto, std::uint_fast16_t const stkSize, void const *const par)
Definition qv.cpp:284
void init(void const *const e, std::uint_fast8_t const qsId) override
Virtual function to take the top-most initial transition in the state machine.
Definition qp.hpp:756
static QActive * registry_[QF_MAX_ACTIVE+1U]
Definition qp.hpp:725
std::uint8_t m_pthre_dis
Definition qp.hpp:723
void register_() noexcept
Definition qf_qact.cpp:67
std::uint_fast8_t getPThre() const noexcept
Definition qp.hpp:833
void dispatch(QEvt const *const e, std::uint_fast8_t const qsId) override
Virtual function to dispatch an event to the state machine.
Definition qp.hpp:765
QACTIVE_EQUEUE_TYPE m_eQueue
Definition qp.hpp:713
QEvt const * get_() noexcept
Definition qf_actq.cpp:355
std::uint8_t m_pthre
Definition qp.hpp:702
std::uint_fast8_t getPrio() const noexcept
Definition qp.hpp:826
QACTIVE_EQUEUE_TYPE const & getEQueue() const noexcept
Definition qp.hpp:838
std::uint8_t m_prio
Definition qp.hpp:701
Event class.
Definition qp.hpp:131
std::uint_fast8_t findMax() const noexcept
Definition qp.hpp:630
void remove(std::uint_fast8_t const n) noexcept
Definition qp.hpp:618
bool verify_(QPSet const *const dis) const noexcept
Definition qp.hpp:650
void update_(QPSet *const dis) const noexcept
Definition qp.hpp:641
bool isEmpty() const noexcept
Definition qp.hpp:583
QF Active Object Framework namespace.
void onCleanup()
void gc(QEvt const *const e) noexcept
Recycle a mutable (mutable) event.
Definition qf_dyn.cpp:213
void bzero_(void *const start, std::uint_fast16_t const len) noexcept
Definition qf_act.cpp:72
QF::Attr priv_
Definition qf_act.cpp:69
void init()
Definition qv.cpp:131
int_t run()
Definition qv.cpp:153
void stop()
Definition qv.cpp:147
void onStartup()
QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept
Definition qk.cpp:67
void schedUnlock(QSchedStatus const prevCeil) noexcept
Definition qk.cpp:106
void onIdle()
QP/C++ framework.
Definition qequeue.hpp:42
QEvt const * QEvtPtr
Pointer to const event instances passed around in QP Framework.
Definition qp.hpp:534
std::uint_fast8_t QSchedStatus
Definition qk.hpp:42
std::uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.hpp:531
void QK_activate_() noexcept
Definition qk.cpp:197
QK_Attr QK_priv_
Definition qk.cpp:149
std::uint_fast8_t QK_sched_() noexcept
Definition qk.cpp:152
void QK_activate_() noexcept
Definition qk.cpp:197
QK_Attr QK_priv_
Definition qk.cpp:149
std::uint_fast8_t QK_sched_() noexcept
Definition qk.cpp:152
int int_t
Alias for assertion-ID numbers in QP assertions and return from QP::QF::run()
Definition qp.hpp:96
#define QF_MEM_APP()
Definition qp.hpp:1322
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.hpp:498
void QF_onContextSw(QP::QActive *prev, QP::QActive *next)
#define QF_MEM_SYS()
Definition qp.hpp:1317
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64)
Internal (package scope) QP/C++ interface.
Sample QP/C++ port.
#define QK_ISR_CONTEXT_()
Definition qp_port.hpp:111
#define QF_INT_DISABLE()
Disable interrupts.
Definition qp_port.hpp:36
#define QF_INT_ENABLE()
Enable interrupts.
Definition qp_port.hpp:39
#define QS_TIME_PRE()
Definition qs_dummy.hpp:155
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.hpp:152
#define QS_FLUSH()
Definition qs_dummy.hpp:76
#define QS_U8_PRE(data_)
Definition qs_dummy.hpp:151
#define QS_END_PRE()
Definition qs_dummy.hpp:150
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.hpp:149
QS/C++ port to a 32-bit CPU, generic C++ compiler.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:58
#define Q_ASSERT_INCRIT(id_, expr_)
Definition qsafe.h:72
#define Q_INVARIANT_INCRIT(id_, expr_)
Definition qsafe.h:154
#define QF_CRIT_EXIT()
Definition qsafe.h:62
#define Q_REQUIRE_INCRIT(id_, expr_)
Definition qsafe.h:136
#define QF_CRIT_STAT
Definition qsafe.h:54