QP/C++  7.0.1
Real-Time Embedded Framework
qxk.cpp
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//============================================================================
31
32#define QP_IMPL // this is QP implementation
33#include "qf_port.hpp" // QF port
34#include "qxk_pkg.hpp" // QXK package-scope internal interface
35#include "qassert.h" // QP embedded systems-friendly assertions
36#ifdef Q_SPY // QS software tracing enabled?
37 #include "qs_port.hpp" // QS port
38 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
39#else
40 #include "qs_dummy.hpp" // disable the QS software tracing
41#endif // Q_SPY
42
43// protection against including this source file in a wrong project
44#ifndef QXK_HPP
45 #error "Source file included in a project NOT based on the QXK kernel"
46#endif // QXK_HPP
47
48//============================================================================
49namespace { // unnamed local namespace
50
52
53// Local-scope objects .......................................................
54class QXKIdleThread : public QP::QActive {
55public:
56 QXKIdleThread() : QP::QActive(nullptr)
57 {}
58};
59
60static QXKIdleThread l_idleThread;
61
62} // unnamed namespace
63
64//============================================================================
65extern "C" {
66
67QXK_Attr QXK_attr_; // global attributes of the QXK kernel
68
69} // extern "C"
70
71//============================================================================
72namespace QP {
73
74//............................................................................
75void QF::init(void) {
76 QF_maxPool_ = 0U;
77 QF_subscrList_ = nullptr;
79
80 bzero(&timeEvtHead_[0], sizeof(timeEvtHead_));
81 bzero(&active_[0], sizeof(active_));
82 bzero(&QXK_attr_, sizeof(QXK_attr_));
83 bzero(&l_idleThread, sizeof(l_idleThread));
84
85 // setup the QXK scheduler as initially locked and not running
87
88 // setup the QXK idle loop...
89 active_[0] = &l_idleThread; // register the idle thread with QF
90 QXK_attr_.idleThread = &l_idleThread; // save the idle thread ptr
91 QXK_attr_.actPrio = l_idleThread.m_prio; // set the base priority
92
93#ifdef QXK_INIT
94 QXK_INIT(); // port-specific initialization of the QXK kernel
95#endif
96}
97
98//............................................................................
99void QF::stop(void) {
100 onCleanup(); // application-specific cleanup callback
101 // nothing else to do for the preemptive QXK kernel
102}
103
104//............................................................................
106static void initial_events(void); // prototype
107static void initial_events(void) {
108 QXK_attr_.lockPrio = 0U; // unlock the scheduler
109
110 // any active objects need to be scheduled before starting event loop?
111 if (QXK_sched_() != 0U) {
112 QXK_activate_(); // process all events produced so far
113 }
114}
115
116//............................................................................
117int_t QF::run(void) {
119 initial_events(); // process all events posted during initialization
120 onStartup(); // application-specific startup callback
121
122 // produce the QS_QF_RUN trace record
125
127
128 // the QXK idle loop...
129 for (;;) {
130 QXK::onIdle(); // application-specific QXK idle callback
131 }
132
133#ifdef __GNUC__ // GNU compiler?
134 return 0;
135#endif
136}
137
138//............................................................................
139void QActive::start(std::uint_fast8_t const prio,
140 QEvt const * * const qSto, std::uint_fast16_t const qLen,
141 void * const stkSto, std::uint_fast16_t const stkSize,
142 void const * const par)
143{
150 && (0U < prio) && (prio <= QF_MAX_ACTIVE)
151 && (stkSto == nullptr)
152 && (stkSize == 0U));
153
154 m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO
155 m_osObject = nullptr; // no private stack for AO
156 m_prio = static_cast<std::uint8_t>(prio); // prio of the AO
157 m_dynPrio = static_cast<std::uint8_t>(prio); // dynamic prio of the AO
158 QF::add_(this); // make QF aware of this AO
159
160 this->init(par, m_prio); // take the top-most initial tran. (virtual)
161 QS_FLUSH(); // flush the trace buffer to the host
162
163 // see if this AO needs to be scheduled in case QXK is running
165 QF_CRIT_E_();
166 if (QXK_sched_() != 0U) { // activation needed?
168 }
169 QF_CRIT_X_();
170}
171
172//............................................................................
173QSchedStatus QXK::schedLock(std::uint_fast8_t const ceiling) noexcept {
175 QF_CRIT_E_();
176
180
181 // first store the previous lock prio if below the ceiling
182 QSchedStatus stat;
183 if (static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio) < ceiling) {
184 stat = (static_cast<QSchedStatus>(QXK_attr_.lockPrio) << 8U);
185 QXK_attr_.lockPrio = static_cast<std::uint8_t>(ceiling);
186
188 QS_TIME_PRE_(); // timestamp
189 // the previous lock prio & new lock prio
192
193 // add the previous lock holder priority
194 stat |= static_cast<QSchedStatus>(QXK_attr_.lockHolder);
195 QXK_attr_.lockHolder = (QXK_attr_.curr != nullptr)
197 : 0U;
198 }
199 else {
200 stat = 0xFFU;
201 }
202 QF_CRIT_X_();
203
204 return stat; // return the status to be saved in a stack variable
205}
206
207//............................................................................
208void QXK::schedUnlock(QSchedStatus const stat) noexcept {
209 // has the scheduler been actually locked by the last QXK_schedLock()?
210 if (stat != 0xFFU) {
211 std::uint_fast8_t const lockPrio
212 = static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio);
213 std::uint_fast8_t const prevPrio
214 = static_cast<std::uint_fast8_t>(stat >> 8U);
216 QF_CRIT_E_();
217
222 && (lockPrio > prevPrio));
223
225 QS_TIME_PRE_(); // timestamp
226 // prio before unlocking & prio after unlocking
227 QS_2U8_PRE_(lockPrio, prevPrio);
229
230 // restore the previous lock priority and lock holder
231 QXK_attr_.lockPrio = static_cast<std::uint8_t>(prevPrio);
232 QXK_attr_.lockHolder = static_cast<std::uint8_t>(stat & 0xFFU);
233
234 // find the highest-prio thread ready to run
235 if (QXK_sched_() != 0U) { // priority found?
236 QXK_activate_(); // activate any unlocked basic threads
237 }
238
239 QF_CRIT_X_();
240 }
241}
242
243} // namespace QP
244
245
246//============================================================================
247extern "C" {
248
249//............................................................................
250std::uint_fast8_t QXK_sched_(void) noexcept {
251 // find the highest-prio thread ready to run
252 std::uint_fast8_t p = QXK_attr_.readySet.findMax();
253
254 // below the lock prio?
255 if (p <= static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio)) {
256 // dynamic priority of the thread holding the lock
257 p = static_cast<std::uint_fast8_t>(
259 if (p != 0U) {
261 }
262 }
263
264 QP::QActive * const next = QP::QF::active_[p];
265
266 // the thread found must be registered in QF
267 Q_ASSERT_ID(620, next != nullptr);
268
269 // is the current thread a basic-thread?
270 if (QXK_attr_.curr == nullptr) {
271
272 // is next a basic-thread?
273 if (next->m_osObject == nullptr) {
274 if (p > static_cast<std::uint_fast8_t>(QXK_attr_.actPrio)) {
275 QXK_attr_.next = next; // set the next AO to activate
276 }
277 else {
278 QXK_attr_.next = nullptr;
279 p = 0U; // no activation needed
280 }
281 }
282 else { // this is an extened-thread
283
285 QS_TIME_PRE_(); // timestamp
286 // prio of the next AO & prio of the curr AO
289
290 QXK_attr_.next = next;
291 p = 0U; // no activation needed
293 }
294 }
295 else { // currently executing an extended-thread
296
297 // is the next thread different from the current?
298 if (next != QXK_attr_.curr) {
299
301 QS_TIME_PRE_(); // timestamp
302 // next prio & current prio
305
306 QXK_attr_.next = next;
307 p = 0U; // no activation needed
309 }
310 else { // next is the same as current
311 QXK_attr_.next = nullptr; // no need to context-switch
312 p = 0U; // no activation needed
313 }
314 }
315 return p;
316}
317
318//............................................................................
319void QXK_activate_(void) {
320 std::uint_fast8_t const pin =
321 static_cast<std::uint_fast8_t>(QXK_attr_.actPrio);
322 QP::QActive *a = QXK_attr_.next; // the next AO (basic-thread) to run
323
324 // QXK Context switch callback defined or QS tracing enabled?
325#if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
326 std::uint_fast8_t pprev = pin;
327#endif // QXK_ON_CONTEXT_SW || Q_SPY
328
330 Q_REQUIRE_ID(700, (a != nullptr) && (pin < QF_MAX_ACTIVE));
331
332 // dynamic priority of the next AO
333 std::uint_fast8_t p = static_cast<std::uint_fast8_t>(a->m_dynPrio);
334
335 // loop until no more ready-to-run AOs of higher prio than the initial
336 do {
337 a = QP::QF::active_[p]; // obtain the pointer to the AO
338
339 QXK_attr_.actPrio = static_cast<std::uint8_t>(p); // new active prio
340 QXK_attr_.next = nullptr; // clear the next AO
341
343 QS_TIME_PRE_(); // timestamp
344 // next prio & prev prio
345 QS_2U8_PRE_(p, pprev);
347
348#if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
349 if (p != pprev) { // changing threads?
350
351#ifdef QXK_ON_CONTEXT_SW
352 Q_ASSERT_ID(710, pprev < QF_MAX_ACTIVE);
353
354 // context-switch callback
355 QXK_onContextSw(((pprev!=0U) ? QP::QF::active_[pprev] : nullptr),
356 a);
357#endif // QXK_ON_CONTEXT_SW
358
359 pprev = p; // update previous priority
360 }
361#endif // QXK_ON_CONTEXT_SW || Q_SPY
362
363 QF_INT_ENABLE(); // unconditionally enable interrupts
364
365 // perform the run-to-completion (RTC) step...
366 // 1. retrieve the event from the AO's event queue, which by this
367 // time must be non-empty and QActive_get_() asserts it.
368 // 2. dispatch the event to the AO's state machine.
369 // 3. determine if event is garbage and collect it if so
370 //
371 QP::QEvt const * const e = a->get_();
372 a->dispatch(e, a->m_prio);
373 QP::QF::gc(e);
374
375 QF_INT_DISABLE(); // unconditionally disable interrupts
376
377 if (a->m_eQueue.isEmpty()) { // empty queue?
379 }
380
381 // find new highest-prio AO ready to run...
382 // NOTE: this part must match the QXK_sched_(),
383 // current is a basic-thread path.
385
386 // below scheduler lock?
387 if (p <= static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio)) {
388 p = static_cast<std::uint_fast8_t>(QXK_attr_.lockHolder);
389 if (p != 0U) {
391 }
392 }
393 a = QP::QF::active_[p];
394
395 // the AO must be registered in QF
396 Q_ASSERT_ID(720, a != nullptr);
397
398 // is the next a basic thread?
399 if (a->m_osObject == nullptr) {
400 if (p > pin) {
401 QXK_attr_.next = a;
402 }
403 else {
404 QXK_attr_.next = nullptr;
405 p = 0U; // no activation needed
406 }
407 }
408 else { // next is the extened thread
409
411 QS_TIME_PRE_(); // timestamp
412 // next prio & curr prio
415
416 QXK_attr_.next = a;
417 p = 0U; // no activation needed
419 }
420 } while (p != 0U); // while activation needed
421
422 QXK_attr_.actPrio = static_cast<std::uint8_t>(pin); // restore base prio
423
424#if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
425 if (pin != 0U) { // resuming an active object?
426 a = QP::QF::active_[pin]; // the pointer to the preempted AO
427
429 QS_TIME_PRE_(); // timestamp
430 QS_2U8_PRE_(pin, pprev); // resumed prio & previous prio
432 }
433 else { // resuming priority==0 --> idle
434 a = nullptr;
435
437 QS_TIME_PRE_(); // timestamp
438 QS_U8_PRE_(pprev); // previous prio
440 }
441
442#ifdef QXK_ON_CONTEXT_SW
443 // context-switch callback
445#endif // QXK_ON_CONTEXT_SW
446
447#endif // QXK_ON_CONTEXT_SW || Q_SPY
448}
449
450//............................................................................
451QP::QActive *QXK_current(void) noexcept {
454
456 QF_CRIT_E_();
457
458 QP::QActive *curr = QXK_attr_.curr;
459 if (curr == nullptr) { // basic thread?
461 }
462 QF_CRIT_X_();
463
465 Q_ENSURE_ID(890, curr != nullptr);
466
467 return curr;
468}
469
470} // extern "C"
QActive active object class (based on QP::QHsm implementation strategy)
Definition: qf.hpp:139
std::uint8_t m_prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.hpp:183
virtual void start(std::uint_fast8_t const prio, QEvt const **const qSto, std::uint_fast16_t const qLen, void *const stkSto, std::uint_fast16_t const stkSize, void const *const par)
Starts execution of an active object and registers the object with the framework.
Definition: qk.cpp:114
QEvt const * get_(void) noexcept
Get an event from the event queue of an active object.
Definition: qf_actq.cpp:245
static void onStartup(void)
Startup QF callback.
static void init(void)
QF initialization.
Definition: qk.cpp:69
static void onCleanup(void)
Cleanup QF callback.
static int_t run(void)
Transfers control to QF to run the application.
Definition: qk.cpp:93
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]
heads of linked lists of time events, one for every clock tick rate
Definition: qf.hpp:1199
static void gc(QEvt const *const e) noexcept
Recycle a dynamic event.
Definition: qf_dyn.cpp:140
static void bzero(void *const start, std::uint_fast16_t const len) noexcept
Clear a specified region of memory to zero.
Definition: qf_act.cpp:81
static void add_(QActive *const a) noexcept
Register an active object to be managed by the framework.
Definition: qf_act.cpp:56
static void stop(void)
Function invoked by the application layer to stop the QF application and return control to the OS/Ker...
Definition: qk.cpp:87
static QActive * active_[QF_MAX_ACTIVE+1U]
Internal array of registered active objects.
Definition: qf.hpp:1137
virtual void dispatch(QEvt const *const e, std::uint_fast8_t const qs_id)
Dispatches an event to QHsm.
Definition: qep_hsm.cpp:192
virtual void init(void const *const e, std::uint_fast8_t const qs_id)
executes the top-most initial transition in QP::QHsm
Definition: qep_hsm.cpp:115
static QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept
QXK selective scheduler lock.
Definition: qxk.cpp:173
static void schedUnlock(QSchedStatus const stat) noexcept
QXK selective scheduler unlock.
Definition: qxk.cpp:208
static void onIdle(void)
QXK idle callback (customized in BSPs for QXK)
namespace associated with the QP/C++ framework
Definition: exa_native.dox:1
@ QS_QF_RUN
QF_run() was entered.
Definition: qs.hpp:671
@ QS_SCHED_IDLE
scheduler became idle
Definition: qs.hpp:650
@ QS_SCHED_LOCK
scheduler was locked
Definition: qs.hpp:647
@ QS_SCHED_UNLOCK
scheduler was unlocked
Definition: qs.hpp:648
@ QS_SCHED_NEXT
scheduler found next task to execute
Definition: qs.hpp:649
@ QS_SCHED_RESUME
scheduler resumed previous task (not idle)
Definition: qs.hpp:651
enum_t QF_maxPubSignal_
the maximum published signal
Definition: qf_ps.cpp:55
std::uint_fast8_t QF_maxPool_
Definition: qf_dyn.cpp:54
static void initial_events(void)
process all events posted during initialization
Definition: qxk.cpp:107
std::uint_fast16_t QSchedStatus
The scheduler lock status.
Definition: qk.hpp:132
QSubscrList * QF_subscrList_
the subscriber list array
Definition: qf_ps.cpp:54
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:102
#define Q_ASSERT_ID(id_, test_)
Definition: qassert.h:135
#define Q_ENSURE_ID(id_, test_)
Definition: qassert.h:271
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:252
int int_t
alias for line numbers in assertions and return from QF::run()
Definition: qep.hpp:64
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.hpp:48
#define QF_CRIT_X_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.hpp:69
#define QF_CRIT_E_()
This is an internal macro for entering a critical section.
Definition: qf_pkg.hpp:58
#define QS_TIME_PRE_()
Internal macro to output time stamp to a QS record.
Definition: qs.hpp:748
#define QS_FLUSH()
Flush the QS trace data to the host.
Definition: qs.hpp:1265
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:161
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Internal QS macro to begin a predefined QS record without critical section.
Definition: qs_pkg.hpp:133
#define QS_END_NOCRIT_PRE_()
Internal QS macro to end a predefiend QS record without critical section.
Definition: qs_pkg.hpp:142
#define QS_2U8_PRE_(data1_, data2_)
Internal QS macro to output 2 unformatted uint8_t data elements.
Definition: qs_pkg.hpp:165
#define QF_MAX_ACTIVE
#define QF_INT_DISABLE()
#define QF_INT_ENABLE()
void QXK_activate_(void)
QXK activator activates the next active object.
Definition: qxk.cpp:319
std::uint_fast8_t QXK_sched_(void) noexcept
QXK scheduler finds the highest-priority thread ready to run.
Definition: qxk.cpp:250
QXK_Attr QXK_attr_
global attributes of the QXK kernel
Definition: qxk.cpp:67
QP::QActive * QXK_current(void) noexcept
return the currently executing active-object/thread
Definition: qxk.cpp:451
std::uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qxk.hpp:70
std::uint8_t volatile lockHolder
prio of the lock holder
Definition: qxk.hpp:71
void QXK_onContextSw(QP::QActive *prev, QP::QActive *next)
QXK context switch callback (customized in BSPs for QXK)
#define QXK_ISR_CONTEXT_()
Internal port-specific macro that reports the execution context.
Definition: qxk.hpp:235
QP::QActive * idleThread
pointer to the idle thread
Definition: qxk.hpp:73
QP::QActive *volatile next
next thread to execute
Definition: qxk.hpp:68
std::uint8_t volatile actPrio
prio of the active basic thread
Definition: qxk.hpp:69
QP::QPSet readySet
ready-set of all threads
Definition: qxk.hpp:74
QP::QActive *volatile curr
currently executing thread
Definition: qxk.hpp:67
attributes of the QXK kernel
Definition: qxk.hpp:66
Internal (package scope) QXK/C++ interface.
#define QXK_CONTEXT_SWITCH_()
Definition: qxk_port.hpp:45
#define QXK_INIT()
Definition: qxk_port.hpp:84
QEvt base class.
Definition: qep.hpp:155
bool hasElement(std::uint_fast8_t const n) const noexcept
the function evaluates to TRUE if the priority set has the element n.
Definition: qpset.hpp:157
void rmove(std::uint_fast8_t const n) noexcept
remove element n from the set, n = 1..64
Definition: qpset.hpp:182
std::uint_fast8_t findMax(void) const noexcept
find the maximum element in the set, returns zero if the set is empty
Definition: qpset.hpp:194