QP/C++  6.9.3
Real-Time Embedded Framework
qk.cpp
Go to the documentation of this file.
1 
39 #define QP_IMPL // this is QF/QK implementation
40 #include "qf_port.hpp" // QF port
41 #include "qf_pkg.hpp" // QF package-scope internal interface
42 #include "qassert.h" // QP assertions
43 #ifdef Q_SPY // QS software tracing enabled?
44  #include "qs_port.hpp" // QS port
45  #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
46 #else
47  #include "qs_dummy.hpp" // disable the QS software tracing
48 #endif // Q_SPY
49 
50 // protection against including this source file in a wrong project
51 #ifndef QK_HPP
52  #error "Source file included in a project NOT based on the QK kernel"
53 #endif // QK_HPP
54 
55 // Public-scope objects ******************************************************
56 extern "C" {
57 
59 
60 QK_Attr QK_attr_; // private attributes of the QK kernel
61 
62 } // extern "C"
63 
64 
65 namespace QP {
66 
67 //****************************************************************************
78 void QF::init(void) {
79  QF_maxPool_ = 0U;
80  QF_subscrList_ = nullptr;
81  QF_maxPubSignal_ = 0;
82 
83  bzero(&QF::timeEvtHead_[0], sizeof(QF::timeEvtHead_));
84  bzero(&active_[0], sizeof(active_));
85  bzero(&QK_attr_, sizeof(QK_attr_));
86 
87  QK_attr_.actPrio = 0U; // prio of QK idle loop
88  QK_attr_.lockPrio = QF_MAX_ACTIVE; // locked
89 
90 #ifdef QK_INIT
91  QK_INIT(); // port-specific initialization of the QK kernel
92 #endif
93 }
94 
95 //****************************************************************************
111 void QF::stop(void) {
112  QF::onCleanup(); // cleanup callback
113  // nothing else to do for the QK preemptive kernel
114 }
115 
116 //****************************************************************************
118 static void initial_events(void); // prototype
119 static void initial_events(void) {
120  QK_attr_.lockPrio = 0U; // scheduler unlocked
121 
122  // any active objects need to be scheduled before starting event loop?
123  if (QK_sched_() != 0U) {
124  QK_activate_(); // activate AOs to process all events posted so far
125  }
126 }
127 
128 //****************************************************************************
137 int_t QF::run(void) {
138  QF_INT_DISABLE();
139  initial_events(); // process all events posted during initialization
140  onStartup(); // application-specific startup callback
141 
142  // produce the QS_QF_RUN trace record
145 
146  QF_INT_ENABLE();
147 
148  // the QK idle loop...
149  for (;;) {
150  QK::onIdle(); // application-specific QK on-idle callback
151  }
152 #ifdef __GNUC__ // GNU compiler?
153  return 0;
154 #endif
155 }
156 
157 //****************************************************************************
158 // @description
159 // Starts execution of the AO and registers the AO with the framework.
160 //
161 // @param[in] prio priority at which to start the active object
162 // @param[in] qSto pointer to the storage for the ring buffer of the
163 // event queue (used only with the built-in QP::QEQueue)
164 // @param[in] qLen length of the event queue [number of QP::QEvt* pointers]
165 // @param[in] stkSto pointer to the stack storage (must be nullptr in QK)
166 // @param[in] stkSize stack size [bytes]
167 // @param[in] par pointer to an extra parameter (might be nullptr)
168 //
169 // @usage
170 // The following example shows starting an AO when a per-task stack is needed:
171 // @include qf_start.cpp
172 //
173 void QActive::start(std::uint_fast8_t const prio,
174  QEvt const * * const qSto, std::uint_fast16_t const qLen,
175  void * const stkSto, std::uint_fast16_t const stkSize,
176  void const * const par)
177 {
178  static_cast<void>(stkSize); // unused paramteter in the QK port
179 
183  Q_REQUIRE_ID(300, (!QK_ISR_CONTEXT_())
184  && (0U < prio) && (prio <= QF_MAX_ACTIVE)
185  && (stkSto == nullptr));
186 
187  m_eQueue.init(qSto, qLen); // initialize the built-in queue
188 
189  m_prio = static_cast<std::uint8_t>(prio); // set the QF prio of this AO
190  QF::add_(this); // make QF aware of this AO
191 
192  this->init(par, m_prio); // take the top-most initial tran. (virtual)
193  QS_FLUSH(); // flush the trace buffer to the host
194 
195  // See if this AO needs to be scheduled in case QK is already running
197  QF_CRIT_E_();
198  if (QK_sched_() != 0U) { // activation needed?
199  QK_activate_();
200  }
201  QF_CRIT_X_();
202 }
203 
204 //****************************************************************************
227 QSchedStatus QK::schedLock(std::uint_fast8_t const ceiling) noexcept {
228  QSchedStatus stat;
230  QF_CRIT_E_();
231 
234  Q_REQUIRE_ID(600, !QK_ISR_CONTEXT_());
235 
236  // first store the previous lock prio if it is below the ceiling
237  if (static_cast<std::uint_fast8_t>(QK_attr_.lockPrio) < ceiling) {
238  stat = (static_cast<QSchedStatus>(QK_attr_.lockPrio) << 8U);
239  QK_attr_.lockPrio = static_cast<std::uint8_t>(ceiling);
240 
242  QS_TIME_PRE_(); // timestamp
243  // prev and new lock prio...
246 
247  // add the previous lock holder priority
248  stat |= static_cast<QSchedStatus>(QK_attr_.lockHolder);
249 
251  }
252  else {
253  stat = 0xFFU;
254  }
255  QF_CRIT_X_();
256 
257  return stat; // return the status to be saved in a stack variable
258 }
259 
260 //****************************************************************************
277 void QK::schedUnlock(QSchedStatus const stat) noexcept {
278  // has the scheduler been actually locked by the last QK_schedLock()?
279  if (stat != 0xFFU) {
280  std::uint_fast8_t const lockPrio =
281  static_cast<std::uint_fast8_t>(QK_attr_.lockPrio);
282  std::uint_fast8_t const prevPrio =
283  static_cast<std::uint_fast8_t>(stat >> 8U);
285  QF_CRIT_E_();
286 
290  Q_REQUIRE_ID(700, (!QK_ISR_CONTEXT_())
291  && (lockPrio > prevPrio));
292 
294  QS_TIME_PRE_(); // timestamp
295  QS_2U8_PRE_(lockPrio, prevPrio); //before & after
297 
298  // restore the previous lock priority and lock holder
299  QK_attr_.lockPrio = static_cast<std::uint8_t>(prevPrio);
300  QK_attr_.lockHolder = static_cast<std::uint8_t>(stat & 0xFFU);
301 
302  // find the highest-prio thread ready to run
303  if (QK_sched_() != 0U) { // priority found?
304  QK_activate_(); // activate any unlocked basic threads
305  }
306 
307  QF_CRIT_X_();
308  }
309 }
310 
311 } // namespace QP
312 
313 //============================================================================
314 extern "C" {
315 
316 //****************************************************************************
329 std::uint_fast8_t QK_sched_(void) noexcept {
330  // find the highest-prio AO with non-empty event queue
332 
333  // is the highest-prio below the active prio?
334  if (p <= static_cast<std::uint_fast8_t>(QK_attr_.actPrio)) {
335  p = 0U; // active object not eligible
336  }
337  else if (p <= static_cast<std::uint_fast8_t>(QK_attr_.lockPrio)) {
338  p = 0U; // active object not eligible
339  }
340  else {
341  Q_ASSERT_ID(410, p <= QF_MAX_ACTIVE);
342  QK_attr_.nextPrio = static_cast<std::uint8_t>(p); // next AO to run
343  }
344  return p;
345 }
346 
347 //****************************************************************************
356 void QK_activate_(void) noexcept {
357  std::uint_fast8_t const pin =
358  static_cast<std::uint_fast8_t>(QK_attr_.actPrio);
360  QP::QActive *a;
361 
362  // QK_attr_.actPrio and QK_attr_.nextPrio must be in ragne
363  Q_REQUIRE_ID(500,
364  (pin <= QF_MAX_ACTIVE)
365  && (0U < p) && (p <= QF_MAX_ACTIVE));
366 
367  // QK Context switch callback defined or QS tracing enabled?
368 #if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
369  std::uint_fast8_t pprev = pin;
370 #endif // QK_ON_CONTEXT_SW || Q_SPY
371 
372  QK_attr_.nextPrio = 0U; // clear for the next time
373 
374  // loop until no more ready-to-run AOs of higher prio than the initial
375  do {
376  a = QP::QF::active_[p]; // obtain the pointer to the AO
377  QK_attr_.actPrio = static_cast<std::uint8_t>(p); // new active prio
378 
380  QS_TIME_PRE_(); // timestamp
381  QS_2U8_PRE_(p, pprev); // sechduled prio & previous prio...
383 
384 #if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
385  if (p != pprev) { // changing threads?
386 
387 #ifdef QK_ON_CONTEXT_SW
388  // context-switch callback
389  QK_onContextSw(((pprev != 0U)
390  ? QP::QF::active_[pprev]
391  : nullptr), a);
392 #endif // QK_ON_CONTEXT_SW
393 
394  pprev = p; // update previous priority
395  }
396 #endif // QK_ON_CONTEXT_SW || Q_SPY
397 
398  QF_INT_ENABLE(); // unconditionally enable interrupts
399 
400  // perform the run-to-completion (RTS) step...
401  // 1. retrieve the event from the AO's event queue, which by this
402  // time must be non-empty and QActive_get_() asserts it.
403  // 2. dispatch the event to the AO's state machine.
404  // 3. determine if event is garbage and collect it if so
405  //
406  QP::QEvt const * const e = a->get_();
407  a->dispatch(e, a->m_prio);
408  QP::QF::gc(e);
409 
410  // determine the next highest-priority AO ready to run...
411  QF_INT_DISABLE();
412 
413  if (a->m_eQueue.isEmpty()) { // empty queue?
415  }
416 
417  // find new highest-prio AO ready to run...
418  p = QK_attr_.readySet.findMax();
419 
420  // is the new priority below the initial preemption threshold?
421  if (p <= pin) {
422  p = 0U; // active object not eligible
423  }
424  else if (p <= static_cast<std::uint_fast8_t>(QK_attr_.lockPrio)) {
425  p = 0U; // active object not eligible
426  }
427  else {
428  Q_ASSERT_ID(510, p <= QF_MAX_ACTIVE);
429  }
430  } while (p != 0U);
431 
432  QK_attr_.actPrio = static_cast<std::uint8_t>(pin); // restore the prio
433 
434 #if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
435 
436  if (pin != 0U) { // resuming an active object?
437  a = QP::QF::active_[pin]; // the pointer to the preempted AO
438 
440  QS_TIME_PRE_(); // timestamp
441  QS_2U8_PRE_(pin, pprev); // resumed prio & previous prio...
443  }
444  else { // resuming priority==0 --> idle
445  a = nullptr; // QK idle loop
446 
448  QS_TIME_PRE_(); // timestamp
449  QS_U8_PRE_(pprev); // previous prio
451  }
452 
453 #ifdef QK_ON_CONTEXT_SW
454  QK_onContextSw(QP::QF::active_[pprev], a); // context-switch callback
455 #endif // QK_ON_CONTEXT_SW
456 
457 #endif // QK_ON_CONTEXT_SW || Q_SPY
458 }
459 
460 } // extern "C"
461 
unsigned int uint_fast16_t
Definition: 16bit/stdint.h:38
unsigned char uint8_t
Definition: 16bit/stdint.h:29
unsigned int uint_fast8_t
Definition: 16bit/stdint.h:36
std::uint8_t m_prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.hpp:185
QEvt const * get_(void) noexcept
Get an event from the event queue of an active object.
Definition: qf_actq.cpp:313
static void gc(QEvt const *const e) noexcept
Recycle a dynamic event.
Definition: qf_dyn.cpp:219
static QActive * active_[QF_MAX_ACTIVE+1U]
array of registered active objects
Definition: qf.hpp:580
virtual void dispatch(QEvt const *const e, std::uint_fast8_t const qs_id)
Dispatches an event to QHsm.
Definition: qep_hsm.cpp:242
Definition: struct.dox:1
@ QS_QF_RUN
QF_run() was entered.
Definition: qs.hpp:162
@ QS_SCHED_IDLE
scheduler became idle
Definition: qs.hpp:141
@ QS_SCHED_LOCK
scheduler was locked
Definition: qs.hpp:138
@ QS_SCHED_UNLOCK
scheduler was unlocked
Definition: qs.hpp:139
@ QS_SCHED_NEXT
scheduler found next task to execute
Definition: qs.hpp:140
@ QS_SCHED_RESUME
scheduler resumed previous task (not idle)
Definition: qs.hpp:142
enum_t QF_maxPubSignal_
the maximum published signal
Definition: qf_ps.cpp:58
std::uint_fast8_t QF_maxPool_
Definition: qf_dyn.cpp:55
static void initial_events(void)
process all events posted during initialization *‍/
Definition: qk.cpp:119
std::uint_fast16_t QSchedStatus
The scheduler lock status.
Definition: qk.hpp:119
QSubscrList * QF_subscrList_
the subscriber list array
Definition: qf_ps.cpp:57
Customizable and memory-efficient assertions for embedded systems.
#define Q_DEFINE_THIS_MODULE(name_)
Definition: qassert.h:120
#define Q_ASSERT_ID(id_, test_)
Definition: qassert.h:155
int int_t
Definition: qassert.h:86
#define Q_REQUIRE_ID(id_, test_)
Definition: qassert.h:279
Internal (package scope) QF/C++ interface.
#define QF_CRIT_STAT_
Definition: qf_pkg.hpp:56
#define QF_CRIT_X_()
Definition: qf_pkg.hpp:77
#define QF_CRIT_E_()
Definition: qf_pkg.hpp:66
#define QF_INT_DISABLE()
Definition: qk/qf_port.hpp:43
#define QF_INT_ENABLE()
Definition: qk/qf_port.hpp:44
QK_Attr QK_attr_
global attributes of the QK kernel
Definition: qk.cpp:60
void QK_activate_(void) noexcept
QK activator activates the next active object. The activated AO preempts.
Definition: qk.cpp:356
std::uint_fast8_t QK_sched_(void) noexcept
QK scheduler finds the highest-priority thread ready to run.
Definition: qk.cpp:329
std::uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qk.hpp:70
std::uint8_t volatile lockHolder
prio of the lock holder
Definition: qk.hpp:71
#define QK_ISR_CONTEXT_()
Internal port-specific macro that reports the execution context.
Definition: qk.hpp:173
std::uint8_t volatile actPrio
prio of the active AO
Definition: qk.hpp:68
QP::QPSet readySet
QK ready-set of AOs and "naked" threads.
Definition: qk.hpp:73
void QK_onContextSw(QP::QActive *prev, QP::QActive *next)
std::uint8_t volatile nextPrio
prio of the next AO to execute
Definition: qk.hpp:69
attributes of the QK kernel (in C for easy access in assembly)
Definition: qk.hpp:67
#define QS_TIME_PRE_()
Definition: qs.hpp:266
#define QS_FLUSH()
Definition: qs.hpp:1049
Dummy definitions of the QS macros that avoid code generation from the QS instrumentation.
#define QS_U8_PRE_(data_)
Definition: qs_dummy.hpp:100
#define QS_BEGIN_NOCRIT_PRE_(rec_, qs_id_)
Definition: qs_dummy.hpp:98
#define QS_END_NOCRIT_PRE_()
Definition: qs_dummy.hpp:99
#define QS_2U8_PRE_(data1_, data2_)
Definition: qs_dummy.hpp:101
Internal (package scope) QS/C++ interface.
QS/C++ port to a 32-bit CPU, generic compiler.
#define QF_MAX_ACTIVE
Definition: qxk/qf_port.hpp:57
void rmove(std::uint_fast8_t const n) noexcept
Definition: qpset.hpp:109
std::uint_fast8_t findMax(void) const noexcept
Definition: qpset.hpp:114