QP/C  8.0.3
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//............................................................................
48QK_Attr QK_priv_;
49
50//............................................................................
51//! @static @public @memberof QK
52QSchedStatus QK_schedLock(uint_fast8_t const ceiling) {
55
57
58 // first store the previous lock prio
59 QSchedStatus stat = 0xFFU; // assume scheduler NOT locked
60 if (ceiling > QK_priv_.lockCeil) { // raising the lock ceiling?
61 QS_BEGIN_PRE(QS_SCHED_LOCK, QK_priv_.actPrio)
62 QS_TIME_PRE(); // timestamp
63 // the previous lock ceiling & new lock ceiling
64 QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)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 = 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
87 && (QK_priv_.lockCeil > prevCeil));
88 QS_BEGIN_PRE(QS_SCHED_UNLOCK, QK_priv_.actPrio)
89 QS_TIME_PRE(); // timestamp
90 // current lock ceiling (old), previous lock ceiling (new)
91 QS_2U8_PRE((uint8_t)QK_priv_.lockCeil, (uint8_t)prevCeil);
93
94 // restore the previous lock ceiling
95 QK_priv_.lockCeil = prevCeil;
96
97 // find if any AOs should be run after unlocking the scheduler
98 if (QK_sched_() != 0U) { // preemption needed?
99 QK_activate_(); // activate any unlocked AOs
100 }
101
102 QF_CRIT_EXIT();
103 }
104}
105
106//............................................................................
107//! @static @private @memberof QK
108uint_fast8_t QK_sched_(void) {
109 // NOTE: this function is entered with interrupts DISABLED
110
111 uint_fast8_t p = 0U; // assume NO activation needed
112 if (QPSet_notEmpty(&QK_priv_.readySet)) {
113 // find the highest-prio AO with non-empty event queue
114 p = QPSet_findMax(&QK_priv_.readySet);
115
116 // is the AO's prio. below the active preemption-threshold?
117 if (p <= QK_priv_.actThre) {
118 p = 0U; // no activation needed
119 }
120 else {
121 // is the AO's prio. below the lock-ceiling?
122 if (p <= QK_priv_.lockCeil) {
123 p = 0U; // no activation needed
124 }
125 else {
126 QK_priv_.nextPrio = p; // next AO to run
127 }
128 }
129 }
130
131 return p;
132}
133
134//............................................................................
135//! @static @private @memberof QK
136uint_fast8_t QK_sched_act_(
137 QActive const * const act,
138 uint_fast8_t const pthre_in)
139{
140 // NOTE: this function is entered with interrupts DISABLED
141
142 uint_fast8_t p = act->prio;
143 if (act->eQueue.frontEvt == (QEvt *)0) { // empty queue?
144 QPSet_remove(&QK_priv_.readySet, p);
145 }
146
147 if (QPSet_isEmpty(&QK_priv_.readySet)) {
148 p = 0U; // no activation needed
149 }
150 else {
151 // find new highest-prio AO ready to run...
152 p = QPSet_findMax(&QK_priv_.readySet);
153 // NOTE: p is guaranteed to be <= QF_MAX_ACTIVE
154
155 // is the new prio. below the initial preemption-threshold?
156 if (p <= pthre_in) {
157 p = 0U; // no activation needed
158 }
159 else {
160 // is the AO's prio. below the lock preemption-threshold?
161 if (p <= QK_priv_.lockCeil) {
162 p = 0U; // no activation needed
163 }
164 }
165 }
166
167 return p;
168}
169
170//............................................................................
171//! @static @private @memberof QK
172void QK_activate_(void) {
173 // NOTE: this function is entered with interrupts DISABLED
174
175 uint_fast8_t const prio_in = QK_priv_.actPrio; // save initial prio.
176 uint_fast8_t p = QK_priv_.nextPrio; // next prio to run
177
178 Q_REQUIRE_INCRIT(500, (prio_in <= QF_MAX_ACTIVE)
179 && (0U < p) && (p <= QF_MAX_ACTIVE));
180
181#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
182 uint_fast8_t pprev = prio_in;
183#endif // QF_ON_CONTEXT_SW || Q_SPY
184
185 QK_priv_.nextPrio = 0U; // clear for the next time
186
187 uint_fast8_t pthre_in = 0U; // assume preempting the idle thread
188 if (prio_in != 0U) { // preempting NOT the idle thread
189 QActive const * const a = QActive_registry_[prio_in];
190 Q_ASSERT_INCRIT(510, a != (QActive *)0);
191
192 pthre_in = (uint_fast8_t)a->pthre;
193 }
194
195 // loop until no more ready-to-run AOs of higher pthre than the initial
196 do {
197 QActive * const a = QActive_registry_[p];
198 Q_ASSERT_INCRIT(520, a != (QActive *)0); // the AO must be registered
199 uint_fast8_t const pthre = (uint_fast8_t)a->pthre;
200
201 // set new active prio. and preemption-threshold
202 QK_priv_.actPrio = p;
203 QK_priv_.actThre = pthre;
204
205#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
206 if (p != pprev) { // changing threads?
207
208 QS_BEGIN_PRE(QS_SCHED_NEXT, p)
209 QS_TIME_PRE(); // timestamp
210 QS_2U8_PRE(p, // prio. of the scheduled AO
211 pprev); // previous prio.
212 QS_END_PRE()
213
214#ifdef QF_ON_CONTEXT_SW
215 QF_onContextSw(QActive_registry_[pprev], a);
216#endif // QF_ON_CONTEXT_SW
217
218 pprev = p; // update previous prio.
219 }
220#endif // QF_ON_CONTEXT_SW || Q_SPY
221
222 QF_INT_ENABLE(); // unconditionally enable interrupts
223
224 QEvt const * const e = QActive_get_(a);
225
226 // dispatch event (virtual call)
227 (*a->super.vptr->dispatch)(&a->super, e, p);
228#if (QF_MAX_EPOOL > 0U)
229 QF_gc(e);
230#endif
231
232 // determine the next highest-prio. AO ready to run...
233 QF_INT_DISABLE(); // unconditionally disable interrupts
234 p = QK_sched_act_(a, pthre_in); // schedule next AO
235
236 } while (p != 0U);
237
238 // restore the active prio. and preemption-threshold
239 QK_priv_.actPrio = prio_in;
240 QK_priv_.actThre = pthre_in;
241
242#if (defined QF_ON_CONTEXT_SW) || (defined Q_SPY)
243 if (prio_in != 0U) { // resuming an active object?
244 QS_BEGIN_PRE(QS_SCHED_NEXT, prio_in)
245 QS_TIME_PRE(); // timestamp
246 // prio. of the resumed AO, previous prio.
247 QS_2U8_PRE(prio_in, pprev);
248 QS_END_PRE()
249
250#ifdef QF_ON_CONTEXT_SW
251 QF_onContextSw(QActive_registry_[pprev],
252 QActive_registry_[prio_in]);
253#endif // QF_ON_CONTEXT_SW
254 }
255 else { // resuming prio.==0 --> idle
256 QS_BEGIN_PRE(QS_SCHED_IDLE, pprev)
257 QS_TIME_PRE(); // timestamp
258 QS_U8_PRE(pprev); // previous prio.
259 QS_END_PRE()
260
261#ifdef QF_ON_CONTEXT_SW
262 QF_onContextSw(QActive_registry_[pprev], (QActive *)0);
263#endif // QF_ON_CONTEXT_SW
264 }
265#endif // QF_ON_CONTEXT_SW || Q_SPY
266}
267
268//............................................................................
269//! @static @public @memberof QF
270void QF_init(void) {
271 QF_bzero_(&QF_priv_, sizeof(QF_priv_));
272 QF_bzero_(&QK_priv_, sizeof(QK_priv_));
273 QF_bzero_(&QActive_registry_[0], sizeof(QActive_registry_));
274
275 // setup the QK scheduler as initially locked and not running
276 QK_priv_.lockCeil = (QF_MAX_ACTIVE + 1U); // scheduler locked
277
278 QTimeEvt_init(); // initialize QTimeEvts
279
280#ifdef QK_INIT
281 QK_INIT(); // port-specific initialization of the QK kernel
282#endif
283}
284
285//............................................................................
286//! @static @public @memberof QF
287void QF_stop(void) {
288 QF_onCleanup(); // application-specific cleanup callback
289 // nothing else to do for the preemptive QK kernel
290}
291
292//............................................................................
293//! @static @public @memberof QF
294int_t QF_run(void) {
296#ifdef Q_SPY
297 // produce the QS_QF_RUN trace record
298 QS_beginRec_((uint_fast8_t)QS_QF_RUN);
299 QS_endRec_();
300#endif // Q_SPY
301
302#ifdef QK_START
303 QK_START(); // port-specific startup of the QK kernel
304#endif
305
306 QK_priv_.lockCeil = 0U; // unlock the QK scheduler
307
308#ifdef QF_ON_CONTEXT_SW
309 // officially switch to the idle context
310 QF_onContextSw((QActive *)0, QActive_registry_[QK_priv_.nextPrio]);
311#endif
312
313 // activate AOs to process events posted so far
314 if (QK_sched_() != 0U) {
315 QK_activate_();
316 }
317
319
320 QF_onStartup(); // app. callback: configure and enable interrupts
321
322 for (;;) { // QK idle loop...
323 QK_onIdle(); // application-specific QK on-idle callback
324 }
325
326#ifdef __GNUC__
327 return 0;
328#endif
329}
330
331//............................................................................
332//! @public @memberof QActive
333void QActive_start(QActive * const me,
334 QPrioSpec const prioSpec,
335 QEvtPtr * const qSto,
336 uint_fast16_t const qLen,
337 void * const stkSto,
338 uint_fast16_t const stkSize,
339 void const * const par)
340{
341 Q_UNUSED_PAR(stkSto); // not needed in QK
342 Q_UNUSED_PAR(stkSize); // not needed in QK
343
346
347 Q_REQUIRE_INCRIT(300, (me->super.vptr != (struct QAsmVtable *)0)
348 && (stkSto == (void *)0));
349 QF_CRIT_EXIT();
350
351 me->prio = (uint8_t)(prioSpec & 0xFFU); // QF-prio. of the AO
352 me->pthre = (uint8_t)(prioSpec >> 8U); // preemption-threshold
353 QActive_register_(me); // make QF aware of this active object
354
355 QEQueue_init(&me->eQueue, qSto, qLen); // init the built-in queue
356
357 // top-most initial tran. (virtual call)
358 (*me->super.vptr->init)(&me->super, par, me->prio);
359 QS_FLUSH(); // flush the trace buffer to the host
360
361 // See if this AO needs to be scheduled if QK is already running
363 if (QK_sched_() != 0U) { // activation needed?
364 QK_activate_();
365 }
366 QF_CRIT_EXIT();
367}
QF_Attr QF_priv_
Definition qf_act.c:45
uint_fast8_t QSchedStatus
Definition qk.h:40
#define Q_UNUSED_PAR(par_)
Helper macro to clearly mark unused parameters of functions.
Definition qp.h:94
QEvt const * QEvtPtr
Pointer to const event instances passed around in QP Framework.
Definition qp.h:136
int int_t
Alias for assertion-ID numbers in QP assertions and return from QF_run()
Definition qp.h:91
uint16_t QPrioSpec
Priority specification for Active Objects in QP.
Definition qp.h:382
#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
QS/C dummy public interface.
#define QS_TIME_PRE()
Definition qs_dummy.h:146
#define QS_2U8_PRE(data1_, data2_)
Definition qs_dummy.h:143
#define QS_FLUSH()
Definition qs_dummy.h:75
#define QS_U8_PRE(data_)
Definition qs_dummy.h:142
#define QS_END_PRE()
Definition qs_dummy.h:141
#define QS_BEGIN_PRE(rec_, qsId_)
Definition qs_dummy.h:140
Sample QS/C port.
QP Functional Safety (FuSa) Subsystem.
#define QF_CRIT_ENTRY()
Definition qsafe.h:39
#define Q_ASSERT_INCRIT(id_, expr_)
General-purpose assertion with user-specified ID number (in critical section)
Definition qsafe.h:49
#define QF_CRIT_EXIT()
Definition qsafe.h:43
#define Q_REQUIRE_INCRIT(id_, expr_)
Assertion for checking a precondition (in critical section)
Definition qsafe.h:86
#define QF_CRIT_STAT
Definition qsafe.h:35
Active object class (based on the QHsm implementation strategy)
Definition qp.h:504
QAsm super
Definition qp.h:505
QACTIVE_EQUEUE_TYPE eQueue
Port-dependent event-queue type (often QEQueue)
Definition qp.h:518
uint8_t prio
QF-priority [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:506
uint8_t pthre
Preemption-threshold [1..QF_MAX_ACTIVE] of this AO.
Definition qp.h:507
struct QAsmVtable const * vptr
Virtual pointer inherited by all QAsm subclasses (see also SAS_QP_OO)
Definition qp.h:179
Virtual table for the QAsm class.
Definition qp.h:221
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:222
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:224
Event class.
Definition qp.h:112
Private attributes of the QK kernel.
Definition qk.h:44
uint_fast8_t actPrio
Definition qk.h:46
uint_fast8_t lockCeil
Definition qk.h:49
uint_fast8_t nextPrio
Definition qk.h:47
uint_fast8_t QK_sched_(void)
Definition qk.c:108
void QK_activate_(void)
Definition qk.c:172
uint_fast8_t QK_sched_act_(QActive const *const act, uint_fast8_t const pthre_in)
Definition qk.c:136
QK_Attr QK_priv_
Definition qk.h:54
void QK_schedUnlock(QSchedStatus const prevCeil)
Definition qk.c:80