QP/C  8.0.0
Real-Time Embedded Framework
Loading...
Searching...
No Matches
qk.c
Go to the documentation of this file.
1//$file${src::qk::qk.c} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
2//
3// Model: qpc.qm
4// File: ${src::qk::qk.c}
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 GNU
18// 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.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
35#define QP_IMPL // this is QP implementation
36#include "qp_port.h" // QP port
37#include "qp_pkg.h" // QP package-scope internal interface
38#include "qsafe.h" // QP Functional Safety (FuSa) Subsystem
39#ifdef Q_SPY // QS software tracing enabled?
40 #include "qs_port.h" // QS port
41 #include "qs_pkg.h" // QS facilities for pre-defined trace records
42#else
43 #include "qs_dummy.h" // disable the QS software tracing
44#endif // Q_SPY
45
46// protection against including this source file in a wrong project
47#ifndef QK_H_
48 #error "Source file included in a project NOT based on the QK kernel"
49#endif // QK_H_
50
51Q_DEFINE_THIS_MODULE("qk")
52
53//$skip${QP_VERSION} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
54// Check for the minimum required QP version
55#if (QP_VERSION < 730U) || (QP_VERSION != ((QP_RELEASE^4294967295U)%0x2710U))
56#error qpc version 7.3.0 or higher required
57#endif
58//$endskip${QP_VERSION} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
59//$define${QK::QK-base} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
60
61//${QK::QK-base::priv_} ......................................................
62QK_Attr QK_priv_;
63
64//${QK::QK-base::schedLock} ..................................................
65//! @static @public @memberof QK
66QSchedStatus QK_schedLock(uint_fast8_t const ceiling) {
69 QF_MEM_SYS();
70
72 Q_INVARIANT_INCRIT(102, QK_priv_.lockCeil
73 == (uint_fast8_t)(~QK_priv_.lockCeil_dis));
74
75 // first store the previous lock prio
76 QSchedStatus stat;
77 if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling?
78 QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio)
79 QS_TIME_PRE(); // timestamp
80 // the previous lock ceiling & new lock ceiling
81 QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)ceiling);
83
84 // previous status of the lock
85 stat = (QSchedStatus)QK_priv_.lockCeil;
86
87 // new status of the lock
88 QK_priv_.lockCeil = ceiling;
89 #ifndef Q_UNSAFE
90 QK_priv_.lockCeil_dis = (uint_fast8_t)(~ceiling);
91 #endif
92 }
93 else {
94 stat = 0xFFU; // scheduler not locked
95 }
96
97 QF_MEM_APP();
99
100 return stat; // return the status to be saved in a stack variable
101}
102
103//${QK::QK-base::schedUnlock} ................................................
104//! @static @public @memberof QK
105void QK_schedUnlock(QSchedStatus const prevCeil) {
106 // has the scheduler been actually locked by the last QK_schedLock()?
107 if (prevCeil != 0xFFU) {
110 QF_MEM_SYS();
111
112 Q_INVARIANT_INCRIT(202, QK_priv_.lockCeil
113 == (uint_fast8_t)(~QK_priv_.lockCeil_dis));
115 && (QK_priv_.lockCeil > prevCeil));
116
117 QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio)
118 QS_TIME_PRE(); // timestamp
119 // current lock ceiling (old), previous lock ceiling (new)
120 QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)prevCeil);
121 QS_END_PRE()
122
123 // restore the previous lock ceiling
124 QK_priv_.lockCeil = prevCeil;
125 #ifndef Q_UNSAFE
126 QK_priv_.lockCeil_dis = (uint_fast8_t)(~prevCeil);
127 #endif
128
129 // find if any AOs should be run after unlocking the scheduler
130 if (QK_sched_() != 0U) { // preemption needed?
131 QK_activate_(); // activate any unlocked AOs
132 }
133
134 QF_MEM_APP();
135 QF_CRIT_EXIT();
136 }
137}
138
139//${QK::QK-base::sched_} .....................................................
140//! @static @private @memberof QK
141uint_fast8_t QK_sched_(void) {
142 // NOTE: this function is entered with interrupts DISABLED
143
144 Q_INVARIANT_INCRIT(402, QPSet_verify_(&QK_priv_.readySet,
145 &QK_priv_.readySet_dis));
146 uint_fast8_t p;
147 if (QPSet_isEmpty(&QK_priv_.readySet)) {
148 p = 0U; // no activation needed
149 }
150 else {
151 // find the highest-prio AO with non-empty event queue
152 p = QPSet_findMax(&QK_priv_.readySet);
153
155 QK_priv_.actThre == (uint_fast8_t)(~QK_priv_.actThre_dis));
156
157 // is the AO's prio. below the active preemption-threshold?
158 if (p <= QK_priv_.actThre) {
159 p = 0U; // no activation needed
160 }
161 else {
162 Q_INVARIANT_INCRIT(422, QK_priv_.lockCeil
163 == (uint_fast8_t)(~QK_priv_.lockCeil_dis));
164
165 // is the AO's prio. below the lock-ceiling?
166 if (p <= QK_priv_.lockCeil) {
167 p = 0U; // no activation needed
168 }
169 else {
170 Q_INVARIANT_INCRIT(432, QK_priv_.nextPrio
171 == (uint_fast8_t)(~QK_priv_.nextPrio_dis));
172 QK_priv_.nextPrio = p; // next AO to run
173 #ifndef Q_UNSAFE
174 QK_priv_.nextPrio_dis = (uint_fast8_t)(~QK_priv_.nextPrio);
175 #endif
176 }
177 }
178 }
179
180 return p;
181}
182
183//${QK::QK-base::activate_} ..................................................
184//! @static @private @memberof QK
185void QK_activate_(void) {
186 // NOTE: this function is entered with interrupts DISABLED
187
188 uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio.
189 uint_fast8_t p = QK_priv_.nextPrio; // next prio to run
190
192 (prio_in == (uint_fast8_t)(~QK_priv_.actPrio_dis))
193 && (p == (uint_fast8_t)(~QK_priv_.nextPrio_dis)));
194 Q_REQUIRE_INCRIT(510, (prio_in <= QF_MAX_ACTIVE)
195 && (0U < p) && (p <= QF_MAX_ACTIVE));
196
197 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
198 uint_fast8_t pprev = prio_in;
199 #endif // QF_ON_CONTEXT_SW || Q_SPY
200
201 QK_priv_.nextPrio = 0U; // clear for the next time
202 #ifndef Q_UNSAFE
203 QK_priv_.nextPrio_dis = (uint_fast8_t)(~0U);
204 #endif
205
206 uint_fast8_t pthre_in;
207 QActive *a;
208 if (prio_in == 0U) { // preempting the idle thread?
209 pthre_in = 0U;
210 }
211 else {
212 a = QActive_registry_[prio_in];
213 Q_ASSERT_INCRIT(510, a != (QActive *)0);
214
215 pthre_in = (uint_fast8_t)a->pthre;
216 Q_INVARIANT_INCRIT(511, pthre_in ==
217 (uint_fast8_t)(~(uint_fast8_t)a->pthre_dis & 0xFFU));
218 }
219
220 // loop until no more ready-to-run AOs of higher pthre than the initial
221 do {
222 a = QActive_registry_[p]; // obtain the pointer to the AO
223 Q_ASSERT_INCRIT(520, a != (QActive *)0); // the AO must be registered
224 uint_fast8_t const pthre = (uint_fast8_t)a->pthre;
225 Q_INVARIANT_INCRIT(522, pthre ==
226 (uint_fast8_t)(~(uint_fast8_t)a->pthre_dis & 0xFFU));
227
228 // set new active prio. and preemption-threshold
229 QK_priv_.actPrio = p;
230 QK_priv_.actThre = pthre;
231 #ifndef Q_UNSAFE
232 QK_priv_.actPrio_dis = (uint_fast8_t)(~p);
233 QK_priv_.actThre_dis = (uint_fast8_t)(~pthre);
234 #endif
235
236 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
237 if (p != pprev) { // changing threads?
238
239 QS_BEGIN_PRE(QS_SCHED_NEXT, p)
240 QS_TIME_PRE(); // timestamp
241 QS_2U8_PRE(p, // prio. of the scheduled AO
242 pprev); // previous prio.
243 QS_END_PRE()
244
245 #ifdef QF_ON_CONTEXT_SW
246 QF_onContextSw(QActive_registry_[pprev], a);
247 #endif // QF_ON_CONTEXT_SW
248
249 pprev = p; // update previous prio.
250 }
251 #endif // QF_ON_CONTEXT_SW || Q_SPY
252
253 QF_MEM_APP();
254 QF_INT_ENABLE(); // unconditionally enable interrupts
255
256 QEvt const * const e = QActive_get_(a);
257 // NOTE QActive_get_() performs QF_MEM_APP() before return
258
259 // dispatch event (virtual call)
260 (*a->super.vptr->dispatch)(&a->super, e, p);
261 #if (QF_MAX_EPOOL > 0U)
262 QF_gc(e);
263 #endif
264
265 // determine the next highest-prio. AO ready to run...
266 QF_INT_DISABLE(); // unconditionally disable interrupts
267 QF_MEM_SYS();
268
269 // internal integrity check (duplicate inverse storage)
270 Q_INVARIANT_INCRIT(532, QPSet_verify_(&QK_priv_.readySet,
271 &QK_priv_.readySet_dis));
272
273 if (a->eQueue.frontEvt == (QEvt *)0) { // empty queue?
274 QPSet_remove(&QK_priv_.readySet, p);
275 #ifndef Q_UNSAFE
276 QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis);
277 #endif
278 }
279
280 if (QPSet_isEmpty(&QK_priv_.readySet)) {
281 p = 0U; // no activation needed
282 }
283 else {
284 // find new highest-prio AO ready to run...
285 p = QPSet_findMax(&QK_priv_.readySet);
286
287 // is the new prio. below the initial preemption-threshold?
288 if (p <= pthre_in) {
289 p = 0U; // no activation needed
290 }
291 else {
292 Q_INVARIANT_INCRIT(542, QK_priv_.lockCeil
293 == (uint_fast8_t)(~QK_priv_.lockCeil_dis));
294
295 // is the AO's prio. below the lock preemption-threshold?
296 if (p <= QK_priv_.lockCeil) {
297 p = 0U; // no activation needed
298 }
299 else {
301 }
302 }
303 }
304 } while (p != 0U);
305
306 // restore the active prio. and preemption-threshold
307 QK_priv_.actPrio = prio_in;
308 QK_priv_.actThre = pthre_in;
309 #ifndef Q_UNSAFE
310 QK_priv_.actPrio_dis = (uint_fast8_t)(~QK_priv_.actPrio);
311 QK_priv_.actThre_dis = (uint_fast8_t)(~QK_priv_.actThre);
312 #endif
313
314 #if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
315 if (prio_in != 0U) { // resuming an active object?
316 a = QActive_registry_[prio_in]; // pointer to preempted AO
317
318 QS_BEGIN_PRE(QS_SCHED_NEXT, prio_in)
319 QS_TIME_PRE(); // timestamp
320 // prio. of the resumed AO, previous prio.
321 QS_2U8_PRE(prio_in, pprev);
322 QS_END_PRE()
323 }
324 else { // resuming prio.==0 --> idle
325 a = (QActive *)0; // QK idle loop
326
327 QS_BEGIN_PRE(QS_SCHED_IDLE, pprev)
328 QS_TIME_PRE(); // timestamp
329 QS_U8_PRE(pprev); // previous prio.
330 QS_END_PRE()
331 }
332
333 #ifdef QF_ON_CONTEXT_SW
334 QF_onContextSw(QActive_registry_[pprev], a);
335 #endif // QF_ON_CONTEXT_SW
336
337 #endif // QF_ON_CONTEXT_SW || Q_SPY
338}
339//$enddef${QK::QK-base} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
340
341//$define${QK::QF-cust} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
342
343//${QK::QF-cust::init} .......................................................
344//! @static @public @memberof QF
345void QF_init(void) {
346 QF_bzero_(&QF_priv_, sizeof(QF_priv_));
347 QF_bzero_(&QK_priv_, sizeof(QK_priv_));
348 QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_));
349
350 // setup the QK scheduler as initially locked and not running
351 QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
352
353 #ifndef Q_UNSAFE
354 QPSet_update_(&QK_priv_.readySet, &QK_priv_.readySet_dis);
355 QK_priv_.actPrio_dis = (uint_fast8_t)(~0U);
356 QK_priv_.nextPrio_dis = (uint_fast8_t)(~0U);
357 QK_priv_.actThre_dis = (uint_fast8_t)(~0U);
358 QK_priv_.lockCeil_dis = (uint_fast8_t)(~QK_priv_.lockCeil);
359 #endif
360
361 QTimeEvt_init(); // initialize QTimeEvts
362
363 #ifdef QK_INIT
364 QK_INIT(); // port-specific initialization of the QK kernel
365 #endif
366}
367
368//${QK::QF-cust::stop} .......................................................
369//! @static @public @memberof QF
370void QF_stop(void) {
371 QF_onCleanup(); // application-specific cleanup callback
372 // nothing else to do for the preemptive QK kernel
373}
374
375//${QK::QF-cust::run} ........................................................
376//! @static @public @memberof QF
377int_t QF_run(void) {
378 #ifdef Q_SPY
379 // produce the QS_QF_RUN trace record
381 QF_MEM_SYS();
382 QS_beginRec_((uint_fast8_t)QS_QF_RUN);
383 QS_endRec_();
384 QF_MEM_APP();
386 #endif // Q_SPY
387
388 QF_onStartup(); // application-specific startup callback
389
391 QF_MEM_SYS();
392
393 #ifdef QK_START
394 QK_START(); // port-specific startup of the QK kernel
395 #endif
396
397 QK_priv_.lockCeil = 0U; // unlock the QK scheduler
398 #ifndef Q_UNSAFE
399 QK_priv_.lockCeil_dis = (uint_fast8_t)(~0U);
400 #endif
401
402 #ifdef QF_ON_CONTEXT_SW
403 // officially switch to the idle context
404 QF_onContextSw((QActive *)0, QActive_registry_[QK_priv_.nextPrio]);
405 #endif
406
407 // activate AOs to process events posted so far
408 if (QK_sched_() != 0U) {
409 QK_activate_();
410 }
411
412 QF_MEM_APP();
414
415 for (;;) { // QK idle loop...
416 QK_onIdle(); // application-specific QK on-idle callback
417 }
418
419 #ifdef __GNUC__
420 return 0;
421 #endif
422}
423//$enddef${QK::QF-cust} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
424
425//$define${QK::QActive} vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv
426
427//${QK::QActive} .............................................................
428
429//${QK::QActive::start} ......................................................
430//! @public @memberof QActive
431void QActive_start(QActive * const me,
432 QPrioSpec const prioSpec,
433 QEvtPtr * const qSto,
434 uint_fast16_t const qLen,
435 void * const stkSto,
436 uint_fast16_t const stkSize,
437 void const * const par)
438{
439 Q_UNUSED_PAR(stkSto); // not needed in QK
440 Q_UNUSED_PAR(stkSize); // not needed in QK
441
444 QF_MEM_SYS();
445
447 && (stkSto == (void *)0));
448 QF_MEM_APP();
449 QF_CRIT_EXIT();
450
451 me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO
452 me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold
453 QActive_register_(me); // make QF aware of this active object
454
455 QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue
456
457 // top-most initial tran. (virtual call)
458 (*me->super.vptr->init)(&me->super, par, me->prio);
459 QS_FLUSH(); // flush the trace buffer to the host
460
461 // See if this AO needs to be scheduled if QK is already running
463 QF_MEM_SYS();
464 if (QK_sched_() != 0U) { // activation needed?
465 QK_activate_();
466 }
467 QF_MEM_APP();
468 QF_CRIT_EXIT();
469}
470//$enddef${QK::QActive} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
QF_Attr QF_priv_
Definition qf_act.c:62
uint_fast8_t QSchedStatus
Definition qk.h:52
#define QF_MEM_APP()
Definition qp.h:1280
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:525
QEvt const * QEvtPtr
Pointer to const event instances passed around in QP Framework.
Definition qp.h:768
#define QF_MEM_SYS()
Definition qp.h:1275
int int_t
Alias for assertion-ID numbers in QP assertions and return from QF_run()
Definition qp.h:96
uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.h:595
#define QF_MAX_ACTIVE
Maximum # Active Objects in the system (1..64)
Definition qp_config.h:123
Internal (package scope) QP/C interface.
Sample QP/C port.
#define QK_ISR_CONTEXT_()
Definition qp_port.h:105
#define QF_INT_DISABLE()
Disable interrupts.
Definition qp_port.h:37
#define QF_INT_ENABLE()
Enable interrupts.
Definition qp_port.h:40
#define QS_TIME_PRE()
Definition qs_dummy.h:147
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.h:144
#define QS_FLUSH()
Definition qs_dummy.h:76
#define QS_U8_PRE(data_)
Definition qs_dummy.h:143
#define QS_END_PRE()
Definition qs_dummy.h:142
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.h:141
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:50
#define Q_ASSERT_INCRIT(id_, expr_)
Definition qsafe.h:64
#define Q_INVARIANT_INCRIT(id_, expr_)
Definition qsafe.h:146
#define QF_CRIT_EXIT()
Definition qsafe.h:54
#define Q_REQUIRE_INCRIT(id_, expr_)
Definition qsafe.h:128
#define QF_CRIT_STAT
Definition qsafe.h:46
Active object class (based on the QHsm implementation strategy)
Definition qp.h:779
uint8_t pthre_dis
Definition qp.h:811
QACTIVE_EQUEUE_TYPE eQueue
Port-dependent event-queue type (often QEQueue)
Definition qp.h:801
QAsm super
Definition qp.h:781
uint8_t prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:784
uint8_t pthre
Preemption-threshold [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:787
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also Object Orientation)
Definition qp.h:260
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:278
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:280
Event class.
Definition qp.h:131
Private attributes of the QK kernel.
Definition qk.h:59