QP/C++ 6.9.3
qxk.cpp
Go to the documentation of this file.
1 
40 #define QP_IMPL // this is QP implementation
41 #include "qf_port.hpp" // QF port
42 #include "qxk_pkg.hpp" // QXK package-scope internal interface
43 #include "qassert.h" // QP embedded systems-friendly assertions
44 #ifdef Q_SPY // QS software tracing enabled?
45  #include "qs_port.hpp" // QS port
46  #include "qs_pkg.hpp" // QS facilities for pre-defined trace records
47 #else
48  #include "qs_dummy.hpp" // disable the QS software tracing
49 #endif // Q_SPY
50 
51 // protection against including this source file in a wrong project
52 #ifndef QXK_HPP
53  #error "Source file included in a project NOT based on the QXK kernel"
54 #endif // QXK_HPP
55 
57 
58 // Public-scope objects ******************************************************
59 extern "C" {
60  QXK_Attr QXK_attr_; // global attributes of the QXK kernel
61 } // extern "C"
62 
63 namespace QP {
64 
65 // Local-scope objects *******************************************************
66 class QXKIdleThread : public QActive {
67 public:
68  QXKIdleThread() : QActive(nullptr)
69  {}
70 };
71 
73 
74 
75 //****************************************************************************
85 void QF::init(void) {
86  QF_maxPool_ = 0U;
87  QF_subscrList_ = nullptr;
88  QF_maxPubSignal_ = 0;
89 
90  bzero(&timeEvtHead_[0], sizeof(timeEvtHead_));
91  bzero(&active_[0], sizeof(active_));
92  bzero(&QXK_attr_, sizeof(QXK_attr_));
93  bzero(&l_idleThread, sizeof(l_idleThread));
94 
95  // setup the QXK scheduler as initially locked and not running
97 
98  // setup the QXK idle loop...
99  active_[0] = &l_idleThread; // register the idle thread with QF
100  QXK_attr_.idleThread = &l_idleThread; // save the idle thread ptr
101  QXK_attr_.actPrio = l_idleThread.m_prio; // set the base priority
102 
103 #ifdef QXK_INIT
104  QXK_INIT(); // port-specific initialization of the QXK kernel
105 #endif
106 }
107 
108 //****************************************************************************
124 void QF::stop(void) {
125  onCleanup(); // application-specific cleanup callback
126  // nothing else to do for the preemptive QXK kernel
127 }
128 
129 //****************************************************************************
131 static void initial_events(void); // prototype
132 static void initial_events(void) {
133  QXK_attr_.lockPrio = 0U; // unlock the scheduler
134 
135  // any active objects need to be scheduled before starting event loop?
136  if (QXK_sched_() != 0U) {
137  QXK_activate_(); // process all events produced so far
138  }
139 }
140 
141 //****************************************************************************
148 int_t QF::run(void) {
149  QF_INT_DISABLE();
150  initial_events(); // process all events posted during initialization
151  onStartup(); // application-specific startup callback
152 
153  // produce the QS_QF_RUN trace record
156 
157  QF_INT_ENABLE();
158 
159  // the QXK idle loop...
160  for (;;) {
161  QXK::onIdle(); // application-specific QXK idle callback
162  }
163 
164 #ifdef __GNUC__ // GNU compiler?
165  return 0;
166 #endif
167 }
168 
169 //****************************************************************************
170 // @description
171 // Starts execution of the AO and registers the AO with the framework.
172 // Also takes the top-most initial transition in the AO's state machine.
173 // This initial transition is taken in the callee's thread of execution.
174 //
175 // @param[in] prio priority at which to start the active object
176 // @param[in] qSto pointer to the storage for the ring buffer of the
177 // event queue (used only with the built-in QP::QEQueue)
178 // @param[in] qLen length of the event queue [events]
179 // @param[in] stkSto pointer to the stack storage (used only when
180 // per-AO stack is needed)
181 // @param[in] stkSize stack size [bytes]
182 // @param[in] par pointer to an extra parameter (might be NULL)
183 //
184 void QActive::start(std::uint_fast8_t const prio,
185  QEvt const * * const qSto, std::uint_fast16_t const qLen,
186  void * const stkSto, std::uint_fast16_t const stkSize,
187  void const * const par)
188 {
195  && (0U < prio) && (prio <= QF_MAX_ACTIVE)
196  && (stkSto == nullptr)
197  && (stkSize == 0U));
198 
199  m_eQueue.init(qSto, qLen); // initialize QEQueue of this AO
200  m_osObject = nullptr; // no private stack for AO
201  m_prio = static_cast<std::uint8_t>(prio); // prio of the AO
202  m_dynPrio = static_cast<std::uint8_t>(prio); // dynamic prio of the AO
203  QF::add_(this); // make QF aware of this AO
204 
205  this->init(par, m_prio); // take the top-most initial tran. (virtual)
206  QS_FLUSH(); // flush the trace buffer to the host
207 
208  // see if this AO needs to be scheduled in case QXK is running
210  QF_CRIT_E_();
211  if (QXK_sched_() != 0U) { // activation needed?
212  QXK_activate_();
213  }
214  QF_CRIT_X_();
215 }
216 
217 //****************************************************************************
239  QSchedStatus stat;
241  QF_CRIT_E_();
242 
246 
247  // first store the previous lock prio if below the ceiling
248  if (static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio) < ceiling) {
249  stat = (static_cast<QSchedStatus>(QXK_attr_.lockPrio) << 8U);
250  QXK_attr_.lockPrio = static_cast<std::uint8_t>(ceiling);
251 
253  QS_TIME_PRE_(); // timestamp
254  // the previous lock prio & new lock prio
257 
258  // add the previous lock holder priority
259  stat |= static_cast<QSchedStatus>(QXK_attr_.lockHolder);
260  QXK_attr_.lockHolder = (QXK_attr_.curr != nullptr)
262  : 0U;
263  }
264  else {
265  stat = 0xFFU;
266  }
267  QF_CRIT_X_();
268 
269  return stat; // return the status to be saved in a stack variable
270 }
271 
272 //****************************************************************************
293 void QXK::schedUnlock(QSchedStatus const stat) noexcept {
294  // has the scheduler been actually locked by the last QXK_schedLock()?
295  if (stat != 0xFFU) {
296  std::uint_fast8_t const lockPrio
297  = static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio);
298  std::uint_fast8_t const prevPrio
299  = static_cast<std::uint_fast8_t>(stat >> 8U);
301  QF_CRIT_E_();
302 
307  && (lockPrio > prevPrio));
308 
310  QS_TIME_PRE_(); // timestamp
311  // prio before unlocking & prio after unlocking
312  QS_2U8_PRE_(lockPrio, prevPrio);
314 
315  // restore the previous lock priority and lock holder
316  QXK_attr_.lockPrio = static_cast<std::uint8_t>(prevPrio);
317  QXK_attr_.lockHolder = static_cast<std::uint8_t>(stat & 0xFFU);
318 
319  // find the highest-prio thread ready to run
320  if (QXK_sched_() != 0U) { // priority found?
321  QXK_activate_(); // activate any unlocked basic threads
322  }
323 
324  QF_CRIT_X_();
325  }
326 }
327 
328 } // namespace QP
329 
330 
331 //============================================================================
332 extern "C" {
333 
334 //****************************************************************************
347  // find the highest-prio thread ready to run
349 
350  // below the lock prio?
351  if (p <= static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio)) {
352  // dynamic priority of the thread holding the lock
353  p = static_cast<std::uint_fast8_t>(
354  QP::QF::active_[QXK_attr_.lockHolder]->m_dynPrio);
355  if (p != 0U) {
357  }
358  }
359 
360  QP::QActive * const next = QP::QF::active_[p];
361 
362  // the thread found must be registered in QF
363  Q_ASSERT_ID(620, next != nullptr);
364 
365  // is the current thread a basic-thread?
366  if (QXK_attr_.curr == nullptr) {
367 
368  // is next a basic-thread?
369  if (next->m_osObject == nullptr) {
370  if (p > static_cast<std::uint_fast8_t>(QXK_attr_.actPrio)) {
371  QXK_attr_.next = next; // set the next AO to activate
372  }
373  else {
374  QXK_attr_.next = nullptr;
375  p = 0U; // no activation needed
376  }
377  }
378  else { // this is an extened-thread
379 
381  QS_TIME_PRE_(); // timestamp
382  // prio of the next AO & prio of the curr AO
385 
386  QXK_attr_.next = next;
387  p = 0U; // no activation needed
389  }
390  }
391  else { // currently executing an extended-thread
392 
393  // is the next thread different from the current?
394  if (next != QXK_attr_.curr) {
395 
397  QS_TIME_PRE_(); // timestamp
398  // next prio & current prio
401 
402  QXK_attr_.next = next;
403  p = 0U; // no activation needed
405  }
406  else { // next is the same as current
407  QXK_attr_.next = nullptr; // no need to context-switch
408  p = 0U; // no activation needed
409  }
410  }
411  return p;
412 }
413 
414 //****************************************************************************
423 void QXK_activate_(void) {
424  std::uint_fast8_t const pin =
425  static_cast<std::uint_fast8_t>(QXK_attr_.actPrio);
426  QP::QActive *a = QXK_attr_.next; // the next AO (basic-thread) to run
427 
428  // QXK Context switch callback defined or QS tracing enabled?
429 #if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
430  std::uint_fast8_t pprev = pin;
431 #endif // QXK_ON_CONTEXT_SW || Q_SPY
432 
434  Q_REQUIRE_ID(700, (a != nullptr) && (pin < QF_MAX_ACTIVE));
435 
436  // dynamic priority of the next AO
437  std::uint_fast8_t p = static_cast<std::uint_fast8_t>(a->m_dynPrio);
438 
439  // loop until no more ready-to-run AOs of higher prio than the initial
440  do {
441  a = QP::QF::active_[p]; // obtain the pointer to the AO
442 
443  QXK_attr_.actPrio = static_cast<std::uint8_t>(p); // new active prio
444  QXK_attr_.next = nullptr; // clear the next AO
445 
447  QS_TIME_PRE_(); // timestamp
448  // next prio & prev prio
449  QS_2U8_PRE_(p, pprev);
451 
452 #if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
453  if (p != pprev) { // changing threads?
454 
455 #ifdef QXK_ON_CONTEXT_SW
456  Q_ASSERT_ID(710, pprev < QF_MAX_ACTIVE);
457 
458  // context-switch callback
459  QXK_onContextSw(((pprev!=0U) ? QP::QF::active_[pprev] : nullptr),
460  a);
461 #endif // QXK_ON_CONTEXT_SW
462 
463  pprev = p; // update previous priority
464  }
465 #endif // QXK_ON_CONTEXT_SW || Q_SPY
466 
467  QF_INT_ENABLE(); // unconditionally enable interrupts
468 
469  // perform the run-to-completion (RTC) step...
470  // 1. retrieve the event from the AO's event queue, which by this
471  // time must be non-empty and QActive_get_() asserts it.
472  // 2. dispatch the event to the AO's state machine.
473  // 3. determine if event is garbage and collect it if so
474  //
475  QP::QEvt const * const e = a->get_();
476  a->dispatch(e, a->m_prio);
477  QP::QF::gc(e);
478 
479  QF_INT_DISABLE(); // unconditionally disable interrupts
480 
481  if (a->m_eQueue.isEmpty()) { // empty queue?
483  }
484 
485  // find new highest-prio AO ready to run...
486  // NOTE: this part must match the QXK_sched_(),
487  // current is a basic-thread path.
488  p = QXK_attr_.readySet.findMax();
489 
490  // below scheduler lock?
491  if (p <= static_cast<std::uint_fast8_t>(QXK_attr_.lockPrio)) {
492  p = static_cast<std::uint_fast8_t>(QXK_attr_.lockHolder);
493  if (p != 0U) {
495  }
496  }
497  a = QP::QF::active_[p];
498 
499  // the AO must be registered in QF
500  Q_ASSERT_ID(720, a != nullptr);
501 
502  // is the next a basic thread?
503  if (a->m_osObject == nullptr) {
504  if (p > pin) {
505  QXK_attr_.next = a;
506  }
507  else {
508  QXK_attr_.next = nullptr;
509  p = 0U; // no activation needed
510  }
511  }
512  else { // next is the extened thread
513 
515  QS_TIME_PRE_(); // timestamp
516  // next prio & curr prio
519 
520  QXK_attr_.next = a;
521  p = 0U; // no activation needed
523  }
524  } while (p != 0U); // while activation needed
525 
526  QXK_attr_.actPrio = static_cast<std::uint8_t>(pin); // restore base prio
527 
528 #if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
529  if (pin != 0U) { // resuming an active object?
530  a = QP::QF::active_[pin]; // the pointer to the preempted AO
531 
533  QS_TIME_PRE_(); // timestamp
534  QS_2U8_PRE_(pin, pprev); // resumed prio & previous prio
536  }
537  else { // resuming priority==0 --> idle
538  a = nullptr;
539 
541  QS_TIME_PRE_(); // timestamp
542  QS_U8_PRE_(pprev); // previous prio
544  }
545 
546 #ifdef QXK_ON_CONTEXT_SW
547  // context-switch callback
548  QXK_onContextSw(QP::QF::active_[pprev], a);
549 #endif // QXK_ON_CONTEXT_SW
550 
551 #endif // QXK_ON_CONTEXT_SW || Q_SPY
552 }
553 
554 //****************************************************************************
555 QP::QActive *QXK_current(void) noexcept {
556  QP::QActive *curr;
558 
559  QF_CRIT_E_();
560 
563 
564  curr = QXK_attr_.curr;
565  if (curr == nullptr) { // basic thread?
567  }
568  QF_CRIT_X_();
569 
571  Q_ENSURE_ID(890, curr != nullptr);
572 
573  return curr;
574 }
575 
576 } // extern "C"
577 
unsigned int uint_fast16_t
fast at-least 16-bit unsigned int
Definition: 16bit/stdint.h:38
unsigned char uint8_t
exact-width 8-bit unsigned int
Definition: 16bit/stdint.h:29
unsigned int uint_fast8_t
fast at-least 8-bit unsigned int
Definition: 16bit/stdint.h:36
QActive active object (based on QP::QHsm implementation)
Definition: qf.hpp:144
std::uint8_t m_prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.hpp:185
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:173
QEvt const * get_(void) noexcept
Get an event from the event queue of an active object.
Definition: qf_actq.cpp:313
static void onStartup(void)
Startup QF callback.
static void init(void)
QF initialization.
Definition: qk.cpp:78
static void onCleanup(void)
Cleanup QF callback.
static int_t run(void)
Transfers control to QF to run the application.
Definition: qk.cpp:137
static QTimeEvt timeEvtHead_[QF_MAX_TICK_RATE]
heads of linked lists of time events, one for every clock tick rate
Definition: qf.hpp:615
static void gc(QEvt const *const e) noexcept
Recycle a dynamic event.
Definition: qf_dyn.cpp:219
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:129
static void add_(QActive *const a) noexcept
Register an active object to be managed by the framework.
Definition: qf_act.cpp:79
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:111
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
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:139
static QSchedStatus schedLock(std::uint_fast8_t const ceiling) noexcept
QXK selective scheduler lock.
Definition: qxk.cpp:238
static void schedUnlock(QSchedStatus const stat) noexcept
QXK selective scheduler unlock.
Definition: qxk.cpp:293
static void onIdle(void)
QXK idle callback (customized in BSPs for QXK)
namespace associated with the QP/C++ framework
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
static QXKIdleThread l_idleThread
Definition: qxk.cpp:72
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_)
Define the user-specified module name for assertions in this file.
Definition: qassert.h:120
#define Q_ASSERT_ID(id_, test_)
General purpose assertion with user-specified assertion-id.
Definition: qassert.h:155
#define Q_ENSURE_ID(id_, test_)
Assertion for checking postconditions with user-specified assertion-id.
Definition: qassert.h:298
int int_t
typedef for assertions-ids and line numbers in assertions.
Definition: qassert.h:86
#define Q_REQUIRE_ID(id_, test_)
Assertion for checking preconditions with user-specified assertion-id.
Definition: qassert.h:279
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.hpp:56
#define QF_CRIT_X_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.hpp:77
#define QF_CRIT_E_()
This is an internal macro for entering a critical section.
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
#define QS_TIME_PRE_()
Definition: qs.hpp:266
#define QS_FLUSH()
Flush the QS trace data to the host.
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
The maximum number of active objects in the application.
Definition: qxk/qf_port.hpp:57
QP::QActive * QXK_current(void) noexcept
return the currently executing active-object/thread
Definition: qxk.cpp:555
void QXK_activate_(void)
QXK activator activates the next active object. The activated AO preempts.
Definition: qxk.cpp:423
std::uint_fast8_t QXK_sched_(void) noexcept
QXK scheduler finds the highest-priority thread ready to run.
Definition: qxk.cpp:346
QXK_Attr QXK_attr_
global attributes of the QXK kernel
Definition: qxk.cpp:60
std::uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qxk.hpp:78
std::uint8_t volatile lockHolder
prio of the lock holder
Definition: qxk.hpp:79
void QXK_onContextSw(QP::QActive *prev, QP::QActive *next)
QXK context switch callback (customized in BSPs for QXK)
#define QXK_ISR_CONTEXT_()
Internal port-specific macro that reports the execution context.
Definition: qxk.hpp:178
QP::QActive * idleThread
pointer to the idle thread
Definition: qxk.hpp:81
QP::QActive *volatile next
next thread to execute
Definition: qxk.hpp:76
std::uint8_t volatile actPrio
prio of the active basic thread
Definition: qxk.hpp:77
QP::QPSet readySet
ready-set of all threads
Definition: qxk.hpp:82
QP::QActive *volatile curr
currently executing thread
Definition: qxk.hpp:75
attributes of the QXK kernel
Definition: qxk.hpp:74
Internal (package scope) QXK/C++ interface.
#define QXK_CONTEXT_SWITCH_()
trigger context switch (used internally in QXK only)
Definition: qxk_port.hpp:50
QEvt base class.
Definition: qep.hpp:209
bool hasElement(std::uint_fast8_t const n) const noexcept
the function evaluates to TRUE if the priority set has the element n.
Definition: qpset.hpp:96
void rmove(std::uint_fast8_t const n) noexcept
remove element n from the set, n = 1..QF_MAX_ACTIVE
Definition: qpset.hpp:109
std::uint_fast8_t findMax(void) const noexcept
Definition: qpset.hpp:114