QP/C 6.9.0
qxk.c
Go to the documentation of this file.
1 
40 #define QP_IMPL /* this is QP implementation */
41 #include "qf_port.h" /* QF port */
42 #include "qxk_pkg.h" /* 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.h" /* QS port */
46  #include "qs_pkg.h" /* QS facilities for pre-defined trace records */
47 #else
48  #include "qs_dummy.h" /* disable the QS software tracing */
49 #endif /* Q_SPY */
50 
51 /* protection against including this source file in a wrong project */
52 #ifndef QXK_H
53  #error "Source file included in a project NOT based on the QXK kernel"
54 #endif /* QXK_H */
55 
57 
58 /* Global-scope objects *****************************************************/
59 QXK_PrivAttr QXK_attr_; /* global private attributes of the QXK kernel */
60 
61 /* Local-scope objects ******************************************************/
63 
64 /****************************************************************************/
75 void QF_init(void) {
76  QF_maxPool_ = 0U;
78  QF_maxPubSignal_ = 0;
79 
81  QF_bzero(&QF_active_[0], sizeof(QF_active_));
82  QF_bzero(&QXK_attr_, sizeof(QXK_attr_));
84 
85  /* setup the QXK scheduler as initially locked and not running */
87 
88  /* setup the QXK idle loop... */
89  QF_active_[0] = &l_idleThread; /* register idle thread with QF */
90  QXK_attr_.idleThread = &l_idleThread; /* save the idle thread ptr */
91  QXK_attr_.actPrio = l_idleThread.prio; /* set the base priority */
92 
93 #ifdef QXK_INIT
94  QXK_INIT(); /* port-specific initialization of the QXK kernel */
95 #endif
96 }
97 
98 /****************************************************************************/
115 void QF_stop(void) {
116  QF_onCleanup(); /* application-specific cleanup callback */
117  /* nothing else to do for the preemptive QXK kernel */
118 }
119 
120 /****************************************************************************/
122 static void initial_events(void); /* prototype */
123 static void initial_events(void) {
124  QXK_attr_.lockPrio = 0U; /* unlock the scheduler */
125 
126  /* any active objects need to be scheduled before starting event loop? */
127  if (QXK_sched_() != 0U) {
128  QXK_activate_(); /* activate AOs to process all events posted so far*/
129  }
130 }
131 
132 /****************************************************************************/
140 int_t QF_run(void) {
141  QF_INT_DISABLE();
142  initial_events(); /* process all events posted during initialization */
143  QF_onStartup(); /* application-specific startup callback */
144 
145  /* produce the QS_QF_RUN trace record */
146  QS_BEGIN_NOCRIT_PRE_(QS_QF_RUN, (void *)0, (void *)0)
148 
149  QF_INT_ENABLE();
150 
151  /* the QXK idle loop... */
152  for (;;) {
153  QXK_onIdle(); /* application-specific QXK idle callback */
154  }
155 
156 #ifdef __GNUC__
157  return 0;
158 #endif
159 }
160 
161 /****************************************************************************/
184 void QActive_start_(QActive * const me, uint_fast8_t prio,
185  QEvt const * * const qSto, uint_fast16_t const qLen,
186  void * const stkSto, uint_fast16_t const stkSize,
187  void const * const par)
188 {
190 
198  && (0U < prio) && (prio <= QF_MAX_ACTIVE)
199  && (stkSto == (void *)0)
200  && (stkSize == 0U));
201 
202  QEQueue_init(&me->eQueue, qSto, qLen); /* initialize the built-in queue */
203  me->osObject = (void *)0; /* no private stack for AO */
204  me->prio = (uint8_t)prio; /* set the current priority of the AO */
205  me->startPrio = (uint8_t)prio; /* set the start priority of the AO */
206  QF_add_(me); /* make QF aware of this active object */
207 
208  QHSM_INIT(&me->super, par); /* top-most initial tran. */
209  QS_FLUSH(); /* flush the trace buffer to the host */
210 
211  /* see if this AO needs to be scheduled in case QXK is already running */
212  QF_CRIT_ENTRY_();
213  if (QXK_sched_() != 0U) { /* activation needed? */
214  QXK_activate_();
215  }
216  QF_CRIT_EXIT_();
217 }
218 
219 /****************************************************************************/
250  QSchedStatus stat;
252  QF_CRIT_ENTRY_();
253 
258 
259  /* first store the previous lock prio if below the ceiling */
260  if ((uint_fast8_t)QXK_attr_.lockPrio < ceiling) {
261  stat = ((QSchedStatus)QXK_attr_.lockPrio << 8);
262  QXK_attr_.lockPrio = (uint8_t)ceiling;
263 
264  QS_BEGIN_NOCRIT_PRE_(QS_SCHED_LOCK, (void *)0, (void *)0)
265  QS_TIME_PRE_(); /* timestamp */
266  /* the previous lock prio & new lock prio */
269 
270  /* add the previous lock holder priority */
272  QXK_attr_.lockHolder = (QXK_attr_.curr != (struct QActive *)0)
273  ? QXK_attr_.curr->prio
274  : 0U;
275  }
276  else {
277  stat = 0xFFU;
278  }
279  QF_CRIT_EXIT_();
280 
281  return stat; /* return the status to be saved in a stack variable */
282 }
283 
284 /****************************************************************************/
306  /* has the scheduler been actually locked by the last QXK_schedLock()? */
307  if (stat != (QSchedStatus)0xFF) {
309  uint_fast8_t prevPrio = (uint_fast8_t)(stat >> 8);
311  QF_CRIT_ENTRY_();
312 
318  && (lockPrio > prevPrio));
319 
320  QS_BEGIN_NOCRIT_PRE_(QS_SCHED_UNLOCK, (void *)0, (void *)0)
321  QS_TIME_PRE_(); /* timestamp */
322  /* prio before unlocking & prio after unlocking */
323  QS_2U8_PRE_(lockPrio, prevPrio);
325 
326  /* restore the previous lock priority and lock holder */
327  QXK_attr_.lockPrio = (uint8_t)prevPrio;
328  QXK_attr_.lockHolder = (uint8_t)(stat & 0xFFU);
329 
330  /* find the highest-prio thread ready to run */
331  if (QXK_sched_() != 0U) { /* priority found? */
332  QXK_activate_(); /* activate any unlocked basic threads */
333  }
334 
335  QF_CRIT_EXIT_();
336  }
337 }
338 
339 /****************************************************************************/
353  uint_fast8_t p; /* next thread priority */
354  QActive *next;
355 
356  /* find the highest-prio thread ready to run */
358 
359  if (p <= (uint_fast8_t)QXK_attr_.lockPrio) { /* below the lock prio? */
360  p = (uint_fast8_t)QXK_attr_.lockHolder; /* thread holding the lock */
361  if (p != 0U) {
363  }
364  }
365  next = QF_active_[p];
366 
367  /* the thread found must be registered in QF */
368  Q_ASSERT_ID(620, next != (QActive *)0);
369 
370  /* is the current thread a basic-thread? */
371  if (QXK_attr_.curr == (struct QActive *)0) {
372 
373  /* is next a basic-thread? */
374  if (next->osObject == (void *)0) {
375  if (p > (uint_fast8_t)QXK_attr_.actPrio) {
376  QXK_attr_.next = next; /* set the next AO to activate */
377  }
378  else {
379  QXK_attr_.next = (struct QActive *)0;
380  p = 0U; /* no activation needed */
381  }
382  }
383  else { /* this is an extened-thread */
384 
386  next)
387  QS_TIME_PRE_(); /* timestamp */
388  /* prio of the next AO & prio of the curr AO */
391 
392  QXK_attr_.next = next;
393  p = 0U; /* no activation needed */
395  }
396  }
397  else { /* currently executing an extended-thread */
398 
399  /* is the next thread different from the current? */
400  if (next != QXK_attr_.curr) {
401 
403  next)
404  QS_TIME_PRE_(); /* timestamp */
405  /* next prio & current prio */
408 
409  QXK_attr_.next = next;
410  p = 0U; /* no activation needed */
412  }
413  else { /* next is the same as the current */
414  QXK_attr_.next = (QActive *)0; /* no need to context-switch */
415  p = 0U; /* no activation needed */
416  }
417  }
418  return p;
419 }
420 
421 /****************************************************************************/
431 void QXK_activate_(void) {
433  QActive *a = QXK_attr_.next; /* the next AO (basic-thread) to run */
434  uint_fast8_t p;
435 
436  /* QXK Context switch callback defined or QS tracing enabled? */
437 #if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
438  uint_fast8_t pprev = pin;
439 #endif /* QXK_ON_CONTEXT_SW || Q_SPY */
440 
442  Q_REQUIRE_ID(700, (a != (QActive *)0) && (pin < QF_MAX_ACTIVE));
443 
444  p = (uint_fast8_t)a->prio; /* priority of the next AO */
445 
446  /* loop until no more ready-to-run AOs of higher prio than the initial */
447  do {
448  QEvt const *e;
449 
450  a = QF_active_[p]; /* obtain the pointer to the AO */
451  QXK_attr_.actPrio = (uint8_t)p; /* this becomes the base prio */
452  QXK_attr_.next = (struct QActive *)0; /* clear the next AO */
453 
455  QS_TIME_PRE_(); /* timestamp */
456  /* next prio & prev prio */
457 
458  QS_2U8_PRE_(p, pprev);
460 
461 #if (defined QXK_ON_CONTEXT_SW) || (defined Q_SPY)
462  if (p != pprev) { /* changing threads? */
463 
464 #ifdef QXK_ON_CONTEXT_SW
465  Q_ASSERT_ID(710, pprev < QF_MAX_ACTIVE);
466 
467  /* context-switch callback */
468  QXK_onContextSw(((pprev != 0U) ? QF_active_[pprev] : (QActive*)0),
469  a);
470 #endif /* QXK_ON_CONTEXT_SW */
471 
472  pprev = p; /* update previous priority */
473  }
474 #endif /* QXK_ON_CONTEXT_SW || Q_SPY */
475 
476  QF_INT_ENABLE(); /* unconditionally enable interrupts */
477 
478  /* perform the run-to-completion (RTC) step...
479  * 1. retrieve the event from the AO's event queue, which by this
480  * time must be non-empty and QActive_get_() asserts it.
481  * 2. dispatch the event to the AO's state machine.
482  * 3. determine if event is garbage and collect it if so
483  */
484  e = QActive_get_(a);
485  QHSM_DISPATCH(&a->super, e);
486  QF_gc(e);
487 
488  QF_INT_DISABLE(); /* unconditionally disable interrupts */
489 
490  if (a->eQueue.frontEvt == (QEvt *)0) { /* empty queue? */
492  }
493 
494  /* find new highest-prio AO ready to run...
495  * NOTE: this part must match the QXK_sched_(),
496  * current is a basic-thread path.
497  */
499 
500  if (p <= (uint_fast8_t)QXK_attr_.lockPrio) { /* below lock prio? */
501  p = (uint_fast8_t)QXK_attr_.lockHolder; /* thread holding lock */
502  if (p != 0U) {
504  }
505  }
506  a = QF_active_[p];
507 
508  /* the AO must be registered in QF */
509  Q_ASSERT_ID(720, a != (QActive *)0);
510 
511  /* is the next a basic thread? */
512  if (a->osObject == (void *)0) {
513  if (p > pin) {
514  QXK_attr_.next = a;
515  }
516  else {
517  QXK_attr_.next = (struct QActive *)0;
518  p = 0U; /* no activation needed */
519  }
520  }
521  else { /* next is the extened thread */
522 
524  QS_TIME_PRE_(); /* timestamp */
525  /* next prio & curr prio */
528 
529  QXK_attr_.next = a;
530  p = 0U; /* no activation needed */
532  }
533  } while (p != 0U); /* while activation needed */
534 
535  QXK_attr_.actPrio = (uint8_t)pin; /* restore the base prio */
536 
537 #if (defined QK_ON_CONTEXT_SW) || (defined Q_SPY)
538  if (pin != 0U) { /* resuming an active object? */
539  a = QF_active_[pin]; /* the pointer to the preempted AO */
540 
542  QS_TIME_PRE_(); /* timestamp */
543  QS_2U8_PRE_(pin, pprev); /* resumed prio & previous prio */
545  }
546  else { /* resuming priority==0 --> idle */
547  a = (QActive *)0;
548 
549  QS_BEGIN_NOCRIT_PRE_(QS_SCHED_IDLE, (void *)0, (void *)0)
550  QS_TIME_PRE_(); /* timestamp */
551  QS_U8_PRE_(pprev); /* previous priority */
553  }
554 
555 #ifdef QXK_ON_CONTEXT_SW
556  /* context-switch callback */
557  QXK_onContextSw(QF_active_[pprev], a);
558 #endif /* QXK_ON_CONTEXT_SW */
559 
560 #endif /* QK_ON_CONTEXT_SW || Q_SPY */
561 }
562 
563 /****************************************************************************/
564 struct QActive *QXK_current(void) {
565  struct QActive *curr;
567 
568  QF_CRIT_ENTRY_();
569 
572 
573  curr = QXK_attr_.curr;
574  if (curr == (struct QActive *)0) { /* basic thread? */
575  curr = QF_active_[QXK_attr_.actPrio];
576  }
577  QF_CRIT_EXIT_();
578 
580  Q_ENSURE_ID(890, curr != (struct QActive *)0);
581 
582  return curr;
583 }
584 
QS_BEGIN_NOCRIT_PRE_
#define QS_BEGIN_NOCRIT_PRE_(rec_, objFilter_, obj_)
Internal macro to begin a predefined QS record without entering critical section.
Definition: qs_pkg.h:76
QF_bzero
void QF_bzero(void *const start, uint_fast16_t len)
Clear a specified region of memory to zero.
Definition: qf_act.c:144
QActive::prio
uint8_t prio
QF priority (1..QF_MAX_ACTIVE) of this active object.
Definition: qf.h:159
l_idleThread
static QActive l_idleThread
Definition: qxk.c:62
QF_onStartup
void QF_onStartup(void)
Startup QF callback.
uint8_t
unsigned char uint8_t
exact-width 8-bit unsigned int
Definition: 16bit/stdint.h:29
QF_run
int_t QF_run(void)
Transfers control to QF to run the application.
Definition: qxk.c:140
QS_U8_PRE_
#define QS_U8_PRE_(data_)
Internal QS macro to output a predefined uint8_t data element.
Definition: qs_pkg.h:93
QActive_start_
void QActive_start_(QActive *const me, uint_fast8_t prio, QEvt const **const qSto, uint_fast16_t const qLen, void *const stkSto, uint_fast16_t const stkSize, void const *const par)
Implementation of the active object start operation.
Definition: qxk.c:184
QXK_onContextSw
void QXK_onContextSw(struct QActive *prev, struct QActive *next)
QXK context switch callback (customized in BSPs for QXK)
QF_CRIT_STAT_
#define QF_CRIT_STAT_
This is an internal macro for defining the critical section status type.
Definition: qf_pkg.h:57
QXK_PrivAttr::curr
struct QActive *volatile curr
current thread pointer (NULL=basic)
Definition: qxk.h:71
QF_CRIT_EXIT_
#define QF_CRIT_EXIT_()
This is an internal macro for exiting a critical section.
Definition: qf_pkg.h:81
Q_ENSURE_ID
#define Q_ENSURE_ID(id_, test_)
Assertion for checking postconditions with user-specified assertion-id.
Definition: qassert.h:298
QF_timeEvtHead_
QTimeEvt QF_timeEvtHead_[QF_MAX_TICK_RATE]
heads of linked lists of time events, one for every clock tick rate
Definition: qf_time.c:54
QS_TIME_PRE_
#define QS_TIME_PRE_()
Definition: qs.h:242
QF_maxPubSignal_
enum_t QF_maxPubSignal_
the maximum published signal
Definition: qf_ps.c:56
QF_CRIT_ENTRY_
#define QF_CRIT_ENTRY_()
This is an internal macro for entering a critical section.
Definition: qf_pkg.h:69
Q_DEFINE_THIS_MODULE
#define Q_DEFINE_THIS_MODULE(name_)
Define the user-specified module name for assertions in this file.
Definition: qassert.h:120
QPSet_findMax
#define QPSet_findMax(me_, n_)
Find the maximum element in the set, and assign it to n_.
Definition: qpset.h:96
QXK_onIdle
void QXK_onIdle(void)
QXK idle callback (customized in BSPs for QXK)
qassert.h
Customizable and memory-efficient assertions for embedded systems.
QXK_PrivAttr
attributes of the QXK kernel
Definition: qxk.h:70
QF_onCleanup
void QF_onCleanup(void)
Cleanup QF callback.
QF_subscrList_
QSubscrList * QF_subscrList_
the subscriber list array
Definition: qf_ps.c:55
QActive
Active Object base class (based on QHsm implementation)
Definition: qf.h:116
qxk_pkg.h
Internal (package scope) QXK/C interface.
QF_gc
void QF_gc(QEvt const *const e)
Recycle a dynamic event.
Definition: qf_dyn.c:230
uint_fast8_t
unsigned int uint_fast8_t
fast at-least 8-bit unsigned int
Definition: 16bit/stdint.h:36
initial_events
static void initial_events(void)
process all events posted during initialization
Definition: qxk.c:123
Q_SPY
#define Q_SPY
The preprocessor switch to activate the QS software tracing instrumentation in the code.
Definition: macros.h:27
QXK_ON_CONTEXT_SW
#define QXK_ON_CONTEXT_SW
This macro enables calling the QXK context-switch callback QXK_onContextSw()
Definition: macros.h:117
QXK_activate_
void QXK_activate_(void)
QXK activator activates the next active object.
Definition: qxk.c:431
QS_SCHED_NEXT
@ QS_SCHED_NEXT
scheduler found next task to execute
Definition: qs.h:140
QActive::super
QHsm super
inherits QHsm
Definition: qf.h:117
QSchedStatus
uint_fast16_t QSchedStatus
QK Scheduler locking.
Definition: qk.h:132
QEvt
Event class.
Definition: qep.h:151
QXK_attr_
QXK_PrivAttr QXK_attr_
global attributes of the QXK kernel
Definition: qxk.c:59
QXK_PrivAttr::idleThread
struct QActive * idleThread
pointer to the idle thread
Definition: qxk.h:77
qs_pkg.h
Internal (package scope) QS/C interface.
QEQueue_init
void QEQueue_init(QEQueue *const me, QEvt const **const qSto, uint_fast16_t const qLen)
Initialize the native QF event queue.
Definition: qf_qeq.c:72
QF_maxPool_
uint_fast8_t QF_maxPool_
Definition: qf_dyn.c:56
QS_SCHED_RESUME
@ QS_SCHED_RESUME
scheduler resumed previous task (not idle)
Definition: qs.h:142
QXK_schedUnlock
void QXK_schedUnlock(QSchedStatus stat)
QXK Scheduler unlock.
Definition: qxk.c:305
AO_OBJ
@ AO_OBJ
active object
Definition: qs.h:976
QActive_get_
QEvt const * QActive_get_(QActive *const me)
Get an event from the event queue of an active object.
Definition: qf_actq.c:332
QF_MAX_ACTIVE
#define QF_MAX_ACTIVE
The maximum number of active objects in the application.
Definition: qxk/qf_port.h:58
QXK_sched_
uint_fast8_t QXK_sched_(void)
QXK scheduler finds the highest-priority thread ready to run.
Definition: qxk.c:352
QHsm::QHSM_DISPATCH
#define QHSM_DISPATCH(me_, e_)
Polymorphically dispatches an event to a HSM.
Definition: qep.h:332
QS_QF_RUN
@ QS_QF_RUN
QF_run() was entered.
Definition: qs.h:162
QF_stop
void QF_stop(void)
Function invoked by the application layer to stop the QF application and return control to the OS/Ker...
Definition: qxk.c:115
QPSet_hasElement
#define QPSet_hasElement(me_, n_)
Evaluates to TRUE if the priority set me_ has element n_.
Definition: qpset.h:82
QXK_PrivAttr::actPrio
uint8_t volatile actPrio
prio of the active AO
Definition: qxk.h:73
QXK_PrivAttr::lockPrio
uint8_t volatile lockPrio
lock prio (0 == no-lock)
Definition: qxk.h:74
QXK_ISR_CONTEXT_
#define QXK_ISR_CONTEXT_()
Internal macro that reports the execution context (ISR vs.
Definition: qxk.h:159
QS_SCHED_UNLOCK
@ QS_SCHED_UNLOCK
scheduler was unlocked
Definition: qs.h:139
QF_INT_ENABLE
#define QF_INT_ENABLE()
Definition: qk/qf_port.h:45
QS_SCHED_LOCK
@ QS_SCHED_LOCK
scheduler was locked
Definition: qs.h:138
QF_active_
QActive * QF_active_[QF_MAX_ACTIVE+1U]
array of registered active objects
Definition: qf_act.c:54
QS_2U8_PRE_
#define QS_2U8_PRE_(data1_, data2_)
Internal QS macro to output 2 predefined uint8_t data elements.
Definition: qs_pkg.h:96
Q_ASSERT_ID
#define Q_ASSERT_ID(id_, test_)
General purpose assertion with user-specified assertion-id.
Definition: qassert.h:155
QXK_PrivAttr::lockHolder
uint8_t volatile lockHolder
prio of the lock holder
Definition: qxk.h:75
Q_REQUIRE_ID
#define Q_REQUIRE_ID(id_, test_)
Assertion for checking preconditions with user-specified assertion-id.
Definition: qassert.h:279
QXK_PrivAttr::readySet
QPSet readySet
ready-set of all threads
Definition: qxk.h:78
QXK_PrivAttr::next
struct QActive *volatile next
next thread pointer to execute
Definition: qxk.h:72
QF_INT_DISABLE
#define QF_INT_DISABLE()
Definition: qk/qf_port.h:44
QS_FLUSH
#define QS_FLUSH()
Flush the QS trace data to the host.
Definition: qs.h:428
QS_priv_
QSPrivAttr QS_priv_
Definition: qs.c:48
int_t
int int_t
typedef for assertions-ids and line numbers in assertions.
Definition: qassert.h:86
QPSet
Priority Set of up to 32 elements.
Definition: qpset.h:68
QXK_CONTEXT_SWITCH_
#define QXK_CONTEXT_SWITCH_()
trigger context switch (used internally in QXK only)
Definition: qxk_port.h:61
QSPrivAttr::locFilter
void const * locFilter[MAX_OBJ]
local QS filters
Definition: qs.h:991
QS_END_NOCRIT_PRE_
#define QS_END_NOCRIT_PRE_()
Internal QS macro to end a predefined QS record without exiting critical section.
Definition: qs_pkg.h:90
QPSet_remove
#define QPSet_remove(me_, n_)
Remove element n_ from the set me_, n_= 1..32.
Definition: qpset.h:90
QF_add_
void QF_add_(QActive *const a)
Register an active object to be managed by the framework.
Definition: qf_act.c:70
QXK_schedLock
QSchedStatus QXK_schedLock(uint_fast8_t ceiling)
QXK Scheduler lock.
Definition: qxk.c:249
QHsm::QHSM_INIT
#define QHSM_INIT(me_, par_)
Polymorphically executes the top-most initial transition in a HSM.
Definition: qep.h:312
QF_init
void QF_init(void)
QF initialization.
Definition: qxk.c:75
QXK_current
struct QActive * QXK_current(void)
return the currently executing active-object/thread
Definition: qxk.c:564
QS_SCHED_IDLE
@ QS_SCHED_IDLE
scheduler became idle
Definition: qs.h:141
uint_fast16_t
unsigned int uint_fast16_t
fast at-least 16-bit unsigned int
Definition: 16bit/stdint.h:38