QP/C 8.1.2
Real-Time Event Framework
Loading...
Searching...
No Matches
qk.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 internal 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
40// protection against including this source file in a wrong project
41#ifndef QK_H_
42 #error Source file included in a project NOT based on the QK kernel
43#endif // QK_H_
44
45Q_DEFINE_THIS_MODULE("qk")
46
47//............................................................................
49
50//............................................................................
51//! @static @public @memberof QK
52QSchedStatus QK_schedLock(uint8_t const ceiling) {
55
56 // scheduler should never be locked inside an ISR
58
59 QSchedStatus stat = 0xFFU; // assume scheduler NOT locked
60 if (ceiling > QK_priv_.lockCeil) { // increasing the lock ceiling?
62 QS_TIME_PRE(); // timestamp
63 // the previous lock ceiling & new lock ceiling
64 QS_2U8_PRE(QK_priv_.lockCeil, ceiling);
66
67 // previous status of the lock
68 stat = (QSchedStatus)QK_priv_.lockCeil;
69
70 // new status of the lock
71 QK_priv_.lockCeil = (uint8_t)ceiling;
72 }
74
75 return stat; // return the status to be saved in a stack variable
76}
77
78//............................................................................
79//! @static @public @memberof QK
80void QK_schedUnlock(QSchedStatus const prevCeil) {
81 // has the scheduler been actually locked by the last QK_schedLock()?
82 if (prevCeil != 0xFFU) {
85
86 // scheduler should never be unlocked inside an ISR
88
89 // the current lock-ceiling must be higher than the previous ceiling
90 Q_REQUIRE_INCRIT(220, QK_priv_.lockCeil > prevCeil);
91
93 QS_TIME_PRE(); // timestamp
94 // current lock ceiling (old), previous lock ceiling (new)
95 QS_2U8_PRE(QK_priv_.lockCeil,
96 (uint8_t)prevCeil);
98
99 // restore the previous lock ceiling
100 QK_priv_.lockCeil = (uint8_t)prevCeil;
101
102 // find if any AOs should be run after unlocking the scheduler
103 if (QK_sched_() != 0U) { // preemption needed?
104 QK_activate_(); // activate any unlocked AOs
105 }
106
107 QF_CRIT_EXIT();
108 }
109}
110
111//............................................................................
112//! @static @private @memberof QK
113uint_fast8_t QK_sched_(void) {
114 // NOTE: this function is entered with interrupts DISABLED
115
116 uint8_t p = 0U; // assume NO activation needed
117 if (QPSet_notEmpty(&QK_priv_.readySet)) {
118 // find the highest-prio AO with non-empty event queue
119 p = (uint8_t)QPSet_findMax(&QK_priv_.readySet);
120
121 // is the AO's prio. below the active preemption-threshold?
122 if (p <= QK_priv_.actThre) {
123 p = 0U; // no activation needed
124 }
125 else {
126 // is the AO's prio. below the lock-ceiling?
127 if (p <= QK_priv_.lockCeil) {
128 p = 0U; // no activation needed
129 }
130 else {
131 QK_priv_.nextPrio = p; // next AO to run
132 }
133 }
134 }
135
136 return p; // the next priority or 0
137}
138
139//............................................................................
140//! @static @private @memberof QK
141uint_fast8_t QK_sched_act_(
142 QActive const * const act,
143 uint_fast8_t const pthre_in)
144{
145 // NOTE: this function is entered with interrupts DISABLED
146
147 uint8_t p = act->prio;
148 if (act->eQueue.frontEvt == (QEvt *)0) { // empty queue?
149 QPSet_remove(&QK_priv_.readySet, p);
150 }
151
152 if (QPSet_isEmpty(&QK_priv_.readySet)) { // no AOs ready to run?
153 p = 0U; // no activation needed
154 }
155 else {
156 // find new highest-prio AO ready to run...
157 p = (uint8_t)QPSet_findMax(&QK_priv_.readySet);
158 // NOTE: p is guaranteed to be <= QF_MAX_ACTIVE
159
160 // is the new prio. below the initial preemption-threshold?
161 if (p <= pthre_in) {
162 p = 0U; // no activation needed
163 }
164 else {
165 // is the AO's prio. below the lock preemption-threshold?
166 if (p <= QK_priv_.lockCeil) {
167 p = 0U; // no activation needed
168 }
169 }
170 }
171
172 return p;
173}
174
175//............................................................................
176//! @static @private @memberof QK
177void QK_activate_(void) {
178 // NOTE: this function is entered with interrupts DISABLED
179
180 uint8_t const prio_in = QK_priv_.actPrio; // save initial prio.
181 uint8_t p = QK_priv_.nextPrio; // next prio to run
182
183 // the activated AO's prio must be in range and cannot be 0 (idle thread)
184 Q_REQUIRE_INCRIT(520, (0U < p) && (p <= QF_MAX_ACTIVE));
185
186 // the initial prio. must be lower than the activated AO's prio.
187 Q_REQUIRE_INCRIT(530, prio_in < p);
188
189#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
190 uint8_t pprev = prio_in;
191#endif // QF_ON_CONTEXT_SW || Q_SPY
192
193 QK_priv_.nextPrio = 0U; // clear for the next time
194
195 uint8_t pthre_in = 0U; // assume preempting the idle thread
196 if (prio_in > 0U) { // preempting a regular thread (NOT the idle thread)?
197 QActive const * const a = QActive_registry_[prio_in];
198
199 // the AO must be registered at prio. prio_in
200 Q_ASSERT_INCRIT(540, a != (QActive *)0);
201
202 pthre_in = a->pthre;
203 }
204
205 // loop until no more ready-to-run AOs of higher pthre than the initial
206 do {
207 QActive * const a = QActive_registry_[p];
208
209 // the AO must be registered at prio. p
210 Q_ASSERT_INCRIT(570, a != (QActive *)0);
211 uint8_t const pthre = a->pthre;
212
213 // set new active prio. and preemption-threshold
214 QK_priv_.actPrio = p;
215 QK_priv_.actThre = pthre;
216
217#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
218 if (p != pprev) { // changing threads?
219
221 QS_TIME_PRE(); // timestamp
222 QS_2U8_PRE(p, pprev);
223 QS_END_PRE()
224
225#ifdef QF_ON_CONTEXT_SW
226 QF_onContextSw(QActive_registry_[pprev], a);
227#endif // QF_ON_CONTEXT_SW
228
229 pprev = p; // update previous prio.
230 }
231#endif // QF_ON_CONTEXT_SW || Q_SPY
232
233 QF_INT_ENABLE(); // unconditionally enable interrupts
234
235 QEvt const * const e = QActive_get_(a);
236 // NOTE QActive_get_() performs QF_MEM_APP() before return
237
238 // dispatch event (virtual call)
239 (*a->super.vptr->dispatch)(&a->super, e, p);
240#if (QF_MAX_EPOOL > 0U)
241 QF_gc(e);
242#endif
243
244 // determine the next highest-prio. AO ready to run...
245 QF_INT_DISABLE(); // unconditionally disable interrupts
246
247 // schedule next AO
248 p = (uint8_t)QK_sched_act_(a, pthre_in);
249
250 } while (p != 0U);
251
252 // restore the active prio. and preemption-threshold
253 QK_priv_.actPrio = prio_in;
254 QK_priv_.actThre = pthre_in;
255
256#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
257 if (prio_in != 0U) { // resuming an active object?
259 QS_TIME_PRE(); // timestamp
260 // prio. of the resumed AO, previous prio.
261 QS_2U8_PRE(prio_in, pprev);
262 QS_END_PRE()
263
264#ifdef QF_ON_CONTEXT_SW
265 QF_onContextSw(QActive_registry_[pprev],
266 QActive_registry_[prio_in]);
267#endif // QF_ON_CONTEXT_SW
268 }
269 else { // resuming prio.==0 --> idle
271 QS_TIME_PRE(); // timestamp
272 QS_U8_PRE(pprev); // previous prio.
273 QS_END_PRE()
274
275#ifdef QF_ON_CONTEXT_SW
276 QF_onContextSw(QActive_registry_[pprev], (QActive *)0);
277#endif // QF_ON_CONTEXT_SW
278 }
279
280#endif // QF_ON_CONTEXT_SW || Q_SPY
281}
282
283//............................................................................
284//! @static @public @memberof QF
285void QF_init(void) {
286 // setup the QK scheduler as initially locked and not running
287 QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
288
289#ifndef Q_UNSAFE
290 QTimeEvt_init(); // initialize QTimeEvts
291#endif // Q_UNSAFE
292
293#ifdef QK_INIT
294 QK_INIT(); // port-specific initialization of the QK kernel
295#endif
296}
297
298//............................................................................
299//! @static @public @memberof QF
300void QF_stop(void) {
301 QF_onCleanup(); // application-specific cleanup callback
302 // nothing else to do for the preemptive QK kernel
303}
304
305//............................................................................
306//! @static @public @memberof QF
307int_t QF_run(void) {
309#ifdef Q_SPY
310 // produce the QS_QF_RUN trace record
311 QS_beginRec_((uint_fast8_t)QS_QF_RUN);
312 QS_endRec_();
313#endif // Q_SPY
314
315#ifdef QK_START
316 QK_START(); // port-specific startup of the QK kernel
317#endif
318
319 QK_priv_.lockCeil = 0U; // unlock the QK scheduler
320
321#ifdef QF_ON_CONTEXT_SW
322 // officially switch to the idle context
323 QF_onContextSw((QActive *)0, QActive_registry_[QK_priv_.nextPrio]);
324#endif
325
326 // activate AOs to process events posted so far
327 if (QK_sched_() != 0U) {
328 QK_activate_();
329 }
330
332
333 QF_onStartup(); // app. callback: configure and enable interrupts
334
335 for (;;) { // QK idle loop...
336 QK_onIdle(); // application-specific QK idle callback
337 }
338}
339
340//............................................................................
341//! @public @memberof QActive
342void QActive_start(QActive * const me,
343 QPrioSpec const prioSpec,
344 QEvtPtr * const qSto,
345 uint_fast16_t const qLen,
346 void * const stkSto,
347 uint_fast16_t const stkSize,
348 void const * const par)
349{
350 Q_UNUSED_PAR(stkSto); // not needed in QK
351 Q_UNUSED_PAR(stkSize); // not needed in QK
352
355
356 // the VPTR for this AO must be valid
357 Q_REQUIRE_INCRIT(900, me->super.vptr != (struct QAsmVtable *)0);
358
359 // stack storage must NOT be provided for an AO (QK does not need it)
360 Q_REQUIRE_INCRIT(910, stkSto == (void *)0);
361 QF_CRIT_EXIT();
362
363 me->prio = (uint8_t)(prioSpec & 0xFFU); // prio. of the AO
364 me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold
365 QActive_register_(me); // register this AO with the framework
366
367 QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue
368
369 // top-most initial tran. (virtual call)
370 (*me->super.vptr->init)(&me->super, par, me->prio);
371 QS_FLUSH(); // flush the trace buffer to the host
372
373 // see if this AO needs to be scheduled if QK is already running
375 if (QK_sched_() != 0U) { // activation needed?
376 QK_activate_();
377 }
378 QF_CRIT_EXIT();
379}
QK QK_priv_
Definition qk.c:48
uint_fast8_t QSchedStatus
The scheduler lock status for QK_schedLock() and QK_schedUnlock().
Definition qk.h:34
#define Q_UNUSED_PAR(par_)
Helper macro to mark unused parameters of functions.
Definition qp.h:90
QEvt const * QEvtPtr
Pointer to const event instances passed around in QP/C Framework.
Definition qp.h:133
int int_t
Alias for assertion-ID numbers in QP assertions and return from QF_run().
Definition qp.h:87
uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.h:383
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64).
Definition qp_config.h:100
QP/C Framework in C internal (package-scope) interface.
Sample QP/C port.
#define QK_ISR_CONTEXT_()
Port-specific method to check if the QK kernel executes in the ISR context (used internally in QK onl...
Definition qp_port.h:197
#define QF_INT_DISABLE()
Port-specific interrupt disable.
Definition qp_port.h:56
#define QF_INT_ENABLE()
Port-specific interrupt enable.
Definition qp_port.h:64
#define QS_TIME_PRE()
Definition qs.h:340
@ QS_QF_RUN
QF_run() was entered.
Definition qs.h:152
@ QS_SCHED_IDLE
scheduler restored the idle task
Definition qs.h:129
@ QS_SCHED_LOCK
scheduler was locked
Definition qs.h:126
@ QS_SCHED_UNLOCK
scheduler was unlocked
Definition qs.h:127
@ QS_SCHED_NEXT
scheduler started next task
Definition qs.h:128
#define QS_FLUSH()
Definition qs.h:262
QS (QP/Spy software tracing) internal (package-scope) interface.
#define QS_2U8_PRE(data1_, data2_)
Output two pre-formatted unsigned 8-bit integer data elements.
Definition qs_pkg.h:41
#define QS_U8_PRE(data_)
Output pre-formatted unsigned 8-bit integer data element.
Definition qs_pkg.h:40
#define QS_END_PRE()
Pre-formatted QS trace record end.
Definition qs_pkg.h:38
#define QS_BEGIN_PRE(rec_, qsId_)
Pre-formatted QS trace record begin.
Definition qs_pkg.h:32
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:40
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section).
Definition qsafe.h:54
#define QF_CRIT_EXIT()
Definition qsafe.h:44
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section).
Definition qsafe.h:98
#define QF_CRIT_STAT
Definition qsafe.h:36
int_t QF_run(void)
Definition qutest.c:215
void QF_init(void)
Definition qutest.c:138
void QF_stop(void)
Definition qutest.c:143
Active object class (based on the QHsm implementation strategy).
Definition qp.h:447
QAsm super
Definition qp.h:448
QACTIVE_EQUEUE_TYPE eQueue
Port-dependent event-queue type (often QEQueue).
Definition qp.h:461
uint8_t prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:449
uint8_t pthre
Preemption-threshold [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:450
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OOA).
Definition qp.h:179
Virtual table for the QAsm class.
Definition qp.h:208
void(* init)(QAsm *const me, void const *const e, uint_fast8_t const qsId)
Virtual function to take the top-most initial transition in the state machine.
Definition qp.h:209
void(* dispatch)(QAsm *const me, QEvt const *const e, uint_fast8_t const qsId)
Virtual function to dispatch an event to the state machine.
Definition qp.h:211
Event class.
Definition qp.h:100
QK preemptive non-blocking kernel (QK namespace emulated as a "class" in C.
Definition qk.h:38
QSchedStatus QK_schedLock(uint8_t const ceiling)
QK selective scheduler lock.
Definition qk.c:52
uint_fast8_t QK_sched_(void)
QK scheduler finds the highest-priority AO ready to run.
Definition qk.c:113
void QK_activate_(void)
QK activator activates the next active object. The activated AO preempts the currently executing AOs.
Definition qk.c:177
uint_fast8_t QK_sched_act_(QActive const *const act, uint_fast8_t const pthre_in)
QK internal helper function to determine whether activation is needed.
Definition qk.c:141
QK QK_priv_
Definition qk.h:68
void QK_schedUnlock(QSchedStatus const prevCeil)
QK selective scheduler unlock.
Definition qk.c:80