QP/C++  7.0.1
Real-Time Embedded Framework
qk.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//============================================================================
30
31#define QP_IMPL // this is QF/QK implementation
32#include "qf_port.hpp" // QF port
33#include "qf_pkg.hpp" // QF package-scope internal interface
34#include "qassert.h" // QP assertions
35#ifdef Q_SPY // QS software tracing enabled?
36 #include "qs_port.hpp" // QS port
37 #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
38#else
39 #include "qs_dummy.hpp" // disable the QS software tracing
40#endif // Q_SPY
41
42// protection against including this source file in a wrong project
43#ifndef QK_HPP
44 #error "Source file included in a project NOT based on the QK kernel"
45#endif // QK_HPP
46
47// unnamed namespace for local definitions with internal linkage
48namespace {
49
51
52
53static void initial_events(void); // prototype
54static void initial_events(void) {
55 QK_attr_.lockPrio = 0U; // scheduler unlocked
56
57 // any active objects need to be scheduled before starting event loop?
58 if (QK_sched_() != 0U) {
59 QK_activate_(); // activate AOs to process all events posted so far
60 }
61}
62
63} // unnamed namespace
64
65//============================================================================
66namespace QP {
67
68//............................................................................
69void QF::init(void) {
70 QF_maxPool_ = 0U;
71 QF_subscrList_ = nullptr;
73
75 bzero(&active_[0], sizeof(active_));
76 bzero(&QK_attr_, sizeof(QK_attr_));
77
78 QK_attr_.actPrio = 0U; // prio of QK idle loop
80
81#ifdef QK_INIT
82 QK_INIT(); // port-specific initialization of the QK kernel
83#endif
84}
85
86//............................................................................
87void QF::stop(void) {
88 QF::onCleanup(); // cleanup callback
89 // nothing else to do for the QK preemptive kernel
90}
91
92//............................................................................
95 initial_events(); // process all events posted during initialization
96 onStartup(); // application-specific startup callback
97
98 // produce the QS_QF_RUN trace record
101
103
104 // the QK idle loop...
105 for (;;) {
106 QK::onIdle(); // application-specific QK on-idle callback
107 }
108#ifdef __GNUC__ // GNU compiler?
109 return 0;
110#endif
111}
112
113//............................................................................
114void QActive::start(std::uint_fast8_t const prio,
115 QEvt const * * const qSto, std::uint_fast16_t const qLen,
116 void * const stkSto, std::uint_fast16_t const stkSize,
117 void const * const par)
118{
119 static_cast<void>(stkSize); // unused paramteter in the QK port
120
125 && (0U < prio) && (prio <= QF_MAX_ACTIVE)
126 && (stkSto == nullptr));
127
128 m_eQueue.init(qSto, qLen); // initialize the built-in queue
129
130 m_prio = static_cast<std::uint8_t>(prio); // set the QF prio of this AO
131 QF::add_(this); // make QF aware of this AO
132
133 this->init(par, m_prio); // take the top-most initial tran. (virtual)
134 QS_FLUSH(); // flush the trace buffer to the host
135
136 // See if this AO needs to be scheduled in case QK is already running
138 QF_CRIT_E_();
139 if (QK_sched_() != 0U) { // activation needed?
140 QK_activate_();
141 }
142 QF_CRIT_X_();
143}
144
145//............................................................................
146QSchedStatus QK::schedLock(std::uint_fast8_t const ceiling) noexcept {
148 QF_CRIT_E_();
149
153
154 // first store the previous lock prio if it is below the ceiling
155 QSchedStatus stat;
156 if (static_cast<std::uint_fast8_t>(QK_attr_.lockPrio) < ceiling) {
157 stat = (static_cast<QSchedStatus>(QK_attr_.lockPrio) << 8U);
158 QK_attr_.lockPrio = static_cast<std::uint8_t>(ceiling);
159
161 QS_TIME_PRE_(); // timestamp
162 // prev and new lock prio...
165
166 // add the previous lock holder priority
167 stat |= static_cast<QSchedStatus>(QK_attr_.lockHolder);
168
170 }
171 else {
172 stat = 0xFFU;
173 }
174 QF_CRIT_X_();
175
176 return stat; // return the status to be saved in a stack variable
177}
178
179//............................................................................
180void QK::schedUnlock(QSchedStatus const stat) noexcept {
181 // has the scheduler been actually locked by the last QK_schedLock()?
182 if (stat != 0xFFU) {
183 std::uint_fast8_t const lockPrio =
184 static_cast<std::uint_fast8_t>(QK_attr_.lockPrio);
185 std::uint_fast8_t const prevPrio =
186 static_cast<std::uint_fast8_t>(stat >> 8U);
188 QF_CRIT_E_();
189
194 && (lockPrio > prevPrio));
195
197 QS_TIME_PRE_(); // timestamp
198 QS_2U8_PRE_(lockPrio, prevPrio); //before & after
200
201 // restore the previous lock priority and lock holder
202 QK_attr_.lockPrio = static_cast<std::uint8_t>(prevPrio);
203 QK_attr_.lockHolder = static_cast<std::uint8_t>(stat & 0xFFU);
204
205 // find the highest-prio thread ready to run
206 if (QK_sched_() != 0U) { // priority found?
207 QK_activate_(); // activate any unlocked basic threads
208 }
209
210 QF_CRIT_X_();
211 }
212}
213
214} // namespace QP
215
216//============================================================================
217extern "C" {
218
219QK_Attr QK_attr_; // private attributes of the QK kernel
220
221//............................................................................
222std::uint_fast8_t QK_sched_(void) noexcept {
223 // find the highest-prio AO with non-empty event queue
224 std::uint_fast8_t p = QK_attr_.readySet.findMax();
225
226 // is the highest-prio below the active prio?
227 if (p <= static_cast<std::uint_fast8_t>(QK_attr_.actPrio)) {
228 p = 0U; // active object not eligible
229 }
230 else if (p <= static_cast<std::uint_fast8_t>(QK_attr_.lockPrio)) {
231 p = 0U; // active object not eligible
232 }
233 else {
234 Q_ASSERT_ID(410, p <= QF_MAX_ACTIVE);
235 QK_attr_.nextPrio = static_cast<std::uint8_t>(p); // next AO to run
236 }
237 return p;
238}
239
240//............................................................................
241void QK_activate_(void) noexcept {
242 std::uint_fast8_t const pin =
243 static_cast<std::uint_fast8_t>(QK_attr_.actPrio);
244 std::uint_fast8_t p = static_cast<std::uint_fast8_t>(QK_attr_.nextPrio);
245
246 // QK_attr_.actPrio and QK_attr_.nextPrio must be in ragne
247 Q_REQUIRE_ID(500,
248 (pin <= QF_MAX_ACTIVE)
249 && (0U < p) && (p <= QF_MAX_ACTIVE));
250
251 // QK Context switch callback defined or QS tracing enabled?
252#if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
253 std::uint_fast8_t pprev = pin;
254#endif // QK_ON_CONTEXT_SW || Q_SPY
255
256 QK_attr_.nextPrio = 0U; // clear for the next time
257
258 // loop until no more ready-to-run AOs of higher prio than the initial
259 QP::QActive *a;
260 do {
261 a = QP::QF::active_[p]; // obtain the pointer to the AO
262 QK_attr_.actPrio = static_cast<std::uint8_t>(p); // new active prio
263
265 QS_TIME_PRE_(); // timestamp
266 QS_2U8_PRE_(p, pprev); // sechduled prio & previous prio...
268
269#if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
270 if (p != pprev) { // changing threads?
271
272#ifdef QK_ON_CONTEXT_SW
273 // context-switch callback
274 QK_onContextSw(((pprev != 0U)
275 ? QP::QF::active_[pprev]
276 : nullptr), a);
277#endif // QK_ON_CONTEXT_SW
278
279 pprev = p; // update previous priority
280 }
281#endif // QK_ON_CONTEXT_SW || Q_SPY
282
283 QF_INT_ENABLE(); // unconditionally enable interrupts
284
285 // perform the run-to-completion (RTS) step...
286 // 1. retrieve the event from the AO's event queue, which by this
287 // time must be non-empty and QActive_get_() asserts it.
288 // 2. dispatch the event to the AO's state machine.
289 // 3. determine if event is garbage and collect it if so
290 //
291 QP::QEvt const * const e = a->get_();
292 a->dispatch(e, a->m_prio);
293 QP::QF::gc(e);
294
295 // determine the next highest-priority AO ready to run...
297
298 if (a->m_eQueue.isEmpty()) { // empty queue?
300 }
301
302 // find new highest-prio AO ready to run...
304
305 // is the new priority below the initial preemption threshold?
306 if (p <= pin) {
307 p = 0U; // active object not eligible
308 }
309 else if (p <= static_cast<std::uint_fast8_t>(QK_attr_.lockPrio)) {
310 p = 0U; // active object not eligible
311 }
312 else {
313 Q_ASSERT_ID(510, p <= QF_MAX_ACTIVE);
314 }
315 } while (p != 0U);
316
317 QK_attr_.actPrio = static_cast<std::uint8_t>(pin); // restore the prio
318
319#if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
320
321 if (pin != 0U) { // resuming an active object?
322 a = QP::QF::active_[pin]; // the pointer to the preempted AO
323
325 QS_TIME_PRE_(); // timestamp
326 QS_2U8_PRE_(pin, pprev); // resumed prio & previous prio...
328 }
329 else { // resuming priority==0 --> idle
330 a = nullptr; // QK idle loop
331
333 QS_TIME_PRE_(); // timestamp
334 QS_U8_PRE_(pprev); // previous prio
336 }
337
338#ifdef QK_ON_CONTEXT_SW
339 QK_onContextSw(QP::QF::active_[pprev], a); // context-switch callback
340#endif // QK_ON_CONTEXT_SW
341
342#endif // QK_ON_CONTEXT_SW || Q_SPY
343}
344
345} // 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
QK selective scheduler lock.
Definition: qk.cpp:146
static void schedUnlock(QSchedStatus const stat) noexcept
QK selective scheduler unlock.
Definition: qk.cpp:180
static void onIdle(void)
QK idle callback (customized in BSPs for QK)
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_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
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: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
QK_Attr QK_attr_
global attributes of the QK kernel (extern "C" to be accessible from C)
Definition: qk.cpp:219
void QK_activate_(void) noexcept
QK activator activates the next active object.
Definition: qk.cpp:241
std::uint_fast8_t QK_sched_(void) noexcept
QK scheduler finds the highest-priority thread ready to run.
Definition: qk.cpp:222
std::uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qk.hpp:62
std::uint8_t volatile lockHolder
prio of the lock holder
Definition: qk.hpp:63
#define QK_ISR_CONTEXT_()
Internal port-specific macro that reports the execution context.
Definition: qk.hpp:225
std::uint8_t volatile actPrio
prio of the active AO
Definition: qk.hpp:60
QP::QPSet readySet
QK ready-set of AOs and "naked" threads.
Definition: qk.hpp:65
void QK_onContextSw(QP::QActive *prev, QP::QActive *next)
QK context switch callback (customized in BSPs for QK)
std::uint8_t volatile nextPrio
prio of the next AO to execute
Definition: qk.hpp:61
attributes of the QK kernel (in C for easy access in assembly)
Definition: qk.hpp:59
#define QK_INIT()
Definition: qk_port.hpp:79
#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()
QEvt base class.
Definition: qep.hpp:155
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