forked from felis/PTP_2.0
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathqp_port.h
3648 lines (3217 loc) · 151 KB
/
qp_port.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
//////////////////////////////////////////////////////////////////////////////
// Product: QP/C++ port to Arduino, cooperative "vanilla" kernel, no Q-SPY
// Last Updated for QP ver: 4.2.04 (modified to fit in one file)
// Date of the Last Update: Oct 03, 2011
//
// Q u a n t u m L e a P s
// ---------------------------
// innovating embedded systems
//
// Copyright (C) 2002-2011 Quantum Leaps, LLC. All rights reserved.
//
// This software may be distributed and modified under the terms of the GNU
// General Public License version 2 (GPL) as published by the Free Software
// Foundation and appearing in the file GPL.TXT included in the packaging of
// this file. Please note that GPL Section 2[b] requires that all works based
// on this software must also be made publicly available under the terms of
// the GPL ("Copyleft").
//
// Alternatively, this software may be distributed and modified under the
// terms of Quantum Leaps commercial licenses, which expressly supersede
// the GPL and are specifically designed for licensees interested in
// retaining the proprietary status of their code.
//
// Contact information:
// Quantum Leaps Web site: http://www.quantum-leaps.com
// e-mail: info@quantum-leaps.com
//////////////////////////////////////////////////////////////////////////////
#ifndef qp_port_h
#define qp_port_h
#include <stdint.h> // C99-standard exact-width integers
#include <avr/pgmspace.h> // accessing data in the program memory (PROGMEM)
#include <avr/io.h> // SREG definition
#include <avr/interrupt.h> // cli()/sei()
// the macro 'PROGMEM' allocates const objects to ROM
//#define Q_ROM PROGMEM
// the macro 'Q_ROM_BYTE' reads a byte from ROM
#define Q_ROM_BYTE(rom_var_) pgm_read_byte_near(&(rom_var_))
// various QF object sizes configuration for this port
#define QF_MAX_ACTIVE 8
#define QF_EVENT_SIZ_SIZE 1
#define QF_EQUEUE_CTR_SIZE 1
#define QF_MPOOL_SIZ_SIZE 1
#define QF_MPOOL_CTR_SIZE 1
#define QF_TIMEEVT_CTR_SIZE 2
// QF critical section entry/exit
#define QF_INT_KEY_TYPE uint8_t
#define QF_INT_LOCK(key_) do { \
(key_) = SREG; \
cli(); \
} while (0)
#define QF_INT_UNLOCK(key_) (SREG = (key_))
#define QF_INT_DISABLE cli();
//////////////////////////////////////////////////////////////////////////////
// DO NOT CHANGE ANYTHING BELOW THIS LINE
#ifdef Q_USE_NAMESPACE
namespace QP {
#endif
// "qevent.h" ================================================================
/// \brief QEvent class and basic macros used by all QP components.
///
/// This header file must be included, perhaps indirectly, in all modules
/// (*.cpp files) that use any component of QP/C++ (such as QEP, QF, or QK).
//////////////////////////////////////////////////////////////////////////////
/// \brief The current QP version number
///
/// \return version of the QP as a hex constant constant 0xXYZZ, where X is
/// a 1-digit major version number, Y is a 1-digit minor version number, and
/// ZZ is a 2-digit release number.
#define QP_VERSION 0x4204U
#ifndef Q_ROM
/// \brief Macro to specify compiler-specific directive for placing a
/// constant object in ROM.
///
/// Many compilers for Harvard-architecture MCUs provide non-stanard
/// extensions to support placement of objects in different memories.
/// In order to conserve the precious RAM, QP uses the Q_ROM macro for
/// all constant objects that can be allocated in ROM.
///
/// To override the following empty definition, you need to define the
/// Q_ROM macro in the qep_port.h header file. Some examples of valid
/// Q_ROM macro definitions are: __code (IAR 8051 compiler), code (Keil
/// Cx51 compiler), PROGMEM (gcc for AVR), __flash (IAR for AVR).
#define Q_ROM
#endif
#ifndef Q_ROM_VAR // if NOT defined, provide the default definition
/// \brief Macro to specify compiler-specific directive for accessing a
/// constant object in ROM.
///
/// Many compilers for MCUs provide different size pointers for
/// accessing objects in various memories. Constant objects allocated
/// in ROM (see #Q_ROM macro) often mandate the use of specific-size
/// pointers (e.g., far pointers) to get access to ROM objects. The
/// macro Q_ROM_VAR specifies the kind of the pointer to be used to access
/// the ROM objects.
///
/// To override the following empty definition, you need to define the
/// Q_ROM_VAR macro in the qep_port.h header file. An example of valid
/// Q_ROM_VAR macro definition is: __far (Freescale HC(S)08 compiler).
#define Q_ROM_VAR
#endif
#ifndef Q_ROM_BYTE
/// \brief Macro to access a byte allocated in ROM
///
/// Some compilers for Harvard-architecture MCUs, such as gcc for AVR, do
/// not generate correct code for accessing data allocated in the program
/// space (ROM). The workaround for such compilers is to explictly add
/// assembly code to access each data element allocated in the program
/// space. The macro Q_ROM_BYTE() retrieves a byte from the given ROM
/// address.
///
/// The Q_ROM_BYTE() macro should be defined for the compilers that
/// cannot handle correctly data allocated in ROM (such as the gcc).
/// If the macro is left undefined, the default definition simply returns
/// the argument and lets the compiler generate the correct code.
#define Q_ROM_BYTE(rom_var_) (rom_var_)
#endif
#ifndef Q_SIGNAL_SIZE
/// \brief The size (in bytes) of the signal of an event. Valid values:
/// 1, 2, or 4; default 1
///
/// This macro can be defined in the QEP port file (qep_port.h) to
/// configure the ::QSignal type. When the macro is not defined, the
/// default of 1 byte is chosen.
#define Q_SIGNAL_SIZE 2
#endif
#if (Q_SIGNAL_SIZE == 1)
typedef uint8_t QSignal;
#elif (Q_SIGNAL_SIZE == 2)
/// \brief QSignal represents the signal of an event.
///
/// The relationship between an event and a signal is as follows. A signal
/// in UML is the specification of an asynchronous stimulus that triggers
/// reactions [<A HREF="http://www.omg.org/docs/ptc/03-08-02.pdf">UML
/// document ptc/03-08-02</A>], and as such is an essential part of an
/// event. (The signal conveys the type of the occurrence-what happened?)
/// However, an event can also contain additional quantitative information
/// about the occurrence in form of event parameters. Please refer to the
typedef uint16_t QSignal;
#elif (Q_SIGNAL_SIZE == 4)
typedef uint32_t QSignal;
#else
#error "Q_SIGNAL_SIZE defined incorrectly, expected 1, 2, or 4"
#endif
//////////////////////////////////////////////////////////////////////////////
/// \brief QEvent base class.
///
/// QEvent represents events without parameters and serves as the base class
/// for derivation of events with parameters.
///
/// \note All data members of the QEvent class must remain public to keep it
/// an AGGREGATE. Therefore, the attribute QEvent::dynamic_ cannot be
/// declared private.
///
/// The following example illustrates how to add an event parameter by
/// inheriting from the QEvent class.
/// \include qep_qevent.cpp
struct QEvent {
QSignal sig; ///< signal of the event instance
uint8_t poolId_; ///< pool ID (0 for static event)
uint8_t refCtr_; ///< reference counter
#ifdef Q_EVT_CTOR
QEvent(QSignal s) : sig(s) {}
virtual ~QEvent() {} // virtual destructor
#endif
};
//////////////////////////////////////////////////////////////////////////////
/// helper macro to calculate static dimension of a 1-dim array \a array_
#define Q_DIM(array_) (sizeof(array_) / sizeof(array_[0]))
// "qep.h" ===================================================================
/// \brief QEP/C++ platform-independent public interface.
///
/// This header file must be included directly or indirectly
/// in all modules (*.cpp files) that use QEP/C++.
//////////////////////////////////////////////////////////////////////////////
/// \brief Provides miscellaneous QEP services.
class QEP {
public:
/// \brief get the current QEP version number string
///
/// \return version of the QEP as a constant 6-character string of the
/// form x.y.zz, where x is a 1-digit major version number, y is a
/// 1-digit minor version number, and zz is a 2-digit release number.
static char const Q_ROM * Q_ROM_VAR getVersion(void);
};
//////////////////////////////////////////////////////////////////////////////
/// \brief Type returned from a state-handler function
typedef uint8_t QState;
/// \brief pointer to state-handler function
typedef QState (*QStateHandler)(void *me, QEvent const *e);
//////////////////////////////////////////////////////////////////////////////
/// \brief Finite State Machine base class
///
/// QFsm represents a traditional non-hierarchical Finite State Machine (FSM)
/// without state hierarchy, but with entry/exit actions.
///
/// QFsm is also a base structure for the ::QHsm class.
///
/// \note QFsm is not intended to be instantiated directly, but rather serves
/// as the base class for derivation of state machines in the application
/// code.
///
/// The following example illustrates how to derive a state machine class
/// from QFsm.
/// \include qep_qfsm.cpp
class QFsm {
protected:
QStateHandler m_state; ///< current active state (state-variable)
public:
/// \brief virtual destructor
virtual ~QFsm();
/// \brief Performs the second step of FSM initialization by triggering
/// the top-most initial transition.
///
/// The argument \a e is constant pointer to ::QEvent or a class
/// derived from ::QEvent.
///
/// \note Must be called only ONCE before QFsm::dispatch()
///
/// The following example illustrates how to initialize a FSM, and
/// dispatch events to it:
/// \include qep_qfsm_use.cpp
void init(QEvent const *e = (QEvent *)0);
/// \brief Dispatches an event to a FSM
///
/// Processes one event at a time in Run-to-Completion (RTC) fashion.
/// The argument \a e is a constant pointer the ::QEvent or a
/// class derived from ::QEvent.
///
/// \note Must be called after QFsm::init().
///
/// \sa example for QFsm::init()
void dispatch(QEvent const *e);
protected:
/// \brief Protected constructor of a FSM.
///
/// Performs the first step of FSM initialization by assigning the
/// initial pseudostate to the currently active state of the state
/// machine.
///
/// \note The constructor is protected to prevent direct instantiating
/// of QFsm objects. This class is intended for subclassing only.
///
/// \sa The ::QFsm example illustrates how to use the QHsm constructor
/// in the constructor initializer list of the derived state machines.
QFsm(QStateHandler initial) : m_state(initial) {}
};
//////////////////////////////////////////////////////////////////////////////
/// \brief Hierarchical State Machine base class
///
/// QHsm represents a Hierarchical Finite State Machine (HSM). QHsm derives
/// from the ::QFsm class and extends the capabilities of a basic FSM
/// with state hierarchy.
///
/// \note QHsm is not intended to be instantiated directly, but rather serves
/// as the base structure for derivation of state machines in the application
/// code.
///
/// The following example illustrates how to derive a state machine class
/// from QHsm.
/// \include qep_qhsm.cpp
class QHsm {
protected:
QStateHandler m_state; ///< current active state (state-variable)
public:
/// \brief virtual destructor
virtual ~QHsm();
/// \brief Performs the second step of HSM initialization by triggering
/// the top-most initial transition.
///
/// \param e constant pointer ::QEvent or a class derived from ::QEvent
/// \note Must be called only ONCE before QHsm::dispatch()
///
/// The following example illustrates how to initialize a HSM, and
/// dispatch events to it:
/// \include qep_qhsm_use.cpp
void init(QEvent const *e = (QEvent *)0);
/// \brief Dispatches an event to a HSM
///
/// Processes one event at a time in Run-to-Completion (RTC) fashion.
/// The argument \a e is a constant pointer the ::QEvent or a
/// class derived from ::QEvent.
///
/// \note Must be called after QHsm::init().
///
/// \sa example for QHsm::init()
void dispatch(QEvent const *e);
/// \brief Tests if a given state is part of the current active state
/// configuratioin
///
/// \param state is a pointer to the state handler function, e.g.,
/// &QCalc::on.
uint8_t isIn(QStateHandler state);
protected:
/// \brief Protected constructor of a HSM.
///
/// Performs the first step of HSM initialization by assigning the
/// initial pseudostate to the currently active state of the state
/// machine.
///
/// \note The constructor is protected to prevent direct instantiating
/// of QHsm objects. This class is intended for subclassing only.
///
/// \sa The ::QHsm example illustrates how to use the QHsm constructor
/// in the constructor initializer list of the derived state machines.
/// \sa QFsm::QFsm()
QHsm(QStateHandler initial) : m_state(initial) {}
/// \brief the top-state.
///
/// QHsm::top() is the ultimate root of state hierarchy in all HSMs
/// derived from ::QHsm. This state handler always returns (QSTATE)0,
/// which means that it "handles" all events.
///
/// \sa Example of the QCalc::on() state handler.
static QState top(QHsm *me, QEvent const *e);
};
/// \brief Value returned by a non-hierarchical state-handler function when
/// it ignores (does not handle) the event.
#define Q_RET_IGNORED ((QState)1)
/// \brief The macro returned from a non-hierarchical state-handler function
/// when it ignores (does not handle) the event.
///
/// You call that macro after the return statement (return Q_IGNORED();)
///
/// \include qepn_qfsm.cpp
#define Q_IGNORED() (Q_RET_IGNORED)
/// \brief Value returned by a state-handler function when it handles
/// the event.
#define Q_RET_HANDLED ((QState)0)
/// \brief Value returned by a state-handler function when it handles
/// the event.
///
/// You call that macro after the return statement (return Q_HANDLED();)
/// Q_HANDLED() can be used both in the FSMs and HSMs.
///
/// \include qepn_qfsm.cpp
#define Q_HANDLED() (Q_RET_HANDLED)
/// \brief Value returned by a state-handler function when it takes a
/// regular state transition.
#define Q_RET_TRAN ((QState)2)
/// \brief Designates a target for an initial or regular transition.
/// Q_TRAN() can be used both in the FSMs and HSMs.
///
/// \include qepn_qtran.cpp
//lint -e960 -e1924 ignore MISRA Rule 42 (comma operator) and C-style cast
#define Q_TRAN(target_) \
(me->m_state = (QStateHandler)(target_), Q_RET_TRAN)
/// \brief Value returned by a state-handler function when it cannot
/// handle the event.
#define Q_RET_SUPER ((QState)3)
/// \brief Designates the superstate of a given state in an HSM.
///
/// \include qep_qhsm.cpp
//lint -e960 -e1924 ignore MISRA Rule 42 (comma operator) and C-style cast
#define Q_SUPER(super_) \
(me->m_state = (QStateHandler)(super_), Q_RET_SUPER)
//////////////////////////////////////////////////////////////////////////////
/// \brief QEP reserved signals.
enum QReservedSignals {
Q_ENTRY_SIG = 1, ///< signal for entry actions
Q_EXIT_SIG, ///< signal for exit actions
Q_INIT_SIG, ///< signal for nested initial transitions
Q_USER_SIG ///< signal to offset user signals
};
// "qequeue.h" ===============================================================
/// \brief platform-independent event queue interface.
///
/// This header file must be included in all QF ports that use native QF
/// event queue implementation. Also, this file is needed when the "raw"
/// thread-safe queues are used for communication between active objects
/// and non-framework entities, such as ISRs, device drivers, or legacy
/// code.
#ifndef QF_EQUEUE_CTR_SIZE
/// \brief The size (in bytes) of the ring-buffer counters used in the
/// native QF event queue implementation. Valid values: 1, 2, or 4;
/// default 1.
///
/// This macro can be defined in the QF port file (qf_port.h) to
/// configure the ::QEQueueCtr type. Here the macro is not defined so the
/// default of 1 byte is chosen.
#define QF_EQUEUE_CTR_SIZE 1
#endif
#if (QF_EQUEUE_CTR_SIZE == 1)
/// \brief The data type to store the ring-buffer counters based on
/// the macro #QF_EQUEUE_CTR_SIZE.
///
/// The dynamic range of this data type determines the maximum length
/// of the ring buffer managed by the native QF event queue.
typedef uint8_t QEQueueCtr;
#elif (QF_EQUEUE_CTR_SIZE == 2)
typedef uint16_t QEQueueCtr;
#elif (QF_EQUEUE_CTR_SIZE == 4)
typedef uint32_t QEQueueCtr;
#else
#error "QF_EQUEUE_CTR_SIZE defined incorrectly, expected 1, 2, or 4"
#endif
//////////////////////////////////////////////////////////////////////////////
/// \brief Native QF Event Queue class
///
/// This structure describes the native QF event queue, which can be used as
/// the event queue for active objects, or as a simple "raw" event queue for
/// thread-safe event passing among non-framework entities, such as ISRs,
/// device drivers, or other third-party components.
///
/// The native QF event queue is configured by defining the macro
/// #QF_EQUEUE_TYPE as ::QEQueue in the specific QF port header file.
///
/// The ::QEQueue structure contains only data members for managing an event
/// queue, but does not contain the storage for the queue buffer, which must
/// be provided externally during the queue initialization.
///
/// The event queue can store only event pointers, not the whole events. The
/// internal implementation uses the standard ring-buffer plus one external
/// location that optimizes the queue operation for the most frequent case
/// of empty queue.
///
/// The ::QEQueue structure is used with two sets of functions. One set is for
/// the active object event queue, which needs to block the active object
/// task when the event queue is empty and unblock it when events are posted
/// to the queue. The interface for the native active object event queue
/// consists of the following functions: QActive::postFIFO_(),
/// QActive::postLIFO_(), and QActive::get_(). Additionally the function
/// QEQueue_init() is used to initialize the queue.
///
/// The other set of functions, uses this structure as a simple "raw" event
/// queue to pass events between entities other than active objects, such as
/// ISRs. The "raw" event queue is not capable of blocking on the get()
/// operation, but is still thread-safe because it uses QF critical section
/// to protect its integrity. The interface for the "raw" thread-safe queue
/// consists of the following functions: QEQueue::postFIFO(),
/// QEQueue::postLIFO(), and QEQueue::get(). Additionally the function
/// QEQueue::init() is used to initialize the queue.
///
/// \note Most event queue operations (both the active object queues and
/// the "raw" queues) internally use the QF critical section. You should be
/// careful not to invoke those operations from other critical sections when
/// nesting of critical sections is not supported.
class QEQueue {
private:
/// \brief pointer to event at the front of the queue
///
/// All incoming and outgoing events pass through the m_frontEvt location.
/// When the queue is empty (which is most of the time), the extra
/// m_frontEvt location allows to bypass the ring buffer altogether,
/// greatly optimizing the performance of the queue. Only bursts of events
/// engage the ring buffer.
///
/// The additional role of this attribute is to indicate the empty status
/// of the queue. The queue is empty if the m_frontEvt location is NULL.
QEvent const *m_frontEvt;
/// \brief pointer to the start of the ring buffer
QEvent const **m_ring;
/// \brief offset of the end of the ring buffer from the start of the
/// buffer m_ring
QEQueueCtr m_end;
/// \brief offset to where next event will be inserted into the buffer
QEQueueCtr m_head;
/// \brief offset of where next event will be extracted from the buffer
QEQueueCtr m_tail;
/// \brief number of free events in the ring buffer
QEQueueCtr m_nFree;
/// \brief minimum number of free events ever in the ring buffer.
///
/// \note this attribute remembers the low-watermark of the ring buffer,
/// which provides a valuable information for sizing event queues.
/// \sa QF::getQueueMargin().
QEQueueCtr m_nMin;
public:
/// \brief Initializes the native QF event queue
///
/// The parameters are as follows: \a qSto[] is the ring buffer storage,
/// \a qLen is the length of the ring buffer in the units of event-
/// pointers.
///
/// \note The actual capacity of the queue is qLen + 1, because of the
/// extra location fornEvt_.
void init(QEvent const *qSto[], QEQueueCtr qLen);
/// \brief "raw" thread-safe QF event queue implementation for the
/// First-In-First-Out (FIFO) event posting. You can call this function
/// from any task context or ISR context. Please note that this function
/// uses internally a critical section.
///
/// \note The function raises an assertion if the native QF queue becomes
/// full and cannot accept the event.
///
/// \sa QEQueue::postLIFO(), QEQueue::get()
void postFIFO(QEvent const *e);
/// \brief "raw" thread-safe QF event queue implementation for the
/// First-In-First-Out (FIFO) event posting. You can call this function
/// from any task context or ISR context. Please note that this function
/// uses internally a critical section.
///
/// \note The function raises an assertion if the native QF queue becomes
/// full and cannot accept the event.
///
/// \sa QEQueue::postLIFO(), QEQueue::get()
void postLIFO(QEvent const *e);
/// \brief "raw" thread-safe QF event queue implementation for the
/// Last-In-First-Out (LIFO) event posting.
///
/// \note The LIFO policy should be used only with great caution because
/// it alters order of events in the queue.
/// \note The function raises an assertion if the native QF queue becomes
/// full and cannot accept the event. You can call this function from
/// any task context or ISR context. Please note that this function uses
/// internally a critical section.
///
/// \sa QEQueue::postFIFO(), QEQueue::get()
QEvent const *get(void);
/// \brief "raw" thread-safe QF event queue operation for
/// obtaining the number of free entries still available in the queue.
///
/// \note This operation needs to be used with caution because the
/// number of free entries can change unexpectedly. The main intent for
/// using this operation is in conjunction with event deferral. In this
/// case the queue is accessed only from a single thread (by a single AO),
/// so the number of free entries cannot change unexpectedly.
///
/// \sa QActive::defer(), QActive::recall()
QEQueueCtr getNFree(void) const {
return m_nFree;
}
private:
friend class QF;
friend class QActive;
};
// "qmpool.h" ================================================================
/// \brief platform-independent memory pool interface.
///
/// This header file must be included in all QF ports that use native QF
/// memory pool implementation.
//////////////////////////////////////////////////////////////////////////////
#ifndef QF_MPOOL_SIZ_SIZE
/// \brief macro to override the default ::QMPoolSize size.
/// Valid values 1, 2, or 4; default 2
#define QF_MPOOL_SIZ_SIZE 2
#endif
#if (QF_MPOOL_SIZ_SIZE == 1)
/// \brief The data type to store the block-size based on the macro
/// #QF_MPOOL_SIZ_SIZE.
///
/// The dynamic range of this data type determines the maximum size
/// of blocks that can be managed by the native QF event pool.
typedef uint8_t QMPoolSize;
#elif (QF_MPOOL_SIZ_SIZE == 2)
typedef uint16_t QMPoolSize;
#elif (QF_MPOOL_SIZ_SIZE == 4)
typedef uint32_t QMPoolSize;
#else
#error "QF_MPOOL_SIZ_SIZE defined incorrectly, expected 1, 2, or 4"
#endif
//////////////////////////////////////////////////////////////////////////////
#ifndef QF_MPOOL_CTR_SIZE
/// \brief macro to override the default QMPoolCtr size.
/// Valid values 1, 2, or 4; default 2
#define QF_MPOOL_CTR_SIZE 2
#endif
#if (QF_MPOOL_CTR_SIZE == 1)
/// \brief The data type to store the block-counter based on the macro
/// #QF_MPOOL_CTR_SIZE.
///
/// The dynamic range of this data type determines the maximum number
/// of blocks that can be stored in the pool.
typedef uint8_t QMPoolCtr;
#elif (QF_MPOOL_CTR_SIZE == 2)
typedef uint16_t QMPoolCtr;
#elif (QF_MPOOL_CTR_SIZE == 4)
typedef uint32_t QMPoolCtr;
#else
#error "QF_MPOOL_CTR_SIZE defined incorrectly, expected 1, 2, or 4"
#endif
//////////////////////////////////////////////////////////////////////////////
/// \brief Native QF memory pool class
///
/// This class describes the native QF memory pool, which can be used as
/// the event pool for dynamic event allocation, or as a fast, deterministic
/// fixed block-size heap for any other objects in your application.
///
/// The ::QMPool structure contains only data members for managing a memory
/// pool, but does not contain the pool storage, which must be provided
/// externally during the pool initialization.
///
/// The native QF event pool is configured by defining the macro
/// #QF_EPOOL_TYPE_ as QEQueue in the specific QF port header file.
class QMPool {
private:
/// start of the memory managed by this memory pool
void *m_start;
/// end of the memory managed by this memory pool
void *m_end;
/// linked list of free blocks
void *m_free;
/// maximum block size (in bytes)
QMPoolSize m_blockSize;
/// total number of blocks
QMPoolCtr m_nTot;
/// number of free blocks remaining
QMPoolCtr m_nFree;
/// minimum number of free blocks ever present in this pool
///
/// \note this attribute remembers the low watermark of the pool,
/// which provides a valuable information for sizing event pools.
/// \sa QF::getPoolMargin().
QMPoolCtr m_nMin;
public:
/// \brief Initializes the native QF event pool
///
/// The parameters are as follows: \a poolSto is the pool storage,
/// \a poolSize is the size of the pool storage in bytes, and
/// \a blockSize is the block size of this pool.
///
/// The caller of this method must make sure that the \a poolSto pointer
/// is properly aligned. In particular, it must be possible to efficiently
/// store a pointer at the location pointed to by \a poolSto.
/// Internally, the QMPool::init() function rounds up the block size
/// \a blockSize so that it can fit an integer number of pointers.
/// This is done to achieve proper alignment of the blocks within the
/// pool.
///
/// \note Due to the rounding of block size the actual capacity of the
/// pool might be less than (\a poolSize / \a blockSize). You can check
/// the capacity of the pool by calling the QF::getPoolMargin() function.
void init(void *poolSto, uint32_t poolSize, QMPoolSize blockSize);
/// \brief Obtains a memory block from a memory pool.
///
/// The only parameter \a me is a pointer to the ::QMPool from which the
/// block is requested. The function returns a pointer to the allocated
/// memory block or NULL if no free blocks are available.
///
/// A allocated block must be returned to the same pool from which it has
/// been allocated.
///
/// This function can be called from any task level or ISR level.
///
/// \note The memory pool \a me must be initialized before any events can
/// be requested from it. Also, the QMPool::get() function uses internally
/// a QF critical section, so you should be careful not to call it from
/// within a critical section when nesting of critical section is not
/// supported.
///
/// \sa QMPool::put()
void *get(void);
/// \brief Returns a memory block back to a memory pool.
///
///
/// This function can be called from any task level or ISR level.
///
/// \note The block must be allocated from the same memory pool to which
/// it is returned. The QMPool::put() function raises an assertion if the
/// returned pointer to the block points outside of the original memory
/// buffer managed by the memory pool. Also, the QMPool::put() function
/// uses internally a QF critical section, so you should be careful not
/// to call it from within a critical section when nesting of critical
/// section is not supported.
///
/// \sa QMPool::get()
void put(void *b);
/// \brief return the fixed block-size of the blocks managed by this pool
QMPoolSize getBlockSize(void) const {
return m_blockSize;
}
private:
friend class QF;
};
// "qpset.h" =================================================================
/// \brief platform-independent priority sets of 8 or 64 elements.
///
/// This header file must be included in those QF ports that use the
/// cooperative multitasking QF scheduler or the QK.
// external declarations of QF lookup tables used inline
extern uint8_t const Q_ROM Q_ROM_VAR QF_log2Lkup[256];
extern uint8_t const Q_ROM Q_ROM_VAR QF_pwr2Lkup[65];
extern uint8_t const Q_ROM Q_ROM_VAR QF_invPwr2Lkup[65];
extern uint8_t const Q_ROM Q_ROM_VAR QF_div8Lkup[65];
//////////////////////////////////////////////////////////////////////////////
/// \brief Priority Set of up to 8 elements for building various schedulers,
/// but also useful as a general set of up to 8 elements of any kind.
///
/// The priority set represents the set of active objects that are ready to
/// run and need to be considered by scheduling processing. The set is capable
/// of storing up to 8 priority levels.
class QPSet8 {
protected:
//////////////////////////////////////////////////////////////////////////
/// \brief bimask representing elements of the set
uint8_t m_bits;
public:
/// \brief the function evaluates to TRUE if the priority set is empty,
/// which means that no active objects are ready to run.
uint8_t isEmpty(void) volatile {
return (uint8_t)(m_bits == (uint8_t)0);
}
/// \brief the function evaluates to TRUE if the priority set has elements,
/// which means that some active objects are ready to run.
uint8_t notEmpty(void) volatile {
return (uint8_t)(m_bits != (uint8_t)0);
}
/// \brief the function evaluates to TRUE if the priority set has the
/// element \a n.
uint8_t hasElement(uint8_t n) volatile {
return (uint8_t)((m_bits & Q_ROM_BYTE(QF_pwr2Lkup[n])) != 0);
}
/// \brief insert element \a n into the set, n = 1..8
void insert(uint8_t n) volatile {
m_bits |= Q_ROM_BYTE(QF_pwr2Lkup[n]);
}
/// \brief remove element \a n from the set, n = 1..8
void remove(uint8_t n) volatile {
m_bits &= Q_ROM_BYTE(QF_invPwr2Lkup[n]);
}
/// \brief find the maximum element in the set,
/// \note returns zero if the set is empty
uint8_t findMax(void) volatile {
return Q_ROM_BYTE(QF_log2Lkup[m_bits]);
}
friend class QPSet64;
};
//////////////////////////////////////////////////////////////////////////////
/// \brief Priority Set of up to 64 elements for building various schedulers,
/// but also useful as a general set of up to 64 elements of any kind.
///
/// The priority set represents the set of active objects that are ready to
/// run and need to be considered by scheduling processing. The set is capable
/// of storing up to 64 priority levels.
///
/// The priority set allows to build cooperative multitasking schedulers
/// to manage up to 64 tasks. It is also used in the Quantum Kernel (QK)
/// preemptive scheduler.
///
/// The inherited 8-bit set is used as the 8-elemtn set of of 8-bit subsets
/// Each bit in the super.bits set represents a subset (8-elements)
/// as follows: \n
/// bit 0 in this->m_bits is 1 when subset[0] is not empty \n
/// bit 1 in this->m_bits is 1 when subset[1] is not empty \n
/// bit 2 in this->m_bits is 1 when subset[2] is not empty \n
/// bit 3 in this->m_bits is 1 when subset[3] is not empty \n
/// bit 4 in this->m_bits is 1 when subset[4] is not empty \n
/// bit 5 in this->m_bits is 1 when subset[5] is not empty \n
/// bit 6 in this->m_bits is 1 when subset[6] is not empty \n
/// bit 7 in this->m_bits is 1 when subset[7] is not empty \n
class QPSet64 : public QPSet8 {
/// \brief subsets representing elements in the set as follows: \n
/// m_subset[0] represent elements 1..8 \n
/// m_subset[1] represent elements 9..16 \n
/// m_subset[2] represent elements 17..24 \n
/// m_subset[3] represent elements 25..32 \n
/// m_subset[4] represent elements 33..40 \n
/// m_subset[5] represent elements 41..48 \n
/// m_subset[6] represent elements 49..56 \n
/// m_subset[7] represent elements 57..64 \n
QPSet8 m_subset[8];
public:
/// \brief the function evaluates to TRUE if the priority set has the
/// element \a n.
uint8_t hasElement(uint8_t n) volatile {
return m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].QPSet8::hasElement(n);
}
/// \brief insert element \a n into the set, n = 1..64
void insert(uint8_t n) volatile {
QPSet8::insert(Q_ROM_BYTE(QF_div8Lkup[n]) + 1);
m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].QPSet8::insert(n);
}
/// \brief remove element \a n from the set, n = 1..64
void remove(uint8_t n) volatile {
if ((m_subset[Q_ROM_BYTE(QF_div8Lkup[n])].m_bits
&= Q_ROM_BYTE(QF_invPwr2Lkup[n])) == (uint8_t)0)
{
QPSet8::remove(Q_ROM_BYTE(QF_div8Lkup[n]) + 1);
}
}
/// \brief find the maximum element in the set,
/// \note returns zero if the set is empty
uint8_t findMax(void) volatile {
if (m_bits != (uint8_t)0) {
uint8_t n = (uint8_t)(Q_ROM_BYTE(QF_log2Lkup[m_bits]) - 1);
return (uint8_t)(Q_ROM_BYTE(QF_log2Lkup[m_subset[n].m_bits])
+ (n << 3));
}
else {
return (uint8_t)0;
}
}
};
//////////////////////////////////////////////////////////////////////////////
// Kernel selection based on QK_PREEMPTIVE
//
#ifdef QK_PREEMPTIVE
/// \brief This macro defines the type of the event queue used for the
/// active objects.
///
/// \note This is just an example of the macro definition. Typically, you need
/// to define it in the specific QF port file (qf_port.h). In case of QK,
/// which always depends on the native QF queue, this macro is defined at the
/// level of the platform-independent interface qk.h.
#define QF_EQUEUE_TYPE QEQueue
#if defined(QK_TLS) || defined(QK_EXT_SAVE)
/// \brief This macro defines the type of the OS-Object used for blocking
/// the native QF event queue when the queue is empty
///
/// In QK, the OS object is used to hold the per-thread flags, which might
/// be used, for example, to rembember the thread attributes (e.g.,
/// if the thread uses a floating point co-processor). The OS object value
/// is set on per-thread basis in QActive::start(). Later, the extended
/// context switch macros (QK_EXT_SAVE() and QK_EXT_RESTORE()) might use
/// the per-thread flags to determine what kind of extended context switch
/// this particular thread needs (e.g., the thread might not be using the
/// coprocessor or might be using a different one).
#define QF_OS_OBJECT_TYPE uint8_t
/// \brief This macro defines the type of the thread handle used for the
/// active objects.
///
/// The thread type in QK is the pointer to the thread-local storage (TLS)
/// This thread-local storage can be set on per-thread basis in
/// QActive::start(). Later, the QK scheduler, passes the pointer to the
/// thread-local storage to the macro #QK_TLS.
#define QF_THREAD_TYPE void *
#endif /* QK_TLS || QK_EXT_SAVE */
#if (QF_MAX_ACTIVE <= 8)
extern QPSet8 volatile QK_readySet_; ///< ready set of QK
#else
extern QPSet64 volatile QK_readySet_; ///< ready set of QK
#endif
#ifdef Q_USE_NAMESPACE
} // namespace QP
#endif
extern "C" {
extern uint8_t volatile QK_currPrio_; ///< current task/interrupt priority
extern uint8_t volatile QK_intNest_; ///< interrupt nesting level
} // extern "C"
#ifdef Q_USE_NAMESPACE
namespace QP {
#endif
// QK active object queue implementation .....................................
/// \brief Platform-dependent macro defining how QF should block the calling
/// task when the QF native queue is empty
///
/// \note This is just an example of #QACTIVE_EQUEUE_WAIT_ for the QK-port
/// of QF. QK never activates a task that has no events to process, so in this
/// case the macro asserts that the queue is not empty. In other QF ports you
// need to define the macro appropriately for the underlying kernel/OS you're
/// using.
#define QACTIVE_EQUEUE_WAIT_(me_) \
Q_ASSERT((me_)->m_eQueue.m_frontEvt != (QEvent *)0)
/// \brief Platform-dependent macro defining how QF should signal the
/// active object task that an event has just arrived.
///
/// The macro is necessary only when the native QF event queue is used.
/// The signaling of task involves unblocking the task if it is blocked.
///
/// \note #QACTIVE_EQUEUE_SIGNAL_ is called from a critical section.
/// It might leave the critical section internally, but must restore
/// the critical section before exiting to the caller.
///
/// \note This is just an example of #QACTIVE_EQUEUE_SIGNAL_ for the QK-port
/// of QF. In other QF ports you need to define the macro appropriately for
/// the underlying kernel/OS you're using.
#define QACTIVE_EQUEUE_SIGNAL_(me_) \
QK_readySet_.insert((me_)->m_prio); \
if (QK_intNest_ == (uint8_t)0) { \
QK_SCHEDULE_(); \
} else ((void)0)
/// \brief Platform-dependent macro defining the action QF should take
/// when the native QF event queue becomes empty.
///
/// \note #QACTIVE_EQUEUE_ONEMPTY_ is called from a critical section.
/// It should not leave the critical section.
///
/// \note This is just an example of #QACTIVE_EQUEUE_ONEMPTY_ for the QK-port
/// of QF. In other QF ports you need to define the macro appropriately for
/// the underlying kernel/OS you're using.
#define QACTIVE_EQUEUE_ONEMPTY_(me_) \
QK_readySet_.remove((me_)->m_prio)
// QK event pool operations ..................................................
/// \brief This macro defines the type of the event pool used in this QF port.
///
/// \note This is just an example of the macro definition. Typically, you need
/// to define it in the specific QF port file (qf_port.h). In case of QK,
/// which always depends on the native QF memory pool, this macro is defined
/// at the level of the platform-independent interface qk.h.
#define QF_EPOOL_TYPE_ QMPool