This commit is contained in:
Quantum Leaps 2016-09-29 19:54:50 -04:00
parent 14dffcaf2b
commit 655608b020
126 changed files with 6422 additions and 3466 deletions

View File

@ -5,7 +5,7 @@
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8 DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "QP/C++" PROJECT_NAME = "QP/C++"
PROJECT_NUMBER = "5.7.0" PROJECT_NUMBER = "5.7.2"
PROJECT_BRIEF = PROJECT_BRIEF =
PROJECT_LOGO = images/header_logo_ql.png PROJECT_LOGO = images/header_logo_ql.png
OUTPUT_DIRECTORY = OUTPUT_DIRECTORY =

View File

@ -5,7 +5,7 @@
#--------------------------------------------------------------------------- #---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8 DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "QP/C++" PROJECT_NAME = "QP/C++"
PROJECT_NUMBER = "5.7.0" PROJECT_NUMBER = "5.7.2"
PROJECT_BRIEF = PROJECT_BRIEF =
PROJECT_LOGO = images/header_logo_ql.png PROJECT_LOGO = images/header_logo_ql.png
OUTPUT_DIRECTORY = OUTPUT_DIRECTORY =

View File

@ -2,6 +2,35 @@ namespace QP {
/** @page history Revision History /** @page history Revision History
@section qpcpp_5_7_2 Version 5.7.2, 2016-10-07
This is the first production release of the "dual-mode" QXK kernel.
"Dual-mode" QXK means that QXK supports both basic-threads (BC1 class
from the OSEK/VDX RTOS specification) as well as extended-threads
(EC1 class from the OSEK/VDX RTOS specification. In other words,
QXK executes active objects (basic threads) like the QK kernel using the
single stack (Main Stack on ARM Cortex-M), but can also execute
traditional *blocking* threads (extended threads).
Only the extended threads (QP::QXThread class) need their private stack
spaces and the overhead of the full context switch. The basic threads
(QP::QMActive and QP::QActive classes) run efficiently using the main stack with
much lower context switch overhead.
The QXK examples have been updated for more thorough demonstration of
the QXK features. The QXK examples are available in the following
directories: dpp_efm32-slstk3401a, dpp_ek-tm4c123gxl, and dpp_nucleo-l053r8.
This release also changes the internal QK implementation to match the
terminology applied in the QXK kernel (e.g., QK_sched_() has been renamed
to QK_activate_() and QK_schedPrio_() to QK_sched_()). These changes fall
into the category of refactoring and have no impact on the API or
performance.
Finally, this release improves the implementation of scheduler locking
in publish-subscribe event delivery.
------------------------------------------------------------------------------
@section qpcpp_5_7_0 Version 5.7.0, 2016-08-31 @section qpcpp_5_7_0 Version 5.7.0, 2016-08-31
This release adds support for sub-machines and sub-machine states for reusing pieces of state machines (an advanced UML concept) to the QMsm-state machine implementation strategy. This feature is to match the upcoming QM 4.0.0. This release adds support for sub-machines and sub-machine states for reusing pieces of state machines (an advanced UML concept) to the QMsm-state machine implementation strategy. This feature is to match the upcoming QM 4.0.0.

View File

@ -16,6 +16,17 @@ namespace QP {
/// failures when the switch Q_NASSERT is defined. /// failures when the switch Q_NASSERT is defined.
#define Q_NASSERT #define Q_NASSERT
/// \brief The preprocessor switch to activate the event-constructors
/// and destructors
///
/// When Q_EVT_CTOR is defined (typically in the qep_port.h header file),
/// QP::QEvt becomes a class with constructor and virtual destructor.
/// More importantly, the subclasses of QEvt (your custom events) can have
/// non-default constructors and destructors. These constructors are then
/// called when events are created (e.g., with Q_NEW()) and the destrucor
/// is invoked before recycling the event with QP::QF::gc().
#define Q_EVT_CTOR
/// \brief The preprocessor switch to activate the QS software tracing /// \brief The preprocessor switch to activate the QS software tracing
/// instrumentation in the code /// instrumentation in the code
/// ///

View File

@ -1,8 +1,8 @@
@echo off @echo off
:: ========================================================================== :: ==========================================================================
:: Product: QP/C++ script for generating Doxygen documentation :: Product: QP/C++ script for generating Doxygen documentation
:: Last Updated for Version: 5.7.0 :: Last Updated for Version: 5.7.2
:: Date of the Last Update: 2016-08-31 :: Date of the Last Update: 2016-09-29
:: ::
:: Q u a n t u m L e a P s :: Q u a n t u m L e a P s
:: --------------------------- :: ---------------------------
@ -38,7 +38,7 @@ echo usage:
echo make echo make
echo make -CHM echo make -CHM
set VERSION=5.7.0 set VERSION=5.7.2
:: Generate Resource Standard Metrics for QP/C++ ............................. :: Generate Resource Standard Metrics for QP/C++ .............................
set DOXHOME="C:\tools\doxygen\bin" set DOXHOME="C:\tools\doxygen\bin"

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
#ifdef qxk_h #ifdef qxk_h
namespace DPP { namespace DPP {
extern QP::QXThread * const XT_Test; extern QP::QXThread * const XT_Test1;
} // namespace DPP
namespace DPP {
extern QP::QXThread * const XT_Test2;
} // namespace DPP } // namespace DPP
#endif // qxk_h #endif // qxk_h

View File

@ -40,7 +40,8 @@ if (!registered) {
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me-&gt;subscribe(EAT_SIG);</action> me-&gt;subscribe(EAT_SIG);
me-&gt;subscribe(TEST_SIG);</action>
<initial_glyph conn="2,3,5,1,20,5,-3"> <initial_glyph conn="2,3,5,1,20,5,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</initial_glyph> </initial_glyph>
@ -49,17 +50,22 @@ me-&gt;subscribe(EAT_SIG);</action>
<entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry> <entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry>
<exit>(void)me-&gt;m_timeEvt.disarm();</exit> <exit>(void)me-&gt;m_timeEvt.disarm();</exit>
<tran trig="TIMEOUT" target="../../2"> <tran trig="TIMEOUT" target="../../2">
<tran_glyph conn="2,14,3,1,20,11,-3"> <tran_glyph conn="2,13,3,1,20,12,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="EAT, DONE"> <tran trig="EAT, DONE">
<action>/* EAT or DONE must be for other Philos than this one */ <action>/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action> Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,18,3,-1,13"> <tran_glyph conn="2,16,3,-1,13">
<action box="0,-2,14,2"/> <action box="0,-2,14,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="TEST">
<tran_glyph conn="2,19,3,-1,13">
<action box="0,-2,11,4"/>
</tran_glyph>
</tran>
<state_glyph node="2,5,17,16"> <state_glyph node="2,5,17,16">
<entry box="1,2,5,2"/> <entry box="1,2,5,2"/>
<exit box="1,4,5,2"/> <exit box="1,4,5,2"/>
@ -143,14 +149,14 @@ QS_SIG_DICTIONARY(DONE_SIG, (void *)0); // global signals
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me-&gt;subscribe(DONE_SIG); me-&gt;subscribe(DONE_SIG);
me-&gt;subscribe(PAUSE_SIG); me-&gt;subscribe(PAUSE_SIG);
me-&gt;subscribe(SERVE_SIG); me-&gt;subscribe(SERVE_SIG);
me-&gt;subscribe(TERMINATE_SIG); me-&gt;subscribe(TEST_SIG);
for (uint8_t n = 0U; n &lt; N_PHILO; ++n) { for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
me-&gt;m_fork[n] = FREE; me-&gt;m_fork[n] = FREE;
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
</initial_glyph> </initial_glyph>
</initial> </initial>
<state name="active"> <state name="active">
<tran trig="TERMINATE"> <tran trig="TEST">
<action>BSP::terminate(0);</action>
<tran_glyph conn="2,11,3,-1,14"> <tran_glyph conn="2,11,3,-1,14">
<action box="0,-2,11,4"/> <action box="0,-2,11,4"/>
</tran_glyph> </tran_glyph>
@ -318,7 +323,8 @@ me-&gt;m_fork[n] = FREE;</action>
</class> </class>
<attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test" type="QP::QXThread * const" visibility="0x00" properties="0x00"/> <attribute name="XT_Test1" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test2" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
</package> </package>
<directory name="."> <directory name=".">
<file name="dpp.h"> <file name="dpp.h">
@ -332,7 +338,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
$declare(AOs::AO_Table) $declare(AOs::AO_Table)
#ifdef qxk_h #ifdef qxk_h
$declare(AOs::XT_Test) $declare(AOs::XT_Test1)
$declare(AOs::XT_Test2)
#endif // qxk_h #endif // qxk_h
#endif // dpp_h</text> #endif // dpp_h</text>

View File

@ -134,6 +134,7 @@ QP::QState Philo::initial(Philo * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me->subscribe(EAT_SIG); me->subscribe(EAT_SIG);
me->subscribe(TEST_SIG);
return QM_TRAN_INIT(&tatbl_); return QM_TRAN_INIT(&tatbl_);
} }
//${AOs::Philo::SM::thinking} ................................................ //${AOs::Philo::SM::thinking} ................................................
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }
// ${AOs::Philo::SM::thinking::TEST}
case TEST_SIG: {
status_ = QM_HANDLED();
break;
}
default: { default: {
status_ = QM_SUPER(); status_ = QM_SUPER();
break; break;
} }
} }
(void)me; // avoid compiler warning in case 'me' is not used
return status_; return status_;
} }
//${AOs::Philo::SM::hungry} .................................................. //${AOs::Philo::SM::hungry} ..................................................

View File

@ -40,10 +40,8 @@ int main() {
static QP::QEvt const *tableQueueSto[N_PHILO]; static QP::QEvt const *tableQueueSto[N_PHILO];
static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO]; static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO];
static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG]; static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG];
static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO];
QP::QF::init(); // initialize the framework and the underlying RT kernel QP::QF::init(); // initialize the framework and the underlying RT kernel
DPP::BSP::init(); // initialize the BSP DPP::BSP::init(); // initialize the BSP

View File

@ -1,7 +1,7 @@
///*************************************************************************** ///***************************************************************************
// Product: DPP example, EFM32-SLSTK3401A board, preemptive QXK kernel // Product: DPP example, EFM32-SLSTK3401A board, preemptive QXK kernel
// Last Updated for Version: 5.6.5 // Last Updated for Version: 5.7.2
// Date of the Last Update: 2016-06-02 // Date of the Last Update: 2016-09-29
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
@ -96,6 +96,7 @@ static QP::QXMutex l_rndMutex; // to protect the random number generator
enum AppRecords { // application-specific trace records enum AppRecords { // application-specific trace records
PHILO_STAT = QP::QS_USER, PHILO_STAT = QP::QS_USER,
PAUSED_STAT,
COMMAND_STAT COMMAND_STAT
}; };
@ -155,8 +156,10 @@ void GPIO_EVEN_IRQHandler(void) {
QXK_ISR_ENTRY(); // inform QXK about entering an ISR QXK_ISR_ENTRY(); // inform QXK about entering an ISR
// for testing... // for testing...
DPP::AO_Table->POST(Q_NEW(QP::QEvt, DPP::MAX_PUB_SIG), //DPP::AO_Table->POST(Q_NEW(QP::QEvt, DPP::MAX_PUB_SIG),
&l_GPIO_EVEN_IRQHandler); // &l_GPIO_EVEN_IRQHandler);
QP::QF::PUBLISH(Q_NEW(QP::QEvt, TEST_SIG), // for testing...
&l_GPIO_EVEN_IRQHandler);
QXK_ISR_EXIT(); // inform QXK about exiting an ISR QXK_ISR_EXIT(); // inform QXK about exiting an ISR
} }
@ -217,6 +220,7 @@ void BSP::init(void) {
QS_OBJ_DICTIONARY(&l_SysTick_Handler); QS_OBJ_DICTIONARY(&l_SysTick_Handler);
QS_OBJ_DICTIONARY(&l_GPIO_EVEN_IRQHandler); QS_OBJ_DICTIONARY(&l_GPIO_EVEN_IRQHandler);
QS_USR_DICTIONARY(PHILO_STAT); QS_USR_DICTIONARY(PHILO_STAT);
QS_USR_DICTIONARY(PAUSED_STAT);
QS_USR_DICTIONARY(COMMAND_STAT); QS_USR_DICTIONARY(COMMAND_STAT);
} }
//............................................................................ //............................................................................
@ -237,10 +241,19 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
void BSP::displayPaused(uint8_t paused) { void BSP::displayPaused(uint8_t paused) {
if (paused != 0U) { if (paused != 0U) {
GPIO->P[LED_PORT].DOUT |= (1U << LED0_PIN); GPIO->P[LED_PORT].DOUT |= (1U << LED0_PIN);
// for testing the extended threads...
static QP::QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U};
XT_Test2->delayCancel(); // make sure Test2 is not delayed
XT_Test2->POST_X(&pauseEvt, 1U, (void *)0); // post to Test2's queue
} }
else { else {
GPIO->P[LED_PORT].DOUT &= ~(1U << LED0_PIN); GPIO->P[LED_PORT].DOUT &= ~(1U << LED0_PIN);
} }
QS_BEGIN(PAUSED_STAT, (void *)0) // application-specific record begin
QS_U8(1, paused); // Paused status
QS_END()
} }
//............................................................................ //............................................................................
uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
@ -260,16 +273,16 @@ uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
} }
//............................................................................ //............................................................................
void BSP::randomSeed(uint32_t seed) { void BSP::randomSeed(uint32_t seed) {
l_rnd = seed;
l_rndMutex.init(N_PHILO); // ceiling <== maximum Philo priority l_rndMutex.init(N_PHILO); // ceiling <== maximum Philo priority
l_rnd = seed;
} }
//............................................................................ //............................................................................
void BSP::ledOn(void) { void BSP::ledOn(void) {
GPIO->P[LED_PORT].DOUT |= (1U << LED0_PIN); GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN);
} }
//............................................................................ //............................................................................
void BSP::ledOff(void) { void BSP::ledOff(void) {
GPIO->P[LED_PORT].DOUT &= ~(1U << LED0_PIN); GPIO->P[LED_PORT].DOUT &= ~(1U << LED1_PIN);
} }
//............................................................................ //............................................................................
void BSP::terminate(int16_t result) { void BSP::terminate(int16_t result) {
@ -313,10 +326,14 @@ void QF::onCleanup(void) {
//............................................................................ //............................................................................
void QXK::onIdle(void) { void QXK::onIdle(void) {
// toggle the User LED on and then off, see NOTE01 // toggle the User LED on and then off, see NOTE01
QF_INT_DISABLE(); // QF_INT_DISABLE();
GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN); // GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN);
GPIO->P[LED_PORT].DOUT &= ~(1U << LED1_PIN); // GPIO->P[LED_PORT].DOUT &= ~(1U << LED1_PIN);
QF_INT_ENABLE(); // QF_INT_ENABLE();
// Some flating point code is to exercise the VFP...
float volatile x = 1.73205F;
x = x * 1.73205F;
#ifdef Q_SPY #ifdef Q_SPY
QS::rxParse(); // parse all the received bytes QS::rxParse(); // parse all the received bytes

View File

@ -1,7 +1,7 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.2 // Last updated for version 5.7.2
// Last updated on 2016-03-31 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
@ -38,23 +38,18 @@
//............................................................................ //............................................................................
int main() { int main() {
static QP::QEvt const *tableQueueSto[N_PHILO]; static QP::QEvt const *tableQueueSto[N_PHILO];
static uint64_t tableStackSto[64];
static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO]; static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO];
static uint64_t philoStackSto[N_PHILO][64];
static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG]; static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG];
static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
// stack for the "naked" test thread // stacks and queues for the extended test threads
static QP::QEvt const *testQueueSto[5]; static QP::QEvt const *test1QueueSto[5];
static uint64_t testStackSto[64]; static uint64_t test1StackSto[64];
static QP::QEvt const *test2QueueSto[5];
// stack for the QXK's idle thread static uint64_t test2StackSto[64];
static uint64_t idleStackSto[32];
QP::QF::init(); // initialize the framework and the underlying RT kernel QP::QF::init(); // initialize the framework and the underlying RT kernel
QP::QXK::init(idleStackSto, sizeof(idleStackSto)); // initialize QXK
DPP::BSP::init(); // initialize the BSP DPP::BSP::init(); // initialize the BSP
// object dictionaries... // object dictionaries...
@ -72,33 +67,42 @@ int main() {
QP::QF::poolInit(smlPoolSto, QP::QF::poolInit(smlPoolSto,
sizeof(smlPoolSto), sizeof(smlPoolSto[0])); sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
// start the active objects... // start the extended Test1 thread
DPP::XT_Test1->start(
static_cast<uint_fast8_t>(1), // QP prio of the thread
test1QueueSto, // event queue storage
Q_DIM(test1QueueSto), // queue length [events]
test1StackSto, // stack storage
sizeof(test1StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event
// start the Philo active objects...
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
DPP::AO_Philo[n]->start( DPP::AO_Philo[n]->start(
static_cast<uint_fast8_t>(n + 1), // QP priority of the AO static_cast<uint_fast8_t>(n + 2), // QP priority of the AO
philoQueueSto[n], // event queue storage philoQueueSto[n], // event queue storage
Q_DIM(philoQueueSto[n]), // queue length [events] Q_DIM(philoQueueSto[n]), // queue length [events]
philoStackSto[n], // stack storage static_cast<void *>(0), // no stack storage
sizeof(philoStackSto[n]), // stack size [bytes] static_cast<uint_fast16_t>(0), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
} }
DPP::AO_Table->start( // start the extended Test2 thread
static_cast<uint_fast8_t>(N_PHILO + 1), // QP priority of the AO DPP::XT_Test2->start(
tableQueueSto, // event queue storage static_cast<uint_fast8_t>(N_PHILO + 2), // QP prio of the thread
Q_DIM(tableQueueSto), // queue length [events] test2QueueSto, // event queue storage
tableStackSto, // stack storage Q_DIM(test2QueueSto), // queue length [events]
sizeof(tableStackSto), // stack size [bytes] test2StackSto, // stack storage
sizeof(test2StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
// start the "naked" test thread DPP::AO_Table->start(
DPP::XT_Test->start( static_cast<uint_fast8_t>(N_PHILO + 3), // QP priority of the AO
static_cast<uint_fast8_t>(10), // QP priority of the AO tableQueueSto, // event queue storage
testQueueSto, // event queue storage Q_DIM(tableQueueSto), // queue length [events]
Q_DIM(testQueueSto), // queue length [events] static_cast<void *>(0), // no stack storage
testStackSto, // stack storage static_cast<uint_fast16_t>(0), // stack size [bytes]
sizeof(testStackSto), // stack size [bytes] static_cast<QP::QEvt *>(0)); // initialization event
static_cast<QP::QEvt *>(0)); // initialization event
return QP::QF::run(); // run the QF application return QP::QF::run(); // run the QF application
} }

View File

@ -1,13 +1,13 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.0 // Last updated for version 5.7.2
// Last updated on 2015-12-28 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
// innovating embedded systems // innovating embedded systems
// //
// Copyright (C) Quantum Leaps, www.state-machine.com. // Copyright (C) Quantum Leaps, LLC. All rights reserved.
// //
// This program is open source software: you can redistribute it and/or // This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published // modify it under the terms of the GNU General Public License as published
@ -28,8 +28,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// Contact information: // Contact information:
// Web: www.state-machine.com // http://www.state-machine.com
// Email: info@state-machine.com // mailto:info@state-machine.com
//**************************************************************************** //****************************************************************************
#include "qpcpp.h" #include "qpcpp.h"
#include "dpp.h" #include "dpp.h"
@ -37,26 +37,74 @@
namespace DPP { namespace DPP {
// local extended-thread objects .............................................
static void Thread1_run(QP::QXThread * const me);
static void Thread2_run(QP::QXThread * const me);
static QP::QXThread l_test1(&Thread1_run, 0U);
static QP::QXThread l_test2(&Thread2_run, 0U);
static QP::QXMutex l_mutex;
static QP::QXSemaphore l_sema;
// global pointer to the test thread .........................................
QP::QXThread * const XT_Test1 = &l_test1;
QP::QXThread * const XT_Test2 = &l_test2;
//............................................................................ //............................................................................
static void thread_function(void *par) { static void Thread1_run(QP::QXThread * const /*me*/) {
(void)par;
l_mutex.init(3U);
for (;;) { for (;;) {
(void)QP::QXThread::queueGet(BSP::TICKS_PER_SEC*2U, 0U); // wait on a semaphore (BLOCK with timeout)
(void)l_sema.wait(BSP::TICKS_PER_SEC, 0U);
BSP::ledOn(); BSP::ledOn();
QP::QXThread::delay(BSP::TICKS_PER_SEC/4U, 0U);
BSP::ledOff();
QP::QXThread::delay(BSP::TICKS_PER_SEC*3U/4U, 0U); l_mutex.lock(); // exercise the mutex
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
l_mutex.unlock();
QP::QXThread::delay(BSP::TICKS_PER_SEC/7, 0U); // BLOCK
// publish to Thread2
QP::QF::PUBLISH(Q_NEW(QP::QEvt, TEST_SIG), &l_test1);
} }
} }
// local "naked" thread object ............................................... //............................................................................
static QP::QXThread l_test(&thread_function, 0U); static void Thread2_run(QP::QXThread * const me) {
// global pointer to the test thread ......................................... // subscribe to the test signal */
QP::QXThread * const XT_Test = &l_test; me->subscribe(TEST_SIG);
// initialize the semaphore before using it
// NOTE: the semaphore is initialized in the highest-priority thread
// that uses it. Alternatively, the semaphore can be initialized
// before any thread runs.
l_sema.init(0U); // start with zero count
for (;;) {
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
// wait on the internal event queue (BLOCK) with timeout
QP::QEvt const *e = me->queueGet(BSP::TICKS_PER_SEC/2, 0U);
BSP::ledOff();
if (e != static_cast<QP::QEvt *>(0)) { // event actually delivered?
QP::QF::gc(e); // recycle the event manually!
}
else {
me->delay(BSP::TICKS_PER_SEC/2, 0U); // wait some more (BLOCK)
l_sema.signal(); // signal Thread1
}
}
}
} // namespace DPP } // namespace DPP

View File

@ -115,14 +115,14 @@ QP::QState Table::initial(Table * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me->subscribe(DONE_SIG); me->subscribe(DONE_SIG);
me->subscribe(PAUSE_SIG); me->subscribe(PAUSE_SIG);
me->subscribe(SERVE_SIG); me->subscribe(SERVE_SIG);
me->subscribe(TERMINATE_SIG); me->subscribe(TEST_SIG);
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
me->m_fork[n] = FREE; me->m_fork[n] = FREE;
@ -143,9 +143,8 @@ QP::QMState const Table::active_s = {
QP::QState Table::active(Table * const me, QP::QEvt const * const e) { QP::QState Table::active(Table * const me, QP::QEvt const * const e) {
QP::QState status_; QP::QState status_;
switch (e->sig) { switch (e->sig) {
// ${AOs::Table::SM::active::TERMINATE} // ${AOs::Table::SM::active::TEST}
case TERMINATE_SIG: { case TEST_SIG: {
BSP::terminate(0);
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }

View File

@ -25,7 +25,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
#ifdef qxk_h #ifdef qxk_h
namespace DPP { namespace DPP {
extern QP::QXThread * const XT_Test; extern QP::QXThread * const XT_Test1;
} // namespace DPP
namespace DPP {
extern QP::QXThread * const XT_Test2;
} // namespace DPP } // namespace DPP
#endif // qxk_h #endif // qxk_h

View File

@ -40,7 +40,8 @@ if (!registered) {
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me-&gt;subscribe(EAT_SIG);</action> me-&gt;subscribe(EAT_SIG);
me-&gt;subscribe(TEST_SIG);</action>
<initial_glyph conn="2,3,5,1,20,5,-3"> <initial_glyph conn="2,3,5,1,20,5,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</initial_glyph> </initial_glyph>
@ -49,17 +50,22 @@ me-&gt;subscribe(EAT_SIG);</action>
<entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry> <entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry>
<exit>(void)me-&gt;m_timeEvt.disarm();</exit> <exit>(void)me-&gt;m_timeEvt.disarm();</exit>
<tran trig="TIMEOUT" target="../../2"> <tran trig="TIMEOUT" target="../../2">
<tran_glyph conn="2,14,3,1,20,11,-3"> <tran_glyph conn="2,13,3,1,20,12,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="EAT, DONE"> <tran trig="EAT, DONE">
<action>/* EAT or DONE must be for other Philos than this one */ <action>/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action> Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,18,3,-1,13"> <tran_glyph conn="2,16,3,-1,13">
<action box="0,-2,14,2"/> <action box="0,-2,14,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="TEST">
<tran_glyph conn="2,19,3,-1,13">
<action box="0,-2,11,4"/>
</tran_glyph>
</tran>
<state_glyph node="2,5,17,16"> <state_glyph node="2,5,17,16">
<entry box="1,2,5,2"/> <entry box="1,2,5,2"/>
<exit box="1,4,5,2"/> <exit box="1,4,5,2"/>
@ -143,14 +149,14 @@ QS_SIG_DICTIONARY(DONE_SIG, (void *)0); // global signals
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me-&gt;subscribe(DONE_SIG); me-&gt;subscribe(DONE_SIG);
me-&gt;subscribe(PAUSE_SIG); me-&gt;subscribe(PAUSE_SIG);
me-&gt;subscribe(SERVE_SIG); me-&gt;subscribe(SERVE_SIG);
me-&gt;subscribe(TERMINATE_SIG); me-&gt;subscribe(TEST_SIG);
for (uint8_t n = 0U; n &lt; N_PHILO; ++n) { for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
me-&gt;m_fork[n] = FREE; me-&gt;m_fork[n] = FREE;
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
</initial_glyph> </initial_glyph>
</initial> </initial>
<state name="active"> <state name="active">
<tran trig="TERMINATE"> <tran trig="TEST">
<action>BSP::terminate(0);</action>
<tran_glyph conn="2,11,3,-1,14"> <tran_glyph conn="2,11,3,-1,14">
<action box="0,-2,11,4"/> <action box="0,-2,11,4"/>
</tran_glyph> </tran_glyph>
@ -318,7 +323,8 @@ me-&gt;m_fork[n] = FREE;</action>
</class> </class>
<attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test" type="QP::QXThread * const" visibility="0x00" properties="0x00"/> <attribute name="XT_Test1" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test2" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
</package> </package>
<directory name="."> <directory name=".">
<file name="dpp.h"> <file name="dpp.h">
@ -332,7 +338,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
$declare(AOs::AO_Table) $declare(AOs::AO_Table)
#ifdef qxk_h #ifdef qxk_h
$declare(AOs::XT_Test) $declare(AOs::XT_Test1)
$declare(AOs::XT_Test2)
#endif // qxk_h #endif // qxk_h
#endif // dpp_h</text> #endif // dpp_h</text>

View File

@ -134,6 +134,7 @@ QP::QState Philo::initial(Philo * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me->subscribe(EAT_SIG); me->subscribe(EAT_SIG);
me->subscribe(TEST_SIG);
return QM_TRAN_INIT(&tatbl_); return QM_TRAN_INIT(&tatbl_);
} }
//${AOs::Philo::SM::thinking} ................................................ //${AOs::Philo::SM::thinking} ................................................
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }
// ${AOs::Philo::SM::thinking::TEST}
case TEST_SIG: {
status_ = QM_HANDLED();
break;
}
default: { default: {
status_ = QM_SUPER(); status_ = QM_SUPER();
break; break;
} }
} }
(void)me; // avoid compiler warning in case 'me' is not used
return status_; return status_;
} }
//${AOs::Philo::SM::hungry} .................................................. //${AOs::Philo::SM::hungry} ..................................................

View File

@ -87,13 +87,28 @@
</folderInfo> </folderInfo>
<fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_pkg.h" name="qxk_pkg.h" rcbsApplicability="disable" resourcePath="QP/qxk_pkg.h" toolsToInvoke=""/> <fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_pkg.h" name="qxk_pkg.h" rcbsApplicability="disable" resourcePath="QP/qxk_pkg.h" toolsToInvoke=""/>
<fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_mutex.cpp" name="qxk_mutex.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_mutex.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.389833599"> <fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_mutex.cpp" name="qxk_mutex.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_mutex.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.389833599">
<tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.389833599" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029"/> <tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.389833599" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029">
</fileInfo> <inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS.1285108316" name="C Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS"/>
<fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_xthr.cpp" name="qxk_xthr.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_xthr.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1670607576"> <inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS.1726548581" name="C++ Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS"/>
<tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1670607576" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029"/> <inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS.2072872125" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS.415933216" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS"/>
</tool>
</fileInfo> </fileInfo>
<fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_sema.cpp" name="qxk_sema.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_sema.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1284799868"> <fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_sema.cpp" name="qxk_sema.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_sema.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1284799868">
<tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1284799868" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029"/> <tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1284799868" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029">
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS.244795563" name="C Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS.744268921" name="C++ Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS.1388658320" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS.688741509" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS"/>
</tool>
</fileInfo>
<fileInfo id="com.ti.ccstudio.buildDefinitions.TMS470.Debug.1412738644.QP/qxk_xthr.cpp" name="qxk_xthr.cpp" rcbsApplicability="disable" resourcePath="QP/qxk_xthr.cpp" toolsToInvoke="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1670607576">
<tool id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029.1670607576" name="ARM Compiler" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.exe.compilerDebug.345747029">
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS.588521963" name="C Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__C_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS.1176554071" name="C++ Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__CPP_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS.478274482" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM_SRCS"/>
<inputType id="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS.358771019" name="Assembly Sources" superClass="com.ti.ccstudio.buildDefinitions.TMS470_15.12.compiler.inputType__ASM2_SRCS"/>
</tool>
</fileInfo> </fileInfo>
<sourceEntries> <sourceEntries>
<entry excluding="QP/qxk_mutex.cpp|QP/qxk_pkg.h|QP/qxk_sema.cpp|QP/qxk_xthr.cpp|QP/qxk.cpp|QP/qs.cpp|QP/qs_rx.cpp|QP/qs_fp.cpp|QP/qs_64bit.cpp|QP/qk.cpp|QP/qk_mutex.cpp|QP/qk.c|QP/qk_mutex.c|ek-tm4c123gxl/iar/startup_TM4C123GH6PM.s|ek-tm4c123gxl/gnu/startup_TM4C123GH6PM.c|ek-tm4c123gxl/arm/startup_TM4C123GH6PM.s|QP/qs.c|QP/qs_rx.c|QP/qs_fp.c|QP/qs_64bit.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/> <entry excluding="QP/qxk_mutex.cpp|QP/qxk_pkg.h|QP/qxk_sema.cpp|QP/qxk_xthr.cpp|QP/qxk.cpp|QP/qs.cpp|QP/qs_rx.cpp|QP/qs_fp.cpp|QP/qs_64bit.cpp|QP/qk.cpp|QP/qk_mutex.cpp|QP/qk.c|QP/qk_mutex.c|ek-tm4c123gxl/iar/startup_TM4C123GH6PM.s|ek-tm4c123gxl/gnu/startup_TM4C123GH6PM.c|ek-tm4c123gxl/arm/startup_TM4C123GH6PM.s|QP/qs.c|QP/qs_rx.c|QP/qs_fp.c|QP/qs_64bit.c" flags="VALUE_WORKSPACE_PATH|RESOLVED" kind="sourcePath" name=""/>

View File

@ -230,15 +230,14 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
//............................................................................ //............................................................................
void BSP::displayPaused(uint8_t paused) { void BSP::displayPaused(uint8_t paused) {
//GPIOF->DATA_Bits[LED_RED] = ((paused != 0U) ? 0xFFU : 0U); //GPIOF->DATA_Bits[LED_RED] = ((paused != 0U) ? 0xFFU : 0U);
static QP::QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U}; static QP::QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U};
XT_Test->POST_X(&pauseEvt, 1U, (void *)0); XT_Test2->POST_X(&pauseEvt, 1U, (void *)0);
//XT_Test->unblock(); //??? unblock the Test thread
// application-specific trace record // application-specific trace record
QS_BEGIN(PAUSED_STAT, AO_Table) QS_BEGIN(PAUSED_STAT, AO_Table)
QS_U8(1, paused); // Paused status QS_U8(1, paused); // Paused status
QS_END() QS_END()
} }
//............................................................................ //............................................................................
uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator

View File

@ -1,7 +1,7 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.2 // Last updated for version 5.7.2
// Last updated on 2016-03-31 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
@ -38,23 +38,18 @@
//............................................................................ //............................................................................
int main() { int main() {
static QP::QEvt const *tableQueueSto[N_PHILO]; static QP::QEvt const *tableQueueSto[N_PHILO];
static uint64_t tableStackSto[64];
static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO]; static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO];
static uint64_t philoStackSto[N_PHILO][64];
static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG]; static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG];
static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
// stack for the "naked" test thread // stacks and queues for the extended test threads
static QP::QEvt const *testQueueSto[5]; static QP::QEvt const *test1QueueSto[5];
static uint64_t testStackSto[64]; static uint64_t test1StackSto[64];
static QP::QEvt const *test2QueueSto[5];
// stack for the QXK's idle thread static uint64_t test2StackSto[64];
static uint64_t idleStackSto[32];
QP::QF::init(); // initialize the framework and the underlying RT kernel QP::QF::init(); // initialize the framework and the underlying RT kernel
QP::QXK::init(idleStackSto, sizeof(idleStackSto)); // initialize QXK
DPP::BSP::init(); // initialize the BSP DPP::BSP::init(); // initialize the BSP
// object dictionaries... // object dictionaries...
@ -72,33 +67,42 @@ int main() {
QP::QF::poolInit(smlPoolSto, QP::QF::poolInit(smlPoolSto,
sizeof(smlPoolSto), sizeof(smlPoolSto[0])); sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
// start the active objects... // start the extended Test1 thread
DPP::XT_Test1->start(
static_cast<uint_fast8_t>(1), // QP prio of the thread
test1QueueSto, // event queue storage
Q_DIM(test1QueueSto), // queue length [events]
test1StackSto, // stack storage
sizeof(test1StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event
// start the Philo active objects...
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
DPP::AO_Philo[n]->start( DPP::AO_Philo[n]->start(
static_cast<uint_fast8_t>(n + 1), // QP priority of the AO static_cast<uint_fast8_t>(n + 2), // QP priority of the AO
philoQueueSto[n], // event queue storage philoQueueSto[n], // event queue storage
Q_DIM(philoQueueSto[n]), // queue length [events] Q_DIM(philoQueueSto[n]), // queue length [events]
philoStackSto[n], // stack storage static_cast<void *>(0), // no stack storage
sizeof(philoStackSto[n]), // stack size [bytes] static_cast<uint_fast16_t>(0), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
} }
DPP::AO_Table->start( // start the extended Test2 thread
static_cast<uint_fast8_t>(N_PHILO + 1), // QP priority of the AO DPP::XT_Test2->start(
tableQueueSto, // event queue storage static_cast<uint_fast8_t>(N_PHILO + 2), // QP prio of the thread
Q_DIM(tableQueueSto), // queue length [events] test2QueueSto, // event queue storage
tableStackSto, // stack storage Q_DIM(test2QueueSto), // queue length [events]
sizeof(tableStackSto), // stack size [bytes] test2StackSto, // stack storage
sizeof(test2StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
// start the "naked" test thread DPP::AO_Table->start(
DPP::XT_Test->start( static_cast<uint_fast8_t>(N_PHILO + 3), // QP priority of the AO
static_cast<uint_fast8_t>(10), // QP priority of the AO tableQueueSto, // event queue storage
testQueueSto, // event queue storage Q_DIM(tableQueueSto), // queue length [events]
Q_DIM(testQueueSto), // queue length [events] static_cast<void *>(0), // no stack storage
testStackSto, // stack storage static_cast<uint_fast16_t>(0), // stack size [bytes]
sizeof(testStackSto), // stack size [bytes] static_cast<QP::QEvt *>(0)); // initialization event
static_cast<QP::QEvt *>(0)); // initialization event
return QP::QF::run(); // run the QF application return QP::QF::run(); // run the QF application
} }

View File

@ -1,13 +1,13 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.0 // Last updated for version 5.7.2
// Last updated on 2015-12-28 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
// innovating embedded systems // innovating embedded systems
// //
// Copyright (C) Quantum Leaps, www.state-machine.com. // Copyright (C) Quantum Leaps, LLC. All rights reserved.
// //
// This program is open source software: you can redistribute it and/or // This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published // modify it under the terms of the GNU General Public License as published
@ -28,8 +28,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// Contact information: // Contact information:
// Web: www.state-machine.com // http://www.state-machine.com
// Email: info@state-machine.com // mailto:info@state-machine.com
//**************************************************************************** //****************************************************************************
#include "qpcpp.h" #include "qpcpp.h"
#include "dpp.h" #include "dpp.h"
@ -37,26 +37,74 @@
namespace DPP { namespace DPP {
// local extended-thread objects .............................................
static void Thread1_run(QP::QXThread * const me);
static void Thread2_run(QP::QXThread * const me);
static QP::QXThread l_test1(&Thread1_run, 0U);
static QP::QXThread l_test2(&Thread2_run, 0U);
static QP::QXMutex l_mutex;
static QP::QXSemaphore l_sema;
// global pointer to the test thread .........................................
QP::QXThread * const XT_Test1 = &l_test1;
QP::QXThread * const XT_Test2 = &l_test2;
//............................................................................ //............................................................................
static void thread_function(void *par) { static void Thread1_run(QP::QXThread * const /*me*/) {
(void)par;
l_mutex.init(3U);
for (;;) { for (;;) {
(void)QP::QXThread::queueGet(BSP::TICKS_PER_SEC*2U, 0U); // wait on a semaphore (BLOCK with timeout)
(void)l_sema.wait(BSP::TICKS_PER_SEC, 0U);
BSP::ledOn(); BSP::ledOn();
QP::QXThread::delay(BSP::TICKS_PER_SEC/4U, 0U);
BSP::ledOff();
QP::QXThread::delay(BSP::TICKS_PER_SEC*3U/4U, 0U); l_mutex.lock(); // exercise the mutex
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
l_mutex.unlock();
QP::QXThread::delay(BSP::TICKS_PER_SEC/7, 0U); // BLOCK
// publish to Thread2
QP::QF::PUBLISH(Q_NEW(QP::QEvt, TEST_SIG), &l_test1);
} }
} }
// local "naked" thread object ............................................... //............................................................................
static QP::QXThread l_test(&thread_function, 0U); static void Thread2_run(QP::QXThread * const me) {
// global pointer to the test thread ......................................... // subscribe to the test signal */
QP::QXThread * const XT_Test = &l_test; me->subscribe(TEST_SIG);
// initialize the semaphore before using it
// NOTE: the semaphore is initialized in the highest-priority thread
// that uses it. Alternatively, the semaphore can be initialized
// before any thread runs.
l_sema.init(0U); // start with zero count
for (;;) {
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
// wait on the internal event queue (BLOCK) with timeout
QP::QEvt const *e = me->queueGet(BSP::TICKS_PER_SEC/2, 0U);
BSP::ledOff();
if (e != static_cast<QP::QEvt *>(0)) { // event actually delivered?
QP::QF::gc(e); // recycle the event manually!
}
else {
me->delay(BSP::TICKS_PER_SEC/2, 0U); // wait some more (BLOCK)
l_sema.signal(); // signal Thread1
}
}
}
} // namespace DPP } // namespace DPP

View File

@ -115,14 +115,14 @@ QP::QState Table::initial(Table * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me->subscribe(DONE_SIG); me->subscribe(DONE_SIG);
me->subscribe(PAUSE_SIG); me->subscribe(PAUSE_SIG);
me->subscribe(SERVE_SIG); me->subscribe(SERVE_SIG);
me->subscribe(TERMINATE_SIG); me->subscribe(TEST_SIG);
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
me->m_fork[n] = FREE; me->m_fork[n] = FREE;
@ -143,9 +143,8 @@ QP::QMState const Table::active_s = {
QP::QState Table::active(Table * const me, QP::QEvt const * const e) { QP::QState Table::active(Table * const me, QP::QEvt const * const e) {
QP::QState status_; QP::QState status_;
switch (e->sig) { switch (e->sig) {
// ${AOs::Table::SM::active::TERMINATE} // ${AOs::Table::SM::active::TEST}
case TERMINATE_SIG: { case TEST_SIG: {
BSP::terminate(0);
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }

View File

@ -25,7 +25,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
#ifdef qxk_h #ifdef qxk_h
namespace DPP { namespace DPP {
extern QP::QXThread * const XT_Test; extern QP::QXThread * const XT_Test1;
} // namespace DPP
namespace DPP {
extern QP::QXThread * const XT_Test2;
} // namespace DPP } // namespace DPP
#endif // qxk_h #endif // qxk_h

View File

@ -40,7 +40,8 @@ if (!registered) {
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me-&gt;subscribe(EAT_SIG);</action> me-&gt;subscribe(EAT_SIG);
me-&gt;subscribe(TEST_SIG);</action>
<initial_glyph conn="2,3,5,1,20,5,-3"> <initial_glyph conn="2,3,5,1,20,5,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</initial_glyph> </initial_glyph>
@ -49,17 +50,22 @@ me-&gt;subscribe(EAT_SIG);</action>
<entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry> <entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry>
<exit>(void)me-&gt;m_timeEvt.disarm();</exit> <exit>(void)me-&gt;m_timeEvt.disarm();</exit>
<tran trig="TIMEOUT" target="../../2"> <tran trig="TIMEOUT" target="../../2">
<tran_glyph conn="2,14,3,1,20,11,-3"> <tran_glyph conn="2,13,3,1,20,12,-3">
<action box="0,-2,6,2"/> <action box="0,-2,6,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="EAT, DONE"> <tran trig="EAT, DONE">
<action>/* EAT or DONE must be for other Philos than this one */ <action>/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action> Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,18,3,-1,13"> <tran_glyph conn="2,16,3,-1,13">
<action box="0,-2,14,2"/> <action box="0,-2,14,2"/>
</tran_glyph> </tran_glyph>
</tran> </tran>
<tran trig="TEST">
<tran_glyph conn="2,19,3,-1,13">
<action box="0,-2,11,4"/>
</tran_glyph>
</tran>
<state_glyph node="2,5,17,16"> <state_glyph node="2,5,17,16">
<entry box="1,2,5,2"/> <entry box="1,2,5,2"/>
<exit box="1,4,5,2"/> <exit box="1,4,5,2"/>
@ -143,14 +149,14 @@ QS_SIG_DICTIONARY(DONE_SIG, (void *)0); // global signals
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me-&gt;subscribe(DONE_SIG); me-&gt;subscribe(DONE_SIG);
me-&gt;subscribe(PAUSE_SIG); me-&gt;subscribe(PAUSE_SIG);
me-&gt;subscribe(SERVE_SIG); me-&gt;subscribe(SERVE_SIG);
me-&gt;subscribe(TERMINATE_SIG); me-&gt;subscribe(TEST_SIG);
for (uint8_t n = 0U; n &lt; N_PHILO; ++n) { for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
me-&gt;m_fork[n] = FREE; me-&gt;m_fork[n] = FREE;
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
</initial_glyph> </initial_glyph>
</initial> </initial>
<state name="active"> <state name="active">
<tran trig="TERMINATE"> <tran trig="TEST">
<action>BSP::terminate(0);</action>
<tran_glyph conn="2,11,3,-1,14"> <tran_glyph conn="2,11,3,-1,14">
<action box="0,-2,11,4"/> <action box="0,-2,11,4"/>
</tran_glyph> </tran_glyph>
@ -318,7 +323,8 @@ me-&gt;m_fork[n] = FREE;</action>
</class> </class>
<attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/> <attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test" type="QP::QXThread * const" visibility="0x00" properties="0x00"/> <attribute name="XT_Test1" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
<attribute name="XT_Test2" type="QP::QXThread * const" visibility="0x00" properties="0x00"/>
</package> </package>
<directory name="."> <directory name=".">
<file name="dpp.h"> <file name="dpp.h">
@ -332,7 +338,7 @@ enum DPPSignals {
DONE_SIG, // published by Philosopher when done eating DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application TEST_SIG, // published by BSP to test the application
MAX_PUB_SIG, // the last published signal MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo HUNGRY_SIG, // posted direclty to Table from hungry Philo
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
$declare(AOs::AO_Table) $declare(AOs::AO_Table)
#ifdef qxk_h #ifdef qxk_h
$declare(AOs::XT_Test) $declare(AOs::XT_Test1)
$declare(AOs::XT_Test2)
#endif // qxk_h #endif // qxk_h
#endif // dpp_h</text> #endif // dpp_h</text>

View File

@ -134,6 +134,7 @@ QP::QState Philo::initial(Philo * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me->subscribe(EAT_SIG); me->subscribe(EAT_SIG);
me->subscribe(TEST_SIG);
return QM_TRAN_INIT(&tatbl_); return QM_TRAN_INIT(&tatbl_);
} }
//${AOs::Philo::SM::thinking} ................................................ //${AOs::Philo::SM::thinking} ................................................
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }
// ${AOs::Philo::SM::thinking::TEST}
case TEST_SIG: {
status_ = QM_HANDLED();
break;
}
default: { default: {
status_ = QM_SUPER(); status_ = QM_SUPER();
break; break;
} }
} }
(void)me; // avoid compiler warning in case 'me' is not used
return status_; return status_;
} }
//${AOs::Philo::SM::hungry} .................................................. //${AOs::Philo::SM::hungry} ..................................................

View File

@ -1,13 +1,13 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.0 // Last updated for version 5.7.2
// Last updated on 2015-12-26 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
// innovating embedded systems // innovating embedded systems
// //
// Copyright (C) Quantum Leaps, www.state-machine.com. // Copyright (C) Quantum Leaps, LLC. All rights reserved.
// //
// This program is open source software: you can redistribute it and/or // This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published // modify it under the terms of the GNU General Public License as published
@ -28,8 +28,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// Contact information: // Contact information:
// Web: www.state-machine.com // http://www.state-machine.com
// Email: info@state-machine.com // mailto:info@state-machine.com
//**************************************************************************** //****************************************************************************
#include "qpcpp.h" #include "qpcpp.h"
#include "dpp.h" #include "dpp.h"
@ -38,23 +38,18 @@
//............................................................................ //............................................................................
int main() { int main() {
static QP::QEvt const *tableQueueSto[N_PHILO]; static QP::QEvt const *tableQueueSto[N_PHILO];
static uint64_t tableStackSto[32];
static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO]; static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO];
static uint64_t philoStackSto[N_PHILO][32];
static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG]; static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG];
static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
// stack for the "naked" test thread // stacks and queues for the extended test threads
static QP::QEvt const *testQueueSto[5]; static QP::QEvt const *test1QueueSto[5];
static uint64_t testStackSto[32]; static uint64_t test1StackSto[64];
static QP::QEvt const *test2QueueSto[5];
// stack for the QXK's idle thread static uint64_t test2StackSto[64];
static uint64_t idleStackSto[16];
QP::QF::init(); // initialize the framework and the underlying RT kernel QP::QF::init(); // initialize the framework and the underlying RT kernel
QP::QXK::init(idleStackSto, sizeof(idleStackSto)); // initialize QXK
DPP::BSP::init(); // initialize the BSP DPP::BSP::init(); // initialize the BSP
// object dictionaries... // object dictionaries...
@ -72,35 +67,42 @@ int main() {
QP::QF::poolInit(smlPoolSto, QP::QF::poolInit(smlPoolSto,
sizeof(smlPoolSto), sizeof(smlPoolSto[0])); sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
// start the active objects... // start the extended Test1 thread
DPP::XT_Test1->start(
static_cast<uint_fast8_t>(1), // QP prio of the thread
test1QueueSto, // event queue storage
Q_DIM(test1QueueSto), // queue length [events]
test1StackSto, // stack storage
sizeof(test1StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event
// start the Philo active objects...
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
DPP::AO_Philo[n]->start( DPP::AO_Philo[n]->start(
static_cast<uint_fast8_t>(n + 1), // QP priority of the AO static_cast<uint_fast8_t>(n + 2), // QP priority of the AO
philoQueueSto[n], // event queue storage philoQueueSto[n], // event queue storage
Q_DIM(philoQueueSto[n]), // queue length [events] Q_DIM(philoQueueSto[n]), // queue length [events]
philoStackSto[n], // stack storage static_cast<void *>(0), // no stack storage
sizeof(philoStackSto[n]), // stack size [bytes] static_cast<uint_fast16_t>(0), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
} }
// leave the priority level (N_PHILO + 1) free for the mutex in BSP // start the extended Test2 thread
DPP::XT_Test2->start(
static_cast<uint_fast8_t>(N_PHILO + 2), // QP prio of the thread
test2QueueSto, // event queue storage
Q_DIM(test2QueueSto), // queue length [events]
test2StackSto, // stack storage
sizeof(test2StackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event
DPP::AO_Table->start( DPP::AO_Table->start(
static_cast<uint_fast8_t>(N_PHILO + 2), // QP priority of the AO static_cast<uint_fast8_t>(N_PHILO + 3), // QP priority of the AO
tableQueueSto, // event queue storage tableQueueSto, // event queue storage
Q_DIM(tableQueueSto), // queue length [events] Q_DIM(tableQueueSto), // queue length [events]
tableStackSto, // stack storage static_cast<void *>(0), // no stack storage
sizeof(tableStackSto), // stack size [bytes] static_cast<uint_fast16_t>(0), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event static_cast<QP::QEvt *>(0)); // initialization event
// start the "naked" test thread
DPP::XT_Test->start(
static_cast<uint_fast8_t>(10), // QP priority of the AO
testQueueSto, // event queue storage
Q_DIM(testQueueSto), // queue length [events]
testStackSto, // stack storage
sizeof(testStackSto), // stack size [bytes]
static_cast<QP::QEvt *>(0)); // initialization event
return QP::QF::run(); // run the QF application return QP::QF::run(); // run the QF application
} }

View File

@ -1,13 +1,13 @@
//**************************************************************************** //****************************************************************************
// DPP example for QXK // DPP example for QXK
// Last updated for version 5.6.0 // Last updated for version 5.7.2
// Last updated on 2015-12-28 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
// innovating embedded systems // innovating embedded systems
// //
// Copyright (C) Quantum Leaps, www.state-machine.com. // Copyright (C) Quantum Leaps, LLC. All rights reserved.
// //
// This program is open source software: you can redistribute it and/or // This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published // modify it under the terms of the GNU General Public License as published
@ -28,8 +28,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
// //
// Contact information: // Contact information:
// Web: www.state-machine.com // http://www.state-machine.com
// Email: info@state-machine.com // mailto:info@state-machine.com
//**************************************************************************** //****************************************************************************
#include "qpcpp.h" #include "qpcpp.h"
#include "dpp.h" #include "dpp.h"
@ -37,26 +37,74 @@
namespace DPP { namespace DPP {
// local extended-thread objects .............................................
static void Thread1_run(QP::QXThread * const me);
static void Thread2_run(QP::QXThread * const me);
static QP::QXThread l_test1(&Thread1_run, 0U);
static QP::QXThread l_test2(&Thread2_run, 0U);
static QP::QXMutex l_mutex;
static QP::QXSemaphore l_sema;
// global pointer to the test thread .........................................
QP::QXThread * const XT_Test1 = &l_test1;
QP::QXThread * const XT_Test2 = &l_test2;
//............................................................................ //............................................................................
static void thread_function(void *par) { static void Thread1_run(QP::QXThread * const /*me*/) {
(void)par;
l_mutex.init(3U);
for (;;) { for (;;) {
(void)QP::QXThread::queueGet(BSP::TICKS_PER_SEC*2U, 0U); // wait on a semaphore (BLOCK with timeout)
(void)l_sema.wait(BSP::TICKS_PER_SEC, 0U);
BSP::ledOn(); BSP::ledOn();
QP::QXThread::delay(BSP::TICKS_PER_SEC/4U, 0U);
BSP::ledOff();
QP::QXThread::delay(BSP::TICKS_PER_SEC*3U/4U, 0U); l_mutex.lock(); // exercise the mutex
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
l_mutex.unlock();
QP::QXThread::delay(BSP::TICKS_PER_SEC/7, 0U); // BLOCK
// publish to Thread2
QP::QF::PUBLISH(Q_NEW(QP::QEvt, TEST_SIG), &l_test1);
} }
} }
// local "naked" thread object ............................................... //............................................................................
static QP::QXThread l_test(&thread_function, 0U); static void Thread2_run(QP::QXThread * const me) {
// global pointer to the test thread ......................................... // subscribe to the test signal */
QP::QXThread * const XT_Test = &l_test; me->subscribe(TEST_SIG);
// initialize the semaphore before using it
// NOTE: the semaphore is initialized in the highest-priority thread
// that uses it. Alternatively, the semaphore can be initialized
// before any thread runs.
l_sema.init(0U); // start with zero count
for (;;) {
// some flating point code to exercise the VFP...
float volatile x = 1.4142135F;
x = x * 1.4142135F;
// wait on the internal event queue (BLOCK) with timeout
QP::QEvt const *e = me->queueGet(BSP::TICKS_PER_SEC/2, 0U);
BSP::ledOff();
if (e != static_cast<QP::QEvt *>(0)) { // event actually delivered?
QP::QF::gc(e); // recycle the event manually!
}
else {
me->delay(BSP::TICKS_PER_SEC/2, 0U); // wait some more (BLOCK)
l_sema.signal(); // signal Thread1
}
}
}
} // namespace DPP } // namespace DPP

View File

@ -115,14 +115,14 @@ QP::QState Table::initial(Table * const me, QP::QEvt const * const e) {
QS_SIG_DICTIONARY(EAT_SIG, (void *)0); QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0); QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0); QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0); QS_SIG_DICTIONARY(TEST_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me->subscribe(DONE_SIG); me->subscribe(DONE_SIG);
me->subscribe(PAUSE_SIG); me->subscribe(PAUSE_SIG);
me->subscribe(SERVE_SIG); me->subscribe(SERVE_SIG);
me->subscribe(TERMINATE_SIG); me->subscribe(TEST_SIG);
for (uint8_t n = 0U; n < N_PHILO; ++n) { for (uint8_t n = 0U; n < N_PHILO; ++n) {
me->m_fork[n] = FREE; me->m_fork[n] = FREE;
@ -143,9 +143,8 @@ QP::QMState const Table::active_s = {
QP::QState Table::active(Table * const me, QP::QEvt const * const e) { QP::QState Table::active(Table * const me, QP::QEvt const * const e) {
QP::QState status_; QP::QState status_;
switch (e->sig) { switch (e->sig) {
// ${AOs::Table::SM::active::TERMINATE} // ${AOs::Table::SM::active::TEST}
case TERMINATE_SIG: { case TEST_SIG: {
BSP::terminate(0);
status_ = QM_HANDLED(); status_ = QM_HANDLED();
break; break;
} }

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="Texas Instruments XDS110 USB Debug Probe" href="connections/TIXDS110_Connection.xml" id="Texas Instruments XDS110 USB Debug Probe" xml="TIXDS110_Connection.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="Texas Instruments XDS110 USB Debug Probe">
<instance XML_version="1.2" href="drivers/tixds510icepick_c.xml" id="drivers" xml="tixds510icepick_c.xml" xmlpath="drivers"/>
<instance XML_version="1.2" href="drivers/tixds510cs_dap.xml" id="drivers" xml="tixds510cs_dap.xml" xmlpath="drivers"/>
<instance XML_version="1.2" href="drivers/tixds510cortexR.xml" id="drivers" xml="tixds510cortexR.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="TMS570LS1224" href="devices/tms570ls1224.xml" id="TMS570LS1224" xml="tms570ls1224.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="TI MSP430 USB1" href="connections/TIMSP430-USB.xml" id="TI MSP430 USB1" xml="TIMSP430-USB.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="TI MSP430 USB1">
<instance XML_version="1.2" href="drivers/msp430_emu.xml" id="drivers" xml="msp430_emu.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="MSP430F5529" href="devices/MSP430F5529.xml" id="MSP430F5529" xml="MSP430F5529.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="TI MSP430 USB1" href="connections/TIMSP430-USB.xml" id="TI MSP430 USB1" xml="TIMSP430-USB.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="TI MSP430 USB1">
<instance XML_version="1.2" href="drivers/msp430_emu.xml" id="drivers" xml="msp430_emu.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="MSP430F5529" href="devices/MSP430F5529.xml" id="MSP430F5529" xml="MSP430F5529.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="TI MSP430 USB1" href="connections/TIMSP430-USB.xml" id="TI MSP430 USB1" xml="TIMSP430-USB.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="TI MSP430 USB1">
<instance XML_version="1.2" href="drivers/msp430_emu.xml" id="drivers" xml="msp430_emu.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="MSP430G2553" href="devices/MSP430G2553.xml" id="MSP430G2553" xml="MSP430G2553.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="TI MSP430 USB1" href="connections/TIMSP430-USB.xml" id="TI MSP430 USB1" xml="TIMSP430-USB.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="TI MSP430 USB1">
<instance XML_version="1.2" href="drivers/msp430_emu.xml" id="drivers" xml="msp430_emu.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="MSP430G2553" href="devices/MSP430G2553.xml" id="MSP430G2553" xml="MSP430G2553.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<configurations XML_version="1.2" id="configurations_0">
<configuration XML_version="1.2" id="configuration_0">
<instance XML_version="1.2" desc="TI MSP430 USB1" href="connections/TIMSP430-USB.xml" id="TI MSP430 USB1" xml="TIMSP430-USB.xml" xmlpath="connections"/>
<connection XML_version="1.2" id="TI MSP430 USB1">
<instance XML_version="1.2" href="drivers/msp430_emu.xml" id="drivers" xml="msp430_emu.xml" xmlpath="drivers"/>
<platform XML_version="1.2" id="platform_0">
<instance XML_version="1.2" desc="MSP430G2553" href="devices/MSP430G2553.xml" id="MSP430G2553" xml="MSP430G2553.xml" xmlpath="devices"/>
</platform>
</connection>
</configuration>
</configurations>

View File

@ -0,0 +1,9 @@
The 'targetConfigs' folder contains target-configuration (.ccxml) files, automatically generated based
on the device and connection settings specified in your project on the Properties > General page.
Please note that in automatic target-configuration management, changes to the project's device and/or
connection settings will either modify an existing or generate a new target-configuration file. Thus,
if you manually edit these auto-generated files, you may need to re-apply your changes. Alternatively,
you may create your own target-configuration file for this project and manage it manually. You can
always switch back to automatic target-configuration management by checking the "Manage the project's
target-configuration automatically" checkbox on the project's Properties > General page.

View File

@ -0,0 +1,60 @@
<?xml version="1.0" encoding="UTF-8"?>
<session version="3.3.0">
<item name="license">GPL</item>
<group name="locked"/>
<group name="settings">
<item name="tabs">1</item>
<item name="windows">0</item>
<item name="grid">3</item>
</group>
<group name="windows">
<item id="AOs::Table::SM">0,0,800,521,*</item>
</group>
<group name="search">
<item name="options">4129280</item>
<item name="replace">0</item>
</group>
<group name="vars"/>
<group name="tools">
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="initial"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="initial"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="initial"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="initial"></item>
<item name="options">0</item>
</group>
<group name="tool">
<item name="icon">0</item>
<item name="title"></item>
<item name="command"></item>
<item name="args"></item>
<item name="initial"></item>
<item name="options">0</item>
</group>
</group>
</session>

View File

@ -0,0 +1,261 @@
##############################################################################
# Product: Makefile for QP/C++, DPP console, Win32, MinGW
# Last updated for version 5.6.4
# Last updated on 2016-05-04
#
# Q u a n t u m L e a P s
# ---------------------------
# innovating embedded systems
#
# Copyright (C) Quantum Leaps, LLC. All rights reserved.
#
# This program is open source software: you can redistribute it and/or
# modify it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Alternatively, this program may be distributed and modified under the
# terms of Quantum Leaps commercial licenses, which expressly supersede
# the GNU General Public License and are specifically designed for
# licensees interested in retaining the proprietary status of their code.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Contact information:
# http://www.state-machine.com
# mailto:info@state-machine.com
##############################################################################
# examples of invoking this Makefile:
# building configurations: Debug (default), Release, and Spy
# make
# make CONF=rel
# make CONF=spy
#
# cleaning configurations: Debug (default), Release, and Spy
# make clean
# make CONF=rel clean
# make CONF=spy clean
#
# NOTE:
# To use this Makefile on Windows, you will need the GNU make utility, which
# is included in the Qtools collection for Windows, see:
# http://sourceforge.net/projects/qpc/files/Qtools/
#
#-----------------------------------------------------------------------------
# project name
#
PROJECT := dpp
#-----------------------------------------------------------------------------
# project directories
#
# location of the QP/C++ framework (if not provided in an environemnt var.)
ifeq ($(QPCPP),)
QPCPP := ../../..
endif
# QP port used in this project
QP_PORT_DIR := $(QPCPP)/ports/win32
# list of all source directories used by this project
VPATH = \
.
# list of all include directories needed by this project
INCLUDES = \
-I. \
-I$(QPCPP)/include
# list of resource include directories needed by this project
RCINCLUDES = \
-IRes
#-----------------------------------------------------------------------------
# files
#
# C source files...
C_SRCS :=
# C++ source files...
CPP_SRCS := \
bsp.cpp \
main.cpp \
philo.cpp \
table.cpp
# Resource files...
RC_SRCS :=
LIB_DIRS :=
LIBS :=
# defines...
# QP_API_VERSION controls the QP API compatibility; 9999 means the latest API
DEFINES := -DQP_API_VERSION=9999
#-----------------------------------------------------------------------------
# MinGW toolset (NOTE: assumed to be on your PATH)
#
# NOTE:
# MinGW toolset is included in the Qtools collection for Windows, see:
# http://sourceforge.net/projects/qpc/files/Qtools/
#
# NOTE:
# This Makefile assumes that the windres utility is available on the
# PATH (NOTE: windres is available in the Qtools collection for Windows)
#
CC := gcc
CPP := g++
#LINK := gcc # for C programs
LINK := g++ # for C++ programs
RC := windres
# basic utilities (included in Qtools for Windows), see:
# http://sourceforge.net/projects/qpc/files/Qtools
MKDIR := mkdir
RM := rm
#-----------------------------------------------------------------------------
# build options for various configurations
#
ifeq (rel, $(CONF)) # Release configuration ..................................
BIN_DIR := rel
CFLAGS = -ffunction-sections -fdata-sections \
-Os -Wall -W $(INCLUDES) $(DEFINES) -DNDEBUG
CPPFLAGS = -ffunction-sections -fdata-sections \
-Os -Wall -W $(INCLUDES) $(DEFINES) -DNDEBUG
else ifeq (spy, $(CONF)) # Spy configuration ................................
# make sure that QTOOLS exists...
ifeq ("$(wildcard $(QTOOLS))","")
$(error QTOOLS not found. Please install Qtools and define QTOOLS env. variable)
endif
INCLUDES += -I$(QTOOLS)/qspy/include
VPATH += $(QTOOLS)/qspy/source
C_SRCS += qspy.c
BIN_DIR := spy
CFLAGS = -g -ffunction-sections -fdata-sections \
-O -Wall -W $(INCLUDES) $(DEFINES) -DQ_SPY
CPPFLAGS = -g -ffunction-sections -fdata-sections \
-O -Wall -W $(INCLUDES) $(DEFINES) -DQ_SPY
else # default Debug configuration ..........................................
BIN_DIR := dbg
CFLAGS = -g -ffunction-sections -fdata-sections \
-O -Wall -W $(INCLUDES) $(DEFINES)
CPPFLAGS = -g -ffunction-sections -fdata-sections \
-O -Wall -W $(INCLUDES) $(DEFINES)
endif # .....................................................................
LINKFLAGS := -Wl,-Map,$(BIN_DIR)/$(PROJECT).map,--cref,--gc-sections
# is it a GUI application (any GUI resources provided?) ...
ifneq (,$(RC_SRCS))
LINKFLAGS += -mwindows
DEFINES += -DQWIN_GUI
endif
#-----------------------------------------------------------------------------
# combine all the soruces...
INCLUDES += -I$(QP_PORT_DIR)
LIB_DIRS += -L$(QP_PORT_DIR)/$(BIN_DIR)
LIBS += -lqp
C_OBJS := $(patsubst %.c, %.o, $(C_SRCS))
CPP_OBJS := $(patsubst %.cpp, %.o, $(CPP_SRCS))
RC_OBJS := $(patsubst %.rc, %.o, $(RC_SRCS))
TARGET_BIN := $(BIN_DIR)/$(PROJECT).bin
TARGET_EXE := $(BIN_DIR)/$(PROJECT).exe
C_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(C_OBJS))
C_DEPS_EXT := $(patsubst %.o, %.d, $(C_OBJS_EXT))
CPP_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(CPP_OBJS))
CPP_DEPS_EXT := $(patsubst %.o, %.d, $(CPP_OBJS_EXT))
RC_OBJS_EXT := $(addprefix $(BIN_DIR)/, $(RC_OBJS))
# create $(BIN_DIR) if it does not exist
ifeq ("$(wildcard $(BIN_DIR))","")
$(shell $(MKDIR) $(BIN_DIR))
endif
#-----------------------------------------------------------------------------
# rules
#
all: $(TARGET_EXE)
#all: $(TARGET_BIN)
$(TARGET_BIN): $(TARGET_EXE)
$(BIN) -O binary $< $@
$(TARGET_EXE) : $(C_OBJS_EXT) $(CPP_OBJS_EXT) $(RC_OBJS_EXT)
$(CPP) $(CPPFLAGS) -c $(QPCPP)/include/qstamp.cpp -o $(BIN_DIR)/qstamp.o
$(LINK) $(LINKFLAGS) $(LIB_DIRS) -o $@ $^ $(BIN_DIR)/qstamp.o $(LIBS)
$(BIN_DIR)/%.d : %.cpp
$(CPP) -MM -MT $(@:.d=.o) $(CPPFLAGS) $< > $@
$(BIN_DIR)/%.d : %.c
$(CC) -MM -MT $(@:.d=.o) $(CFLAGS) $< > $@
$(BIN_DIR)/%.o : %.cpp
$(CPP) $(CPPFLAGS) -c $< -o $@
$(BIN_DIR)/%.o : %.c
$(CC) $(CFLAGS) -c $< -o $@
$(BIN_DIR)/%.o : %.rc
$(RC) $(RCINCLUDES) -i $< -o $@
# include dependency files only if our goal depends on their existence
ifneq ($(MAKECMDGOALS),clean)
ifneq ($(MAKECMDGOALS),show)
-include $(C_DEPS_EXT) $(CPP_DEPS_EXT)
endif
endif
.PHONY : clean
clean:
-$(RM) $(BIN_DIR)/*.o \
$(BIN_DIR)/*.d \
$(BIN_DIR)/*.exe \
$(BIN_DIR)/*.map
show:
@echo PROJECT = $(PROJECT)
@echo CONF = $(CONF)
@echo VPATH = $(VPATH)
@echo C_SRCS = $(C_SRCS)
@echo CPP_SRCS = $(CPP_SRCS)
@echo C_OBJS_EXT = $(C_OBJS_EXT)
@echo C_DEPS_EXT = $(C_DEPS_EXT)
@echo CPP_DEPS_EXT = $(CPP_DEPS_EXT)
@echo CPP_OBJS_EXT = $(CPP_OBJS_EXT)
@echo RC_OBJS_EXT = $(RC_OBJS_EXT)
@echo LIB_DIRS = $(LIB_DIRS)
@echo LIBS = $(LIBS)

View File

@ -0,0 +1,249 @@
//****************************************************************************
// Product: DPP example (console)
// Last Updated for Version: 5.6.0
// Date of the Last Update: 2015-12-26
//
// Q u a n t u m L e a P s
// ---------------------------
// innovating embedded systems
//
// Copyright (C) Quantum Leaps, LLC. All rights reserved.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Alternatively, this program may be distributed and modified under the
// terms of Quantum Leaps commercial licenses, which expressly supersede
// the GNU General Public License and are specifically designed for
// licensees interested in retaining the proprietary status of their code.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Contact information:
// http://www.state-machine.com
// mailto:info@state-machine.com
//****************************************************************************
#include "qpcpp.h"
#include "dpp.h"
#include "bsp.h"
#include <conio.h>
#include <stdio.h>
#ifdef Q_SPY
#include <time.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // Win32 API
#include "qspy.h" // QSPY interface
#endif
//****************************************************************************
namespace DPP {
Q_DEFINE_THIS_FILE
// local variables -----------------------------------------------------------
static uint32_t l_rnd; // random seed
#ifdef Q_SPY
enum {
PHILO_STAT = QP::QS_USER
};
static bool l_isRunning;
static uint8_t const l_clock_tick = 0U;
#endif
//............................................................................
void BSP_init(void) {
printf("Dining Philosopher Problem example"
"\nQP %s\n"
"Press 'p' to pause\n"
"Press 's' to serve\n"
"Press ESC to quit...\n",
QP::versionStr);
BSP_randomSeed(1234U);
Q_ALLEGE(QS_INIT((void *)0));
QS_OBJ_DICTIONARY(&l_clock_tick); // must be called *after* QF::init()
QS_USR_DICTIONARY(PHILO_STAT);
}
//............................................................................
void BSP_terminate(int16_t result) {
(void)result;
#ifdef Q_SPY
l_isRunning = false; // stop the QS output thread
#endif
QP::QF::stop(); // stop the main "ticker thread"
}
//............................................................................
void BSP_displayPhilStat(uint8_t n, char const *stat) {
printf("Philosopher %2d is %s\n", (int)n, stat);
QS_BEGIN(PHILO_STAT, AO_Philo[n]) // application-specific record begin
QS_U8(1, n); // Philosopher number
QS_STR(stat); // Philosopher status
QS_END()
}
//............................................................................
void BSP_displayPaused(uint8_t paused) {
printf("Paused is %s\n", paused ? "ON" : "OFF");
}
//............................................................................
uint32_t BSP_random(void) { // a very cheap pseudo-random-number generator
// "Super-Duper" Linear Congruential Generator (LCG)
// LCG(2^32, 3*7*11*13*23, 0, seed)
//
l_rnd = l_rnd * (3U*7U*11U*13U*23U);
return l_rnd >> 8;
}
//............................................................................
void BSP_randomSeed(uint32_t seed) {
l_rnd = seed;
}
} // namespace DPP
//****************************************************************************
namespace QP {
//............................................................................
void QF::onStartup(void) {
QF_setTickRate(DPP::BSP_TICKS_PER_SEC); // set the desired tick rate
}
//............................................................................
void QF::onCleanup(void) {
}
//............................................................................
void QF_onClockTick(void) {
QF::TICK_X(0U, &DPP::l_clock_tick); // process time events at rate 0
if (_kbhit()) { // any key pressed?
int ch = _getch();
if (ch == '\33') { // see if the ESC key pressed
QF::PUBLISH(Q_NEW(QEvt, DPP::TERMINATE_SIG), &DPP::l_clock_tick);
}
else if (ch == 'p') {
QF::PUBLISH(Q_NEW(QEvt, DPP::PAUSE_SIG), &DPP::l_clock_tick);
}
else if (ch == 's') {
QF::PUBLISH(Q_NEW(QEvt, DPP::SERVE_SIG), &DPP::l_clock_tick);
}
}
}
//............................................................................
extern "C" void Q_onAssert(char const * const module, int loc) {
QS_ASSERTION(module, loc, 10000U); // report assertion to QS
fprintf(stderr, "Assertion failed in %s, location %d", module, loc);
QP::QF::stop();
}
//----------------------------------------------------------------------------
#ifdef Q_SPY // define QS callbacks
//............................................................................
static DWORD WINAPI idleThread(LPVOID par) { // signature for CreateThread()
(void)par;
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_IDLE);
DPP::l_isRunning = true;
while (DPP::l_isRunning) {
uint16_t nBytes = 256;
uint8_t const *block;
QF_CRIT_ENTRY(dummy);
block = QS::getBlock(&nBytes);
QF_CRIT_EXIT(dummy);
if (block != (uint8_t *)0) {
QSPY_parse(block, nBytes);
}
Sleep(50); // wait for a while
}
return 0; // return success
}
//............................................................................
bool QS::onStartup(void const *arg) {
static uint8_t qsBuf[4*1024]; // 4K buffer for Quantum Spy
initBuf(qsBuf, sizeof(qsBuf));
(void)arg;
QSPY_config(QP_VERSION, // version
QS_OBJ_PTR_SIZE, // objPtrSize
QS_FUN_PTR_SIZE, // funPtrSize
QS_TIME_SIZE, // tstampSize
Q_SIGNAL_SIZE, // sigSize,
QF_EVENT_SIZ_SIZE, // evtSize
QF_EQUEUE_CTR_SIZE, // queueCtrSize
QF_MPOOL_CTR_SIZE, // poolCtrSize
QF_MPOOL_SIZ_SIZE, // poolBlkSize
QF_TIMEEVT_CTR_SIZE,// tevtCtrSize
(void *)0, // matFile,
(void *)0,
(QSPY_CustParseFun)0); // customized parser function
// set up the QS filters...
QS_FILTER_ON(QS_QEP_STATE_ENTRY);
QS_FILTER_ON(QS_QEP_STATE_EXIT);
QS_FILTER_ON(QS_QEP_STATE_INIT);
QS_FILTER_ON(QS_QEP_INIT_TRAN);
QS_FILTER_ON(QS_QEP_INTERN_TRAN);
QS_FILTER_ON(QS_QEP_TRAN);
QS_FILTER_ON(QS_QEP_IGNORED);
QS_FILTER_ON(QS_QEP_DISPATCH);
QS_FILTER_ON(QS_QEP_UNHANDLED);
QS_FILTER_ON(QS_QF_ACTIVE_POST_FIFO);
QS_FILTER_ON(QS_QF_ACTIVE_POST_LIFO);
QS_FILTER_ON(QS_QF_PUBLISH);
QS_FILTER_ON(DPP::PHILO_STAT);
return CreateThread(NULL, 1024, &idleThread, (void *)0, 0, NULL)
!= (HANDLE)0; // return the status of creating the idle thread
}
//............................................................................
void QS::onCleanup(void) {
DPP::l_isRunning = false;
QSPY_stop();
}
//............................................................................
void QS::onFlush(void) {
for (;;) {
uint16_t nBytes = 1024U;
uint8_t const *block;
QF_CRIT_ENTRY(dummy);
block = getBlock(&nBytes);
QF_CRIT_EXIT(dummy);
if (block != static_cast<uint8_t const *>(0)) {
QSPY_parse(block, nBytes);
nBytes = 1024U;
}
else {
break;
}
}
}
//............................................................................
QSTimeCtr QS::onGetTime(void) {
return (QSTimeCtr)clock();
}
//............................................................................
extern "C" void QSPY_onPrintLn(void) {
fputs(QSPY_line, stdout);
fputc('\n', stdout);
}
#endif // Q_SPY
//----------------------------------------------------------------------------
} // namespace QP

View File

@ -0,0 +1,52 @@
//****************************************************************************
// Product: DPP example
// Last Updated for Version: 4.5.02
// Date of the Last Update: Jun 30, 2012
//
// Q u a n t u m L e a P s
// ---------------------------
// innovating embedded systems
//
// Copyright (C) 2002-2012 Quantum Leaps, LLC. All rights reserved.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Alternatively, this program may be distributed and modified under the
// terms of Quantum Leaps commercial licenses, which expressly supersede
// the GNU General Public License and are specifically designed for
// licensees interested in retaining the proprietary status of their code.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Contact information:
// Quantum Leaps Web sites: http://www.quantum-leaps.com
// http://www.state-machine.com
// e-mail: info@quantum-leaps.com
//****************************************************************************
#ifndef bsp_h
#define bsp_h
namespace DPP {
uint32_t const BSP_TICKS_PER_SEC = static_cast<uint32_t>(50);
void BSP_init(void);
void BSP_displayPaused(uint8_t const paused);
void BSP_displayPhilStat(uint8_t const n, char_t const *stat);
void BSP_terminate(int16_t const result);
void BSP_randomSeed(uint32_t const seed); // random seed
uint32_t BSP_random(void); // pseudo-random generator
} // namespace DPP
#endif // bsp_h

View File

@ -0,0 +1,62 @@
//****************************************************************************
// Model: dpp.qm
// File: ./dpp.h
//
// This code has been generated by QM tool (see state-machine.com/qm).
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//****************************************************************************
//${.::dpp.h} ................................................................
#ifndef dpp_h
#define dpp_h
namespace DPP {
enum DPPSignals {
EAT_SIG = QP::Q_USER_SIG, // published by Table to let a philosopher eat
DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application
MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo
MAX_SIG // the last signal
};
} // namespace DPP
namespace DPP {
//${Events::TableEvt} ........................................................
class TableEvt : public QP::QEvt {
public:
uint8_t philoNum;
};
} // namespace DPP
// number of philosophers
#define N_PHILO ((uint8_t)5)
namespace DPP {
extern QP::QMActive * const AO_Philo[N_PHILO];
} // namespace DPP
namespace DPP {
extern QP::QMActive * const AO_Table;
} // namespace DPP
#endif // dpp_h

View File

@ -0,0 +1,448 @@
<?xml version="1.0" encoding="UTF-8"?>
<model version="3.3.0">
<documentation>Dining Philosopher Problem example with MSM state machines</documentation>
<framework name="qpcpp"/>
<package name="Events" stereotype="0x01" namespace="DPP::">
<class name="TableEvt" superclass="qpcpp::QEvt">
<attribute name="philoNum" type="uint8_t" visibility="0x00" properties="0x00"/>
</class>
</package>
<package name="AOs" stereotype="0x02" namespace="DPP::">
<class name="Philo" superclass="qpcpp::QMActive">
<attribute name="m_timeEvt" type="QP::QTimeEvt" visibility="0x02" properties="0x00"/>
<operation name="Philo" type="" visibility="0x00" properties="0x00">
<code> : QMActive(Q_STATE_CAST(&amp;Philo::initial)),
m_timeEvt(this, TIMEOUT_SIG, 0U)</code>
</operation>
<statechart>
<initial target="../1">
<action>static bool registered = false; // starts off with 0, per C-standard
(void)e; // suppress the compiler warning about unused parameter
if (!registered) {
registered = true;
QS_OBJ_DICTIONARY(&amp;l_philo[0]);
QS_OBJ_DICTIONARY(&amp;l_philo[0].m_timeEvt);
QS_OBJ_DICTIONARY(&amp;l_philo[1]);
QS_OBJ_DICTIONARY(&amp;l_philo[1].m_timeEvt);
QS_OBJ_DICTIONARY(&amp;l_philo[2]);
QS_OBJ_DICTIONARY(&amp;l_philo[2].m_timeEvt);
QS_OBJ_DICTIONARY(&amp;l_philo[3]);
QS_OBJ_DICTIONARY(&amp;l_philo[3].m_timeEvt);
QS_OBJ_DICTIONARY(&amp;l_philo[4]);
QS_OBJ_DICTIONARY(&amp;l_philo[4].m_timeEvt);
QS_FUN_DICTIONARY(&amp;Philo::initial);
QS_FUN_DICTIONARY(&amp;Philo::thinking);
QS_FUN_DICTIONARY(&amp;Philo::hungry);
QS_FUN_DICTIONARY(&amp;Philo::eating);
}
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me-&gt;subscribe(EAT_SIG);</action>
<initial_glyph conn="2,3,5,1,20,5,-3">
<action box="0,-2,6,2"/>
</initial_glyph>
</initial>
<state name="thinking">
<entry>me-&gt;m_timeEvt.armX(think_time(), 0U);</entry>
<exit>(void)me-&gt;m_timeEvt.disarm();</exit>
<tran trig="TIMEOUT" target="../../2">
<tran_glyph conn="2,14,3,1,20,11,-3">
<action box="0,-2,6,2"/>
</tran_glyph>
</tran>
<tran trig="EAT, DONE">
<action>/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,18,3,-1,13">
<action box="0,-2,14,2"/>
</tran_glyph>
</tran>
<state_glyph node="2,5,17,16">
<entry box="1,2,5,2"/>
<exit box="1,4,5,2"/>
</state_glyph>
</state>
<state name="hungry">
<entry>TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG);
pe-&gt;philoNum = PHILO_ID(me);
AO_Table-&gt;POST(pe, me);</entry>
<tran trig="EAT">
<choice target="../../../3">
<guard>Q_EVT_CAST(TableEvt)-&gt;philoNum == PHILO_ID(me)</guard>
<choice_glyph conn="15,30,5,1,7,13,-3">
<action box="1,0,19,4"/>
</choice_glyph>
</choice>
<tran_glyph conn="2,30,3,-1,13">
<action box="0,-2,14,2"/>
</tran_glyph>
</tran>
<tran trig="DONE">
<action>/* DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,36,3,-1,14">
<action box="0,-2,14,2"/>
</tran_glyph>
</tran>
<state_glyph node="2,23,17,16">
<entry box="1,2,5,2"/>
</state_glyph>
</state>
<state name="eating">
<entry>me-&gt;m_timeEvt.armX(eat_time(), 0U);</entry>
<exit>TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
pe-&gt;philoNum = PHILO_ID(me);
QP::QF::PUBLISH(pe, me);
(void)me-&gt;m_timeEvt.disarm();</exit>
<tran trig="TIMEOUT" target="../../1">
<tran_glyph conn="2,51,3,1,22,-41,-5">
<action box="0,-2,6,2"/>
</tran_glyph>
</tran>
<tran trig="EAT, DONE">
<action>/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)-&gt;philoNum != PHILO_ID(me));</action>
<tran_glyph conn="2,55,3,-1,13">
<action box="0,-2,14,2"/>
</tran_glyph>
</tran>
<state_glyph node="2,41,17,18">
<entry box="1,2,5,2"/>
<exit box="1,4,5,2"/>
</state_glyph>
</state>
<state_diagram size="37,61"/>
</statechart>
</class>
<class name="Table" superclass="qpcpp::QActive">
<attribute name="m_fork[N_PHILO]" type="uint8_t" visibility="0x02" properties="0x00"/>
<attribute name="m_isHungry[N_PHILO]" type="bool" visibility="0x02" properties="0x00"/>
<operation name="Table" type="" visibility="0x00" properties="0x00">
<code> : QActive(Q_STATE_CAST(&amp;Table::initial))
for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
m_fork[n] = FREE;
m_isHungry[n] = false;
}</code>
</operation>
<statechart>
<initial target="../1/3">
<action>(void)e; // suppress the compiler warning about unused parameter
QS_OBJ_DICTIONARY(&amp;l_table);
QS_FUN_DICTIONARY(&amp;QP::QHsm::top);
QS_FUN_DICTIONARY(&amp;Table::initial);
QS_FUN_DICTIONARY(&amp;Table::active);
QS_FUN_DICTIONARY(&amp;Table::serving);
QS_FUN_DICTIONARY(&amp;Table::paused);
QS_SIG_DICTIONARY(DONE_SIG, (void *)0); // global signals
QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me-&gt;subscribe(DONE_SIG);
me-&gt;subscribe(PAUSE_SIG);
me-&gt;subscribe(SERVE_SIG);
me-&gt;subscribe(TERMINATE_SIG);
for (uint8_t n = 0U; n &lt; N_PHILO; ++n) {
me-&gt;m_fork[n] = FREE;
me-&gt;m_isHungry[n] = false;
BSP_displayPhilStat(n, THINKING);
}</action>
<initial_glyph conn="3,3,5,1,44,18,-9">
<action box="0,-2,6,2"/>
</initial_glyph>
</initial>
<state name="active">
<history target="../3">
<history_glyph conn="45,12,1,0,-13,7"/>
</history>
<tran trig="TERMINATE">
<action>BSP_terminate(0);</action>
<tran_glyph conn="2,11,3,-1,14">
<action box="0,-2,11,4"/>
</tran_glyph>
</tran>
<tran trig="EAT">
<action>Q_ERROR();</action>
<tran_glyph conn="2,15,3,-1,14">
<action box="0,-2,10,4"/>
</tran_glyph>
</tran>
<state name="serving">
<entry brief="give pending permitions to eat">for (uint8_t n = 0U; n &lt; N_PHILO; ++n) { // give permissions to eat...
if (me-&gt;m_isHungry[n]
&amp;&amp; (me-&gt;m_fork[LEFT(n)] == FREE)
&amp;&amp; (me-&gt;m_fork[n] == FREE))
{
me-&gt;m_fork[LEFT(n)] = USED;
me-&gt;m_fork[n] = USED;
TableEvt *te = Q_NEW(TableEvt, EAT_SIG);
te-&gt;philoNum = n;
QP::QF::PUBLISH(te, me);
me-&gt;m_isHungry[n] = false;
BSP_displayPhilStat(n, EATING);
}
}</entry>
<tran trig="HUNGRY">
<action>uint8_t n = Q_EVT_CAST(TableEvt)-&gt;philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n &lt; N_PHILO) &amp;&amp; (!me-&gt;m_isHungry[n]));
BSP_displayPhilStat(n, HUNGRY);
uint8_t m = LEFT(n);</action>
<choice>
<guard brief="both free">(me-&gt;m_fork[m] == FREE) &amp;&amp; (me-&gt;m_fork[n] == FREE)</guard>
<action>me-&gt;m_fork[m] = USED;
me-&gt;m_fork[n] = USED;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe-&gt;philoNum = n;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(n, EATING);</action>
<choice_glyph conn="19,26,5,-1,10">
<action box="1,0,10,2"/>
</choice_glyph>
</choice>
<choice>
<guard>else</guard>
<action>me-&gt;m_isHungry[n] = true;</action>
<choice_glyph conn="19,26,4,-1,5,10">
<action box="1,5,6,2"/>
</choice_glyph>
</choice>
<tran_glyph conn="4,26,3,-1,15">
<action box="0,-2,8,2"/>
</tran_glyph>
</tran>
<tran trig="DONE">
<action>uint8_t n = Q_EVT_CAST(TableEvt)-&gt;philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n &lt; N_PHILO) &amp;&amp; (!me-&gt;m_isHungry[n]));
BSP_displayPhilStat(n, THINKING);
uint8_t m = LEFT(n);
// both forks of Phil[n] must be used
Q_ASSERT((me-&gt;m_fork[n] == USED) &amp;&amp; (me-&gt;m_fork[m] == USED));
me-&gt;m_fork[m] = FREE;
me-&gt;m_fork[n] = FREE;
m = RIGHT(n); // check the right neighbor
if (me-&gt;m_isHungry[m] &amp;&amp; (me-&gt;m_fork[m] == FREE)) {
me-&gt;m_fork[n] = USED;
me-&gt;m_fork[m] = USED;
me-&gt;m_isHungry[m] = false;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe-&gt;philoNum = m;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(m, EATING);
}
m = LEFT(n); // check the left neighbor
n = LEFT(m); // left fork of the left neighbor
if (me-&gt;m_isHungry[m] &amp;&amp; (me-&gt;m_fork[n] == FREE)) {
me-&gt;m_fork[m] = USED;
me-&gt;m_fork[n] = USED;
me-&gt;m_isHungry[m] = false;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe-&gt;philoNum = m;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(m, EATING);
}</action>
<tran_glyph conn="4,34,3,-1,15">
<action box="0,-2,6,2"/>
</tran_glyph>
</tran>
<tran trig="EAT">
<action>Q_ERROR();</action>
<tran_glyph conn="4,37,3,-1,15">
<action box="0,-2,12,4"/>
</tran_glyph>
</tran>
<tran trig="PAUSE" target="../../4">
<tran_glyph conn="4,41,3,1,37,6,-3">
<action box="0,-2,7,2"/>
</tran_glyph>
</tran>
<state_glyph node="4,19,34,24">
<entry box="1,2,27,2"/>
</state_glyph>
</state>
<state name="paused">
<entry>BSP_displayPaused(1U);</entry>
<exit>BSP_displayPaused(0U);</exit>
<tran trig="SERVE" target="../../3">
<tran_glyph conn="4,57,3,1,39,-29,-5">
<action box="0,-2,7,2"/>
</tran_glyph>
</tran>
<tran trig="HUNGRY">
<action>uint8_t n = Q_EVT_CAST(TableEvt)-&gt;philoNum;
// philo ID must be in range and he must be not hungry
Q_ASSERT((n &lt; N_PHILO) &amp;&amp; (!me-&gt;m_isHungry[n]));
me-&gt;m_isHungry[n] = true;
BSP_displayPhilStat(n, HUNGRY);</action>
<tran_glyph conn="4,60,3,-1,15">
<action box="0,-2,6,2"/>
</tran_glyph>
</tran>
<tran trig="DONE">
<action>uint8_t n = Q_EVT_CAST(TableEvt)-&gt;philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n &lt; N_PHILO) &amp;&amp; (!me-&gt;m_isHungry[n]));
BSP_displayPhilStat(n, THINKING);
uint8_t m = LEFT(n);
/* both forks of Phil[n] must be used */
Q_ASSERT((me-&gt;m_fork[n] == USED) &amp;&amp; (me-&gt;m_fork[m] == USED));
me-&gt;m_fork[m] = FREE;
me-&gt;m_fork[n] = FREE;</action>
<tran_glyph conn="4,63,3,-1,15">
<action box="0,-2,6,2"/>
</tran_glyph>
</tran>
<state_glyph node="4,45,34,20">
<entry box="1,2,18,4"/>
<exit box="1,6,18,4"/>
</state_glyph>
</state>
<state_glyph node="2,5,43,62"/>
</state>
<state_diagram size="49,69"/>
</statechart>
</class>
<attribute name="AO_Philo[N_PHILO]" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
<attribute name="AO_Table" type="QP::QMActive * const" visibility="0x00" properties="0x00"/>
</package>
<directory name=".">
<file name="dpp.h">
<text>#ifndef dpp_h
#define dpp_h
namespace DPP {
enum DPPSignals {
EAT_SIG = QP::Q_USER_SIG, // published by Table to let a philosopher eat
DONE_SIG, // published by Philosopher when done eating
PAUSE_SIG, // published by BSP to pause the application
SERVE_SIG, // published by BSP to serve re-start serving forks
TERMINATE_SIG, // published by BSP to terminate the application
MAX_PUB_SIG, // the last published signal
HUNGRY_SIG, // posted direclty to Table from hungry Philo
MAX_SIG // the last signal
};
} // namespace DPP
$declare(Events::TableEvt)
// number of philosophers
#define N_PHILO ((uint8_t)5)
$declare(AOs::AO_Philo[N_PHILO])
$declare(AOs::AO_Table)
#endif // dpp_h</text>
</file>
<file name="philo.cpp">
<text>#include &quot;qpcpp.h&quot;
#include &quot;dpp.h&quot;
#include &quot;bsp.h&quot;
Q_DEFINE_THIS_FILE
// Active object class -------------------------------------------------------
$declare(AOs::Philo)
namespace DPP {
// Local objects -------------------------------------------------------------
static Philo l_philo[N_PHILO]; // storage for all Philos
// helper function to provide a randomized think time for Philos
inline QP::QTimeEvtCtr think_time() {
return static_cast&lt;QP::QTimeEvtCtr&gt;((BSP_random() % BSP_TICKS_PER_SEC)
+ (BSP_TICKS_PER_SEC/2U));
}
// helper function to provide a randomized eat time for Philos
inline QP::QTimeEvtCtr eat_time() {
return static_cast&lt;QP::QTimeEvtCtr&gt;((BSP_random() % BSP_TICKS_PER_SEC)
+ BSP_TICKS_PER_SEC);
}
// helper function to provide the ID of Philo &quot;me&quot;
inline uint8_t PHILO_ID(Philo const * const me) {
return static_cast&lt;uint8_t&gt;(me - l_philo);
}
enum InternalSignals { // internal signals
TIMEOUT_SIG = MAX_SIG
};
// Global objects ------------------------------------------------------------
QP::QMActive * const AO_Philo[N_PHILO] = { // &quot;opaque&quot; pointers to Philo AO
&amp;l_philo[0],
&amp;l_philo[1],
&amp;l_philo[2],
&amp;l_philo[3],
&amp;l_philo[4]
};
} // namespace DPP
// Philo definition ----------------------------------------------------------
$define(AOs::Philo)</text>
</file>
<file name="table.cpp">
<text>#include &quot;qpcpp.h&quot;
#include &quot;dpp.h&quot;
#include &quot;bsp.h&quot;
Q_DEFINE_THIS_FILE
// Active object class -------------------------------------------------------
$declare(AOs::Table)
namespace DPP {
// helper function to provide the RIGHT neighbour of a Philo[n]
inline uint8_t RIGHT(uint8_t const n) {
return static_cast&lt;uint8_t&gt;((n + (N_PHILO - 1U)) % N_PHILO);
}
// helper function to provide the LEFT neighbour of a Philo[n]
inline uint8_t LEFT(uint8_t const n) {
return static_cast&lt;uint8_t&gt;((n + 1U) % N_PHILO);
}
static uint8_t const FREE = static_cast&lt;uint8_t&gt;(0);
static uint8_t const USED = static_cast&lt;uint8_t&gt;(1);
static char_t const * const THINKING = &amp;&quot;thinking&quot;[0];
static char_t const * const HUNGRY = &amp;&quot;hungry &quot;[0];
static char_t const * const EATING = &amp;&quot;eating &quot;[0];
// Local objects -------------------------------------------------------------
static Table l_table; // the single instance of the Table active object
// Global-scope objects ------------------------------------------------------
QP::QMActive * const AO_Table = &amp;l_table; // &quot;opaque&quot; AO pointer
} // namespace DPP
//............................................................................
$define(AOs::Table)</text>
</file>
</directory>
</model>

View File

@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Express 2013 for Windows Desktop
VisualStudioVersion = 12.0.21005.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dpp", "dpp.vcxproj", "{8CC465F7-872E-4D03-B93C-1B64858B4E11}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
QSpy|Win32 = QSpy|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.Debug|Win32.ActiveCfg = Debug|Win32
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.Debug|Win32.Build.0 = Debug|Win32
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.Release|Win32.ActiveCfg = Release|Win32
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.Release|Win32.Build.0 = Release|Win32
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.QSpy|Win32.ActiveCfg = QSpy|Win32
{8CC465F7-872E-4D03-B93C-1B64858B4E11}.QSpy|Win32.Build.0 = QSpy|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@ -0,0 +1,165 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="QSpy|Win32">
<Configuration>QSpy</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{8CC465F7-872E-4D03-B93C-1B64858B4E11}</ProjectGuid>
<RootNamespace>dpp</RootNamespace>
<Keyword>Win32Proj</Keyword>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>NotSet</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>NotSet</CharacterSet>
<WholeProgramOptimization>true</WholeProgramOptimization>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<CharacterSet>NotSet</CharacterSet>
<PlatformToolset>v120</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<_ProjectFileVersion>10.0.40219.1</_ProjectFileVersion>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>
<OutDir Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'">$(SolutionDir)$(Configuration)\</OutDir>
<IntDir Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'">$(Configuration)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'">true</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;../../../include;../../../ports/win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4127</DisableSpecificWarnings>
<CompileAs>Default</CompileAs>
</ClCompile>
<Link>
<AdditionalDependencies>qp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../../../ports/win32/$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
<PreBuildEvent>
<Command>cmd /c "del $(OutDir)qstamp.obj"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<Optimization>MaxSpeed</Optimization>
<IntrinsicFunctions>true</IntrinsicFunctions>
<AdditionalIncludeDirectories>.;../../../include;../../../ports/win32;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NDEBUG;snprintf=_snprintf;WIN32;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<FunctionLevelLinking>true</FunctionLevelLinking>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4127</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>qp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../../../ports/win32/$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<OptimizeReferences>true</OptimizeReferences>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<TargetMachine>MachineX86</TargetMachine>
</Link>
<PreBuildEvent>
<Command>cmd /c "del $(OutDir)qstamp.obj"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='QSpy|Win32'">
<ClCompile>
<Optimization>Disabled</Optimization>
<AdditionalIncludeDirectories>.;../../../include;../../../ports/win32;$(QTOOLS)/qspy/include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>Q_SPY;snprintf=_snprintf;WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<MinimalRebuild>false</MinimalRebuild>
<ExceptionHandling>
</ExceptionHandling>
<BasicRuntimeChecks>Default</BasicRuntimeChecks>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<WarningLevel>Level4</WarningLevel>
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
<DisableSpecificWarnings>4127</DisableSpecificWarnings>
</ClCompile>
<Link>
<AdditionalDependencies>qp.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>../../../ports/win32/$(Configuration);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
<GenerateDebugInformation>true</GenerateDebugInformation>
<SubSystem>Console</SubSystem>
<TargetMachine>MachineX86</TargetMachine>
</Link>
<PreBuildEvent>
<Command>cmd /c "del $(OutDir)qstamp.obj"</Command>
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="$(QTOOLS)\qspy\source\qspy.c">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
</ClCompile>
<ClCompile Include="..\..\..\include\qstamp.cpp" />
<ClCompile Include="bsp.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="philo.cpp" />
<ClCompile Include="table.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bsp.h" />
<ClInclude Include="dpp.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<ClCompile Include="bsp.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="philo.cpp" />
<ClCompile Include="table.cpp" />
<ClCompile Include="$(QTOOLS)\qspy\source\qspy.c">
<Filter>QSPY</Filter>
</ClCompile>
<ClCompile Include="..\..\..\include\qstamp.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="bsp.h" />
<ClInclude Include="dpp.h" />
</ItemGroup>
<ItemGroup>
<Filter Include="QSPY">
<UniqueIdentifier>{e71349b5-55dc-4b60-b27b-62172fe84f0c}</UniqueIdentifier>
</Filter>
</ItemGroup>
</Project>

View File

@ -0,0 +1,77 @@
//****************************************************************************
// DPP example
// Last updated for version 5.4.0
// Last updated on 2015-04-29
//
// Q u a n t u m L e a P s
// ---------------------------
// innovating embedded systems
//
// Copyright (C) Quantum Leaps, www.state-machine.com.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Alternatively, this program may be distributed and modified under the
// terms of Quantum Leaps commercial licenses, which expressly supersede
// the GNU General Public License and are specifically designed for
// licensees interested in retaining the proprietary status of their code.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
// Contact information:
// Web: www.state-machine.com
// Email: info@state-machine.com
//****************************************************************************
#include "qpcpp.h"
#include "dpp.h"
#include "bsp.h"
//............................................................................
int main() {
static QP::QEvt const *tableQueueSto[N_PHILO];
static QP::QEvt const *philoQueueSto[N_PHILO][N_PHILO];
static QP::QSubscrList subscrSto[DPP::MAX_PUB_SIG];
static QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO];
QP::QF::init(); // initialize the framework and the underlying RT kernel
DPP::BSP_init(); // initialize the BSP
// object dictionaries...
QS_OBJ_DICTIONARY(smlPoolSto);
QS_OBJ_DICTIONARY(tableQueueSto);
QS_OBJ_DICTIONARY(philoQueueSto[0]);
QS_OBJ_DICTIONARY(philoQueueSto[1]);
QS_OBJ_DICTIONARY(philoQueueSto[2]);
QS_OBJ_DICTIONARY(philoQueueSto[3]);
QS_OBJ_DICTIONARY(philoQueueSto[4]);
QP::QF::psInit(subscrSto, Q_DIM(subscrSto)); // init publish-subscribe
// initialize event pools...
QP::QF::poolInit(smlPoolSto,
sizeof(smlPoolSto), sizeof(smlPoolSto[0]));
// start the active objects...
for (uint8_t n = 0U; n < N_PHILO; ++n) {
DPP::AO_Philo[n]->start((uint8_t)(n + 1U),
philoQueueSto[n], Q_DIM(philoQueueSto[n]),
(void *)0, 0U);
}
DPP::AO_Table->start((uint8_t)(N_PHILO + 1U),
tableQueueSto, Q_DIM(tableQueueSto),
(void *)0, 0U);
return QP::QF::run(); // run the QF application
}

View File

@ -0,0 +1,303 @@
//****************************************************************************
// Model: dpp.qm
// File: ./philo.cpp
//
// This code has been generated by QM tool (see state-machine.com/qm).
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//****************************************************************************
//${.::philo.cpp} ............................................................
#include "qpcpp.h"
#include "dpp.h"
#include "bsp.h"
Q_DEFINE_THIS_FILE
// Active object class -------------------------------------------------------
namespace DPP {
//${AOs::Philo} ..............................................................
class Philo : public QP::QMActive {
private:
QP::QTimeEvt m_timeEvt;
public:
Philo();
protected:
static QP::QState initial(Philo * const me, QP::QEvt const * const e);
static QP::QState thinking (Philo * const me, QP::QEvt const * const e);
static QP::QState thinking_e(Philo * const me);
static QP::QState thinking_x(Philo * const me);
static QP::QMState const thinking_s;
static QP::QState hungry (Philo * const me, QP::QEvt const * const e);
static QP::QState hungry_e(Philo * const me);
static QP::QMState const hungry_s;
static QP::QState eating (Philo * const me, QP::QEvt const * const e);
static QP::QState eating_e(Philo * const me);
static QP::QState eating_x(Philo * const me);
static QP::QMState const eating_s;
};
} // namespace DPP
namespace DPP {
// Local objects -------------------------------------------------------------
static Philo l_philo[N_PHILO]; // storage for all Philos
// helper function to provide a randomized think time for Philos
inline QP::QTimeEvtCtr think_time() {
return static_cast<QP::QTimeEvtCtr>((BSP_random() % BSP_TICKS_PER_SEC)
+ (BSP_TICKS_PER_SEC/2U));
}
// helper function to provide a randomized eat time for Philos
inline QP::QTimeEvtCtr eat_time() {
return static_cast<QP::QTimeEvtCtr>((BSP_random() % BSP_TICKS_PER_SEC)
+ BSP_TICKS_PER_SEC);
}
// helper function to provide the ID of Philo "me"
inline uint8_t PHILO_ID(Philo const * const me) {
return static_cast<uint8_t>(me - l_philo);
}
enum InternalSignals { // internal signals
TIMEOUT_SIG = MAX_SIG
};
// Global objects ------------------------------------------------------------
QP::QMActive * const AO_Philo[N_PHILO] = { // "opaque" pointers to Philo AO
&l_philo[0],
&l_philo[1],
&l_philo[2],
&l_philo[3],
&l_philo[4]
};
} // namespace DPP
// Philo definition ----------------------------------------------------------
namespace DPP {
//${AOs::Philo} ..............................................................
//${AOs::Philo::Philo} .......................................................
Philo::Philo()
: QMActive(Q_STATE_CAST(&Philo::initial)),
m_timeEvt(this, TIMEOUT_SIG, 0U)
{}
//${AOs::Philo::SM} ..........................................................
QP::QState Philo::initial(Philo * const me, QP::QEvt const * const e) {
static struct {
QP::QMState const *target;
QP::QActionHandler act[2];
} const tatbl_ = { // transition-action table
&thinking_s,
{
Q_ACTION_CAST(&thinking_e), // entry
Q_ACTION_CAST(0) // zero terminator
}
};
// ${AOs::Philo::SM::initial}
static bool registered = false; // starts off with 0, per C-standard
(void)e; // suppress the compiler warning about unused parameter
if (!registered) {
registered = true;
QS_OBJ_DICTIONARY(&l_philo[0]);
QS_OBJ_DICTIONARY(&l_philo[0].m_timeEvt);
QS_OBJ_DICTIONARY(&l_philo[1]);
QS_OBJ_DICTIONARY(&l_philo[1].m_timeEvt);
QS_OBJ_DICTIONARY(&l_philo[2]);
QS_OBJ_DICTIONARY(&l_philo[2].m_timeEvt);
QS_OBJ_DICTIONARY(&l_philo[3]);
QS_OBJ_DICTIONARY(&l_philo[3].m_timeEvt);
QS_OBJ_DICTIONARY(&l_philo[4]);
QS_OBJ_DICTIONARY(&l_philo[4].m_timeEvt);
QS_FUN_DICTIONARY(&Philo::initial);
QS_FUN_DICTIONARY(&Philo::thinking);
QS_FUN_DICTIONARY(&Philo::hungry);
QS_FUN_DICTIONARY(&Philo::eating);
}
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
me->subscribe(EAT_SIG);
return QM_TRAN_INIT(&tatbl_);
}
//${AOs::Philo::SM::thinking} ................................................
QP::QMState const Philo::thinking_s = {
static_cast<QP::QMState const *>(0), // superstate (top)
Q_STATE_CAST(&thinking),
Q_ACTION_CAST(&thinking_e),
Q_ACTION_CAST(&thinking_x),
Q_ACTION_CAST(0) // no intitial tran.
};
// ${AOs::Philo::SM::thinking}
QP::QState Philo::thinking_e(Philo * const me) {
me->m_timeEvt.armX(think_time(), 0U);
return QM_ENTRY(&thinking_s);
}
// ${AOs::Philo::SM::thinking}
QP::QState Philo::thinking_x(Philo * const me) {
(void)me->m_timeEvt.disarm();
return QM_EXIT(&thinking_s);
}
// ${AOs::Philo::SM::thinking}
QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Philo::SM::thinking::TIMEOUT}
case TIMEOUT_SIG: {
static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
} const tatbl_ = { // transition-action table
&hungry_s,
{
Q_ACTION_CAST(&thinking_x), // exit
Q_ACTION_CAST(&hungry_e), // entry
Q_ACTION_CAST(0) // zero terminator
}
};
status_ = QM_TRAN(&tatbl_);
break;
}
// ${AOs::Philo::SM::thinking::EAT, DONE}
case EAT_SIG: // intentionally fall through
case DONE_SIG: {
/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
status_ = QM_HANDLED();
break;
}
default: {
status_ = QM_SUPER();
break;
}
}
return status_;
}
//${AOs::Philo::SM::hungry} ..................................................
QP::QMState const Philo::hungry_s = {
static_cast<QP::QMState const *>(0), // superstate (top)
Q_STATE_CAST(&hungry),
Q_ACTION_CAST(&hungry_e),
Q_ACTION_CAST(0), // no exit action
Q_ACTION_CAST(0) // no intitial tran.
};
// ${AOs::Philo::SM::hungry}
QP::QState Philo::hungry_e(Philo * const me) {
TableEvt *pe = Q_NEW(TableEvt, HUNGRY_SIG);
pe->philoNum = PHILO_ID(me);
AO_Table->POST(pe, me);
return QM_ENTRY(&hungry_s);
}
// ${AOs::Philo::SM::hungry}
QP::QState Philo::hungry(Philo * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Philo::SM::hungry::EAT}
case EAT_SIG: {
// ${AOs::Philo::SM::hungry::EAT::[Q_EVT_CAST(TableEvt)->philoNum=~}
if (Q_EVT_CAST(TableEvt)->philoNum == PHILO_ID(me)) {
static struct {
QP::QMState const *target;
QP::QActionHandler act[2];
} const tatbl_ = { // transition-action table
&eating_s,
{
Q_ACTION_CAST(&eating_e), // entry
Q_ACTION_CAST(0) // zero terminator
}
};
status_ = QM_TRAN(&tatbl_);
}
else {
status_ = QM_UNHANDLED();
}
break;
}
// ${AOs::Philo::SM::hungry::DONE}
case DONE_SIG: {
/* DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
status_ = QM_HANDLED();
break;
}
default: {
status_ = QM_SUPER();
break;
}
}
return status_;
}
//${AOs::Philo::SM::eating} ..................................................
QP::QMState const Philo::eating_s = {
static_cast<QP::QMState const *>(0), // superstate (top)
Q_STATE_CAST(&eating),
Q_ACTION_CAST(&eating_e),
Q_ACTION_CAST(&eating_x),
Q_ACTION_CAST(0) // no intitial tran.
};
// ${AOs::Philo::SM::eating}
QP::QState Philo::eating_e(Philo * const me) {
me->m_timeEvt.armX(eat_time(), 0U);
return QM_ENTRY(&eating_s);
}
// ${AOs::Philo::SM::eating}
QP::QState Philo::eating_x(Philo * const me) {
TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
pe->philoNum = PHILO_ID(me);
QP::QF::PUBLISH(pe, me);
(void)me->m_timeEvt.disarm();
return QM_EXIT(&eating_s);
}
// ${AOs::Philo::SM::eating}
QP::QState Philo::eating(Philo * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Philo::SM::eating::TIMEOUT}
case TIMEOUT_SIG: {
static struct {
QP::QMState const *target;
QP::QActionHandler act[3];
} const tatbl_ = { // transition-action table
&thinking_s,
{
Q_ACTION_CAST(&eating_x), // exit
Q_ACTION_CAST(&thinking_e), // entry
Q_ACTION_CAST(0) // zero terminator
}
};
status_ = QM_TRAN(&tatbl_);
break;
}
// ${AOs::Philo::SM::eating::EAT, DONE}
case EAT_SIG: // intentionally fall through
case DONE_SIG: {
/* EAT or DONE must be for other Philos than this one */
Q_ASSERT(Q_EVT_CAST(TableEvt)->philoNum != PHILO_ID(me));
status_ = QM_HANDLED();
break;
}
default: {
status_ = QM_SUPER();
break;
}
}
return status_;
}
} // namespace DPP

View File

@ -0,0 +1,311 @@
//****************************************************************************
// Model: dpp.qm
// File: ./table.cpp
//
// This code has been generated by QM tool (see state-machine.com/qm).
// DO NOT EDIT THIS FILE MANUALLY. All your changes will be lost.
//
// This program is open source software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published
// by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
// for more details.
//****************************************************************************
//${.::table.cpp} ............................................................
#include "qpcpp.h"
#include "dpp.h"
#include "bsp.h"
Q_DEFINE_THIS_FILE
// Active object class -------------------------------------------------------
namespace DPP {
//${AOs::Table} ..............................................................
class Table : public QP::QActive {
private:
uint8_t m_fork[N_PHILO];
bool m_isHungry[N_PHILO];
public:
Table();
protected:
static QP::QState initial(Table * const me, QP::QEvt const * const e);
static QP::QState active(Table * const me, QP::QEvt const * const e);
static QP::QState serving(Table * const me, QP::QEvt const * const e);
static QP::QState paused(Table * const me, QP::QEvt const * const e);
private:
QP::QStateHandler his_active;
};
} // namespace DPP
namespace DPP {
// helper function to provide the RIGHT neighbour of a Philo[n]
inline uint8_t RIGHT(uint8_t const n) {
return static_cast<uint8_t>((n + (N_PHILO - 1U)) % N_PHILO);
}
// helper function to provide the LEFT neighbour of a Philo[n]
inline uint8_t LEFT(uint8_t const n) {
return static_cast<uint8_t>((n + 1U) % N_PHILO);
}
static uint8_t const FREE = static_cast<uint8_t>(0);
static uint8_t const USED = static_cast<uint8_t>(1);
static char_t const * const THINKING = &"thinking"[0];
static char_t const * const HUNGRY = &"hungry "[0];
static char_t const * const EATING = &"eating "[0];
// Local objects -------------------------------------------------------------
static Table l_table; // the single instance of the Table active object
// Global-scope objects ------------------------------------------------------
QP::QMActive * const AO_Table = &l_table; // "opaque" AO pointer
} // namespace DPP
//............................................................................
namespace DPP {
//${AOs::Table} ..............................................................
//${AOs::Table::Table} .......................................................
Table::Table()
: QActive(Q_STATE_CAST(&Table::initial))
{
for (uint8_t n = 0U; n < N_PHILO; ++n) {
m_fork[n] = FREE;
m_isHungry[n] = false;
}
}
//${AOs::Table::SM} ..........................................................
QP::QState Table::initial(Table * const me, QP::QEvt const * const e) {
// ${AOs::Table::SM::initial}
(void)e; // suppress the compiler warning about unused parameter
QS_OBJ_DICTIONARY(&l_table);
QS_FUN_DICTIONARY(&QP::QHsm::top);
QS_FUN_DICTIONARY(&Table::initial);
QS_FUN_DICTIONARY(&Table::active);
QS_FUN_DICTIONARY(&Table::serving);
QS_FUN_DICTIONARY(&Table::paused);
QS_SIG_DICTIONARY(DONE_SIG, (void *)0); // global signals
QS_SIG_DICTIONARY(EAT_SIG, (void *)0);
QS_SIG_DICTIONARY(PAUSE_SIG, (void *)0);
QS_SIG_DICTIONARY(SERVE_SIG, (void *)0);
QS_SIG_DICTIONARY(TERMINATE_SIG, (void *)0);
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal just for Table
me->subscribe(DONE_SIG);
me->subscribe(PAUSE_SIG);
me->subscribe(SERVE_SIG);
me->subscribe(TERMINATE_SIG);
for (uint8_t n = 0U; n < N_PHILO; ++n) {
me->m_fork[n] = FREE;
me->m_isHungry[n] = false;
BSP_displayPhilStat(n, THINKING);
}
// state history attributes
me->his_active = Q_STATE_CAST(&serving);
return Q_TRAN(&serving);
}
//${AOs::Table::SM::active} ..................................................
QP::QState Table::active(Table * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Table::SM::active}
case Q_EXIT_SIG: {
me->his_active = me->state(); // save history
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::TERMINATE}
case TERMINATE_SIG: {
BSP_terminate(0);
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::EAT}
case EAT_SIG: {
Q_ERROR();
status_ = Q_HANDLED();
break;
}
default: {
status_ = Q_SUPER(&QP::QHsm::top);
break;
}
}
return status_;
}
//${AOs::Table::SM::active::serving} .........................................
QP::QState Table::serving(Table * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Table::SM::active::serving}
case Q_ENTRY_SIG: {
for (uint8_t n = 0U; n < N_PHILO; ++n) { // give permissions to eat...
if (me->m_isHungry[n]
&& (me->m_fork[LEFT(n)] == FREE)
&& (me->m_fork[n] == FREE))
{
me->m_fork[LEFT(n)] = USED;
me->m_fork[n] = USED;
TableEvt *te = Q_NEW(TableEvt, EAT_SIG);
te->philoNum = n;
QP::QF::PUBLISH(te, me);
me->m_isHungry[n] = false;
BSP_displayPhilStat(n, EATING);
}
}
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::serving::HUNGRY}
case HUNGRY_SIG: {
uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n < N_PHILO) && (!me->m_isHungry[n]));
BSP_displayPhilStat(n, HUNGRY);
uint8_t m = LEFT(n);
// ${AOs::Table::SM::active::serving::HUNGRY::[bothfree]}
if ((me->m_fork[m] == FREE) && (me->m_fork[n] == FREE)) {
me->m_fork[m] = USED;
me->m_fork[n] = USED;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe->philoNum = n;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(n, EATING);
status_ = Q_HANDLED();
}
// ${AOs::Table::SM::active::serving::HUNGRY::[else]}
else {
me->m_isHungry[n] = true;
status_ = Q_HANDLED();
}
break;
}
// ${AOs::Table::SM::active::serving::DONE}
case DONE_SIG: {
uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n < N_PHILO) && (!me->m_isHungry[n]));
BSP_displayPhilStat(n, THINKING);
uint8_t m = LEFT(n);
// both forks of Phil[n] must be used
Q_ASSERT((me->m_fork[n] == USED) && (me->m_fork[m] == USED));
me->m_fork[m] = FREE;
me->m_fork[n] = FREE;
m = RIGHT(n); // check the right neighbor
if (me->m_isHungry[m] && (me->m_fork[m] == FREE)) {
me->m_fork[n] = USED;
me->m_fork[m] = USED;
me->m_isHungry[m] = false;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe->philoNum = m;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(m, EATING);
}
m = LEFT(n); // check the left neighbor
n = LEFT(m); // left fork of the left neighbor
if (me->m_isHungry[m] && (me->m_fork[n] == FREE)) {
me->m_fork[m] = USED;
me->m_fork[n] = USED;
me->m_isHungry[m] = false;
TableEvt *pe = Q_NEW(TableEvt, EAT_SIG);
pe->philoNum = m;
QP::QF::PUBLISH(pe, me);
BSP_displayPhilStat(m, EATING);
}
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::serving::EAT}
case EAT_SIG: {
Q_ERROR();
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::serving::PAUSE}
case PAUSE_SIG: {
status_ = Q_TRAN(&paused);
break;
}
default: {
status_ = Q_SUPER(&active);
break;
}
}
return status_;
}
//${AOs::Table::SM::active::paused} ..........................................
QP::QState Table::paused(Table * const me, QP::QEvt const * const e) {
QP::QState status_;
switch (e->sig) {
// ${AOs::Table::SM::active::paused}
case Q_ENTRY_SIG: {
BSP_displayPaused(1U);
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::paused}
case Q_EXIT_SIG: {
BSP_displayPaused(0U);
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::paused::SERVE}
case SERVE_SIG: {
status_ = Q_TRAN(&serving);
break;
}
// ${AOs::Table::SM::active::paused::HUNGRY}
case HUNGRY_SIG: {
uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
// philo ID must be in range and he must be not hungry
Q_ASSERT((n < N_PHILO) && (!me->m_isHungry[n]));
me->m_isHungry[n] = true;
BSP_displayPhilStat(n, HUNGRY);
status_ = Q_HANDLED();
break;
}
// ${AOs::Table::SM::active::paused::DONE}
case DONE_SIG: {
uint8_t n = Q_EVT_CAST(TableEvt)->philoNum;
// phil ID must be in range and he must be not hungry
Q_ASSERT((n < N_PHILO) && (!me->m_isHungry[n]));
BSP_displayPhilStat(n, THINKING);
uint8_t m = LEFT(n);
/* both forks of Phil[n] must be used */
Q_ASSERT((me->m_fork[n] == USED) && (me->m_fork[m] == USED));
me->m_fork[m] = FREE;
me->m_fork[n] = FREE;
status_ = Q_HANDLED();
break;
}
default: {
status_ = Q_SUPER(&active);
break;
}
}
return status_;
}
} // namespace DPP

View File

@ -3,8 +3,8 @@
/// @ingroup qep /// @ingroup qep
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-09 /// Last updated on 2016-09-25
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -43,15 +43,15 @@
//! The current QP version as a decimal constant XYZ, where X is a 1-digit //! The current QP version as a decimal constant XYZ, where X is a 1-digit
// major version number, Y is a 1-digit minor version number, and Z is // major version number, Y is a 1-digit minor version number, and Z is
// a 1-digit release number. // a 1-digit release number.
#define QP_VERSION 570 #define QP_VERSION 572
//! The current QP version number string of the form X.Y.Z, where X is //! The current QP version number string of the form X.Y.Z, where X is
// a 1-digit major version number, Y is a 1-digit minor version number, // a 1-digit major version number, Y is a 1-digit minor version number,
// and Z is a 1-digit release number. // and Z is a 1-digit release number.
#define QP_VERSION_STR "5.7.0" #define QP_VERSION_STR "5.7.2"
//! Tamperproof current QP release (5.7.0) and date (16-08-31) //! Tamperproof current QP release (5.7.2) and date (16-10-07)
#define QP_RELEASE 0xA02320D5U #define QP_RELEASE 0xA00845D3U
//**************************************************************************** //****************************************************************************
#ifndef Q_SIGNAL_SIZE #ifndef Q_SIGNAL_SIZE
@ -525,6 +525,16 @@ public:
//! Obtain the current active child state of a given parent //! Obtain the current active child state of a given parent
QStateHandler childState(QStateHandler const parent); QStateHandler childState(QStateHandler const parent);
private:
//! operation inherited from QMsm, but disallowed in QHsm
bool isInState(QMState const *state) const;
//! operation inherited from QMsm, but disallowed in QHsm
QMState const *stateObj(void) const;
//! operation inherited from QMsm, but disallowed in QHsm
QMState const *childStateObj(QMState const * const parent) const;
protected: protected:
//! Protected constructor of a HSM //! Protected constructor of a HSM
QHsm(QStateHandler const initial); QHsm(QStateHandler const initial);

View File

@ -3,8 +3,8 @@
/// @ingroup qf /// @ingroup qf
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.1
/// Last updated on 2016-09-14 /// Last updated on 2016-09-23
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -40,15 +40,14 @@
#define qf_h #define qf_h
//**************************************************************************** //****************************************************************************
#ifndef qpset_h
#include "qpset.h"
#endif
#ifdef Q_EVT_CTOR #ifdef Q_EVT_CTOR
#include <new> // for placement new #include <new> // for placement new
#endif // Q_EVT_CTOR #endif // Q_EVT_CTOR
//****************************************************************************
#if (QF_MAX_ACTIVE < 1) || (63 < QF_MAX_ACTIVE)
#error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..63"
#endif
//**************************************************************************** //****************************************************************************
// apply defaults for all undefined configuration parameters // apply defaults for all undefined configuration parameters
// //
@ -155,6 +154,7 @@ class QMActive : public QMsm {
QF_EQUEUE_TYPE m_eQueue; QF_EQUEUE_TYPE m_eQueue;
#endif #endif
public: // for access from extern "C" functions
#ifdef QF_OS_OBJECT_TYPE #ifdef QF_OS_OBJECT_TYPE
//! OS-dependent per-thread object. //! OS-dependent per-thread object.
/// @description /// @description
@ -313,6 +313,16 @@ public:
protected: protected:
//! protected constructor (abstract class) //! protected constructor (abstract class)
QActive(QStateHandler const initial); QActive(QStateHandler const initial);
private:
//! operation inherited from QMsm, but disallowed in QActive
bool isInState(QMState const *state) const;
//! operation inherited from QMsm, but disallowed in QActive
QMState const *stateObj(void) const;
//! operation inherited from QMsm, but disallowed in QActive
QMState const *childStateObj(QMState const * const parent) const;
}; };
@ -448,43 +458,19 @@ private:
friend class QF; friend class QF;
#ifdef qxk_h #ifdef qxk_h
friend class QXThread; friend class QXThread;
friend void QXK_activate_(void);
#endif // qxk_h #endif // qxk_h
}; };
//**************************************************************************** //****************************************************************************
//! The size of the Subscriber list bit array //! Subscriber List
/// @description
/// The size is determined of the maximum number of active objects in the
/// application configured by the #QF_MAX_ACTIVE macro.
uint8_t const QF_SUBSCR_LIST_SIZE =
static_cast<uint8_t>(((QF_MAX_ACTIVE - 1) / 8) + 1);
//! Subscriber List class
/// @description /// @description
/// This data type represents a set of active objects that subscribe to /// This data type represents a set of active objects that subscribe to
/// a given signal. The set is represented as an array of bits, where each /// a given signal. The set is represented as priority-set, where each
/// bit corresponds to the unique priority of an active object. /// bit corresponds to the unique priority of an active object.
class QSubscrList { typedef QPSet QSubscrList;
private:
//! An array of bits representing subscriber active objects.
/// @description
/// Each bit in the array corresponds to the unique priority of the
/// active object. The size of the array is determined of the maximum
/// number of active objects in the application configured by the
/// #QF_MAX_ACTIVE macro.@n
/// @n
/// For example, an active object of priority p is a subscriber if the
/// following is true: ((m_bits[QF_div8Lkup[p]] & QF::pwr2Lkup[p]) != 0)
///
/// @sa QP::QF::psInit(), QP::QF::div8Lkup, QP::QF::pwr2Lkup,
/// and #QF_MAX_ACTIVE
uint8_t m_bits[QF_SUBSCR_LIST_SIZE];
friend class QF;
friend class QMActive;
};
//**************************************************************************** //****************************************************************************
//! QF services. //! QF services.

View File

@ -3,8 +3,8 @@
/// @ingroup qk /// @ingroup qk
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-28
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -57,32 +57,31 @@
extern "C" { extern "C" {
struct QK_Attr { struct QK_Attr {
uint_fast8_t volatile curr; //!< priority of the current executing AO uint_fast8_t volatile actPrio; //!< prio of the active AO
uint_fast8_t volatile next; //!< priority of the next AO to execute uint_fast8_t volatile nextPrio; //!< prio of the next AO to execute
void volatile *aux; //!< auxiliary attribute used in the port
uint_fast8_t volatile lockPrio; //!< lock prio (0 == no-lock) uint_fast8_t volatile lockPrio; //!< lock prio (0 == no-lock)
uint_fast8_t volatile lockHolder; //!< prio of the lock holder uint_fast8_t volatile lockHolder; //!< prio of the lock holder
#ifndef QK_ISR_CONTEXT_ #ifndef QK_ISR_CONTEXT_
uint_fast8_t volatile intNest; //!< ISR nesting level uint_fast8_t volatile intNest; //!< ISR nesting level
#endif // QK_ISR_CONTEXT_ #endif // QK_ISR_CONTEXT_
#if (QF_MAX_ACTIVE <= 8) QP::QPSet readySet; //!< QK ready-set of AOs and "naked" threads
QP::QPSet8 readySet; //!< QK ready-set of AOs and "naked" threads
#else
QP::QPSet64 readySet; //!< QK ready-set of AOs and "naked" threads
#endif
}; };
//! global attributes of the QK kernel //! global attributes of the QK kernel
extern QK_Attr QK_attr_; extern QK_Attr QK_attr_;
//! QK initialization //! QK initialization
/// @descritption
/// QK_init() is called from QF_init() in qk.c. This function is
/// defined in the QK ports.
void QK_init(void); void QK_init(void);
//! QK scheduler //! QK scheduler finds the highest-priority thread ready to run
void QK_sched_(uint_fast8_t p); uint_fast8_t QK_sched_(void);
//! Find the highest-priority task ready to run //! QK activator activates the next active object. The activated AO preempts
uint_fast8_t QK_schedPrio_(void); // the currently executing AOs.
void QK_activate_(void);
} // extern "C" } // extern "C"
@ -152,24 +151,28 @@ private:
(QK_attr_.intNest != static_cast<uint_fast8_t>(0)) (QK_attr_.intNest != static_cast<uint_fast8_t>(0))
#endif // QK_ISR_CONTEXT_ #endif // QK_ISR_CONTEXT_
// QF-specific scheduler locking // QK-specific scheduler locking
//! Internal port-specific macro to represent the scheduler lock status //! Internal macro to represent the scheduler lock status
// that needs to be preserved to allow nesting of locks. // that needs to be preserved to allow nesting of locks.
#define QF_SCHED_STAT_TYPE_ QMutex //
#define QF_SCHED_STAT_ QMutex schedLock_;
//! Internal port-specific macro for selective scheduler locking. //! Internal macro for selective scheduler locking.
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \ #define QF_SCHED_LOCK_(prio_) do { \
if (QK_ISR_CONTEXT_()) { \ if (QK_ISR_CONTEXT_()) { \
(pLockStat_)->m_lockPrio = \ schedLock_.m_lockPrio = static_cast<uint_fast8_t>(0); \
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
} else { \ } else { \
(pLockStat_)->init((prio_)); \ schedLock_.init((prio_)); \
(pLockStat_)->lock(); \ schedLock_.lock(); \
} \ } \
} while (false) } while (false)
//! Internal port-specific macro for selective scheduler unlocking. //! Internal macro for selective scheduler unlocking.
#define QF_SCHED_UNLOCK_(pLockStat_) (pLockStat_)->unlock() #define QF_SCHED_UNLOCK_() do { \
if (schedLock_.m_lockPrio != static_cast<uint_fast8_t>(0)) { \
schedLock_.unlock(); \
} \
} while (false)
// native event queue operations... // native event queue operations...
#define QACTIVE_EQUEUE_WAIT_(me_) \ #define QACTIVE_EQUEUE_WAIT_(me_) \
@ -178,9 +181,8 @@ private:
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ #define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
QK_attr_.readySet.insert((me_)->m_prio); \ QK_attr_.readySet.insert((me_)->m_prio); \
if (!QK_ISR_CONTEXT_()) { \ if (!QK_ISR_CONTEXT_()) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (p != static_cast<uint_fast8_t>(0)) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
} while (false) } while (false)

View File

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Product: PC-Lint 9.x option file for linting QP/C++ applications // Product: PC-Lint 9.x option file for linting QP/C++ applications
// Last updated for version 5.7.0 // Last updated for version 5.7.2
// Last updated on 2016-08-21 // Last updated on 2016-09-26
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
@ -171,6 +171,9 @@
-esym(1536, // 9-3-1, 9-3-2 Exposing low access member -esym(1536, // 9-3-1, 9-3-2 Exposing low access member
QP::QMActive::m_osObject, QP::QMActive::m_osObject,
QP::QMActive::m_thread) QP::QMActive::m_thread)
-estring(1960, // 11-0-1(req) non-private data member
m_thread,
m_prio)
-estring(1923, // 16-2-2 could become const variable -estring(1923, // 16-2-2 could become const variable
QF_MAX_ACTIVE, QF_MAX_ACTIVE,
QF_MAX_EPOOL, QF_MAX_EPOOL,
@ -260,9 +263,8 @@
// QK // QK
-estring(1960, // 11-0-1(req) non-private data member -estring(1960, // 11-0-1(req) non-private data member
curr, actPrio,
next, nextPrio,
aux,
lockPrio, lockPrio,
lockHolder, lockHolder,
intNest, intNest,

View File

@ -3,8 +3,8 @@
/// @ingroup qf /// @ingroup qf
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-26 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -39,197 +39,195 @@
#ifndef qpset_h #ifndef qpset_h
#define qpset_h #define qpset_h
#if (QF_MAX_ACTIVE < 1) || (64 < QF_MAX_ACTIVE)
#error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..64"
#endif
namespace QP { namespace QP {
//**************************************************************************** /****************************************************************************/
// useful lookup tables /* Log-base-2 calculations ...*/
#ifndef QF_LOG2 #ifndef QF_LOG2
//! Macro to return (log2(n_) + 1), where @p n_ = 0..255. //! Lookup table for (log2(n) + 1), where n = 0..255 */
/// @description ///
/// This macro delivers the 1-based number of the most significant 1-bit
/// of a byte. This macro can be re-implemented in the QP-nano ports,
/// if the processor supports special instructions, such as CLZ (count
/// leading zeros).@n
/// @n
/// If the macro is not defined in the port, the default implementation
/// uses a lookup table.
#define QF_LOG2(n_) (QF_log2Lkup[(n_)])
//! Lookup table for (log2(n) + 1), where n is the index into the table.
/// @description /// @description
/// This lookup delivers the 1-based number of the most significant 1-bit /// This lookup delivers the 1-based number of the most significant 1-bit
/// of a byte. /// of a byte.
extern uint8_t const QF_log2Lkup[256]; extern uint8_t const QF_log2Lkup[256];
#define QF_LOG2LKUP 1 //! function that returns (log2(x) + 1), where @p x is uint32_t */
#endif // QF_LOG2 ///
/// @description
/// This function returns the 1-based number of the most significant 1-bit
/// of a 32-bit number. This function can be replaced in the QP ports, if
/// the CPU supports special instructions, such as CLZ
/// (count leading zeros).
///
inline uint_fast8_t QF_LOG2(uint32_t const x) {
uint_fast8_t n;
uint_fast8_t i;
//! Lookup table for (1 << ((n-1) % 8)), where n is the index into the table. if ((x >> 16) != static_cast<uint32_t>(0)) {
/// @note Index range n = 0..64. The first index (n == 0) should never if ((x >> 24) != static_cast<uint32_t>(0)) {
/// be used. i = static_cast<uint_fast8_t>(x >> 24);
extern uint8_t const QF_pwr2Lkup[65]; n = static_cast<uint_fast8_t>(24);
}
else {
i = static_cast<uint_fast8_t>(x >> 16);
n = static_cast<uint_fast8_t>(16);
}
}
else {
if ((x >> 8) != static_cast<uint32_t>(0)) {
i = static_cast<uint_fast8_t>(x >> 8);
n = static_cast<uint_fast8_t>(8);
}
else {
i = static_cast<uint_fast8_t>(x);
n = static_cast<uint_fast8_t>(0);
}
}
return static_cast<uint_fast8_t>(QF_log2Lkup[i]) + n;
}
//! Lookup table for ~(1 << ((n-1) % 8)), where n is the index into the table. #endif // QF_LOG2
/// @note
/// Index range n = 0..64. The first index (n == 0) should never be used.
extern uint8_t const QF_invPwr2Lkup[65];
//! Lookup table for (n-1)/8
/// @note
/// Index range n = 0..64. The first index (n == 0) should never be used.
extern uint8_t const QF_div8Lkup[65];
//**************************************************************************** //****************************************************************************
//! Priority Set of up to 8 elements for building various schedulers, #if (QF_MAX_ACTIVE <= 32)
//! but also useful as a general set of up to 8 elements of any kind. //! Priority Set of up to 32 elements */
/// @description ///
/// The priority set represents the set of active objects that are ready to /// 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 /// run and need to be considered by the scheduling algorithm. The set is
/// of storing up to 8 priority levels. /// capable of storing up to 32 priority levels.
class QPSet8 { ///
class QPSet {
uint_fast8_t volatile m_bits; //!< bimask representing elements of the set uint32_t volatile m_bits; //!< bitmask with a bit for each element
public: public:
//! the function evaluates to TRUE if the set is empty, which means that //! Makes the priority set @p me_ empty.
//! no active objects are ready to run. void setEmpty(void) {
bool isEmpty(void) const { m_bits = static_cast<uint32_t>(0);
return (m_bits == static_cast<uint_fast8_t>(0));
} }
//! the function evaluates to TRUE if the set has elements, which means //! Evaluates to true if the priority set is empty
//! that some active objects are ready to run. bool isEmpty(void) const {
return (m_bits == static_cast<uint32_t>(0));
}
//! Evaluates to true if the priority set is not empty
bool notEmpty(void) const { bool notEmpty(void) const {
return (m_bits != static_cast<uint_fast8_t>(0)); return (m_bits != static_cast<uint32_t>(0));
} }
//! the function evaluates to TRUE if the priority set has the element n. //! the function evaluates to TRUE if the priority set has the element n.
bool hasElement(uint_fast8_t const n) const { bool hasElement(uint_fast8_t const n) const {
return return (m_bits & (static_cast<uint32_t>(1)
((m_bits & static_cast<uint_fast8_t>(QF_pwr2Lkup[n])) << (n - static_cast<uint_fast8_t>(1))))
!= static_cast<uint_fast8_t>(0)); != static_cast<uint32_t>(0);
} }
//! insert element @p n into the set, n = 1..8 //! insert element @p n into the set, n = 1..8
void insert(uint_fast8_t const n) { void insert(uint_fast8_t const n) {
m_bits |= static_cast<uint_fast8_t>(QF_pwr2Lkup[n]); m_bits |= static_cast<uint32_t>(
static_cast<uint32_t>(1) << (n - static_cast<uint_fast8_t>(1)));
} }
//! remove element @p n from the set, n = 1..8 //! remove element @p n from the set, n = 1..8
void remove(uint_fast8_t const n) { void remove(uint_fast8_t const n) {
m_bits &= static_cast<uint_fast8_t>(QF_invPwr2Lkup[n]); m_bits &= static_cast<uint32_t>(
~(static_cast<uint32_t>(1) << (n - static_cast<uint_fast8_t>(1))));
} }
//! find the maximum element in the set, returns zero if the set is empty //! find the maximum element in the set, returns zero if the set is empty
uint_fast8_t findMax(void) const { uint_fast8_t findMax(void) const {
return static_cast<uint_fast8_t>(QF_LOG2(m_bits)); return QF_LOG2(m_bits);
} }
}; };
//**************************************************************************** #else // QF_MAX_ACTIVE > 32
//! 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. //! Priority Set of up to 64 elements
/// @description ///
/// The priority set represents the set of active objects that are ready to /// 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 /// run and need to be considered by the scheduling algorithm. The set is
/// of storing up to 64 priority levels.@n /// capable of storing up to 64 priority levels.
/// @n ///
/// The priority set allows to build cooperative multitasking schedulers class QPSet {
/// to manage up to 64 tasks. It is also used in the Quantum Kernel (QK)
/// preemptive scheduler.
class QPSet64 {
//! bimask representing 8-element subsets of the set uint32_t volatile m_bits[2]; //!< two bitmasks with a bit for each element
/// @description
/// Each bit in the bytes set represents a subset (8-elements)
/// as follows: @n
/// bit 0 in m_bytes is 1 when m_bits[0] is not empty @n
/// bit 1 in m_bytes is 1 when m_bits[1] is not empty @n
/// bit 2 in m_bytes is 1 when m_bits[2] is not empty @n
/// bit 3 in m_bytes is 1 when m_bits[3] is not empty @n
/// bit 4 in m_bytes is 1 when m_bits[4] is not empty @n
/// bit 5 in m_bytes is 1 when m_bits[5] is not empty @n
/// bit 6 in m_bytes is 1 when m_bits[6] is not empty @n
/// bit 7 in m_bytes is 1 when m_bits[7] is not empty @n
uint_fast8_t volatile m_bytes;
//! bits representing elements in the set as follows: @n
/// @description
/// m_bits[0] represent elements 1..8 @n
/// m_bits[1] represent elements 9..16 @n
/// m_bits[2] represent elements 17..24 @n
/// m_bits[3] represent elements 25..32 @n
/// m_bits[4] represent elements 33..40 @n
/// m_bits[5] represent elements 41..48 @n
/// m_bits[6] represent elements 49..56 @n
/// m_bits[7] represent elements 57..64 @n
uint_fast8_t volatile m_bits[8];
public: public:
//! the function evaluates to TRUE if the set is empty, which means //! Makes the priority set @p me_ empty.
//! that no active objects are ready to run. void setEmpty(void) {
bool isEmpty(void) const { m_bits[0] = static_cast<uint32_t>(0);
return (m_bytes == static_cast<uint_fast8_t>(0)); m_bits[1] = static_cast<uint32_t>(0);
} }
//! the function evaluates to TRUE if the set has elements, which means //! Evaluates to true if the priority set is empty
//! that some active objects are ready to run. // the following logic avoids UB in volatile access for MISRA compliantce
bool isEmpty(void) const {
return (m_bits[0] == static_cast<uint32_t>(0))
? (m_bits[1] == static_cast<uint32_t>(0))
: false;
}
//! Evaluates to true if the priority set is not empty
// the following logic avoids UB in volatile access for MISRA compliantce
bool notEmpty(void) const { bool notEmpty(void) const {
return (m_bytes != static_cast<uint_fast8_t>(0)); return (m_bits[0] != static_cast<uint32_t>(0))
? true
: (m_bits[1] != static_cast<uint32_t>(0));
} }
//! the function evaluates to TRUE if the priority set has the element n. //! the function evaluates to TRUE if the priority set has the element n.
bool hasElement(uint_fast8_t const n) const { bool hasElement(uint_fast8_t const n) const {
uint_fast8_t const m = return (n <= static_cast<uint_fast8_t>(32))
static_cast<uint_fast8_t>(QF_div8Lkup[n]); ? ((m_bits[0] & (static_cast<uint32_t>(1)
return ((m_bits[m] << (n - static_cast<uint_fast8_t>(1))))
& static_cast<uint_fast8_t>(QF_pwr2Lkup[n])) != static_cast<uint32_t>(0))
!= static_cast<uint_fast8_t>(0)); : ((m_bits[1] & (static_cast<uint32_t>(1)
<< (n - static_cast<uint_fast8_t>(33))))
!= static_cast<uint32_t>(0));
} }
//! insert element @p n into the set, n = 1..64 //! insert element @p n into the set, n = 1..64
void insert(uint_fast8_t const n) { void insert(uint_fast8_t const n) {
uint_fast8_t m = if (n <= static_cast<uint_fast8_t>(32)) {
static_cast<uint_fast8_t>(QF_div8Lkup[n]); m_bits[0] |= (static_cast<uint32_t>(1)
m_bits[m] |= static_cast<uint_fast8_t>(QF_pwr2Lkup[n]); << (n - static_cast<uint_fast8_t>(1)));
m_bytes |= }
static_cast<uint_fast8_t>(QF_pwr2Lkup[m else {
+ static_cast<uint_fast8_t>(1)]); m_bits[1] |= (static_cast<uint32_t>(1)
<< (n - static_cast<uint_fast8_t>(33)));
}
} }
//! remove element @p n from the set, n = 1..64 //! remove element @p n from the set, n = 1..64
void remove(uint_fast8_t const n) { void remove(uint_fast8_t const n) {
uint_fast8_t m = if (n <= static_cast<uint_fast8_t>(32)) {
static_cast<uint_fast8_t>(QF_div8Lkup[n]); (m_bits[0] &= ~(static_cast<uint32_t>(1)
m_bits[m] &= static_cast<uint_fast8_t>(QF_invPwr2Lkup[n]); << (n - static_cast<uint_fast8_t>(1))));
if (m_bits[m] == static_cast<uint_fast8_t>(0)) { }
m_bytes &= static_cast<uint_fast8_t>( else {
QF_invPwr2Lkup[m + static_cast<uint_fast8_t>(1)]); (m_bits[1] &= ~(static_cast<uint32_t>(1)
<< (n - static_cast<uint_fast8_t>(33))));
} }
} }
//! find the maximum element in the set, returns zero if the set is empty //! find the maximum element in the set, returns zero if the set is empty
uint_fast8_t findMax(void) const { uint_fast8_t findMax(void) const {
uint_fast8_t n; return (m_bits[1] != static_cast<uint32_t>(0))
if (m_bytes != static_cast<uint_fast8_t>(0)) { ? (QF_LOG2(m_bits[1]) + static_cast<uint_fast8_t>(32)) \
n = static_cast<uint_fast8_t>( : (QF_LOG2(m_bits[0]));
static_cast<uint_fast8_t>(QF_LOG2(m_bytes))
- static_cast<uint_fast8_t>(1));
n = static_cast<uint_fast8_t>(
static_cast<uint_fast8_t>(QF_LOG2(m_bits[n]))
+ static_cast<uint_fast8_t>(n << 3));
}
else {
n = static_cast<uint_fast8_t>(0);
}
return n;
} }
}; };
#endif // QF_MAX_ACTIVE
} // namespace QP } // namespace QP
#endif // qpset_h #endif // qpset_h

View File

@ -3,8 +3,8 @@
/// @ingroup qv /// @ingroup qv
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.2 /// Last updated for version 5.7.2
/// Last updated on 2016-03-31 /// Last updated on 2016-09-28
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -92,12 +92,7 @@ public:
//**************************************************************************** //****************************************************************************
extern "C" { extern "C" {
extern QP::QPSet QV_readySet_; //!< ready set of AOs
#if (QF_MAX_ACTIVE <= 8)
extern QP::QPSet8 QV_readySet_; //!< ready set of AOs
#else
extern QP::QPSet64 QV_readySet_; //!< ready set of AOs
#endif
} // extern "C" } // extern "C"
//**************************************************************************** //****************************************************************************
@ -105,11 +100,10 @@ extern "C" {
#ifdef QP_IMPL #ifdef QP_IMPL
// QF-specific scheduler locking (not needed in QV) // QV-specific scheduler locking (not needed in QV)
#define QF_SCHED_STAT_TYPE_ struct { uint_fast8_t m_lockPrio; } #define QF_SCHED_STAT_
#define QF_SCHED_LOCK_(pLockStat_, dummy) ((pLockStat_)->m_lockPrio \ #define QF_SCHED_LOCK_(dummy) ((void)0)
= static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1)) #define QF_SCHED_UNLOCK_() ((void)0)
#define QF_SCHED_UNLOCK_(dummy) ((void)0)
// native event queue operations... // native event queue operations...
#define QACTIVE_EQUEUE_WAIT_(me_) \ #define QACTIVE_EQUEUE_WAIT_(me_) \

View File

@ -4,8 +4,8 @@
/// @ingroup qxk /// @ingroup qxk
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.2 /// Last updated for version 5.7.2
/// Last updated on 2016-03-31 /// Last updated on 2016-09-28
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -53,18 +53,12 @@
/// ///
#define QF_EQUEUE_TYPE QEQueue #define QF_EQUEUE_TYPE QEQueue
//! OS-dependent per-thread operating-system object
/// @description
/// The use of this member depends on the CPU. For example, in port to
/// ARM Cortex-M with FPU this member is used to store the LR.
///
#define QF_OS_OBJECT_TYPE void*
//! OS-dependent representation of the private thread //! OS-dependent representation of the private thread
/// @description /// @description
/// QXK uses this member to store thread attributes. /// QXK uses this member to store the private stack poiner for the thread.
/// (The private stack pointer is NULL for AO-threads).///
/// ///
#define QF_THREAD_TYPE QXK_ThreadType #define QF_THREAD_TYPE void*
//**************************************************************************** //****************************************************************************
extern "C" { extern "C" {
@ -73,33 +67,38 @@ extern "C" {
struct QXK_Attr { struct QXK_Attr {
void *curr; //!< currently executing thread void *curr; //!< currently executing thread
void *next; //!< next thread to execute void *next; //!< next thread to execute
uint_fast8_t volatile actPrio; //!< prio of the active basic thread
uint_fast8_t volatile lockPrio; //!< lock prio (0 == no-lock) uint_fast8_t volatile lockPrio; //!< lock prio (0 == no-lock)
uint_fast8_t volatile lockHolder; //!< prio of the lock holder uint_fast8_t volatile lockHolder; //!< prio of the lock holder
#ifndef QXK_ISR_CONTEXT_ #ifndef QXK_ISR_CONTEXT_
uint_fast8_t volatile intNest; //!< ISR nesting level uint_fast8_t volatile intNest; //!< ISR nesting level
#endif // QXK_ISR_CONTEXT_ #endif // QXK_ISR_CONTEXT_
#if (QF_MAX_ACTIVE <= 8) QP::QPSet readySet; //!< ready-set of basic- and extended-threads
QP::QPSet8 readySet; //!< QXK ready-set of AOs and "naked" threads
#else
QP::QPSet64 readySet; //!< QXK ready-set of AOs and "naked" threads
#endif
}; };
//! global attributes of the QXK kernel //! global attributes of the QXK kernel
extern QXK_Attr QXK_attr_; extern QXK_Attr QXK_attr_;
//! QXK initialization
///
/// QXK_init() is called from QF_init() in qxk.c. This function is
/// defined in the QXK ports.
///
void QXK_init(void);
//! QXK scheduler finds the highest-priority thread ready to run
uint_fast8_t QXK_sched_(void);
//! QXK activator activates the next active object. The activated AO preempts
// the currently executing AOs.
//
void QXK_activate_(void);
} // extern "C" } // extern "C"
//**************************************************************************** //****************************************************************************
namespace QP { namespace QP {
//****************************************************************************
//! Type of the QMActive.m_thread member for the QXK kernel
struct QXK_ThreadType {
void *m_stack; //!< top of the per-thread stack
// ... possibly other thread attributes in the future
};
//**************************************************************************** //****************************************************************************
//! QXK services. //! QXK services.
/// @description /// @description
@ -164,14 +163,6 @@ private:
} // namespace QP } // namespace QP
//****************************************************************************
extern "C" {
//! The QXK scheduler for internal use only (might be in assembly)
void QXK_sched_(void);
} // extern "C"
//**************************************************************************** //****************************************************************************
// interface used only inside QF, but not in applications // interface used only inside QF, but not in applications
@ -186,36 +177,44 @@ void QXK_sched_(void);
(QXK_attr_.intNest != static_cast<uint_fast8_t>(0)) (QXK_attr_.intNest != static_cast<uint_fast8_t>(0))
#endif // QXK_ISR_CONTEXT_ #endif // QXK_ISR_CONTEXT_
// QF-specific scheduler locking // QXK-specific scheduler locking
#define QF_SCHED_STAT_TYPE_ QXMutex //! Internal macro to represent the scheduler lock status
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \ // that needs to be preserved to allow nesting of locks.
if (QXK_ISR_CONTEXT_()) { \ //
(pLockStat_)->m_lockPrio = \ #define QF_SCHED_STAT_ QXMutex schedLock_;
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
} else { \
(pLockStat_)->init((prio_)); \
(pLockStat_)->lock(); \
} \
} while (0)
#define QF_SCHED_UNLOCK_(pLockStat_) (pLockStat_)->unlock()
//! Internal macro for selective scheduler locking.
#define QF_SCHED_LOCK_(prio_) do { \
if (QXK_ISR_CONTEXT_()) { \
schedLock_.m_lockPrio = static_cast<uint_fast8_t>(0); \
} else { \
schedLock_.init((prio_)); \
schedLock_.lock(); \
} \
} while (false)
//! Internal macro for selective scheduler unlocking.
#define QF_SCHED_UNLOCK_() do { \
if (schedLock_.m_lockPrio != static_cast<uint_fast8_t>(0)) { \
schedLock_.unlock(); \
} \
} while (false)
// native event queue operations...
#define QACTIVE_EQUEUE_WAIT_(me_) \ #define QACTIVE_EQUEUE_WAIT_(me_) \
if ((me_)->m_eQueue.m_frontEvt == static_cast<QEvt *>(0)) { \ Q_ASSERT_ID(0, (me_)->m_eQueue.m_frontEvt != static_cast<QEvt *>(0))
QXK_attr_.readySet.remove((me_)->m_prio); \
QXK_sched_(); \
QF_CRIT_EXIT_(); \
QF_CRIT_EXIT_NOP(); \
QF_CRIT_ENTRY_(); \
}
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \ #define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
QXK_attr_.readySet.insert((me_)->m_prio); \ QXK_attr_.readySet.insert((me_)->m_prio); \
if (!QXK_ISR_CONTEXT_()) { \ if (!QXK_ISR_CONTEXT_()) { \
QXK_sched_(); \ if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
QXK_activate_(); \
} \
} \ } \
} while (false) } while (false)
#define QACTIVE_EQUEUE_ONEMPTY_(me_) ((void)0) #define QACTIVE_EQUEUE_ONEMPTY_(me_) \
QXK_attr_.readySet.remove((me_)->m_prio)
// native QF event pool operations... // native QF event pool operations...
#define QF_EPOOL_TYPE_ QMPool #define QF_EPOOL_TYPE_ QMPool

View File

@ -1,10 +1,10 @@
/// @file /// @file
/// @brief QXK/C++ naked (blocking) thread /// @brief QXK/C++ extended (blocking) thread
/// @ingroup qxk /// @ingroup qxk
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-27 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -41,13 +41,15 @@
namespace QP { namespace QP {
class QXThread; // forward declaration
//! Thread handler pointer-to-function //! Thread handler pointer-to-function
typedef void (*QXThreadHandler)(void * const par); typedef void (*QXThreadHandler)(QXThread * const me);
//**************************************************************************** //****************************************************************************
//! Extended (blocking) thread of the QXK preemptive kernel //! Extended (blocking) thread of the QXK preemptive kernel
/// @description /// @description
/// QP::QXThread represents the "naked" (blocking) thread of the QXK kernel. /// QP::QXThread represents the extended (blocking) thread of the QXK kernel.
/// Each blocking thread in the application must be represented by the /// Each blocking thread in the application must be represented by the
/// corresponding QP::QXThread instance /// corresponding QP::QXThread instance
/// ///
@ -69,13 +71,7 @@ public:
//! public constructor //! public constructor
QXThread(QXThreadHandler const handler, uint_fast8_t const tickRate); QXThread(QXThreadHandler const handler, uint_fast8_t const tickRate);
//! block (suspend) the current "naked" thread //! delay (block) the current extended thread for a specified # ticks
static void block(void);
//! unblock (resume) a given thread
void unblock(void) const;
//! delay (block) the current "naked" thread for a specified # ticks
static bool delay(uint_fast16_t const nTicks, static bool delay(uint_fast16_t const nTicks,
uint_fast8_t const tickRate); uint_fast8_t const tickRate);
@ -83,7 +79,7 @@ public:
bool delayCancel(void); bool delayCancel(void);
//! obtain a message from the private message queue (block if no messages) //! obtain a message from the private message queue (block if no messages)
static void const *queueGet(uint_fast16_t const nTicks, static QEvt const *queueGet(uint_fast16_t const nTicks,
uint_fast8_t const tickRate); uint_fast8_t const tickRate);
// virtual function overrides... // virtual function overrides...
@ -94,7 +90,7 @@ public:
//! Dispatches an event to QMsm //! Dispatches an event to QMsm
virtual void dispatch(QEvt const * const e); virtual void dispatch(QEvt const * const e);
//! Starts execution of a "naked" thread and registers the thread //! Starts execution of an extended thread and registers the thread
//! with the framework. //! with the framework.
virtual void start(uint_fast8_t const prio, virtual void start(uint_fast8_t const prio,
QEvt const *qSto[], uint_fast16_t const qLen, QEvt const *qSto[], uint_fast16_t const qLen,
@ -111,7 +107,7 @@ public:
} }
#ifndef Q_SPY #ifndef Q_SPY
//! Posts an event @p e directly to the event queue of the "naked" //! Posts an event @p e directly to the event queue of the extended
//! thread @p me using the First-In-First-Out (FIFO) policy. //! thread @p me using the First-In-First-Out (FIFO) policy.
virtual bool post_(QEvt const * const e, uint_fast16_t const margin); virtual bool post_(QEvt const * const e, uint_fast16_t const margin);
#else #else
@ -156,11 +152,7 @@ public:
private: private:
uint_fast16_t m_count; uint_fast16_t m_count;
#if (QF_MAX_ACTIVE <= 8) QPSet m_waitSet; //!< set of extended threads waiting on this semaphore
QPSet8 m_waitSet; //!< set of "naked" threads waiting on this semaphore
#else
QPSet64 m_waitSet; //!< set of "naked" threads waiting on this semaphore
#endif
}; };
} // namespace QP } // namespace QP

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-M, ARM-KEIL toolset /// @brief QK/C++ port to ARM Cortex-M, ARM-KEIL toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -49,14 +49,12 @@ static __inline uint32_t QK_get_IPSR(void) {
// QK interrupt entry and exit // QK interrupt entry and exit
#define QK_ISR_ENTRY() ((void)0) #define QK_ISR_ENTRY() ((void)0)
#define QK_ISR_EXIT() do { \ #define QK_ISR_EXIT() do { \
uint_fast8_t nextPrio_; \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
nextPrio_ = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
QK_attr_.next = nextPrio_; \
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ ((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))); \ static_cast<uint32_t>(1U << 28))); \
} \ } \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (0) } while (0)

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler ; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-08-08 ; Date of the Last Update: 2016-09-26
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -34,18 +34,14 @@
EXPORT QK_init EXPORT QK_init
EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name
EXPORT NMI_Handler ; CMSIS-compliant NMI exception name
IMPORT QK_sched_ ; external reference IMPORT QK_activate_ ; external reference
IMPORT QK_attr_ ; QK attribute structure IMPORT QK_attr_ ; QK attribute structure
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI EQU (0xFF:SHR:2) QF_BASEPRI EQU (0xFF:SHR:2)
; NOTE: keep in synch with the QK_Attr struct in "qk.h" !!!
QK_CURR EQU 0
QK_NEXT EQU 4
QK_AUX EQU 8
AREA |.text|, CODE, READONLY AREA |.text|, CODE, READONLY
THUMB THUMB
@ -53,8 +49,7 @@ QK_AUX EQU 8
;***************************************************************************** ;*****************************************************************************
; The QK_init() function sets the priority of PendSV to 0xFF (lowest). ; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
; Also, it initializes QK_attr_.aux to zero. Both these operations ; This operation is performed in a critical section.
; are performed in a critical section.
;***************************************************************************** ;*****************************************************************************
QK_init QK_init
MRS r0,PRIMASK ; store the state of the PRIMASK in r0 MRS r0,PRIMASK ; store the state of the PRIMASK in r0
@ -67,15 +62,6 @@ QK_init
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; The QK_attr_.aux attribute is used to determine the purpose of the
; PendSV exception. When (QK_attr_.aux == 0), PendSV is used for
; scheduling the next thread. Otherwise (QK_attr_.aux != 0), it is
; used for returning to the preempted thread. Here QK_attr_.aux is
; initialized to zero.
LDR r3,=QK_attr_
MOVS r2,#0
STR r2,[r3,#QK_AUX] ; QK_attr_.aux := 0
MSR PRIMASK,r0 ; restore the original PRIMASK MSR PRIMASK,r0 ; restore the original PRIMASK
BX lr ; return to the caller BX lr ; return to the caller
@ -101,49 +87,8 @@ QK_init
; check for the asynchronous preemption. ; check for the asynchronous preemption.
;***************************************************************************** ;*****************************************************************************
PendSV_Handler PendSV_Handler
; Prepare some constants in registers before entering critical section
; Check QK_attr_.aux to determine the purpose of this PendSV exception. LDR r3,=0xE000ED04 ; Interrupt Control and State Register
; When (QK_attr_.aux == 0), PendSV is used for scheduling the next thread.
; Otherwise (QK_attr_.aux != 0), it is used for returning to the
; preempted thread.
;
; NOTE: no critical section is necessary, because the only other place
; QK_attr_.aux is accessed is inside a critical section and no other
; instance of PendSV can preempt itself.
;
LDR r3,=QK_attr_
LDR r0,[r3,#QK_AUX] ; r0 := QK_attr_.aux
CMP r0,#0 ; if (QK_attr_.aux == 0) ...
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
BEQ PendSV_save_lr ; save lr before checking QK_attr_.next
ELSE
BEQ PendSV_check_next ; go straight to checking QK_attr_.next
ENDIF ; VFP available
; Here you know that (QK_attr_.aux != 0), meaning that this PendSV
; instance has been triggered for returning to the preempted thread.
; The no-FPU exception stack frame of this PendSV instance is removed
; from the stack
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
; clear QK_attr_.aux for the next time
MOVS r1,#0
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
B PendSV_check_next
PendSV_save_lr
; When FPU is available, the lr of this exception needs to be saved,
; because it contains the information about the type of the exception
; stack frame (FPU/no-FPU registers).
PUSH {r0,lr} ; push lr (EXC_RETURN) plus stack "aligner"
ENDIF ; VFP available
PendSV_check_next
; Prepare some constants (an address and a bitmask) before entering
; a critical section...
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit) LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
@ -151,6 +96,9 @@ PendSV_check_next
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)? IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSID i ; disable interrupts (set PRIMASK) CPSID i ; disable interrupts (set PRIMASK)
ELSE ; M3/M4/M7 ELSE ; M3/M4/M7
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
ENDIF ; VFP available
MOVS r0,#QF_BASEPRI MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts MSR BASEPRI,r0 ; selectively disable interrupts
ENDIF ; M3/M4/M7 ENDIF ; M3/M4/M7
@ -158,38 +106,21 @@ PendSV_check_next
; The PendSV exception handler can be preempted by an interrupt, ; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to ; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV. ; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV) STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
; Check QK_attr_.next, which contains the priority of the next thread ; The QK activator must be called in a Thread mode, while this code
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero ; executes in the Handler mode of the PendSV exception. The switch
; (QK_attr_.next != 0), this next thread needs to be scheduled. ; to the Thread mode is accomplished by returning from PendSV using
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to ; a fabricated exception stack frame, where the return address is
; simply return without activating the QK scheduler. ; QK_activate_().
; ;
LDR r0,[r3,#QK_NEXT] ; r0 := QK_attr_.next ; NOTE: the QK activator is called with interrupts DISABLED and also
CMP r0,#0 ; if (QK_attr_.next == 0) ... ; returns with interrupts DISABLED.
BEQ PendSV_ret LSRS r3,r1,#3 ; r3 := (1 << 24), set the T bit (new xpsr)
LDR r2,=QK_activate_ ; address of the QK activator (new pc)
; set QK_attr_.next to 0 for the next time LDR r1,=Thread_ret ; return address after the call (new lr)
MOVS r1,#0
STR r1,[r3,#QK_NEXT] ; QK_attr_.next := 0
PendSV_call_sched
; The QK scheduler must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the thread context is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QK scheduler QK_sched_().
;
; NOTE: the QK scheduler is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,=QK_sched_ ; address of the QK scheduler (new pc)
LDR r1,=Thread_sched_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame SUB sp,sp,#8*4 ; reserve space for exception stack frame
STR r0,[sp] ; save the prio argument (new r0)
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr STM r0!,{r1-r3} ; save xpsr,pc,lr
@ -197,44 +128,22 @@ PendSV_call_sched
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9 MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QK scheduler BX r0 ; exception-return to the QK scheduler
; This part of the code executes when PendSV returns to the preempted task
PendSV_ret
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK)
ELSE ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
ENDIF ; M3/M4/M7
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... ;*****************************************************************************
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC ; Thread_ret is a helper function executed when the QXK activator returns.
ELSE ; VFP not available... ;
BX lr ; return to the preempted task ; NOTE: Thread_ret does not execute in the PendSV context!
ENDIF ; VFP available ; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
; NOTE: the following code does not execute in the PendSV context! Thread_ret
;=========================================================================
; NOTE: QK scheduler returns with interrupts DISABLED.
Thread_sched_ret
; After the QK scheduler returns, we need to resume the preempted ; After the QK scheduler returns, we need to resume the preempted
; task. However, this must be accomplished by a return-from-exception, ; task. However, this must be accomplished by a return-from-exception,
; while we are still in the task context. The switch to the exception ; while we are still in the task context. The switch to the exception
; contex is accomplished by triggering the PendSV exception. ; contex is accomplished by triggering the NMI exception.
; NOTE: The NMI exception is triggered with nterrupts DISABLED,
; because QK scheduler disables interrutps before return.
; set QK_attr_.aux to non-zero to tell the next PendSV instance ; before triggering the NMI exception, make sure that the
; to remove its own stack frame
LDR r3,=QK_attr_
MOVS r1,#0xFF
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0xFF (not zero)
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK)
ELSE ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
; before triggering the PendSV exception, make sure that the
; VFP stack frame will NOT be used... ; VFP stack frame will NOT be used...
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL MRS r0,CONTROL ; r0 := CONTROL
@ -242,14 +151,38 @@ Thread_sched_ret
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit) MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
ENDIF ; VFP available ENDIF ; VFP available
ENDIF ; M3/M4/M7 ; trigger NMI to return to preempted task...
; trigger PendSV to return to preempted task...
LDR r0,=0xE000ED04 ; Interrupt Control and State Register LDR r0,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit) LSLS r1,r1,#31 ; r0 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV) STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
B . ; wait for preemption by PendSV B . ; wait for preemption by NMI
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
ELSE ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
ELSE
BX lr ; return to the preempted task
ENDIF ; VFP available ENDIF
ENDIF ; M3/M4/M7
ALIGN ; make sure the END is properly aligned ALIGN ; make sure the END is properly aligned

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, GNU-ARM toolset /// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, GNU-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -50,14 +50,12 @@ static inline uint32_t QK_get_IPSR(void) {
// QK interrupt entry and exit // QK interrupt entry and exit
#define QK_ISR_ENTRY() ((void)0) #define QK_ISR_ENTRY() ((void)0)
#define QK_ISR_EXIT() do { \ #define QK_ISR_EXIT() do { \
uint_fast8_t nextPrio_; \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
nextPrio_ = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
QK_attr_.next = nextPrio_; \
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ ((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))); \ static_cast<uint32_t>(1U << 28))); \
} \ } \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (0) } while (0)

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
* Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), GNU-ARM assembler * Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), GNU-ARM assembler
* Last Updated for Version: 5.7.0 * Last Updated for Version: 5.7.2
* Date of the Last Update: 2016-08-08 * Date of the Last Update: 2016-09-26
* *
* Q u a n t u m L e a P s * Q u a n t u m L e a P s
* --------------------------- * ---------------------------
@ -38,16 +38,10 @@
/* NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! */ /* NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! */
.equ QF_BASEPRI,(0xFF >> 2) .equ QF_BASEPRI,(0xFF >> 2)
/* NOTE: keep in synch with the QK_Attr struct in "qk.h" !!! */
.equ QK_CURR,0
.equ QK_NEXT,4
.equ QK_AUX,8
/***************************************************************************** /*****************************************************************************
* The QK_init() function sets the priority of PendSV to 0xFF (lowest). * The QK_init() function sets the priority of PendSV to 0xFF (lowest).
* Also, it initializes QK_attr_.aux to zero. Both these operations * This operation is performed in a critical section.
* are performed in a critical section.
*****************************************************************************/ *****************************************************************************/
.section .text.QK_init .section .text.QK_init
.global QK_init .global QK_init
@ -64,16 +58,6 @@ QK_init:
ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */ ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */
STR r2,[r1,#8] /* write the System 12-15 Priority Register */ STR r2,[r1,#8] /* write the System 12-15 Priority Register */
/* The QK_attr_.aux attribute is used to determine the purpose of the
* PendSV exception. When (QK_attr_.aux == 0), PendSV is used for
* scheduling the next thread. Otherwise (QK_attr_.aux != 0), it is
* used for returning to the preempted thread. Here QK_attr_.aux is
* initialized to zero.
*/
LDR r3,=QK_attr_
MOVS r2,#0
STR r2,[r3,#QK_AUX] /* QK_attr_.aux := 0 */
MSR PRIMASK,r0 /* restore the original PRIMASK */ MSR PRIMASK,r0 /* restore the original PRIMASK */
BX lr /* return to the caller */ BX lr /* return to the caller */
.size QK_init, . - QK_init .size QK_init, . - QK_init
@ -102,62 +86,21 @@ QK_init:
.section .text.PendSV_Handler .section .text.PendSV_Handler
.global PendSV_Handler /* CMSIS-compliant exception name */ .global PendSV_Handler /* CMSIS-compliant exception name */
.type PendSV_Handler, %function .type PendSV_Handler, %function
.type Thread_sched_ret, %function /* to ensure the label is THUMB */
PendSV_Handler: PendSV_Handler:
/* Prepare some constants in registers before entering critical section */
/* Check QK_attr_.aux to determine the purpose of this PendSV exception. LDR r3,=0xE000ED04 /* Interrupt Control and State Register */
* When (QK_attr_.aux == 0), PendSV is used for scheduling the next thread.
* Otherwise (QK_attr_.aux != 0), it is used for returning to the
* preempted thread.
*
* NOTE: no critical section is necessary, because the only other place
* QK_attr_.aux is accessed is inside a critical section and no other
* instance of PendSV can preempt itself.
*/
LDR r3,=QK_attr_
LDR r0,[r3,#QK_AUX] /* r0 := QK_attr_.aux */
CMP r0,#0 /* if (QK_attr_.aux == 0) ... */
.ifdef __FPU_PRESENT /* if VFP available... */
BEQ PendSV_save_lr /* save lr before checking QK_attr_.next */
.else
BEQ PendSV_check_next /* go straight to checking QK_attr_.next */
.endif /* VFP available */
/* Here you know that (QK_attr_.aux != 0), meaning that this PendSV
* instance has been triggered for returning to the preempted thread.
* The no-FPU exception stack frame of this PendSV instance is removed
* from the stack
*/
ADD sp,sp,#(8*4) /* remove one 8-register exception frame */
/* clear QK_attr_.aux for the next time */
MOVS r1,#0
STR r1,[r3,#QK_AUX] /* QK_attr_.aux = 0 */
.ifdef __FPU_PRESENT /* if VFP available... */
B PendSV_check_next
PendSV_save_lr:
/* When FPU is available, the lr of this exception needs to be saved,
* because it contains the information about the type of the exception
* stack frame (FPU/no-FPU registers).
*/
PUSH {r0,lr} /* push lr (EXC_RETURN) plus stack "aligner" */
.endif /* VFP available */
PendSV_check_next:
/* Prepare some constants (an address and a bitmask) before entering
* a critical section...
*/
LDR r2,=0xE000ED04 /* Interrupt Control and State Register */
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#27 /* r0 := (1 << 27) (UNPENDSVSET bit) */ LSLS r1,r1,#27 /* r0 := (1 << 27) (UNPENDSVSET bit) */
/* <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<< */ /* <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<< */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */ .if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSID i /* disable interrupts (set PRIMASK) */ CPSID i /* disable interrupts (set PRIMASK) */
.else /* M3/M4/M7 */ .else /* M3/M4/M7 */
.ifdef __FPU_PRESENT /* if VFP available... */
PUSH {r0,lr} /* ... push EXC_RETURN plus stack-aligner */
.endif /* VFP */
MOVS r0,#QF_BASEPRI MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 /* selectively disable interrupts */ MSR BASEPRI,r0 /* selectively disable interrupts */
.endif /* M3/M4/M7 */ .endif /* M3/M4/M7 */
@ -166,36 +109,20 @@ PendSV_check_next:
* which might pend PendSV exception again. The following write to * which might pend PendSV exception again. The following write to
* ICSR[27] un-pends any such spurious instance of PendSV. * ICSR[27] un-pends any such spurious instance of PendSV.
*/ */
STR r1,[r2] /* ICSR[27] := 1 (unpend PendSV) */ STR r1,[r3] /* ICSR[27] := 1 (unpend PendSV) */
/* Check QK_attr_.next, which contains the priority of the next thread /* The QK activator must be called in a Thread mode, while this code
* to run, which is set in QK_ISR_EXIT(). If this priority is non-zero * executes in the Handler mode of the PendSV exception. The switch
* (QK_attr_.next != 0), this next thread needs to be scheduled. * to the Thread mode is accomplished by returning from PendSV using
* Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to * a fabricated exception stack frame, where the return address is
* simply return without activating the QK scheduler. * QK_activate_().
*/
LDR r0,[r3,#QK_NEXT] /* r0 := QK_attr_.next */
CMP r0,#0 /* if (QK_attr_.next == 0) ... */
BEQ PendSV_ret
/* set QK_attr_.next to 0 for the next time */
MOVS r1,#0
STR r1,[r3,#QK_NEXT] /* QK_attr_.next := 0 */
PendSV_call_sched:
/* The QK scheduler must be called in a thread context, while this code
* executes in the handler contex of the PendSV exception. The switch
* to the thread context is accomplished by returning from PendSV using
* a fabricated exception stack frame, where the return address is the
* QK scheduler QK_sched_().
* *
* NOTE: the QK scheduler is called with interrupts DISABLED and also * NOTE: the QK activator is called with interrupts DISABLED and also
* it returns with interrupts DISABLED. * returns with interrupts DISABLED.
*/ */
MOVS r3,#1 LSRS r3,r1,#3 /* r3 := (1 << 24), set the T bit (new xpsr) */
LSLS r3,r3,#24 /* r3:=(1 << 24), set the T bit (new xpsr) */ LDR r2,=QK_activate_ /* address of the QK activator (new pc) */
LDR r2,=QK_sched_ /* address of the QK scheduler (new pc) */ LDR r1,=Thread_ret /* return address after the call (new lr) */
LDR r1,=Thread_sched_ret /* return address after the call (new lr) */
SUB sp,sp,#8*4 /* reserve space for exception stack frame */ SUB sp,sp,#8*4 /* reserve space for exception stack frame */
STR r0,[sp] /* save the prio argument (new r0) */ STR r0,[sp] /* save the prio argument (new r0) */
@ -205,45 +132,26 @@ PendSV_call_sched:
MOVS r0,#6 MOVS r0,#6
MVNS r0,r0 /* r0 := ~6 == 0xFFFFFFF9 */ MVNS r0,r0 /* r0 := ~6 == 0xFFFFFFF9 */
BX r0 /* exception-return to the QK scheduler */ BX r0 /* exception-return to the QK scheduler */
.size PendSV_Handler, . - PendSV_Handler
/* This code executes when PendSV returns to the preempted thread */
PendSV_ret:
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
.else /* M3/M4/M7 */
MOVS r0,#0
MSR BASEPRI,r0 /* enable interrupts (clear BASEPRI) */
.endif /* M3/M4/M7 */
/* >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>> */
.ifdef __FPU_PRESENT /* if VFP available... */ /*****************************************************************************
POP {r0,pc} /* pop stack "aligner" and EXC_RETURN to PC */ * Thread_ret is a helper function executed when the QXK activator returns.
.else *
BX lr /* return to the preempted task */ * NOTE: Thread_ret does not execute in the PendSV context!
.endif /* VFP */ * NOTE: Thread_ret executes entirely with interrupts DISABLED.
****************************************************************************/
.section .text.Thread_ret
.type Thread_ret, %function
/* NOTE: the following code does not execute in the PendSV context! Thread_ret:
*=========================================================================
* NOTE: QK scheduler returns with interrupts DISABLED.
*/
Thread_sched_ret:
/* After the QK scheduler returns, we need to resume the preempted /* After the QK scheduler returns, we need to resume the preempted
* task. However, this must be accomplished by a return-from-exception, * task. However, this must be accomplished by a return-from-exception,
* while we are still in the task context. The switch to the exception * while we are still in the task context. The switch to the exception
* contex is accomplished by triggering the PendSV exception. * contex is accomplished by triggering the NMI exception.
* NOTE: The NMI exception is triggered with nterrupts DISABLED,
* because QK scheduler disables interrutps before return.
*/ */
/* set QK_attr_.aux to non-zero to tell the next PendSV instance
* to remove its own stack frame
*/
LDR r3,=QK_attr_
MOVS r1,#0xFF
STR r1,[r3,#QK_AUX] /* QK_attr_.aux = 0xFF (not zero) */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
.else /* M3/M4/M7 */
MOVS r0,#0
MSR BASEPRI,r0 /* enable interrupts (clear BASEPRI) */
/* before triggering the PendSV exception, make sure that the /* before triggering the PendSV exception, make sure that the
* VFP stack frame will NOT be used... * VFP stack frame will NOT be used...
@ -254,14 +162,44 @@ Thread_sched_ret:
MSR CONTROL,r0 /* CONTROL := r0 (clear CONTROL[2] FPCA bit) */ MSR CONTROL,r0 /* CONTROL := r0 (clear CONTROL[2] FPCA bit) */
.endif /* VFP available */ .endif /* VFP available */
.endif /* M3/M4/M7 */
/* trigger PendSV to return to preempted task... */ /* trigger NMI to return to preempted task... */
LDR r0,=0xE000ED04 /* Interrupt Control and State Register */ LDR r0,=0xE000ED04 /* Interrupt Control and State Register */
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#28 /* r0 := (1 << 28) (PendSV bit) */ LSLS r1,r1,#31 /* r0 := (1 << 31) (NMI bit) */
STR r1,[r0] /* ICSR[28] := 1 (pend PendSV) */ STR r1,[r0] /* ICSR[31] := 1 (pend NMI) */
B . /* wait for preemption by PendSV */ B . /* wait for preemption by NMI */
.size PendSV_Handler, . - PendSV_Handler .size Thread_ret, . - Thread_ret
/*****************************************************************************
* The NMI_Handler exception handler is used for returning back to the
* interrupted task. The NMI exception simply removes its own interrupt
* stack frame from the stack and returns to the preempted task using the
* interrupt stack frame that must be at the top of the stack.
*
* NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
* to re-enable interrupts before it returns to the preempted task.
*****************************************************************************/
.section .text.NMI_Handler
.global NMI_Handler
.type NMI_Handler, %function
NMI_Handler:
ADD sp,sp,#(8*4) /* remove one 8-register exception frame */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
BX lr /* return to the preempted task */
.else /* M3/M4/M7 */
MOVS r0,#0
MSR BASEPRI,r0 /* enable interrupts (clear BASEPRI) */
.ifdef __FPU_PRESENT /* if VFP available... */
POP {r0,pc} /* pop stack "aligner" and EXC_RETURN to PC */
.else
BX lr /* return to the preempted task */
.endif /* VFP available */
.endif /* M3/M4/M7 */
.size NMI_Handler, . - NMI_Handler
.end .end

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, IAR-ARM toolset /// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, IAR-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -43,14 +43,12 @@
/* QK interrupt entry and exit */ /* QK interrupt entry and exit */
#define QK_ISR_ENTRY() ((void)0) #define QK_ISR_ENTRY() ((void)0)
#define QK_ISR_EXIT() do { \ #define QK_ISR_EXIT() do { \
uint_fast8_t nextPrio_; \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
nextPrio_ = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
QK_attr_.next = nextPrio_; \
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ ((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))); \ static_cast<uint32_t>(1U << 28))); \
} \ } \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (0) } while (0)

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler ; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-08-08 ; Date of the Last Update: 2016-09-26
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -34,23 +34,18 @@
PUBLIC QK_init PUBLIC QK_init
PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception name PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception name
PUBLIC NMI_Handler ; CMSIS-compliant NMI exception name
EXTERN QK_sched_ ; external reference EXTERN QK_activate_ ; external reference
EXTERN QK_attr_ ; QK attribute structure EXTERN QK_attr_ ; QK attribute structure
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI EQU (0xFF >> 2) QF_BASEPRI EQU (0xFF >> 2)
; NOTE: keep in synch with the QK_Attr struct in "qk.h" !!!
QK_CURR EQU 0
QK_NEXT EQU 4
QK_AUX EQU 8
RSEG CODE:CODE:NOROOT(2) RSEG CODE:CODE:NOROOT(2)
;***************************************************************************** ;*****************************************************************************
; The QK_init() function sets the priority of PendSV to 0xFF (lowest). ; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
; Also, it initializes QK_attr_.aux to zero. Both these operations ; This operation is performed in a nestable critical section.
; are performed in a critical section.
;***************************************************************************** ;*****************************************************************************
QK_init: QK_init:
MRS r0,PRIMASK ; store the state of the PRIMASK in r0 MRS r0,PRIMASK ; store the state of the PRIMASK in r0
@ -63,15 +58,6 @@ QK_init:
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; The QK_attr_.aux attribute is used to determine the purpose of the
; PendSV exception. When (QK_attr_.aux == 0), PendSV is used for
; scheduling the next thread. Otherwise (QK_attr_.aux != 0), it is
; used for returning to the preempted thread. Here QK_attr_.aux is
; initialized to zero.
LDR r3,=QK_attr_
MOVS r2,#0
STR r2,[r3,#QK_AUX] ; QK_attr_.aux := 0
MSR PRIMASK,r0 ; restore the original PRIMASK MSR PRIMASK,r0 ; restore the original PRIMASK
BX lr ; return to the caller BX lr ; return to the caller
@ -97,49 +83,8 @@ QK_init:
; check for the asynchronous preemption. ; check for the asynchronous preemption.
;***************************************************************************** ;*****************************************************************************
PendSV_Handler: PendSV_Handler:
; Prepare some constants in registers before entering critical section
; Check QK_attr_.aux to determine the purpose of this PendSV exception. LDR r3,=0xE000ED04 ; Interrupt Control and State Register
; When (QK_attr_.aux == 0), PendSV is used for scheduling the next thread.
; Otherwise (QK_attr_.aux != 0), it is used for returning to the
; preempted thread.
;
; NOTE: no critical section is necessary, because the only other place
; QK_attr_.aux is accessed is inside a critical section and no other
; instance of PendSV can preempt itself.
;
LDR r3,=QK_attr_
LDR r0,[r3,#QK_AUX] ; r0 := QK_attr_.aux
CMP r0,#0 ; if (QK_attr_.aux == 0) ...
#ifdef __ARMVFP__ ; if VFP available...
BEQ PendSV_save_lr ; save lr before checking QK_attr_.next
#else
BEQ PendSV_check_next ; go straight to checking QK_attr_.next
#endif
; Here you know that (QK_attr_.aux != 0), meaning that this PendSV
; instance has been triggered for returning to the preempted thread.
; The no-FPU exception stack frame of this PendSV instance is removed
; from the stack
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
; clear QK_attr_.aux for the next time
MOVS r1,#0
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0
#ifdef __ARMVFP__ ; if VFP available...
B PendSV_check_next
PendSV_save_lr:
; When FPU is available, the lr of this exception needs to be saved,
; because it contains the information about the type of the exception
; stack frame (FPU/no-FPU registers).
PUSH {r0,lr} ; push lr (EXC_RETURN) plus stack "aligner"
#endif ; VFP available
PendSV_check_next:
; Prepare some constants (an address and a bitmask) before entering
; a critical section...
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit) LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
@ -147,6 +92,9 @@ PendSV_check_next:
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ? #if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSID i ; disable interrupts (set PRIMASK) CPSID i ; disable interrupts (set PRIMASK)
#else ; M3/M4/M7 #else ; M3/M4/M7
#ifdef __ARMVFP__ ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
#endif ; VFP available
MOVS r0,#QF_BASEPRI MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts MSR BASEPRI,r0 ; selectively disable interrupts
#endif ; M3/M4/M7 #endif ; M3/M4/M7
@ -154,38 +102,21 @@ PendSV_check_next:
; The PendSV exception handler can be preempted by an interrupt, ; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to ; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV. ; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV) STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
; Check QK_attr_.next, which contains the priority of the next thread ; The QK activator must be called in a Thread mode, while this code
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero ; executes in the Handler mode of the PendSV exception. The switch
; (QK_attr_.next != 0), this next thread needs to be scheduled. ; to the Thread mode is accomplished by returning from PendSV using
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to ; a fabricated exception stack frame, where the return address is
; simply return without activating the QK scheduler. ; QK_activate_().
; ;
LDR r0,[r3,#QK_NEXT] ; r0 := QK_attr_.next ; NOTE: the QK activator is called with interrupts DISABLED and also
CMP r0,#0 ; if (QK_attr_.next == 0) ... ; returns with interrupts DISABLED.
BEQ PendSV_ret LSRS r3,r1,#3 ; r3 := (1 << 24), set the T bit (new xpsr)
LDR r2,=QK_activate_ ; address of the QK activator (new pc)
; set QK_attr_.next to 0 for the next time LDR r1,=Thread_ret ; return address after the call (new lr)
MOVS r1,#0
STR r1,[r3,#QK_NEXT] ; QK_attr_.next := 0
PendSV_call_sched:
; The QK scheduler must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the thread context is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QK scheduler QK_sched_().
;
; NOTE: the QK scheduler is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,=QK_sched_ ; address of the QK scheduler (new pc)
LDR r1,=Thread_sched_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame SUB sp,sp,#8*4 ; reserve space for exception stack frame
STR r0,[sp] ; save the prio argument (new r0)
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr STM r0!,{r1-r3} ; save xpsr,pc,lr
@ -193,44 +124,22 @@ PendSV_call_sched:
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9 MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QK scheduler BX r0 ; exception-return to the QK scheduler
; This part of the code executes when PendSV returns to the preempted task
PendSV_ret:
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
#else ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
#endif ; M3/M4/M7
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
#ifdef __ARMVFP__ ; if VFP available... ;*****************************************************************************
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC ; Thread_ret is a helper function executed when the QXK activator returns.
#else ; VFP not available... ;
BX lr ; return to the preempted task ; NOTE: Thread_ret does not execute in the PendSV context!
#endif ; VFP ; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
; NOTE: the following code does not execute in the PendSV context! Thread_ret:
;=========================================================================
; NOTE: QK scheduler returns with interrupts DISABLED.
Thread_sched_ret:
; After the QK scheduler returns, we need to resume the preempted ; After the QK scheduler returns, we need to resume the preempted
; task. However, this must be accomplished by a return-from-exception, ; task. However, this must be accomplished by a return-from-exception,
; while we are still in the task context. The switch to the exception ; while we are still in the task context. The switch to the exception
; contex is accomplished by triggering the PendSV exception. ; contex is accomplished by triggering the NMI exception.
; NOTE: The NMI exception is triggered with nterrupts DISABLED,
; because QK scheduler disables interrutps before return.
; set QK_attr_.aux to non-zero to tell the next PendSV instance ; before triggering the NMI exception, make sure that the
; to remove its own stack frame
LDR r3,=QK_attr_
MOVS r1,#0xFF
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0xFF (not zero)
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
#else ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
; before triggering the PendSV exception, make sure that the
; VFP stack frame will NOT be used... ; VFP stack frame will NOT be used...
#ifdef __ARMVFP__ ; if VFP available... #ifdef __ARMVFP__ ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL MRS r0,CONTROL ; r0 := CONTROL
@ -238,14 +147,38 @@ Thread_sched_ret:
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit) MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
#endif ; VFP available #endif ; VFP available
#endif ; M3/M4/M7 ; trigger NMI to return to preempted task...
; trigger PendSV to return to preempted task...
LDR r0,=0xE000ED04 ; Interrupt Control and State Register LDR r0,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit) LSLS r1,r1,#31 ; r0 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV) STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
B . ; wait for preemption by PendSV B . ; wait for preemption by NMI
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler:
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
#else ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
#ifdef __ARMVFP__ ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
#else ; no VFP
BX lr ; return to the preempted task
#endif ; no VFP
#endif ; M3/M4/M7
ALIGNROM 2,0xFF ; make sure the END is properly aligned ALIGNROM 2,0xFF ; make sure the END is properly aligned

View File

@ -60,8 +60,8 @@
#define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI) #define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI)
#define QF_INT_ENABLE() QF_set_BASEPRI(0U) #define QF_INT_ENABLE() QF_set_BASEPRI(0U)
// the intrinsic function _norm() generates the CLZ instruction // Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_))) #define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
// assembly function for setting the BASEPRI register // assembly function for setting the BASEPRI register
extern "C" void QF_set_BASEPRI(unsigned basePri); extern "C" void QF_set_BASEPRI(unsigned basePri);

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, TI-ARM toolset /// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, TI-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -43,14 +43,12 @@
// QK interrupt entry and exit // QK interrupt entry and exit
#define QK_ISR_ENTRY() ((void)0) #define QK_ISR_ENTRY() ((void)0)
#define QK_ISR_EXIT() do { \ #define QK_ISR_EXIT() do { \
uint_fast8_t nextPrio_; \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
nextPrio_ = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
QK_attr_.next = nextPrio_; \
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ ((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))); \ static_cast<uint32_t>(1U << 28))); \
} \ } \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (0) } while (0)

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), TI-ARM assembler ; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), TI-ARM assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-08-08 ; Date of the Last Update: 2016-09-26
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -34,30 +34,29 @@
.global QK_init .global QK_init
.global PendSV_Handler ; CMSIS-compliant PendSV exception name .global PendSV_Handler ; CMSIS-compliant PendSV exception name
.global NMI_Handler ; CMSIS-compliant NMI exception name
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
.global QF_set_BASEPRI ; set BASEPRI register .global QF_set_BASEPRI ; set BASEPRI register
.endif ; M3/M4/M7
.global QK_get_IPSR ; get the IPSR .global QK_get_IPSR ; get the IPSR
.global assert_failed ; low-level assert handler .global assert_failed ; low-level assert handler
.ref QK_sched_ ; external reference
.ref QK_attr_ ; external reference .ref QK_attr_ ; external reference
.ref QK_activate_ ; external reference
.ref Q_onAssert ; external reference .ref Q_onAssert ; external reference
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI: .equ (0xFF >> 2) QF_BASEPRI: .equ (0xFF >> 2)
; NOTE: keep in synch with the QK_Attr struct in "qk.h" !!!
QK_CURR: .equ 0
QK_NEXT: .equ 4
QK_AUX: .equ 8
.text .text
.thumb .thumb
;***************************************************************************** ;*****************************************************************************
; The QK_init() function sets the priority of PendSV to 0xFF (lowest). ; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
; Also, it initializes QK_attr_.aux to zero. Both these operations ; This operation is performed in a nestable critical section.
; are performed in a critical section.
;***************************************************************************** ;*****************************************************************************
QK_init: .asmfunc QK_init: .asmfunc
MRS r0,PRIMASK ; store the state of the PRIMASK in r0 MRS r0,PRIMASK ; store the state of the PRIMASK in r0
@ -70,15 +69,6 @@ QK_init: .asmfunc
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; The QK_attr_.aux attribute is used to determine the purpose of the
; PendSV exception. When (QK_attr_.aux == 0), PendSV is used for
; scheduling the next thread. Otherwise (QK_attr_.aux != 0), it is
; used for returning to the preempted thread. Here QK_attr_.aux is
; initialized to zero.
LDR r3,QK_attr_addr
MOVS r2,#0
STR r2,[r3,#QK_AUX] ; QK_attr_.aux := 0
MSR PRIMASK,r0 ; restore the original PRIMASK MSR PRIMASK,r0 ; restore the original PRIMASK
BX lr ; return to the caller BX lr ; return to the caller
.endasmfunc .endasmfunc
@ -105,53 +95,16 @@ QK_init: .asmfunc
;***************************************************************************** ;*****************************************************************************
PendSV_Handler: .asmfunc PendSV_Handler: .asmfunc
; Check QK_attr_.aux to determine the purpose of this PendSV exception. ; Prepare some constants in registers before entering critical section
; When (QK_attr_.aux == 0), PendSV is used for scheduling the next thread. LDR r3,ICSR_addr ; Interrupt Control and State Register
; Otherwise (QK_attr_.aux != 0), it is used for returning to the
; preempted thread.
;
; NOTE: no critical section is necessary, because the only other place
; QK_attr_.aux is accessed is inside a critical section and no other
; instance of PendSV can preempt itself.
;
LDR r3,QK_attr_addr
LDR r0,[r3,#QK_AUX] ; r0 := QK_attr_.aux
CMP r0,#0 ; if (QK_attr_.aux == 0) ...
.if __TI_VFP_SUPPORT__ ; if VFP available...
BEQ PendSV_save_lr ; save lr before checking QK_attr_.next
.else
BEQ PendSV_check_next ; go straight to checking QK_attr_.next
.endif ; VFP available
; Here you know that (QK_attr_.aux != 0), meaning that this PendSV
; instance has been triggered for returning to the preempted thread.
; The no-FPU exception stack frame of this PendSV instance is removed
; from the stack
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
; clear QK_attr_.aux for the next time
MOVS r1,#0
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0
.if __TI_VFP_SUPPORT__ ; if VFP available...
B PendSV_check_next
PendSV_save_lr:
; When FPU is available, the lr of this exception needs to be saved,
; because it contains the information about the type of the exception
; stack frame (FPU/no-FPU registers).
PUSH {r0,lr} ; push lr (EXC_RETURN) plus stack "aligner"
.endif ; VFP available
PendSV_check_next:
; Prepare some constants (an address and a bitmask) before entering
; a critical section...
LDR r2,ICSR_addr ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit) LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<< ; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__ .if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
.if __TI_VFP_SUPPORT__ ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
.endif ; VFP available
MOVS r0,#QF_BASEPRI MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts MSR BASEPRI,r0 ; selectively disable interrupts
.else ; Cortex-M0/M0+/M1 ? .else ; Cortex-M0/M0+/M1 ?
@ -161,82 +114,45 @@ PendSV_check_next:
; The PendSV exception handler can be preempted by an interrupt, ; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to ; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV. ; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV) STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
; Check QK_attr_.next, which contains the priority of the next thread ; The QK activator must be called in a Thread mode, while this code
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero ; executes in the Handler mode of the PendSV exception. The switch
; (QK_attr_.next != 0), this next thread needs to be scheduled. ; to the Thread mode is accomplished by returning from PendSV using
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to ; a fabricated exception stack frame, where the return address is
; simply return without activating the QK scheduler. ; QK_activate_().
; ;
LDR r0,[r3,#QK_NEXT] ; r0 := QK_attr_.next ; NOTE: the QK activator is called with interrupts DISABLED and also
CMP r0,#0 ; if (QK_attr_.next == 0) ... ; returns with interrupts DISABLED.
BEQ PendSV_ret LSRS r3,r1,#3 ; r3 := (1 << 24), set the T bit (new xpsr)
LDR r2,QK_activate_addr ; address of the QK activator (new pc)
; set QK_attr_.next to 0 for the next time LDR r1,Thread_ret_addr ; return address after the call (new lr)
MOVS r1,#0
STR r1,[r3,#QK_NEXT] ; QK_attr_.next := 0
PendSV_call_sched:
; The QK scheduler must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the thread context is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QK scheduler QK_sched_().
;
; NOTE: the QK scheduler is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,QK_sched_addr ; address of the QK scheduler (new pc)
LDR r1,Thread_sched_ret_addr ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame SUB sp,sp,#8*4 ; reserve space for exception stack frame
STR r0,[sp] ; save the prio argument (new r0)
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6 MOVS r0,#6
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9 MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QK scheduler BX r0 ; exception-return to the QK scheduler
; This part of the code executes when PendSV returns to the preempted task
PendSV_ret:
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
.else ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
.endif ; M0/M0+/M1
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
.if __TI_VFP_SUPPORT__ ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
.else ; no VFP
BX lr ; return to the preempted task
.endif ; VFP
.endasmfunc .endasmfunc
; NOTE: the following code does not execute in the PendSV context!
;========================================================================= ;*****************************************************************************
; NOTE: QK scheduler returns with interrupts DISABLED. ; Thread_ret is a helper function executed when the QXK activator returns.
Thread_sched_ret: .asmfunc ; to ensure that the label is THUMB ;
; NOTE: Thread_ret does not execute in the PendSV context!
; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
Thread_ret: .asmfunc ; to ensure that the label is THUMB
; After the QK scheduler returns, we need to resume the preempted ; After the QK scheduler returns, we need to resume the preempted
; task. However, this must be accomplished by a return-from-exception, ; task. However, this must be accomplished by a return-from-exception,
; while we are still in the task context. The switch to the exception ; while we are still in the task context. The switch to the exception
; contex is accomplished by triggering the PendSV exception. ; contex is accomplished by triggering the NMI exception.
; NOTE: The NMI exception is triggered with nterrupts DISABLED,
; because QK scheduler disables interrutps before return.
; set QK_attr_.aux to non-zero to tell the next PendSV instance ; before triggering the NMI exception, make sure that the
; to remove its own stack frame
LDR r3,QK_attr_addr
MOVS r1,#0xFF
STR r1,[r3,#QK_AUX] ; QK_attr_.aux = 0xFF (not zero)
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
; before triggering the PendSV exception, make sure that the
; VFP stack frame will NOT be used... ; VFP stack frame will NOT be used...
.if __TI_VFP_SUPPORT__ ; if VFP available... .if __TI_VFP_SUPPORT__ ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL MRS r0,CONTROL ; r0 := CONTROL
@ -244,29 +160,57 @@ Thread_sched_ret: .asmfunc ; to ensure that the label is THUMB
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit) MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
.endif ; VFP .endif ; VFP
.else ; Cortex-M0/M0+/M1 ? ; trigger NMI to return to preempted thread...
CPSIE i ; enable interrupts (clear PRIMASK)
.endif ; M0/M0+/M1
; trigger PendSV to return to preempted thread...
LDR r0,ICSR_addr ; Interrupt Control and State Register LDR r0,ICSR_addr ; Interrupt Control and State Register
MOVS r1,#1 MOVS r1,#1
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit) LSLS r1,r1,#31 ; r0 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV) STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
Thread_wait_PendSV: Thread_wait_NMI:
B Thread_wait_PendSV ; wait for preemption by PendSV B Thread_wait_NMI ; wait for preemption by NMI
.endasmfunc
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler: .asmfunc
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
.if __TI_VFP_SUPPORT__ ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
.else
BX lr ; return to the preempted task
.endif
.else ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
.endif ; M0/M0+/M1
.endasmfunc .endasmfunc
;***************************************************************************** ;*****************************************************************************
; The QF_set_BASEPRI function sets the BASEPRI register to the value ; The QF_set_BASEPRI function sets the BASEPRI register to the value
; passed in r0. ; passed in r0.
; NOTE: The BASEPRI register is implemented only in ARMv7 architecture
; and is **not** available in ARMv6 (M0/M0+/M1)
;
; C prototype: void QF_set_BASEPRI(unsigned basePri); ; C prototype: void QF_set_BASEPRI(unsigned basePri);
;***************************************************************************** ;*****************************************************************************
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
QF_set_BASEPRI: .asmfunc QF_set_BASEPRI: .asmfunc
MSR BASEPRI,r0 ; set BASEPRI MSR BASEPRI,r0 ; set BASEPRI
BX lr ; return to the caller BX lr ; return to the caller
.endasmfunc .endasmfunc
.endif ; M3/M4/M7
;***************************************************************************** ;*****************************************************************************
@ -302,5 +246,5 @@ VTOR_addr: .word 0xE000ED08
; Addresses for PC-relative LDR ; Addresses for PC-relative LDR
;***************************************************************************** ;*****************************************************************************
QK_attr_addr: .word QK_attr_ QK_attr_addr: .word QK_attr_
QK_sched_addr: .word QK_sched_ QK_activate_addr: .word QK_activate_
Thread_sched_ret_addr .word Thread_sched_ret Thread_ret_addr .word Thread_ret

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, GNU-ARM toolset /// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, GNU-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -68,7 +68,7 @@
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 // Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - __builtin_clz(n_))) #define QF_LOG2(n_) (static_cast<uint_fast8_t>(32U - __builtin_clz(n_)))
#endif #endif

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, IAR-ARM toolset /// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, IAR-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.4.0 /// Last updated for version 5.7.2
/// Last updated on 2015-05-04 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -65,7 +65,7 @@
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 // Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - __CLZ(n_))) #define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __CLZ(x_)))
#endif #endif
// QF critical section entry/exit... // QF critical section entry/exit...

View File

@ -3,8 +3,8 @@
/// @brief QF/C++ port to Cortex-M, cooperative QV kernel, TI-ARM CCS toolset /// @brief QF/C++ port to Cortex-M, cooperative QV kernel, TI-ARM CCS toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.5.1 /// Last updated for version 5.7.2
/// Last updated on 2015-09-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -60,8 +60,8 @@
#define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI) #define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI)
#define QF_INT_ENABLE() QF_set_BASEPRI(0U) #define QF_INT_ENABLE() QF_set_BASEPRI(0U)
// the intrinsic function _norm() generates the CLZ instruction // Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_))) #define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
// assembly function for setting the BASEPRI register // assembly function for setting the BASEPRI register
extern "C" void QF_set_BASEPRI(unsigned basePri); extern "C" void QF_set_BASEPRI(unsigned basePri);

View File

@ -1,48 +1,45 @@
/** /// @file
* @file /// @brief QEP/C++ port to ARM Cortex-M, generic C++ compiler
* @brief QEP/C port, generic C99 compiler /// @cond
* @ingroup ports ///***************************************************************************
* @cond /// Last updated for version 5.4.0
****************************************************************************** /// Last updated on 2015-03-14
* Last Updated for Version: 5.6.0 ///
* Date of the Last Update: 2015-11-20 /// Q u a n t u m L e a P s
* /// ---------------------------
* Q u a n t u m L e a P s /// innovating embedded systems
* --------------------------- ///
* innovating embedded systems /// Copyright (C) Quantum Leaps, All rights reserved.
* ///
* Copyright (C) Quantum Leaps, LLC. All rights reserved. /// This program is open source software: you can redistribute it and/or
* /// modify it under the terms of the GNU General Public License as published
* This program is open source software: you can redistribute it and/or /// by the Free Software Foundation, either version 3 of the License, or
* modify it under the terms of the GNU General Public License as published /// (at your option) any later version.
* by the Free Software Foundation, either version 3 of the License, or ///
* (at your option) any later version. /// Alternatively, this program may be distributed and modified under the
* /// terms of Quantum Leaps commercial licenses, which expressly supersede
* Alternatively, this program may be distributed and modified under the /// the GNU General Public License and are specifically designed for
* terms of Quantum Leaps commercial licenses, which expressly supersede /// licensees interested in retaining the proprietary status of their code.
* the GNU General Public License and are specifically designed for ///
* licensees interested in retaining the proprietary status of their code. /// This program is distributed in the hope that it will be useful,
* /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// GNU General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ///
* GNU General Public License for more details. /// You should have received a copy of the GNU General Public License
* /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License ///
* along with this program. If not, see <http://www.gnu.org/licenses/>. /// Contact information:
* /// Web: www.state-machine.com
* Contact information: /// Email: info@state-machine.com
* http://www.state-machine.com ///***************************************************************************
* mailto:info@state-machine.com /// @endcond
******************************************************************************
* @endcond
*/
#ifndef qep_port_h #ifndef qep_port_h
#define qep_port_h #define qep_port_h
#include <stdint.h> /* Exact-width types. WG14/N843 C99 Standard */ #include <stdint.h> // Exact-width types. WG14/N843 C99 Standard
#include <stdbool.h> /* Boolean type. WG14/N843 C99 Standard */
#include "qep.h" /* QEP platform-independent public interface */ #include "qep.h" // QEP platform-independent public interface
#endif /* qep_port_h */ #endif // qep_port_h

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, preemptive QXK kernel, ARM-KEIL toolset /// @brief QF/C++ port to ARM Cortex-M, preemptive QXK kernel, ARM-KEIL toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -65,7 +65,7 @@
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 // Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - __clz(n_))) #define QF_LOG2(n_) (static_cast<uint_fast8_t>(32U - __clz(n_)))
// inline function for setting the BASEPRI register // inline function for setting the BASEPRI register
static __inline void QF_set_BASEPRI(unsigned basePri) { static __inline void QF_set_BASEPRI(unsigned basePri) {

View File

@ -1,62 +1,54 @@
/** /// @file
* @file /// @brief QS/C++ port to ARM Cortex-M, generic compiler
* @brief QS/C port to a 32-bit CPU and a generic C compiler. /// @cond
* @ingroup qs ///***************************************************************************
* @cond /// Last updated for version 5.6.0
****************************************************************************** /// Last updated on 2015-12-26
* Last updated for version 5.6.0 ///
* Last updated on 2015-12-18 /// Q u a n t u m L e a P s
* /// ---------------------------
* Q u a n t u m L e a P s /// innovating embedded systems
* --------------------------- ///
* innovating embedded systems /// Copyright (C) Quantum Leaps. All rights reserved.
* ///
* Copyright (C) Quantum Leaps, LLC. All rights reserved. /// This program is open source software: you can redistribute it and/or
* /// modify it under the terms of the GNU General Public License as published
* This program is open source software: you can redistribute it and/or /// by the Free Software Foundation, either version 3 of the License, or
* modify it under the terms of the GNU General Public License as published /// (at your option) any later version.
* by the Free Software Foundation, either version 3 of the License, or ///
* (at your option) any later version. /// Alternatively, this program may be distributed and modified under the
* /// terms of Quantum Leaps commercial licenses, which expressly supersede
* Alternatively, this program may be distributed and modified under the /// the GNU General Public License and are specifically designed for
* terms of Quantum Leaps commercial licenses, which expressly supersede /// licensees interested in retaining the proprietary status of their code.
* the GNU General Public License and are specifically designed for ///
* licensees interested in retaining the proprietary status of their code. /// This program is distributed in the hope that it will be useful,
* /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// GNU General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ///
* GNU General Public License for more details. /// You should have received a copy of the GNU General Public License
* /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License ///
* along with this program. If not, see <http://www.gnu.org/licenses/>. /// Contact information:
* /// http://www.state-machine.com
* Contact information: /// mailto:info@state-machine.com
* http://www.state-machine.com ///***************************************************************************
* mailto:info@state-machine.com /// @endcond
******************************************************************************
* @endcond
*/
#ifndef qs_port_h #ifndef qs_port_h
#define qs_port_h #define qs_port_h
/* QS time-stamp size in bytes */ #define QS_TIME_SIZE 4
#define QS_TIME_SIZE 4 #define QS_OBJ_PTR_SIZE 4
#define QS_FUN_PTR_SIZE 4
/* object pointer size in bytes */ //****************************************************************************
#define QS_OBJ_PTR_SIZE 4 // NOTE: QS might be used with or without other QP components, in which case
// the separate definitions of the macros QF_CRIT_STAT_TYPE, QF_CRIT_ENTRY,
// and QF_CRIT_EXIT are needed. In this port QS is configured to be used with
// the QF framework, by simply including "qf_port.h" *before* "qs.h".
//
#include "qf_port.h" // use QS with QF
#include "qs.h" // QS platform-independent public interface
/* function pointer size in bytes */ #endif // qs_port_h
#define QS_FUN_PTR_SIZE 4
/*****************************************************************************
* NOTE: QS might be used with or without other QP components, in which
* case the separate definitions of the macros QF_CRIT_STAT_TYPE,
* QF_CRIT_ENTRY, and QF_CRIT_EXIT are needed. In this port QS is configured
* to be used with the other QP component, by simply including "qf_port.h"
* *before* "qs.h".
*/
#include "qf_port.h" /* use QS with QF */
#include "qs.h" /* QS platform-independent public interface */
#endif /* qs_port_h */

View File

@ -2,8 +2,8 @@
/// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, ARM-KEIL toolset /// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, ARM-KEIL toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -52,15 +52,18 @@ static __inline uint32_t QK_get_IPSR(void) {
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ (*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28)) static_cast<uint32_t>(1U << 28))
// QXK interrupt entry and exit // QXK ISR entry and exit
#define QXK_ISR_ENTRY() ((void)0) #define QXK_ISR_ENTRY() ((void)0)
#define QXK_ISR_EXIT() do { \ #define QXK_ISR_EXIT() do { \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
QXK_sched_(); \ if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
QXK_CONTEXT_SWITCH_(); \
} \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (false) } while (false)
#include "qxk.h" // QXK platform-independent public interface #include "qxk.h" // QXK platform-independent public interface
#endif // qxk_port_h #endif // qxk_port_h

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), ARM-Keil assembler ; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), ARM-Keil assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-07-14 ; Date of the Last Update: 2016-09-25
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -32,23 +32,27 @@
; mailto:info@state-machine.com ; mailto:info@state-machine.com
;***************************************************************************** ;*****************************************************************************
EXPORT QXK_start_ ; start the QXK multitasking EXPORT QXK_init ; initialze the QXK kernel
EXPORT QXK_stackInit_ ; initialize the stack of each thread EXPORT QXK_stackInit_ ; initialize the stack of an extended thread
EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name
EXPORT NMI_Handler ; CMSIS-compliant NMI exception name
IMPORT QXK_attr_ ; QXK attribute structure IMPORT QXK_attr_ ; QXK attribute structure
IMPORT QXK_activate_ ; external reference
IMPORT QXK_threadRet_ ; return from a thread function IMPORT QXK_threadRet_ ; return from a thread function
IMPORT assert_failed ; assert-failure handler
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI EQU (0xFF:SHR:2) QF_BASEPRI EQU (0xFF:SHR:2)
; NOTE: keep in synch with the QXK struct in "qxk.h" !!! ; NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!!
QXK_CURR EQU 0 QXK_CURR EQU 0
QXK_NEXT EQU 4 QXK_NEXT EQU 4
QXK_TOP_PRIO EQU 8
; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! ; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!!
QMACTIVE_OSOBJECT EQU 40 QMACTIVE_THREAD EQU 40
QMACTIVE_THREAD EQU 44 QMACTIVE_PRIO EQU 44
AREA |.text|, CODE, READONLY AREA |.text|, CODE, READONLY
@ -57,13 +61,13 @@ QMACTIVE_THREAD EQU 44
PRESERVE8 ; this code preserves 8-byte stack alignment PRESERVE8 ; this code preserves 8-byte stack alignment
;***************************************************************************** ;*****************************************************************************
; The QXK_start_ function starts QXK multitasking. ; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
; The C signature: void QXK_start_(void); ; This operation is performed in a nestable critical section.
;
; NOTE: QXK_start_() must be called with interrupts disabled and
; returns with interrupts **enabled**.
;***************************************************************************** ;*****************************************************************************
QXK_start_ QXK_init
MRS r0,PRIMASK ; store the state of the PRIMASK in r0
CPSID i ; disable interrupts (set PRIMASK)
LDR r1,=0xE000ED18 ; System Handler Priority Register LDR r1,=0xE000ED18 ; System Handler Priority Register
LDR r2,[r1,#8] ; load the System 12-15 Priority Register LDR r2,[r1,#8] ; load the System 12-15 Priority Register
MOVS r3,#0xFF MOVS r3,#0xFF
@ -71,112 +75,195 @@ QXK_start_
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; set the MSP to the top of the C-STACK, which is the first entry MSR PRIMASK,r0 ; restore the original PRIMASK
; in the vector table. (This recovers any stack used so far by main().) BX lr ; return to the caller
LDR r0,=0xE000ED08 ; r0 := address of Vector Table Offset register
LDR r0,[r0,#0] ; r0 := contents of VTOR
LDR r0,[r0] ; r0 := VT[0] (first entry is the top of stack)
MSR MSP,r0 ; main SP := initial top of stack
; set the current QXK thread to the next QXK thread
LDR r1,=QXK_attr_
LDR r2,[r1,#QXK_NEXT] ; r2 := QXK_attr_.next
STR r2,[r1,#QXK_CURR] ; QXK_attr_.curr := r2
; get the top of stack of the current QXK thread ;*****************************************************************************
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP) ; The PendSV_Handler exception handler is used for handling context switch
; pop r4-r11 from the PSP ; and asynchronous preemption in QXK. The use of the PendSV exception is
MOVS r1,r0 ; r1 := top of stack ; the recommended and most efficient method for performing context switches
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 ; with ARM Cortex-M.
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers ;
; The PendSV exception should have the lowest priority in the whole system
; (0xFF, see QXK_init). All other exceptions and interrupts should have higher
; priority. For example, for NVIC with 2 priority bits all interrupts and
; exceptions must have numerical value of priority lower than 0xC0. In this
; case the interrupt priority levels available to your applications are (in
; the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro, which triggers PendSV when it detects a need for
; a context switch or asynchronous preemption.
;
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK scheduler needs to
; check for the asynchronous preemption.
;*****************************************************************************
PendSV_Handler
; Prepare some constants (an address and a bitmask) before entering
; a critical section...
LDR r3,=QXK_attr_
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSID i ; disable interrupts (set PRIMASK)
ELSE ; M3/M4/M7
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts
ENDIF ; M3/M4/M7
; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; Check QXK_attr_.next, which contains the pointer to the next thread
; to run, which is set in QXK_ISR_EXIT(). This pointer must not be NULL.
LDR r0,[r3,#QXK_NEXT] ; r1 := QXK_attr_.next
CMP r0,#0 ; is (QXK_attr_.next == 0)?
BEQ PendSV_error ; branch if (QXK_attr_.next == 0)
; Load pointers into registers...
MOV r12,r0 ; save QXK_attr_.next in r12
LDR r2,[r0,#QMACTIVE_THREAD] ; r2 := QXK_attr_.next->thread
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr
CMP r1,#0 ; (QXK_attr_.curr != 0)?
BNE PendSV_save_ex ; branch if (current thread is extended)
CMP r2,#0 ; (QXK_attr_.next->thread != 0)?
BNE PendSV_save_ao ; branch if (next tread is extended)
PendSV_activate
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
ENDIF ; VFP available
; The QXK activator must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the Thread mode is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QXK activator QXK_activate_().
;
; NOTE: the QXK activator is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,=QXK_activate_ ; address of the QXK activator (new pc)
LDR r1,=Thread_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QXK activator
;-------------------------------------------------------------------------
PendSV_error
LDR r3,=assert_failed
BX r3 ; long-branch to the assertion-handler
;=========================================================================
; Saving AO-thread before crossing to eXtended-thread
; expected register contents:
; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_save_ao
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
PUSH {r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
ELSE ; M3/M4/M7
PUSH {r4-r11} ; save r4-r11 on top of the exception frame
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero...
VSTMDBEQ sp!,{s16-s31} ; ... save VFP registers s16..s31
PUSH {r0,lr} ; save the "aligner" and the EXC_RETURN value
ENDIF ; VFP available
ENDIF ; M3/M4/M7
CMP r2,#0
BNE PendSV_restore_ex ; branch if (QXK_attr_.next->thread != 0)
; otherwise continue to restoring next AO-thread...
;-------------------------------------------------------------------------
; Restoring AO-thread after crossing from eXtended-thread
; expected register contents:
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_restore_ao
MOVS r0,#0
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := 0 (QXK_attr_.next)
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r0!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack ADD sp,sp,#(8*4) ; remove 8 registers from the stack
MSR PSP,r0 ; set PSP to the thread's SP MOVS r1,#6
ISB ; flush the instruction pipeline MVNS r1,r1 ; r2 := ~6 == 0xFFFFFFF9
MOV lr,r1 ; make sure MSP is used
ELSE ; M3/M4/M7
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
POP {r0,lr} ; restore alighner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if EXC_RETURN[4] is zero...
VLDMIAEQ sp!,{s16-s31} ; ... restore VFP registers s16..s31
ELSE
BIC lr,lr,#(1 << 2) ; make sure MSP is used
ENDIF ; VFP available
POP {r4-r11} ; restore r4-r11 from the next thread's stack
ENDIF ; M3/M4/M7
; switch CPU to using the Process Stack Pointer (PSP) MOV r0,r12 ; r0 := QXK_attr_.next
MRS r0,CONTROL LDR r0,[r0,#QMACTIVE_PRIO] ; r0 := QXK_attr_.next->prio
MOVS r1,#(1:SHL:1) LDR r1,[r3,#QXK_TOP_PRIO] ; r1 := QXK_attr_.topPrio
ORRS r0,r0,r1 ; set the Active Stack Pointer CMP r1,r0
MSR CONTROL,r0 ; switch to PSP BCC PendSV_activate ; if (next->prio > topPrio) activate the next AO
DSB ; make sure all data access completes
ISB ; flush the instruction pipeline
; fake return from an exception... ; otherwise re-enable interrupts and return to the preempted AO-thread
POP {r0-r3} ; pop R0..R3
; NOTE: R0 holds the 'par' argument of the
; thread function
POP {r1,r2} ; pop R12 and LR into low registers r1,r2
MOV r12,r1
MOV lr,r2
POP {r1,r2} ; pop PC to R1 and xPSR to R2
; NOTE: it's OK to clobber R1 and R2
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)? IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
ELSE ; M3/M4/M7 ELSE ; M3/M4/M7
MOVS r0,#0
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
; enable VFP by enabling CP10 and CP11 coprocessors
LDR.W r3,=0xE000ED88 ; r3 := &CPACR
LDR r2,[r3] ; r2 := CPACR
ORR r2,r2,#(0xF:SHL:20); r2 := r2 | (CP10 | CP11)
STR r2,[r3] ; CPACR = r2
DSB ; make sure all data access completes
ISB ; reset the instruction pipeline
; enable Automatic State Preservation and Lazy Stacking in the VFP
LDR r3,=0xE000EF34 ; r3 := &FPCCR
LDR r2,[r3] ; r2 := FPCCR
ORR r2,r2,#(3:SHL:30) ; set ASPEN | LSPEN
STR r2,[r3] ; FPCCR &= ~(ASPEN | LSPEN)
; clear the VFP Context Active (FPCA) bit in CONTROL
MRS r2,CONTROL ; r2 := CONTROL (NOTE: it's OK to clobber R2)
BICS r2,r2,#(1:SHL:2) ; r2 := r2 & ~(1 << 2) (FPCA bit)
MSR CONTROL,r2 ; CONTROL := r2 (clear CONTROL[2] FPCA bit)
ENDIF ; VFP available
MOVS r2,#0 ; NOTE: it's OK to clobber R2
MSR BASEPRI,r2 ; enable interrupts (clear BASEPRI)
ENDIF ; M3/M4/M7 ENDIF ; M3/M4/M7
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
BX lr ; return to the preempted AO-thread
BX r1 ; return to the "interrupted" thread (PC) ;-------------------------------------------------------------------------
; Saving extended-thread before crossing to AO-thread
; expected register contents:
;***************************************************************************** ; r0 -> QXK_attr_.next
; The PendSV_Handler exception handler is used for context switching in QXK. ; r1 -> QXK_attr_.curr
; The use of the PendSV exception is the recommended and most efficient ; r2 -> QXK_attr_.next->thread (SP)
; method for performing context switches with ARM Cortex-M. ; r3 -> &QXK_attr_
; ; r12 -> QXK_attr_.next
; The PendSV exception should have the lowest priority in the whole system PendSV_save_ex
; (0xFF, see QXK_start_). All other exceptions and interrupts should have
; higher priority. For example, for NVIC with 2 priority bits all interrupts
; and exceptions must have numerical value of priority lower than 0xC0.
; In this case the interrupt priority levels available to your applications
; are (in the order from the lowest to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel-aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro to trigger the PendSV exception.
;
; The C signature (CMSIS): void PendSV_Handler(void);
;
; NOTE:
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK context switch
; must occur.
;*****************************************************************************
PendSV_Handler
MRS r0,PSP ; r0 := Process Stack Pointer MRS r0,PSP ; r0 := Process Stack Pointer
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
LDR r3,=QXK_attr_
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)? IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11 SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!) MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
@ -186,83 +273,128 @@ PendSV_Handler
MOV r6,r10 MOV r6,r10
MOV r7,r11 MOV r7,r11
STMIA r1!,{r4-r7} ; save the high registers STMIA r1!,{r4-r7} ; save the high registers
; NOTE: at this point r0 still holds the top of stack ; NOTE: at this point r0 holds the top of stack
CPSID i ; disable interrupts (set PRIMASK)
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
ELSE ; M3/M4/M7 ELSE ; M3/M4/M7
STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
TST lr,#(1:SHL:4) ; is it return with the VFP exception frame? TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31 VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
ENDIF ; VFP available ENDIF ; VFP available
MOVS r1,#QF_BASEPRI
MSR BASEPRI,r1 ; selectively disable interrupts
ENDIF ; M3/M4/M7 ENDIF ; M3/M4/M7
; NOTE: This PendSV exception handler can be preempted by an ; store the SP of the current extended-thread
; interrupt, which might pend PendSV exception again. This STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
; would be a problem, because the QK scheduler would run again MOV r0,r12 ; QXK_attr_.next (restore value)
; after this PendSV instance, so the same AO would be scheduled
; twice. The following write to ICSR[7] un-pends any such spurious
; instance of PendSV.
MOVS r1,#1
LSLS r1,r1,#27 ; r1 := (1 << 27) (UNPENDSVSET bit)
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; store the SP of the current QXK thread, CMP r2,#0
; which was set in QXK_ISR_EXIT(). BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
LDR r2,[r3,#QXK_CURR] ; otherwise continue to restoring next extended-thread...
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... ;-------------------------------------------------------------------------
; store the LR of the current QXK thread... ; Restoring extended-thread after crossing from AO-thread
STR lr,[r2,#QMACTIVE_OSOBJECT] ; QXK_attr_.curr->osObject := lr ; expected register contents:
ENDIF ; VFP available ; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; set current to the next... ; r2 -> QXK_attr_.next->thread (SP)
LDR r2,[r3,#QXK_NEXT] ; r2 := QXK_attr_.next ; r3 -> &QXK_attr_
STR r2,[r3,#QXK_CURR] ; QXK_attr_.curr := r2 ; r12 -> QXK_attr_.next
PendSV_restore_ex
; restore the SP of the next thread... STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := r0 (QXK_attr_.next)
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP)
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
; restore the LR of the next thread...
LDR lr,[r2,#QMACTIVE_OSOBJECT] ; lr := QXK_attr_.next->osObject
ENDIF ; VFP available
; exit the critical section ; exit the critical section
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)? IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
MOVS r1,r0 ; r1 := top of stack MOVS r0,r2 ; r2 := top of stack
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r2!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack MOVS r2,r0 ; r2 := holds the new top of stack
MOVS r1,#2
MVNS r1,r1 ; r1 := ~2 == 0xFFFFFFFD
MOV lr,r1 ; make sure PSP is used
ELSE ; M3/M4/M7 ELSE ; M3/M4/M7
MOVS r3,#0 MOVS r1,#1
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI) MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available... IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
TST lr,#(1:SHL:4) ; is it return to the VFP exception frame? LDMIA r2!,{r1,lr} ; restore aligner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VLDMIAEQ r0!,{s16-s31} ; ... restore VFP registers s16..s31 VLDMIAEQ r2!,{s16-s31} ; ... restore VFP registers s16..s31
ELSE
ORR lr,lr,#(1 << 2) ; make sure PSP is used
ENDIF ; VFP available ENDIF ; VFP available
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack
ENDIF ; M3/M4/M7 ENDIF ; M3/M4/M7
; set the PSP to the next thread's SP ; set the PSP to the next thread's SP
MSR PSP,r0 ; Process Stack Pointer := r0 MSR PSP,r2 ; Process Stack Pointer := r2
BX lr ; return to the next thread BX lr ; return to the next extended-thread
;*****************************************************************************
; Thread_ret is a helper function executed when the QXK activator returns.
;
; NOTE: Thread_ret does not execute in the PendSV context!
; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
Thread_ret
; After the QXK activator returns, we need to resume the preempted
; thread. However, this must be accomplished by a return-from-exception,
; while we are still in the thread context. The switch to the exception
; contex is accomplished by triggering the NMI exception.
; before triggering the NMI exception, make sure that the
; VFP stack frame will NOT be used...
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL
BICS r0,r0,#4 ; r0 := r0 & ~4 (FPCA bit)
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
ENDIF ; VFP available
; trigger NMI to return to preempted task...
; NOTE: The NMI exception is triggered with nterrupts DISABLED
LDR r0,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1
LSLS r1,r1,#31 ; r1 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
B . ; wait for preemption by NMI
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
ELSE ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
ELSE ; no VFP
BX lr ; return to the preempted task
ENDIF ; no VFP
ENDIF ; M3/M4/M7
;***************************************************************************** ;*****************************************************************************
@ -287,8 +419,9 @@ QXK_stackInit_
; r2 - begining of stack ; r2 - begining of stack
; r3 - size of stack [bytes] ; r3 - size of stack [bytes]
MOV r12,r0 ; temporarily save r0 in r12 (act)
STR r1,[r0,#QMACTIVE_THREAD] ; temporarily save the thread routine
ADDS r3,r2,r3 ; r3 := end of stack (top of stack) ADDS r3,r2,r3 ; r3 := end of stack (top of stack)
MOV r12,r0 ; save r0 in r12
; round up the beginning of stack to the 8-byte boundary ; round up the beginning of stack to the 8-byte boundary
; r2 := (((r2 -1) >> 3) + 1) << 3; ; r2 := (((r2 -1) >> 3) + 1) << 3;
@ -302,77 +435,82 @@ QXK_stackInit_
LSRS r0,r3,#3 LSRS r0,r3,#3
LSLS r3,r0,#3 LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
ENDIF ; VFP available
MOV r0,r12 ; pre-fill the unused part of the stack with 0xDEADBEEF...................
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
; pre-fill the stack with 0xDEADBEEF
LDR r0,=0xDEADBEEF LDR r0,=0xDEADBEEF
MOV r1,r0 MOV r1,r0
QXK_stackInit_fill QXK_stackInit_fill
STMIA r2!,{r0,r1} STMIA r2!,{r0,r1}
CMP r2,r3 CMP r2,r3
BLT.N QXK_stackInit_fill BLT.N QXK_stackInit_fill
; prepare the standard exception (without VFP) stack frame... ; prepare the standard exception (without VFP) stack frame................
MOV r0,r12 ; restore r0 from r12 (act)
MOV r0,r12 ; restore r0 from r12 LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
MOVS r2,#0x04
STR r2,[r3,#0*4] ; r4
MOVS r2,#0x05
STR r2,[r3,#1*4] ; r5
MOVS r2,#0x06
STR r2,[r3,#2*4] ; r6
MOVS r2,#0x07
STR r2,[r3,#3*4] ; r7
MOVS r2,#0x08
STR r2,[r3,#4*4] ; r8
MOVS r2,#0x09
STR r2,[r3,#5*4] ; r9
MOVS r2,#0x0A
STR r2,[r3,#6*4] ; r10
MOVS r2,#0x0B
STR r2,[r3,#7*4] ; r11
STR r0,[r3,#8*4] ; r0 (argument to thread routine, act pointer)
MOVS r2,#0x01
STR r2,[r3,#9*4] ; r1
MOVS r2,#0x02
STR r2,[r3,#10*4] ; r2
MOVS r2,#0x03
STR r2,[r3,#11*4] ; r3
MOVS r2,#0x0C
STR r2,[r3,#12*4] ; r12
LDR r2,=QXK_threadRet_
STR r2,[r3,#13*4] ; LR (return address)
LDR r1,[r0,#QMACTIVE_OSOBJECT] ; r1 := saved thread routine
STR r1,[r3,#14*4] ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STR r2,[r3,#15*4] ; xPSR
STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
MOVS r2,#0
STMIA r3!,{r2} ; stack "aligner"
; synthesize EXC_RETURN for return to Thread mode with no FPU-state
MOVS r2,#2 MOVS r2,#2
MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD
STR r2,[r0,#QMACTIVE_OSOBJECT] ; act->thread.osObject := lr STMIA r3!,{r2} ; save EXC_RETURN
ENDIF ; VFP available
MOVS r2,#0x04
STMIA r3!,{r2} ; r4
MOVS r2,#0x05
STMIA r3!,{r2} ; r5
MOVS r2,#0x06
STMIA r3!,{r2} ; r6
MOVS r2,#0x07
STMIA r3!,{r2} ; r7
MOVS r2,#0x08
STMIA r3!,{r2} ; r8
MOVS r2,#0x09
STMIA r3!,{r2} ; r9
MOVS r2,#0x0A
STMIA r3!,{r2} ; r10
MOVS r2,#0x0B
STMIA r3!,{r2} ; r11
STMIA r3!,{r0} ; r0 (argument to thread routine, me pointer)
MOVS r2,#0x01
STMIA r3!,{r2} ; r1
MOVS r2,#0x02
STMIA r3!,{r2} ; r2
MOVS r2,#0x03
STMIA r3!,{r2} ; r3
MOVS r2,#0x0C
STMIA r3!,{r2} ; r12
LDR r2,=QXK_threadRet_
STMIA r3!,{r2} ; LR (return address)
STMIA r3!,{r1} ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STMIA r3!,{r2} ; xPSR
BX lr ; return to the caller BX lr ; return to the caller

View File

@ -1,48 +1,45 @@
/** /// @file
* @file /// @brief QEP/C++ port to ARM Cortex-M, generic C++ compiler
* @brief QEP/C port, generic C99 compiler /// @cond
* @ingroup ports ///***************************************************************************
* @cond /// Last updated for version 5.4.0
****************************************************************************** /// Last updated on 2015-03-14
* Last Updated for Version: 5.6.0 ///
* Date of the Last Update: 2015-11-20 /// Q u a n t u m L e a P s
* /// ---------------------------
* Q u a n t u m L e a P s /// innovating embedded systems
* --------------------------- ///
* innovating embedded systems /// Copyright (C) Quantum Leaps, All rights reserved.
* ///
* Copyright (C) Quantum Leaps, LLC. All rights reserved. /// This program is open source software: you can redistribute it and/or
* /// modify it under the terms of the GNU General Public License as published
* This program is open source software: you can redistribute it and/or /// by the Free Software Foundation, either version 3 of the License, or
* modify it under the terms of the GNU General Public License as published /// (at your option) any later version.
* by the Free Software Foundation, either version 3 of the License, or ///
* (at your option) any later version. /// Alternatively, this program may be distributed and modified under the
* /// terms of Quantum Leaps commercial licenses, which expressly supersede
* Alternatively, this program may be distributed and modified under the /// the GNU General Public License and are specifically designed for
* terms of Quantum Leaps commercial licenses, which expressly supersede /// licensees interested in retaining the proprietary status of their code.
* the GNU General Public License and are specifically designed for ///
* licensees interested in retaining the proprietary status of their code. /// This program is distributed in the hope that it will be useful,
* /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// GNU General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ///
* GNU General Public License for more details. /// You should have received a copy of the GNU General Public License
* /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License ///
* along with this program. If not, see <http://www.gnu.org/licenses/>. /// Contact information:
* /// Web: www.state-machine.com
* Contact information: /// Email: info@state-machine.com
* http://www.state-machine.com ///***************************************************************************
* mailto:info@state-machine.com /// @endcond
******************************************************************************
* @endcond
*/
#ifndef qep_port_h #ifndef qep_port_h
#define qep_port_h #define qep_port_h
#include <stdint.h> /* Exact-width types. WG14/N843 C99 Standard */ #include <stdint.h> // Exact-width types. WG14/N843 C99 Standard
#include <stdbool.h> /* Boolean type. WG14/N843 C99 Standard */
#include "qep.h" /* QEP platform-independent public interface */ #include "qep.h" // QEP platform-independent public interface
#endif /* qep_port_h */ #endif // qep_port_h

View File

@ -1,60 +1,59 @@
/** /// @file
* @file /// @brief QF/C++ port to ARM Cortex-M, dual-mode QXK kernel, GNU-ARM toolset
* @brief QF/C port to Cortex-M, preemptive QXK kernel, GNU-ARM toolset /// @cond
* @cond ///***************************************************************************
****************************************************************************** /// Last updated for version 5.7.2
* Last Updated for Version: 5.6.0 /// Last updated on 2016-09-26
* Date of the Last Update: 2015-12-11 ///
* /// Q u a n t u m L e a P s
* Q u a n t u m L e a P s /// ---------------------------
* --------------------------- /// innovating embedded systems
* innovating embedded systems ///
* /// Copyright (C) Quantum Leaps, www.state-machine.com.
* Copyright (C) Quantum Leaps, LLC. All rights reserved. ///
* /// This program is open source software: you can redistribute it and/or
* This program is open source software: you can redistribute it and/or /// modify it under the terms of the GNU General Public License as published
* modify it under the terms of the GNU General Public License as published /// by the Free Software Foundation, either version 3 of the License, or
* by the Free Software Foundation, either version 3 of the License, or /// (at your option) any later version.
* (at your option) any later version. ///
* /// Alternatively, this program may be distributed and modified under the
* Alternatively, this program may be distributed and modified under the /// terms of Quantum Leaps commercial licenses, which expressly supersede
* terms of Quantum Leaps commercial licenses, which expressly supersede /// the GNU General Public License and are specifically designed for
* the GNU General Public License and are specifically designed for /// licensees interested in retaining the proprietary status of their code.
* licensees interested in retaining the proprietary status of their code. ///
* /// This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details.
* GNU General Public License for more details. ///
* /// You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>. ///
* /// Contact information:
* Contact information: /// Web: www.state-machine.com
* http://www.state-machine.com /// Email: info@state-machine.com
* mailto:info@state-machine.com ///***************************************************************************
****************************************************************************** /// @endcond
* @endcond
*/
#ifndef qf_port_h #ifndef qf_port_h
#define qf_port_h #define qf_port_h
/* The maximum number of active objects in the application, see NOTE1 */ // The maximum number of active objects in the application, see NOTE1
#define QF_MAX_ACTIVE 32 #define QF_MAX_ACTIVE 32
/* The maximum number of system clock tick rates */ // The maximum number of system clock tick rates
#define QF_MAX_TICK_RATE 2 #define QF_MAX_TICK_RATE 2
/* QF interrupt disable/enable and log2()... */ // QF interrupt disable/enable and log2()...
#if (__ARM_ARCH == 6) /* Cortex-M0/M0+/M1 ?, see NOTE02 */ #if (__ARM_ARCH == 6) /* Cortex-M0/M0+/M1 ?, see NOTE02 */
#define QF_INT_DISABLE() __asm volatile ("cpsid i") #define QF_INT_DISABLE() __asm volatile ("cpsid i")
#define QF_INT_ENABLE() __asm volatile ("cpsie i") #define QF_INT_ENABLE() __asm volatile ("cpsie i")
/* QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2 */ // QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2
#define QF_AWARE_ISR_CMSIS_PRI 0 #define QF_AWARE_ISR_CMSIS_PRI 0
#else /* Cortex-M3/M4/M7, see NOTE3 */ #else // Cortex-M3/M4/M7 see NOTE03
#define QF_SET_BASEPRI(val_) __asm volatile (\ #define QF_SET_BASEPRI(val_) __asm volatile (\
"movs r0,%0 \n\t" \ "movs r0,%0 \n\t" \
@ -62,68 +61,69 @@
#define QF_INT_DISABLE() QF_SET_BASEPRI(QF_BASEPRI) #define QF_INT_DISABLE() QF_SET_BASEPRI(QF_BASEPRI)
#define QF_INT_ENABLE() QF_SET_BASEPRI(0U) #define QF_INT_ENABLE() QF_SET_BASEPRI(0U)
/* NOTE: keep in synch with the value defined in "qk_port.s", NOTE4 */ // NOTE: keep in synch with the value defined in "qk_port.s", NOTE4
#define QF_BASEPRI (0xFFU >> 2) #define QF_BASEPRI (0xFFU >> 2)
/* QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE5 */ // QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE5
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
/* Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 */ // Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - __builtin_clz(n_))) #define QF_LOG2(x_) \
(static_cast<uint_fast8_t>(32U - __builtin_clz(x_)))
#endif #endif
/* QF critical section entry/exit */ // QF critical section entry/exit
/* QF_CRIT_STAT_TYPE not defined: unconditional interrupt disabling" policy */ // QF_CRIT_STAT_TYPE not defined: unconditional interrupt disabling" policy
#define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE() #define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE()
#define QF_CRIT_EXIT(dummy) QF_INT_ENABLE() #define QF_CRIT_EXIT(dummy) QF_INT_ENABLE()
#define QF_CRIT_EXIT_NOP() __asm volatile ("isb") #define QF_CRIT_EXIT_NOP() __asm volatile ("isb")
#include "qep_port.h" /* QEP port */ #include "qep_port.h" // QEP port
#include "qxk_port.h" /* QXK port */ #include "qxk_port.h" // QXK port
#include "qf.h" /* QF platform-independent public interface */ #include "qf.h" // QF platform-independent public interface
#include "qxthread.h" /* QXK naked thread */ #include "qxthread.h" // QXK naked thread
/***************************************************************************** //****************************************************************************
* NOTE1: // NOTE1:
* The maximum number of active objects QF_MAX_ACTIVE can be increased // The maximum number of active objects QF_MAX_ACTIVE can be increased
* up to 63, if necessary. Here it is set to a lower level to save some RAM. // up to 63, if necessary. Here it is set to a lower level to save some RAM.
* //
* NOTE2: // NOTE2:
* On Cortex-M0/M0+/M1 (architecture v6-M, v6S-M), the interrupt disabling // On Cortex-M0/M0+/M1 (architecture v6-M, v6S-M), the interrupt disabling
* policy uses the PRIMASK register to disable interrupts globally. The // policy uses the PRIMASK register to disable interrupts globally. The
* QF_AWARE_ISR_CMSIS_PRI level is zero, meaning that all interrupts are // QF_AWARE_ISR_CMSIS_PRI level is zero, meaning that all interrupts are
* "kernel-aware". // "kernel-aware".
* //
* NOTE3: // NOTE3:
* On Cortex-M3/M4/M7, the interrupt disable/enable policy uses the BASEPRI // On Cortex-M3/M4/M7, the interrupt disable/enable policy uses the BASEPRI
* register (which is not implemented in Cortex-M0/M0+/M1) to disable // register (which is not implemented in Cortex-M0/M0+/M1) to disable
* interrupts only with priority lower than the level specified by the // interrupts only with priority lower than the level specified by the
* QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e., // QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e.,
* with numerical priority values lower than QF_BASEPRI) are not disabled in // with numerical priority values lower than QF_BASEPRI) are not disabled in
* this method. These free-running interrupts are not allowed to call any QF // this method. These free-running interrupts are not allowed to call any QF
* services, because QF is not aware of these interrupts. Coversely, only // services, because QF is not aware of these interrupts. Coversely, only
* "QF-aware" interrupts, with numerical values of priorities eqal to or // "QF-aware" interrupts, with numerical values of priorities eqal to or
* higher than QF_BASEPRI, can call QF services. // higher than QF_BASEPRI, can call QF services.
* //
* NOTE4: // NOTE4:
* For Cortex-M3/M4/M7, the macro QF_BASEPRI leaves the top 2 priority bits // For Cortex-M3/M4/M7, the macro QF_BASEPRI leaves the top 2 priority bits
* empty for QF-aware interrupts. This is the highest-possible priority // empty for QF-aware interrupts. This is the highest-possible priority
* (lowest possible numerical value) for the guaranteed 3 priority bits // (lowest possible numerical value) for the guaranteed 3 priority bits
* implemented in the NVIC. // implemented in the NVIC.
* //
* NOTE5: // NOTE5:
* The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating // The QF_AWARE_ISR_CMSIS_PRI macro is useful as an offset for enumerating
* the QF-aware interrupt priority levels in the applications, whereas the // the QF-aware interrupt priority levels in the applications, whereas the
* numerical values of the QF-aware interrupts must be greater or equal to // numerical values of the QF-aware interrupts must be greater or equal to
* QF_AWARE_ISR_CMSIS_PRI. The enumerated values based on // QF_AWARE_ISR_CMSIS_PRI. The enumerated values based on
* QF_AWARE_ISR_CMSIS_PRI can be passed directly to the CMSIS function // QF_AWARE_ISR_CMSIS_PRI can be passed directly to the CMSIS function
* NVIC_SetPriority(), which shifts them by (8 - __NVIC_PRIO_BITS) into the // NVIC_SetPriority(), which shifts them by (8 - __NVIC_PRIO_BITS) into the
* correct bit position, while __NVIC_PRIO_BITS is the CMSIS macro defining // correct bit position, while __NVIC_PRIO_BITS is the CMSIS macro defining
* the number of implemented priority bits in the NVIC. Please note that // the number of implemented priority bits in the NVIC. Please note that
* the macro QF_AWARE_ISR_CMSIS_PRI is intended only for applications and // the macro QF_AWARE_ISR_CMSIS_PRI is intended only for applications and
* is not used inside the QF port, which remains generic and not dependent // is not used inside the QF port, which remains generic and not dependent
* on the number of implemented priority bits in the NVIC. // on the number of implemented priority bits in the NVIC.
*/ //
#endif /* qf_port_h */ #endif // qf_port_h

View File

@ -1,62 +1,54 @@
/** /// @file
* @file /// @brief QS/C++ port to ARM Cortex-M, generic compiler
* @brief QS/C port to a 32-bit CPU and a generic C compiler. /// @cond
* @ingroup qs ///***************************************************************************
* @cond /// Last updated for version 5.6.0
****************************************************************************** /// Last updated on 2015-12-26
* Last updated for version 5.6.0 ///
* Last updated on 2015-12-18 /// Q u a n t u m L e a P s
* /// ---------------------------
* Q u a n t u m L e a P s /// innovating embedded systems
* --------------------------- ///
* innovating embedded systems /// Copyright (C) Quantum Leaps. All rights reserved.
* ///
* Copyright (C) Quantum Leaps, LLC. All rights reserved. /// This program is open source software: you can redistribute it and/or
* /// modify it under the terms of the GNU General Public License as published
* This program is open source software: you can redistribute it and/or /// by the Free Software Foundation, either version 3 of the License, or
* modify it under the terms of the GNU General Public License as published /// (at your option) any later version.
* by the Free Software Foundation, either version 3 of the License, or ///
* (at your option) any later version. /// Alternatively, this program may be distributed and modified under the
* /// terms of Quantum Leaps commercial licenses, which expressly supersede
* Alternatively, this program may be distributed and modified under the /// the GNU General Public License and are specifically designed for
* terms of Quantum Leaps commercial licenses, which expressly supersede /// licensees interested in retaining the proprietary status of their code.
* the GNU General Public License and are specifically designed for ///
* licensees interested in retaining the proprietary status of their code. /// This program is distributed in the hope that it will be useful,
* /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* This program is distributed in the hope that it will be useful, /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// GNU General Public License for more details.
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ///
* GNU General Public License for more details. /// You should have received a copy of the GNU General Public License
* /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* You should have received a copy of the GNU General Public License ///
* along with this program. If not, see <http://www.gnu.org/licenses/>. /// Contact information:
* /// http://www.state-machine.com
* Contact information: /// mailto:info@state-machine.com
* http://www.state-machine.com ///***************************************************************************
* mailto:info@state-machine.com /// @endcond
******************************************************************************
* @endcond
*/
#ifndef qs_port_h #ifndef qs_port_h
#define qs_port_h #define qs_port_h
/* QS time-stamp size in bytes */ #define QS_TIME_SIZE 4
#define QS_TIME_SIZE 4 #define QS_OBJ_PTR_SIZE 4
#define QS_FUN_PTR_SIZE 4
/* object pointer size in bytes */ //****************************************************************************
#define QS_OBJ_PTR_SIZE 4 // NOTE: QS might be used with or without other QP components, in which case
// the separate definitions of the macros QF_CRIT_STAT_TYPE, QF_CRIT_ENTRY,
// and QF_CRIT_EXIT are needed. In this port QS is configured to be used with
// the QF framework, by simply including "qf_port.h" *before* "qs.h".
//
#include "qf_port.h" // use QS with QF
#include "qs.h" // QS platform-independent public interface
/* function pointer size in bytes */ #endif // qs_port_h
#define QS_FUN_PTR_SIZE 4
/*****************************************************************************
* NOTE: QS might be used with or without other QP components, in which
* case the separate definitions of the macros QF_CRIT_STAT_TYPE,
* QF_CRIT_ENTRY, and QF_CRIT_EXIT are needed. In this port QS is configured
* to be used with the other QP component, by simply including "qf_port.h"
* *before* "qs.h".
*/
#include "qf_port.h" /* use QS with QF */
#include "qs.h" /* QS platform-independent public interface */
#endif /* qs_port_h */

View File

@ -1,46 +1,45 @@
/** /// @file
* @file /// @brief QXK/C++ port to ARM Cortex-M, GNU-ARM toolset
* @brief QXK/C port to ARM Cortex-M, GNU-ARM compiler /// @cond
* @cond ///***************************************************************************
****************************************************************************** /// Last updated for version 5.7.2
* Last Updated for Version: 5.6.0 /// Last updated on 2016-09-26
* Date of the Last Update: 2015-12-09 ///
* /// Q u a n t u m L e a P s
* Q u a n t u m L e a P s /// ---------------------------
* --------------------------- /// innovating embedded systems
* innovating embedded systems ///
* /// Copyright (C) Quantum Leaps, www.state-machine.com.
* Copyright (C) Quantum Leaps, LLC. All rights reserved. ///
* /// This program is open source software: you can redistribute it and/or
* This program is open source software: you can redistribute it and/or /// modify it under the terms of the GNU General Public License as published
* modify it under the terms of the GNU General Public License as published /// by the Free Software Foundation, either version 3 of the License, or
* by the Free Software Foundation, either version 3 of the License, or /// (at your option) any later version.
* (at your option) any later version. ///
* /// Alternatively, this program may be distributed and modified under the
* Alternatively, this program may be distributed and modified under the /// terms of Quantum Leaps commercial licenses, which expressly supersede
* terms of Quantum Leaps commercial licenses, which expressly supersede /// the GNU General Public License and are specifically designed for
* the GNU General Public License and are specifically designed for /// licensees interested in retaining the proprietary status of their code.
* licensees interested in retaining the proprietary status of their code. ///
* /// This program is distributed in the hope that it will be useful,
* This program is distributed in the hope that it will be useful, /// but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of /// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the /// GNU General Public License for more details.
* GNU General Public License for more details. ///
* /// You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License /// along with this program. If not, see <http://www.gnu.org/licenses/>.
* along with this program. If not, see <http://www.gnu.org/licenses/>. ///
* /// Contact information:
* Contact information: /// Web: www.state-machine.com
* http://www.state-machine.com /// Email: info@state-machine.com
* mailto:info@state-machine.com ///***************************************************************************
****************************************************************************** /// @endcond
* @endcond
*/
#ifndef qxk_port_h #ifndef qxk_port_h
#define qxk_port_h #define qxk_port_h
/* determination if the code executes in the ISR context */ // determination if the code executes in the ISR context
#define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != (uint32_t)0) #define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != static_cast<uint32_t>(0))
__attribute__((always_inline)) __attribute__((always_inline))
static inline uint32_t QXK_get_IPSR(void) { static inline uint32_t QXK_get_IPSR(void) {
@ -49,18 +48,23 @@ static inline uint32_t QXK_get_IPSR(void) {
return regIPSR; return regIPSR;
} }
// trigger the PendSV exception to pefrom the context switch
#define QXK_CONTEXT_SWITCH_() \ #define QXK_CONTEXT_SWITCH_() \
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = (uint32_t)(1U << 28)) (*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))
/* QXK interrupt entry and exit */ // QXK ISR entry and exit
#define QXK_ISR_ENTRY() ((void)0) #define QXK_ISR_ENTRY() ((void)0)
#define QXK_ISR_EXIT() do { \ #define QXK_ISR_EXIT() do { \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
QXK_sched_(); \ if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
QXK_CONTEXT_SWITCH_(); \
} \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (0) } while (false)
#include "qxk.h" /* QXK platform-independent public interface */
#endif /* qxk_port_h */ #include "qxk.h" // QXK platform-independent public interface
#endif // qxk_port_h

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
* Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), GNU-ARM assembler * Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), GNU-ARM assembler
* Last Updated for Version: 5.7.0 * Last Updated for Version: 5.7.2
* Date of the Last Update: 2016-07-14 * Date of the Last Update: 2016-09-25
* *
* Q u a n t u m L e a P s * Q u a n t u m L e a P s
* --------------------------- * ---------------------------
@ -37,28 +37,28 @@
/* NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! */ /* NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! */
.equ QF_BASEPRI,(0xFF >> 2) .equ QF_BASEPRI,(0xFF >> 2)
/* NOTE: keep in synch with the QXK struct in "qxk.h" !!! */ /* NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!! */
.equ QXK_CURR,0 .equ QXK_CURR,0
.equ QXK_NEXT,4 .equ QXK_NEXT,4
.equ QXK_TOP_PRIO,8
/* NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! */ /* NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! */
.equ QMACTIVE_OSOBJECT,40 .equ QMACTIVE_THREAD,40
.equ QMACTIVE_THREAD,44 .equ QMACTIVE_PRIO,44
/***************************************************************************** /*****************************************************************************
* The QXK_start_ function starts QXK multitasking. * The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
* The C signature: void QXK_start_(void); * This operation is performed in a nestable critical section.
*
* NOTE: QXK_start_() must be called with interrupts disabled and
* returns with interrupts **enabled**.
*****************************************************************************/ *****************************************************************************/
.section .text.QXK_start_ .section .text.QXK_init
.global QXK_start_ .global QXK_init
.type QXK_start_, %function .type QXK_init, %function
.thumb .thumb
QXK_start_: QXK_init:
MRS r0,PRIMASK /* store the state of the PRIMASK in r0 */
CPSID i /* disable interrupts (set PRIMASK) */
LDR r1,=0xE000ED18 /* System Handler Priority Register */ LDR r1,=0xE000ED18 /* System Handler Priority Register */
LDR r2,[r1,#8] /* load the System 12-15 Priority Register */ LDR r2,[r1,#8] /* load the System 12-15 Priority Register */
MOVS r3,#0xFF MOVS r3,#0xFF
@ -66,205 +66,355 @@ QXK_start_:
ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */ ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */
STR r2,[r1,#8] /* write the System 12-15 Priority Register */ STR r2,[r1,#8] /* write the System 12-15 Priority Register */
/* set the MSP to the top of the C-STACK, which is the first entry MSR PRIMASK,r0 /* restore the original PRIMASK */
* in the vector table. (This recovers any stack used so far by main()). BX lr /* return to the caller */
*/ .size QXK_init, . - QXK_init
LDR r0,=0xE000ED08 /* r0 := address of Vector Table Offset reg */
LDR r0,[r0,#0] /* r0 := contents of VTOR */
LDR r0,[r0] /* r0 := VT[0] (first entry is top of stack) */
MSR MSP,r0 /* main SP := initial top of stack */
/* set the current QXK thread to the next QXK thread */
LDR r1,=QXK_attr_
LDR r2,[r1,#QXK_NEXT] /* r2 := QXK_attr_.next */
STR r2,[r1,#QXK_CURR] /* QXK_attr_.curr := r2 */
/* get the top of stack of the current QXK thread */
LDR r0,[r2,#QMACTIVE_THREAD] /* r0 := QXK_attr_.next->thread (SP) */
/* pop r4-r11 from the PSP */
MOVS r1,r0 /* r1 := top of stack */
ADDS r0,r0,#(4*4) /* point r0 to the 4 high registers r7-r11 */
LDMIA r0!,{r4-r7} /* pop the 4 high registers into low regs */
MOV r8,r4 /* move low registers into high registers */
MOV r9,r5
MOV r10,r6
MOV r11,r7
LDMIA r1!,{r4-r7} /* pop the low registers */
/* NOTE: at this point r0 holds the new top of stack */
MSR PSP,r0 /* set PSP to the thread's SP */
ISB /* flush the instruction pipeline */
/* switch CPU to using the Process Stack Pointer (PSP) */
MRS r0,CONTROL
MOVS r1,#(1 << 1)
ORRS r0,r0,r1 /* set the Active Stack Pointer */
MSR CONTROL,r0 /* switch to PSP */
DSB /* make sure all data access completes */
ISB /* flush the instruction pipeline */
/* fake return from an exception... */
POP {r0-r3} /* pop R0..R3 */
/* NOTE: R0 holds the 'par' arg of thread fun */
POP {r1,r2} /* pop R12 and LR into low registers r1,r2 */
MOV r12,r1
MOV lr,r2
POP {r1,r2} /* pop PC to R1 and xPSR to R2 */
/* NOTE: it's OK to clobber R1 and R2 */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
.else /* M3/M4/M7 */
.ifdef __FPU_PRESENT /* if VFP available... */
/* enable the VFP by enabling CP10 and CP11 coprocessors */
LDR.W r3,=0xE000ED88 /* r3 := &CPACR */
LDR r2,[r3] /* r2 := CPACR */
ORR r2,r2,#(0xF << 20)/* r2 := r2 | (CP10 | CP11) */
STR r2,[r3] /* CPACR = r2 */
DSB /* make sure all data access completes */
ISB /* reset the instruction pipeline */
/* enable Automatic State Preservation and Lazy Stacking in the VFP */
LDR r3,=0xE000EF34 /* r3 := &FPCCR */
LDR r2,[r3] /* r2 := FPCCR */
ORR r2,r2,#(3 << 30) /* set ASPEN | LSPEN */
STR r2,[r3] /* FPCCR &= ~(ASPEN | LSPEN) */
/* clear the VFP Context Active (FPCA) bit in CONTROL */
MRS r2,CONTROL /* r2 := CONTROL (NOTE: it's OK to clobber R2)*/
BICS r2,r2,#(1 << 2) /* r2 := r2 & ~(1 << 2) (FPCA bit) */
MSR CONTROL,r2 /* CONTROL := r2 (clear CONTROL[2] FPCA bit) */
.endif /* VFP available */
MOVS r2,#0 /* NOTE: it's OK to clobber R2 */
MSR BASEPRI,r2 /* enable interrupts (clear BASEPRI) */
.endif /* M3/M4/M7 */
BX r1 /* return to the "interrupted" thread (PC) */
.size QXK_start_, . - QXK_start_
/***************************************************************************** /*****************************************************************************
* The PendSV_Handler exception handler is used for context switching in QXK. * The PendSV_Handler exception handler is used for handling context switch
* The use of the PendSV exception is the recommended and most efficient * and asynchronous preemption in QXK. The use of the PendSV exception is
* method for performing context switches with ARM Cortex-M. * the recommended and most efficient method for performing context switches
* with ARM Cortex-M.
* *
* The PendSV exception should have the lowest priority in the whole system * The PendSV exception should have the lowest priority in the whole system
* (0xFF, see QXK_start_). All other exceptions and interrupts should have * (0xFF, see QXK_init). All other exceptions and interrupts should have higher
* higher priority. For example, for NVIC with 2 priority bits all interrupts * priority. For example, for NVIC with 2 priority bits all interrupts and
* and exceptions must have numerical value of priority lower than 0xC0. * exceptions must have numerical value of priority lower than 0xC0. In this
* In this case the interrupt priority levels available to your applications * case the interrupt priority levels available to your applications are (in
* are (in the order from the lowest to the highest urgency): 0x80, 0x40, 0x00. * the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00.
* *
* Also, *all* "kernel-aware" ISRs in the QXK application must call the * Also, *all* "kernel aware" ISRs in the QXK application must call the
* QXK_ISR_EXIT() macro to trigger the PendSV exception. * QXK_ISR_EXIT() macro, which triggers PendSV when it detects a need for
* a context switch or asynchronous preemption.
* *
* The C signature (CMSIS): void PendSV_Handler(void);
*
* NOTE:
* Due to tail-chaining and its lowest priority, the PendSV exception will be * Due to tail-chaining and its lowest priority, the PendSV exception will be
* entered immediately after the exit from the *last* nested interrupt (or * entered immediately after the exit from the *last* nested interrupt (or
* exception). In QXK, this is exactly the time when the QXK context switch * exception). In QXK, this is exactly the time when the QXK scheduler needs to
* must occur. * check for the asynchronous preemption.
*****************************************************************************/ *****************************************************************************/
.section .text.PendSV_Handler .section .text.PendSV_Handler
.global PendSV_Handler /* CMSIS-compliant exception name */ .global PendSV_Handler /* CMSIS-compliant exception name */
.type PendSV_Handler, %function .type PendSV_Handler, %function
PendSV_Handler: PendSV_Handler:
/* Prepare some constants (an address and a bitmask) before entering
MRS r0,PSP /* r0 := Process Stack Pointer */ * a critical section...
LDR r2,=0xE000ED04 /* Interrupt Control and State Register */ */
LDR r3,=QXK_attr_ LDR r3,=QXK_attr_
LDR r2,=0xE000ED04 /* Interrupt Control and State Register */
MOVS r1,#1
LSLS r1,r1,#27 /* r0 := (1 << 27) (UNPENDSVSET bit) */
/*<<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<*/
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSID i /* disable interrupts (set PRIMASK) */
.else /* M3/M4/M7 */
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 /* selectively disable interrupts */
.endif /* M3/M4/M7 */
/* The PendSV exception handler can be preempted by an interrupt,
* which might pend PendSV exception again. The following write to
* ICSR[27] un-pends any such spurious instance of PendSV.
*/
STR r1,[r2] /* ICSR[27] := 1 (unpend PendSV) */
/* Check QXK_attr_.next, which contains the pointer to the next thread
* to run, which is set in QXK_ISR_EXIT(). This pointer must not be NULL.
*/
LDR r0,[r3,#QXK_NEXT] /* r1 := QXK_attr_.next */
CMP r0,#0 /* is (QXK_attr_.next == 0)? */
BEQ PendSV_error /* branch if (QXK_attr_.next == 0) */
/* Load pointers into registers... */
MOV r12,r0 /* save QXK_attr_.next in r12 */
LDR r2,[r0,#QMACTIVE_THREAD] /* r2 := QXK_attr_.next->thread */
LDR r1,[r3,#QXK_CURR] /* r1 := QXK_attr_.curr */
CMP r1,#0 /* (QXK_attr_.curr != 0)? */
BNE PendSV_save_ex /* branch if (current thread is extended) */
CMP r2,#0 /* (QXK_attr_.next->thread != 0)? */
BNE PendSV_save_ao /* branch if (next tread is extended) */
PendSV_activate:
.ifdef __FPU_PRESENT /* if VFP available... */
PUSH {r0,lr} /* ...push lr (EXC_RETURN) plus stack-aligner */
.endif /* VFP available */
/* The QXK activator must be called in a thread context, while this code
* executes in the handler contex of the PendSV exception. The switch
* to the Thread mode is accomplished by returning from PendSV using
* a fabricated exception stack frame, where the return address is the
* QXK activator QXK_activate_().
*
* NOTE: the QXK activator is called with interrupts DISABLED and also
* it returns with interrupts DISABLED.
*/
MOVS r3,#1
LSLS r3,r3,#24 /* r3:=(1 << 24), set the T bit (new xpsr) */
LDR r2,=QXK_activate_ /* address of the QXK activator (new pc) */
LDR r1,=Thread_ret /* return address after the call (new lr) */
SUB sp,sp,#8*4 /* reserve space for exception stack frame */
ADD r0,sp,#5*4 /* r0 := 5 registers below the top of stack */
STM r0!,{r1-r3} /* save xpsr,pc,lr */
MOVS r0,#6
MVNS r0,r0 /* r0 := ~6 == 0xFFFFFFF9 */
BX r0 /* exception-return to the QXK activator */
/*----------------------------------------------------------------------*/
PendSV_error:
LDR r3,=assert_failed
BX r3 /* long-branch to the assertion-handler */
/*========================================================================
* Saving AO-thread before crossing to eXtended-thread
* expected register contents:
* r0 -> QXK_attr_.next
* r1 -> QXK_attr_.curr
* r2 -> QXK_attr_.next->thread (SP)
* r3 -> &QXK_attr_
* r12 -> QXK_attr_.next
*/
PendSV_save_ao:
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
PUSH {r4-r7} /* save the low registers */
MOV r4,r8 /* move the high registers to low registers...*/
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} /* save the high registers */
.else /* M3/M4/M7 */
PUSH {r4-r11} /* save r4-r11 on top of the exception frame */
.ifdef __FPU_PRESENT /* if VFP available... */
TST lr,#(1 << 4) /* is it return with the VFP exception frame? */
IT EQ /* if lr[4] is zero... */
VSTMDBEQ sp!,{s16-s31} /* ... save VFP registers s16..s31 */
PUSH {r0,lr} /* save the "aligner" and the EXC_RETURN */
.endif /* VFP available */
.endif /* M3/M4/M7 */
CMP r2,#0
BNE PendSV_restore_ex /* branch if (QXK_attr_.next->thread != 0) */
/* otherwise continue to restoring next AO-thread... */
/*------------------------------------------------------------------------
* Restoring AO-thread after crossing from eXtended-thread
* expected register contents:
* r1 -> QXK_attr_.curr
* r2 -> QXK_attr_.next->thread (SP)
* r3 -> &QXK_attr_
* r12 -> QXK_attr_.next
*/
PendSV_restore_ao:
MOVS r0,#0
STR r0,[r3,#QXK_CURR] /* QXK_attr_.curr := 0 (QXK_attr_.next) */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
MOV r0,sp /* r0 := top of stack */
MOV r1,r0
ADDS r1,r1,#(4*4) /* point r0 to the 4 high registers r7-r11 */
LDMIA r1!,{r4-r7} /* pop the 4 high registers into low registers*/
MOV r8,r4 /* move low registers into high registers */
MOV r9,r5
MOV r10,r6
MOV r11,r7
LDMIA r0!,{r4-r7} /* pop the low registers */
ADD sp,sp,#(8*4) /* remove 8 registers from the stack */
MOVS r1,#6
MVNS r1,r1 /* r2 := ~6 == 0xFFFFFFF9 */
MOV lr,r1 /* make sure MSP is used */
.else /* M3/M4/M7 */
.ifdef __FPU_PRESENT /* if VFP available... */
POP {r0,lr} /* restore alighner and EXC_RETURN into lr */
TST lr,#(1 << 4) /* is it return to the VFP exception frame? */
IT EQ /* if EXC_RETURN[4] is zero... */
VLDMIAEQ sp!,{s16-s31} /* ... restore VFP registers s16..s31 */
.else
BIC lr,lr,#(1 << 2) /* make sure MSP is used */
.endif /* VFP available */
POP {r4-r11} /* restore r4-r11 from the next thread's stack*/
.endif /* M3/M4/M7 */
MOV r0,r12 /* r0 := QXK_attr_.next */
LDR r0,[r0,#QMACTIVE_PRIO] /* r0 := QXK_attr_.next->prio */
LDR r1,[r3,#QXK_TOP_PRIO] /* r1 := QXK_attr_.topPrio */
CMP r1,r0
BCC PendSV_activate /* if (next->prio > topPrio) activate next AO */
/* otherwise re-enable interrupts and return to the preempted AO-thread */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
.else /* M3/M4/M7 */
MOVS r0,#0
MSR BASEPRI,r0 /* enable interrupts (clear BASEPRI) */
.endif /* M3/M4/M7 */
/*>>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>*/
BX lr /* return to the preempted AO-thread */
/*------------------------------------------------------------------------
* Saving extended-thread before crossing to AO-thread
* expected register contents:
* r0 -> QXK_attr_.next
* r1 -> QXK_attr_.curr
* r2 -> QXK_attr_.next->thread (SP)
* r3 -> &QXK_attr_
* r12 -> QXK_attr_.next
*/
PendSV_save_ex:
MRS r0,PSP /* r0 := Process Stack Pointer */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */ .if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
SUBS r0,r0,#(8*4) /* make room for 8 registers r4-r11 */ SUBS r0,r0,#(8*4) /* make room for 8 registers r4-r11 */
MOVS r1,r0 /* r1 := temporary PSP (do not clobber r0!) */ MOVS r1,r0 /* r1 := temporary PSP (do not clobber r0!) */
STMIA r1!,{r4-r7} /* save the low registers */ STMIA r1!,{r4-r7} /* save the low registers */
MOV r4,r8 /* move the high registers to low registers */ MOV r4,r8 /* move the high registers to low registers...*/
MOV r5,r9 MOV r5,r9
MOV r6,r10 MOV r6,r10
MOV r7,r11 MOV r7,r11
STMIA r1!,{r4-r7} /* save the high registers */ STMIA r1!,{r4-r7} /* save the high registers */
/* NOTE: at this point r0 still holds the top of stack */ /* NOTE: at this point r0 holds the top of stack */
CPSID i /* disable interrupts (set PRIMASK) */
LDR r1,[r3,#QXK_CURR] /* r1 := QXK_attr_.curr (restore value) */
.else /* M3/M4/M7 */ .else /* M3/M4/M7 */
STMDB r0!,{r4-r11} /* save r4-r11 on top of the exception frame */ STMDB r0!,{r4-r11} /* save r4-r11 on top of the exception frame */
.ifdef __FPU_PRESENT /* if VFP available... */ .ifdef __FPU_PRESENT /* if VFP available... */
TST lr,#(1 << 4) /* is it return with the VFP exception frame? */ TST lr,#(1 << 4) /* is it return with the VFP exception frame? */
IT EQ /* if lr[4] is zero... */ IT EQ /* if lr[4] is zero... */
VSTMDBEQ r0!,{s16-s31} /* ... save VFP registers s16..s31 */ VSTMDBEQ r0!,{s16-s31} /* ... save VFP registers s16..s31 */
STMDB r0!,{r1,lr} /* save the "aligner" and the EXC_RETURN */
.endif /* VFP available */ .endif /* VFP available */
MOVS r1,#QF_BASEPRI
MSR BASEPRI,r1 /* selectively disable interrupts */
.endif /* M3/M4/M7 */ .endif /* M3/M4/M7 */
/* NOTE: This PendSV exception handler can be preempted by an /* store the SP of the current extended-thread */
* interrupt, which might pend PendSV exception again. This STR r0,[r1,#QMACTIVE_THREAD] /* QXK_attr_.curr->thread := r0 */
* would be a problem, because the QK scheduler would run again MOV r0,r12 /* QXK_attr_.next (restore value) */
* after this PendSV instance, so the same AO would be scheduled
* twice. The following write to ICSR[7] un-pends any such spurious CMP r2,#0
* instance of PendSV. BEQ PendSV_restore_ao /* branch if (QXK_attr_.next->thread == 0) */
/* otherwise continue to restoring next extended-thread... */
/*------------------------------------------------------------------------
* Restoring extended-thread after crossing from AO-thread
* expected register contents:
* r0 -> QXK_attr_.next
* r1 -> QXK_attr_.curr
* r2 -> QXK_attr_.next->thread (SP)
* r3 -> &QXK_attr_
* r12 -> QXK_attr_.next
*/ */
MOVS r1,#1 PendSV_restore_ex:
LSLS r1,r1,#27 /* r1 := (1 << 27) (UNPENDSVSET bit) */ STR r0,[r3,#QXK_CURR] /* QXK_attr_.curr := r0 (QXK_attr_.next) */
STR r1,[r2] /* ICSR[27] := 1 (unpend PendSV) */
/* store the SP of the current QXK thread... */
LDR r1,=QXK_attr_
LDR r2,[r3,#QXK_CURR]
STR r0,[r2,#QMACTIVE_THREAD] /* QXK_attr_.curr->thread := r0 */
.ifdef __FPU_PRESENT /* if VFP available... */
/* store the LR of the current QXK thread... */
STR lr,[r2,#QMACTIVE_OSOBJECT] /* QXK_attr_.curr->osObject := lr */
.endif /* VFP available */
/* set current to the next... */
LDR r2,[r3,#QXK_NEXT] /* r2 := QXK_attr_.next */
STR r2,[r3,#QXK_CURR] /* QXK_attr_.curr := r2 */
/* restore the SP of the next thread... */
LDR r0,[r2,#QMACTIVE_THREAD] /* r0 := QXK_attr_.next->thread (SP) */
.ifdef __FPU_PRESENT /* if VFP available... */
/* restore the LR of the next thread... */
LDR lr,[r2,#QMACTIVE_OSOBJECT] /* lr := QXK_attr_.next->osObject */
.endif /* VFP available */
/* exit the critical section */ /* exit the critical section */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */ .if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */ CPSIE i /* enable interrupts (clear PRIMASK) */
MOVS r1,r0 /* r1 := top of stack */ MOVS r0,r2 /* r2 := top of stack */
ADDS r0,r0,#(4*4) /* point r0 to the 4 high registers r7-r11 */ ADDS r0,r0,#(4*4) /* point r0 to the 4 high registers r7-r11 */
LDMIA r0!,{r4-r7} /* pop the 4 high registers into low regs */ LDMIA r0!,{r4-r7} /* pop the 4 high registers into low registers*/
MOV r8,r4 /* move low registers into high registers */ MOV r8,r4 /* move low registers into high registers */
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} /* pop the low registers */ LDMIA r2!,{r4-r7} /* pop the low registers */
/* NOTE: at this point r0 holds the new top of stack */ MOVS r2,r0 /* r2 := holds the new top of stack */
.else /* M3/M4/M7 */
MOVS r3,#0
MSR BASEPRI,r3 /* enable interrupts (clear BASEPRI) */
MOVS r1,#2
MVNS r1,r1 /* r1 := ~2 == 0xFFFFFFFD */
MOV lr,r1 /* make sure PSP is used */
.else /* M3/M4/M7 */
MOVS r1,#1
MSR BASEPRI,r1 /* enable interrupts (clear BASEPRI) */
.ifdef __FPU_PRESENT /* if VFP available... */ .ifdef __FPU_PRESENT /* if VFP available... */
LDMIA r2!,{r1,lr} /* restore aligner and EXC_RETURN into lr */
TST lr,#(1 << 4) /* is it return to the VFP exception frame? */ TST lr,#(1 << 4) /* is it return to the VFP exception frame? */
IT EQ /* if lr[4] is zero... */ IT EQ /* if lr[4] is zero... */
VLDMIAEQ r0!,{s16-s31} /* ... restore VFP registers s16..s31 */ VLDMIAEQ r2!,{s16-s31} /* ... restore VFP registers s16..s31 */
.else
ORR lr,lr,#(1 << 2) /* make sure PSP is used */
.endif /* VFP available */ .endif /* VFP available */
LDMIA r2!,{r4-r11} /* restore r4-r11 from the next thread's stack*/
LDMIA r0!,{r4-r11} /* restore r4-r11 from next thread's stack */
.endif /* M3/M4/M7 */ .endif /* M3/M4/M7 */
/* set the PSP to the next thread's SP */ /* set the PSP to the next thread's SP */
MSR PSP,r0 /* Process Stack Pointer := r0 */ MSR PSP,r2 /* Process Stack Pointer := r2 */
BX lr /* return to the next thread */ BX lr /* return to the next extended-thread */
.size PendSV_Handler, . - PendSV_Handler .size PendSV_Handler, . - PendSV_Handler
/*****************************************************************************
* Thread_ret is a helper function executed when the QXK activator returns.
*
* NOTE: Thread_ret does not execute in the PendSV context!
* NOTE: Thread_ret executes entirely with interrupts DISABLED.
*****************************************************************************/
.section .text.Thread_ret
.type Thread_ret, %function
Thread_ret:
/* After the QXK activator returns, we need to resume the preempted
* thread. However, this must be accomplished by a return-from-exception,
* while we are still in the thread context. The switch to the exception
* contex is accomplished by triggering the NMI exception.
*/
/* before triggering the NMI exception, make sure that the
* VFP stack frame will NOT be used...
*/
.ifdef __FPU_PRESENT /* if VFP available... */
MRS r0,CONTROL /* r0 := CONTROL */
BICS r0,r0,#4 /* r0 := r0 & ~4 (FPCA bit) */
MSR CONTROL,r0 /* CONTROL := r0 (clear CONTROL[2] FPCA bit) */
.endif /* VFP available */
/* trigger NMI to return to preempted task...
* NOTE: The NMI exception is triggered with nterrupts DISABLED
*/
LDR r0,=0xE000ED04 /* Interrupt Control and State Register */
MOVS r1,#1
LSLS r1,r1,#31 /* r1 := (1 << 31) (NMI bit) */
STR r1,[r0] /* ICSR[31] := 1 (pend NMI) */
B . /* wait for preemption by NMI */
.size Thread_ret, . - Thread_ret
/*****************************************************************************
* The NMI_Handler exception handler is used for returning back to the
* interrupted task. The NMI exception simply removes its own interrupt
* stack frame from the stack and returns to the preempted task using the
* interrupt stack frame that must be at the top of the stack.
*
* NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
* to re-enable interrupts before it returns to the preempted task.
*****************************************************************************/
.section .text.NMI_Handler
.global NMI_Handler
.type NMI_Handler, %function
.thumb
NMI_Handler:
ADD sp,sp,#(8*4) /* remove one 8-register exception frame */
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
CPSIE i /* enable interrupts (clear PRIMASK) */
BX lr /* return to the preempted task */
.else /* M3/M4/M7 */
MOVS r0,#0
MSR BASEPRI,r0 /* enable interrupts (clear BASEPRI) */
.ifdef __FPU_PRESENT /* if VFP available... */
POP {r0,pc} /* pop stack "aligner" and EXC_RETURN to PC */
.else /* no VFP */
BX lr /* return to the preempted task */
.endif /* VFP available */
.endif /* M3/M4/M7 */
.size NMI_Handler, . - NMI_Handler
/***************************************************************************** /*****************************************************************************
* Initialize the private stack of a QXK thread. * Initialize the private stack of a QXK thread.
* *
@ -293,92 +443,100 @@ QXK_stackInit_:
* r3 - size of stack [bytes] * r3 - size of stack [bytes]
*/ */
MOV r12,r0 /* temporarily save r0 in r12 (act) */
STR r1,[r0,#QMACTIVE_THREAD] /* temporarily save the thread routine */
ADDS r3,r2,r3 /* r3 := end of stack (top of stack) */ ADDS r3,r2,r3 /* r3 := end of stack (top of stack) */
MOV r12,r0 /* save r0 in r12 */
/* round up the beginning of stack to the 8-byte boundary */ /* round up the beginning of stack to the 8-byte boundary
/* r2 := (((r2 -1) >> 3) + 1) << 3 */ * r2 := (((r2 -1) >> 3) + 1) << 3;
*/
SUBS r0,r2,#1 SUBS r0,r2,#1
LSRS r0,r0,#3 LSRS r0,r0,#3
ADDS r0,r0,#1 ADDS r0,r0,#1
LSLS r2,r0,#3 LSLS r2,r0,#3
/* round down the end of stack to the 8-byte boundary */ /* round down the end of stack to the 8-byte boundary
/* r3 := (r3 >> 3) << 3 */ * r3 := (r3 >> 3) << 3;
*/
LSRS r0,r3,#3 LSRS r0,r3,#3
LSLS r3,r0,#3 LSLS r3,r0,#3
/* make room for the thread's stack frame... */
SUBS r3,r3,#16*4 /* r3 := top of the 16-register stack frame */ SUBS r3,r3,#16*4 /* r3 := top of the 16-register stack frame */
.ifdef __FPU_PRESENT /* if VFP available... */
SUBS r3,r3,#2*4 /* r3 := top of the 18-register stack frame */
.endif /* VFP available */
MOV r0,r12 /* pre-fill the unused part of the stack with 0xDEADBEEF................*/
STR r1,[r0,#QMACTIVE_OSOBJECT] /* temporarily save thread routine */
/* pre-fill the stack with 0xDEADBEEF */
LDR r0,=0xDEADBEEF LDR r0,=0xDEADBEEF
MOV r1,r0 MOV r1,r0
QXK_stackInit_fill: QXK_stackInit_fill:
STMIA r2!,{r0,r1} STMIA r2!,{r0,r1}
CMP r2,r3 CMP r2,r3
BLT.N QXK_stackInit_fill BLT.N QXK_stackInit_fill
/* prepare the standard exception (without VFP) stack frame... */ /* prepare the standard exception (without VFP) stack frame.............*/
MOV r0,r12 /* restore r0 from r12 (act) */
MOV r0,r12 /* restore r0 from r12 */ LDR r1,[r0,#QMACTIVE_THREAD] /* restore the thread routine */
MOVS r2,#0x04
STR r2,[r3,#0*4] /* r4 */
MOVS r2,#0x05
STR r2,[r3,#1*4] /* r5 */
MOVS r2,#0x06
STR r2,[r3,#2*4] /* r6 */
MOVS r2,#0x07
STR r2,[r3,#3*4] /* r7 */
MOVS r2,#0x08
STR r2,[r3,#4*4] /* r8 */
MOVS r2,#0x09
STR r2,[r3,#5*4] /* r9 */
MOVS r2,#0x0A
STR r2,[r3,#6*4] /* r10 */
MOVS r2,#0x0B
STR r2,[r3,#7*4] /* r11 */
STR r0,[r3,#8*4] /* r0 (argument 'act' to thread routine) */
MOVS r2,#0x01
STR r2,[r3,#9*4] /* r1 */
MOVS r2,#0x02
STR r2,[r3,#10*4] /* r2 */
MOVS r2,#0x03
STR r2,[r3,#11*4] /* r3 */
MOVS r2,#0x0C
STR r2,[r3,#12*4] /* r12 */
LDR r2,=QXK_threadRet_
STR r2,[r3,#13*4] /* LR (return address) */
LDR r1,[r0,#QMACTIVE_OSOBJECT] /* r1 := saved thread routine */
STR r1,[r3,#14*4] /* PC (entry point, thread routine) */
MOVS r2,#1
LSLS r2,r2,#24 /* r2 := 0x01000000 */
STR r2,[r3,#15*4] /* xPSR */
STR r3,[r0,#QMACTIVE_THREAD] /* act->thread := top of stack */ STR r3,[r0,#QMACTIVE_THREAD] /* act->thread := top of stack */
.ifdef __FPU_PRESENT /* if VFP available... */
MOVS r2,#0
STMIA r3!,{r2} /* stack "aligner" */
/* synthesize EXC_RETURN for return to Thread mode with no FPU-state */
MOVS r2,#2 MOVS r2,#2
MVNS r2,r2 /* r2 := ~2 == 0xFFFFFFFD */ MVNS r2,r2 /* r2 := ~2 == 0xFFFFFFFD */
STR r2,[r0,#QMACTIVE_OSOBJECT] /* act->thread.osObject := lr */ STMIA r3!,{r2} /* save EXC_RETURN */
.endif /* VFP available */
MOVS r2,#0x04
STMIA r3!,{r2} /* r4 */
MOVS r2,#0x05
STMIA r3!,{r2} /* r5 */
MOVS r2,#0x06
STMIA r3!,{r2} /* r6 */
MOVS r2,#0x07
STMIA r3!,{r2} /* r7 */
MOVS r2,#0x08
STMIA r3!,{r2} /* r8 */
MOVS r2,#0x09
STMIA r3!,{r2} /* r9 */
MOVS r2,#0x0A
STMIA r3!,{r2} /* r10 */
MOVS r2,#0x0B
STMIA r3!,{r2} /* r11 */
STMIA r3!,{r0} /* r0 (argument to thread routine, me pointer)*/
MOVS r2,#0x01
STMIA r3!,{r2} /* r1 */
MOVS r2,#0x02
STMIA r3!,{r2} /* r2 */
MOVS r2,#0x03
STMIA r3!,{r2} /* r3 */
MOVS r2,#0x0C
STMIA r3!,{r2} /* r12 */
LDR r2,=QXK_threadRet_
STMIA r3!,{r2} /* LR (return address) */
STMIA r3!,{r1} /* PC (entry point, thread routine) */
MOVS r2,#1
LSLS r2,r2,#24 /* r2 := 0x01000000 */
STMIA r3!,{r2} /* xPSR */
BX lr /* return to the caller */ BX lr /* return to the caller */
.size QXK_stackInit_, . - QXK_stackInit_ .size QXK_stackInit_, . - QXK_stackInit_

View File

@ -2,8 +2,8 @@
/// @brief QF/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset /// @brief QF/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-28 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -53,19 +53,19 @@
// QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2 // QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2
#define QF_AWARE_ISR_CMSIS_PRI 0 #define QF_AWARE_ISR_CMSIS_PRI 0
#else // Cortex-M3/M4/M4F, see NOTE03 #else // Cortex-M3/M4/M7, see NOTE03
#define QF_INT_DISABLE() __set_BASEPRI(QF_BASEPRI) #define QF_INT_DISABLE() __set_BASEPRI(QF_BASEPRI)
#define QF_INT_ENABLE() __set_BASEPRI(0U) #define QF_INT_ENABLE() __set_BASEPRI(0U)
// NOTE: keep in synch with the value defined in "qk_port.s", see NOTE4 // NOTE: keep in synch with the value defined in "qxk_port.s", see NOTE4
#define QF_BASEPRI (0xFFU >> 2) #define QF_BASEPRI (0xFFU >> 2)
// QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE5 // QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE5
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS)) #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
// Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 // Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - __CLZ(n_))) #define QF_LOG2(n_) ((uint_fast8_t)(32U - __CLZ(n_)))
#endif #endif
// QF critical section entry/exit... // QF critical section entry/exit...
@ -78,7 +78,7 @@
#include "qep_port.h" // QEP port #include "qep_port.h" // QEP port
#include "qxk_port.h" // QXK port #include "qxk_port.h" // QXK port
#include "qf.h" // QF platform-independent public interface #include "qf.h" // QF platform-independent public interface
#include "qxthread.h" // QXK naked thread #include "qxthread.h" // QXK extended thread interface
//**************************************************************************** //****************************************************************************
// NOTE1: // NOTE1:
@ -92,7 +92,7 @@
// "kernel-aware". // "kernel-aware".
// //
// NOTE3: // NOTE3:
// On Cortex-M3/M4/M4F, the interrupt disable/enable policy uses the BASEPRI // On Cortex-M3/M4/M7, the interrupt disable/enable policy uses the BASEPRI
// register (which is not implemented in Cortex-M0/M0+/M1) to disable // register (which is not implemented in Cortex-M0/M0+/M1) to disable
// interrupts only with priority lower than the level specified by the // interrupts only with priority lower than the level specified by the
// QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e., // QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e.,
@ -103,7 +103,7 @@
// higher than QF_BASEPRI, can call QF services. // higher than QF_BASEPRI, can call QF services.
// //
// NOTE4: // NOTE4:
// For Cortex-M3/M4/M4F, the macro QF_BASEPRI leaves the top 2 priority bits // For Cortex-M3/M4/M7, the macro QF_BASEPRI leaves the top 2 priority bits
// empty for QF-aware interrupts. This is the highest-possible priority // empty for QF-aware interrupts. This is the highest-possible priority
// (lowest possible numerical value) for the guaranteed 3 priority bits // (lowest possible numerical value) for the guaranteed 3 priority bits
// implemented in the NVIC. // implemented in the NVIC.

View File

@ -2,8 +2,8 @@
/// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset /// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-28 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -46,12 +46,14 @@
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \ (*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28)) static_cast<uint32_t>(1U << 28))
// QXK interrupt entry and exit // QXK ISR entry and exit
#define QXK_ISR_ENTRY() ((void)0) #define QXK_ISR_ENTRY() ((void)0)
#define QXK_ISR_EXIT() do { \ #define QXK_ISR_EXIT() do { \
QF_INT_DISABLE(); \ QF_INT_DISABLE(); \
QXK_sched_(); \ if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
QXK_CONTEXT_SWITCH_(); \
} \
QF_INT_ENABLE(); \ QF_INT_ENABLE(); \
} while (false) } while (false)

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), IAR ARM assembler ; Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR-ARM assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-07-14 ; Date of the Last Update: 2016-09-25
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -32,34 +32,38 @@
; mailto:info@state-machine.com ; mailto:info@state-machine.com
;***************************************************************************** ;*****************************************************************************
PUBLIC QXK_start_ ; start the QXK multitasking PUBLIC QXK_init ; initialze the QXK kernel
PUBLIC QXK_stackInit_ ; initialize the stack of each thread PUBLIC QXK_stackInit_ ; initialize the stack of an extended thread
PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception name
PUBLIC NMI_Handler ; CMSIS-compliant NMI exception name
EXTERN QXK_attr_ ; QXK attribute structure EXTERN QXK_attr_ ; QXK attribute structure
EXTERN QXK_activate_ ; external reference
EXTERN QXK_threadRet_ ; return from a thread function EXTERN QXK_threadRet_ ; return from a thread function
EXTERN assert_failed ; assert-failure handler
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI EQU (0xFF >> 2) QF_BASEPRI EQU (0xFF >> 2)
; NOTE: keep in synch with the QXK struct in "qxk.h" !!! ; NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!!
QXK_CURR EQU 0 QXK_CURR EQU 0
QXK_NEXT EQU 4 QXK_NEXT EQU 4
QXK_TOP_PRIO EQU 8
; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! ; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!!
QMACTIVE_OSOBJECT EQU 40 QMACTIVE_THREAD EQU 40
QMACTIVE_THREAD EQU 44 QMACTIVE_PRIO EQU 44
RSEG CODE:CODE:NOROOT(2) RSEG CODE:CODE:NOROOT(2)
;***************************************************************************** ;*****************************************************************************
; The QXK_start_ function starts QXK multitasking. ; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
; The C signature: void QXK_start_(void); ; This operation is performed in a nestable critical section.
;
; NOTE: QXK_start_() must be called with interrupts disabled and
; returns with interrupts **enabled**.
;***************************************************************************** ;*****************************************************************************
QXK_start_: QXK_init:
MRS r0,PRIMASK ; store the state of the PRIMASK in r0
CPSID i ; disable interrupts (set PRIMASK)
LDR r1,=0xE000ED18 ; System Handler Priority Register LDR r1,=0xE000ED18 ; System Handler Priority Register
LDR r2,[r1,#8] ; load the System 12-15 Priority Register LDR r2,[r1,#8] ; load the System 12-15 Priority Register
MOVS r3,#0xFF MOVS r3,#0xFF
@ -67,112 +71,195 @@ QXK_start_:
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; set the MSP to the top of the C-STACK, which is the first entry MSR PRIMASK,r0 ; restore the original PRIMASK
; in the vector table. (This recovers any stack used so far by main().) BX lr ; return to the caller
LDR r0,=0xE000ED08 ; r0 := address of Vector Table Offset register
LDR r0,[r0,#0] ; r0 := contents of VTOR
LDR r0,[r0] ; r0 := VT[0] (first entry is the top of stack)
MSR MSP,r0 ; main SP := initial top of stack
; set the current QXK thread to the next QXK thread
LDR r1,=QXK_attr_
LDR r2,[r1,#QXK_NEXT] ; r2 := QXK_attr_.next
STR r2,[r1,#QXK_CURR] ; QXK_attr_.curr := r2
; get the top of stack of the current QXK thread ;*****************************************************************************
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP) ; The PendSV_Handler exception handler is used for handling context switch
; pop r4-r11 from the PSP ; and asynchronous preemption in QXK. The use of the PendSV exception is
MOVS r1,r0 ; r1 := top of stack ; the recommended and most efficient method for performing context switches
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 ; with ARM Cortex-M.
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers ;
; The PendSV exception should have the lowest priority in the whole system
; (0xFF, see QXK_init). All other exceptions and interrupts should have higher
; priority. For example, for NVIC with 2 priority bits all interrupts and
; exceptions must have numerical value of priority lower than 0xC0. In this
; case the interrupt priority levels available to your applications are (in
; the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro, which triggers PendSV when it detects a need for
; a context switch or asynchronous preemption.
;
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK scheduler needs to
; check for the asynchronous preemption.
;*****************************************************************************
PendSV_Handler:
; Prepare some constants (an address and a bitmask) before entering
; a critical section...
LDR r3,=QXK_attr_
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSID i ; disable interrupts (set PRIMASK)
#else ; M3/M4/M7
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts
#endif ; M3/M4/M7
; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; Check QXK_attr_.next, which contains the pointer to the next thread
; to run, which is set in QXK_ISR_EXIT(). This pointer must not be NULL.
LDR r0,[r3,#QXK_NEXT] ; r1 := QXK_attr_.next
CMP r0,#0 ; is (QXK_attr_.next == 0)?
BEQ PendSV_error ; branch if (QXK_attr_.next == 0)
; Load pointers into registers...
MOV r12,r0 ; save QXK_attr_.next in r12
LDR r2,[r0,#QMACTIVE_THREAD] ; r2 := QXK_attr_.next->thread
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr
CMP r1,#0 ; (QXK_attr_.curr != 0)?
BNE PendSV_save_ex ; branch if (current thread is extended)
CMP r2,#0 ; (QXK_attr_.next->thread != 0)?
BNE PendSV_save_ao ; branch if (next tread is extended)
PendSV_activate:
#ifdef __ARMVFP__ ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
#endif ; VFP available
; The QXK activator must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the Thread mode is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QXK activator QXK_activate_().
;
; NOTE: the QXK activator is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,=QXK_activate_ ; address of the QXK activator (new pc)
LDR r1,=Thread_ret ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QXK activator
;-------------------------------------------------------------------------
PendSV_error:
LDR r3,=assert_failed
BX r3 ; long-branch to the assertion-handler
;=========================================================================
; Saving AO-thread before crossing to eXtended-thread
; expected register contents:
; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_save_ao:
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
PUSH {r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
#else ; M3/M4/M7
PUSH {r4-r11} ; save r4-r11 on top of the exception frame
#ifdef __ARMVFP__ ; if VFP available...
TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero...
VSTMDBEQ sp!,{s16-s31} ; ... save VFP registers s16..s31
PUSH {r0,lr} ; save the "aligner" and the EXC_RETURN value
#endif ; VFP available
#endif ; M3/M4/M7
CMP r2,#0
BNE PendSV_restore_ex ; branch if (QXK_attr_.next->thread != 0)
; otherwise continue to restoring next AO-thread...
;-------------------------------------------------------------------------
; Restoring AO-thread after crossing from eXtended-thread
; expected register contents:
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_restore_ao:
MOVS r0,#0
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := 0 (QXK_attr_.next)
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r0!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack ADD sp,sp,#(8*4) ; remove 8 registers from the stack
MSR PSP,r0 ; set PSP to the thread's SP MOVS r1,#6
ISB ; flush the instruction pipeline MVNS r1,r1 ; r2 := ~6 == 0xFFFFFFF9
MOV lr,r1 ; make sure MSP is used
#else ; M3/M4/M7
#ifdef __ARMVFP__ ; if VFP available...
POP {r0,lr} ; restore alighner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if EXC_RETURN[4] is zero...
VLDMIAEQ sp!,{s16-s31} ; ... restore VFP registers s16..s31
#else
BIC lr,lr,#(1 << 2) ; make sure MSP is used
#endif ; VFP available
POP {r4-r11} ; restore r4-r11 from the next thread's stack
#endif ; M3/M4/M7
; switch CPU to using the Process Stack Pointer (PSP) MOV r0,r12 ; r0 := QXK_attr_.next
MRS r0,CONTROL LDR r0,[r0,#QMACTIVE_PRIO] ; r0 := QXK_attr_.next->prio
MOVS r1,#(1 << 1) LDR r1,[r3,#QXK_TOP_PRIO] ; r1 := QXK_attr_.topPrio
ORRS r0,r0,r1 ; set the Active Stack Pointer CMP r1,r0
MSR CONTROL,r0 ; switch to PSP BCC PendSV_activate ; if (next->prio > topPrio) activate the next AO
DSB ; make sure all data access completes
ISB ; flush the instruction pipeline
; fake return from an exception... ; otherwise re-enable interrupts and return to the preempted AO-thread
POP {r0-r3} ; pop R0..R3
; NOTE: R0 holds the 'par' argument of the
; thread function
POP {r1,r2} ; pop R12 and LR into low registers r1,r2
MOV r12,r1
MOV lr,r2
POP {r1,r2} ; pop PC to R1 and xPSR to R2
; NOTE: it's OK to clobber R1 and R2
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ? #if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
#else ; M3/M4/M7 #else ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
#endif ; M3/M4/M7
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
BX lr ; return to the preempted AO-thread
#ifdef __ARMVFP__ ; if VFP available... ;-------------------------------------------------------------------------
; enable VFP by enabling CP10 and CP11 coprocessors ; Saving extended-thread before crossing to AO-thread
LDR.W r3,=0xE000ED88 ; r3 := &CPACR ; expected register contents:
LDR r2,[r3] ; r2 := CPACR ; r0 -> QXK_attr_.next
ORR r2,r2,#(0xF << 20); r2 := r2 | (CP10 | CP11) ; r1 -> QXK_attr_.curr
STR r2,[r3] ; CPACR = r2 ; r2 -> QXK_attr_.next->thread (SP)
DSB ; make sure all data access completes ; r3 -> &QXK_attr_
ISB ; reset the instruction pipeline ; r12 -> QXK_attr_.next
PendSV_save_ex:
; enable Automatic State Preservation and Lazy Stacking in the VFP
LDR r3,=0xE000EF34 ; r3 := &FPCCR
LDR r2,[r3] ; r2 := FPCCR
ORR r2,r2,#(3 << 30) ; set ASPEN | LSPEN
STR r2,[r3] ; FPCCR &= ~(ASPEN | LSPEN)
; clear the VFP Context Active (FPCA) bit in CONTROL
MRS r2,CONTROL ; r2 := CONTROL (NOTE: it's OK to clobber R2)
BICS r2,r2,#(1 << 2) ; r2 := r2 & ~(1 << 2) (FPCA bit)
MSR CONTROL,r2 ; CONTROL := r2 (clear CONTROL[2] FPCA bit)
#endif ; VFP available
MOVS r2,#0 ; NOTE: it's OK to clobber R2
MSR BASEPRI,r2 ; enable interrupts (clear BASEPRI)
#endif ; M3/M4/M7
BX r1 ; return to the "interrupted" thread (PC)
;*****************************************************************************
; The PendSV_Handler exception handler is used for context switching in QXK.
; The use of the PendSV exception is the recommended and most efficient
; method for performing context switches with ARM Cortex-M.
;
; The PendSV exception should have the lowest priority in the whole system
; (0xFF, see QXK_start_). All other exceptions and interrupts should have
; higher priority. For example, for NVIC with 2 priority bits all interrupts
; and exceptions must have numerical value of priority lower than 0xC0.
; In this case the interrupt priority levels available to your applications
; are (in the order from the lowest to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel-aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro to trigger the PendSV exception.
;
; The C signature (CMSIS): void PendSV_Handler(void);
;
; NOTE:
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK context switch
; must occur.
;*****************************************************************************
PendSV_Handler:
MRS r0,PSP ; r0 := Process Stack Pointer MRS r0,PSP ; r0 := Process Stack Pointer
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
LDR r3,=QXK_attr_
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ? #if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11 SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!) MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
@ -182,83 +269,128 @@ PendSV_Handler:
MOV r6,r10 MOV r6,r10
MOV r7,r11 MOV r7,r11
STMIA r1!,{r4-r7} ; save the high registers STMIA r1!,{r4-r7} ; save the high registers
; NOTE: at this point r0 still holds the top of stack ; NOTE: at this point r0 holds the top of stack
CPSID i ; disable interrupts (set PRIMASK)
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
#else ; M3/M4/M7 #else ; M3/M4/M7
STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame
#ifdef __ARMVFP__ ; if VFP available... #ifdef __ARMVFP__ ; if VFP available...
TST lr,#(1 << 4) ; is it return with the VFP exception frame? TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31 VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
#endif ; VFP available #endif ; VFP available
MOVS r1,#QF_BASEPRI
MSR BASEPRI,r1 ; selectively disable interrupts
#endif ; M3/M4/M7 #endif ; M3/M4/M7
; NOTE: This PendSV exception handler can be preempted by an ; store the SP of the current extended-thread
; interrupt, which might pend PendSV exception again. This STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
; would be a problem, because the QK scheduler would run again MOV r0,r12 ; QXK_attr_.next (restore value)
; after this PendSV instance, so the same AO would be scheduled
; twice. The following write to ICSR[7] un-pends any such spurious
; instance of PendSV.
MOVS r1,#1
LSLS r1,r1,#27 ; r1 := (1 << 27) (UNPENDSVSET bit)
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; store the SP of the current QXK thread, CMP r2,#0
; which was set in QXK_ISR_EXIT(). BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
LDR r2,[r3,#QXK_CURR] ; otherwise continue to restoring next extended-thread...
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
#ifdef __ARMVFP__ ; if VFP available... ;-------------------------------------------------------------------------
; store the LR of the current QXK thread... ; Restoring extended-thread after crossing from AO-thread
STR lr,[r2,#QMACTIVE_OSOBJECT] ; QXK_attr_.curr->osObject := lr ; expected register contents:
#endif ; VFP available ; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; set current to the next... ; r2 -> QXK_attr_.next->thread (SP)
LDR r2,[r3,#QXK_NEXT] ; r2 := QXK_attr_.next ; r3 -> &QXK_attr_
STR r2,[r3,#QXK_CURR] ; QXK_attr_.curr := r2 ; r12 -> QXK_attr_.next
PendSV_restore_ex:
; restore the SP of the next thread... STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := r0 (QXK_attr_.next)
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP)
#ifdef __ARMVFP__ ; if VFP available...
; restore the LR of the next thread...
LDR lr,[r2,#QMACTIVE_OSOBJECT] ; lr := QXK_attr_.next->osObject
#endif ; VFP available
; exit the critical section ; exit the critical section
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ? #if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
MOVS r1,r0 ; r1 := top of stack MOVS r0,r2 ; r2 := top of stack
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r2!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack MOVS r2,r0 ; r2 := holds the new top of stack
#else ; M3/M4/M7
MOVS r3,#0
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI)
MOVS r1,#2
MVNS r1,r1 ; r1 := ~2 == 0xFFFFFFFD
MOV lr,r1 ; make sure PSP is used
#else ; M3/M4/M7
MOVS r1,#1
MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
#ifdef __ARMVFP__ ; if VFP available... #ifdef __ARMVFP__ ; if VFP available...
LDMIA r2!,{r1,lr} ; restore aligner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame? TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VLDMIAEQ r0!,{s16-s31} ; ... restore VFP registers s16..s31 VLDMIAEQ r2!,{s16-s31} ; ... restore VFP registers s16..s31
#else
ORR lr,lr,#(1 << 2) ; make sure PSP is used
#endif ; VFP available #endif ; VFP available
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack #endif ; M3/M4/M7
#endif ; M3/M4/M7
; set the PSP to the next thread's SP ; set the PSP to the next thread's SP
MSR PSP,r0 ; Process Stack Pointer := r0 MSR PSP,r2 ; Process Stack Pointer := r2
BX lr ; return to the next thread BX lr ; return to the next extended-thread
;*****************************************************************************
; Thread_ret is a helper function executed when the QXK activator returns.
;
; NOTE: Thread_ret does not execute in the PendSV context!
; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
Thread_ret:
; After the QXK activator returns, we need to resume the preempted
; thread. However, this must be accomplished by a return-from-exception,
; while we are still in the thread context. The switch to the exception
; contex is accomplished by triggering the NMI exception.
; before triggering the NMI exception, make sure that the
; VFP stack frame will NOT be used...
#ifdef __ARMVFP__ ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL
BICS r0,r0,#4 ; r0 := r0 & ~4 (FPCA bit)
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
#endif ; VFP available
; trigger NMI to return to preempted task...
; NOTE: The NMI exception is triggered with nterrupts DISABLED
LDR r0,=0xE000ED04 ; Interrupt Control and State Register
MOVS r1,#1
LSLS r1,r1,#31 ; r1 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
B . ; wait for preemption by NMI
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler:
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
#else ; M3/M4/M7
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
#ifdef __ARMVFP__ ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
#else ; no VFP
BX lr ; return to the preempted task
#endif ; no VFP
#endif ; M3/M4/M7
;***************************************************************************** ;*****************************************************************************
@ -283,8 +415,9 @@ QXK_stackInit_:
; r2 - begining of stack ; r2 - begining of stack
; r3 - size of stack [bytes] ; r3 - size of stack [bytes]
MOV r12,r0 ; temporarily save r0 in r12 (act)
STR r1,[r0,#QMACTIVE_THREAD] ; temporarily save the thread routine
ADDS r3,r2,r3 ; r3 := end of stack (top of stack) ADDS r3,r2,r3 ; r3 := end of stack (top of stack)
MOV r12,r0 ; save r0 in r12
; round up the beginning of stack to the 8-byte boundary ; round up the beginning of stack to the 8-byte boundary
; r2 := (((r2 -1) >> 3) + 1) << 3; ; r2 := (((r2 -1) >> 3) + 1) << 3;
@ -298,77 +431,82 @@ QXK_stackInit_:
LSRS r0,r3,#3 LSRS r0,r3,#3
LSLS r3,r0,#3 LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
#ifdef __ARMVFP__ ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
#endif ; VFP available
MOV r0,r12 ; pre-fill the unused part of the stack with 0xDEADBEEF...................
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
; pre-fill the stack with 0xDEADBEEF
LDR r0,=0xDEADBEEF LDR r0,=0xDEADBEEF
MOV r1,r0 MOV r1,r0
QXK_stackInit_fill: QXK_stackInit_fill:
STMIA r2!,{r0,r1} STMIA r2!,{r0,r1}
CMP r2,r3 CMP r2,r3
BLT.N QXK_stackInit_fill BLT.N QXK_stackInit_fill
; prepare the standard exception (without VFP) stack frame... ; prepare the standard exception (without VFP) stack frame................
MOV r0,r12 ; restore r0 from r12 (act)
MOV r0,r12 ; restore r0 from r12 LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
MOVS r2,#0x04
STR r2,[r3,#0*4] ; r4
MOVS r2,#0x05
STR r2,[r3,#1*4] ; r5
MOVS r2,#0x06
STR r2,[r3,#2*4] ; r6
MOVS r2,#0x07
STR r2,[r3,#3*4] ; r7
MOVS r2,#0x08
STR r2,[r3,#4*4] ; r8
MOVS r2,#0x09
STR r2,[r3,#5*4] ; r9
MOVS r2,#0x0A
STR r2,[r3,#6*4] ; r10
MOVS r2,#0x0B
STR r2,[r3,#7*4] ; r11
STR r0,[r3,#8*4] ; r0 (argument to thread routine, act pointer)
MOVS r2,#0x01
STR r2,[r3,#9*4] ; r1
MOVS r2,#0x02
STR r2,[r3,#10*4] ; r2
MOVS r2,#0x03
STR r2,[r3,#11*4] ; r3
MOVS r2,#0x0C
STR r2,[r3,#12*4] ; r12
LDR r2,=QXK_threadRet_
STR r2,[r3,#13*4] ; LR (return address)
LDR r1,[r0,#QMACTIVE_OSOBJECT] ; r1 := saved thread routine
STR r1,[r3,#14*4] ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STR r2,[r3,#15*4] ; xPSR
STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack
#ifdef __ARMVFP__ ; if VFP available...
MOVS r2,#0
STMIA r3!,{r2} ; stack "aligner"
; synthesize EXC_RETURN for return to Thread mode with no FPU-state
MOVS r2,#2 MOVS r2,#2
MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD
STR r2,[r0,#QMACTIVE_OSOBJECT] ; act->thread.osObject := lr STMIA r3!,{r2} ; save EXC_RETURN
#endif ; VFP available
MOVS r2,#0x04
STMIA r3!,{r2} ; r4
MOVS r2,#0x05
STMIA r3!,{r2} ; r5
MOVS r2,#0x06
STMIA r3!,{r2} ; r6
MOVS r2,#0x07
STMIA r3!,{r2} ; r7
MOVS r2,#0x08
STMIA r3!,{r2} ; r8
MOVS r2,#0x09
STMIA r3!,{r2} ; r9
MOVS r2,#0x0A
STMIA r3!,{r2} ; r10
MOVS r2,#0x0B
STMIA r3!,{r2} ; r11
STMIA r3!,{r0} ; r0 (argument to thread routine, me pointer)
MOVS r2,#0x01
STMIA r3!,{r2} ; r1
MOVS r2,#0x02
STMIA r3!,{r2} ; r2
MOVS r2,#0x03
STMIA r3!,{r2} ; r3
MOVS r2,#0x0C
STMIA r3!,{r2} ; r12
LDR r2,=QXK_threadRet_
STMIA r3!,{r2} ; LR (return address)
STMIA r3!,{r1} ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STMIA r3!,{r2} ; xPSR
BX lr ; return to the caller BX lr ; return to the caller

View File

@ -1,9 +1,9 @@
/// @file /// @file
/// @brief QF/C++ port to ARM Cortex-M, QXK kernel, TI-ARM toolset /// @brief QF/C++ port to ARM Cortex-M, dual-mode QXK kernel, TI-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -58,8 +58,8 @@
#define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI) #define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI)
#define QF_INT_ENABLE() QF_set_BASEPRI(0U) #define QF_INT_ENABLE() QF_set_BASEPRI(0U)
// the intrinsic function _norm() generates the CLZ instruction // Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_))) #define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
// assembly function for setting the BASEPRI register // assembly function for setting the BASEPRI register
extern "C" void QF_set_BASEPRI(unsigned basePri); extern "C" void QF_set_BASEPRI(unsigned basePri);

View File

@ -2,8 +2,8 @@
/// @brief QXK/C++ port to ARM Cortex-M, preemptive QXK kernel, TI-ARM toolset /// @brief QXK/C++ port to ARM Cortex-M, preemptive QXK kernel, TI-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-30 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -38,15 +38,29 @@
#ifndef qxk_port_h #ifndef qxk_port_h
#define qxk_port_h #define qxk_port_h
// QXK context switch (trigger PendSV exception) // determination if the code executes in the ISR context
#define QXK_CONTEXT_SWITCH_() \ #define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != static_cast<uint32_t>(0))
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = (uint32_t)(1U << 28))
// trigger the PendSV exception to pefrom the context switch
#define QXK_CONTEXT_SWITCH_() \
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
static_cast<uint32_t>(1U << 28))
// QXK ISR entry and exit
#define QXK_ISR_ENTRY() ((void)0)
#define QXK_ISR_EXIT() do { \
QF_INT_DISABLE(); \
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
QXK_CONTEXT_SWITCH_(); \
} \
QF_INT_ENABLE(); \
} while (false)
// QXK interrupt entry and exit (defined in assembly)
extern "C" { extern "C" {
void QXK_ISR_ENTRY(void); // get the IPSR defined in assembly
void QXK_ISR_EXIT(void); uint32_t QXK_get_IPSR(void);
} // extern "C" }
#include "qxk.h" // QXK platform-independent public interface #include "qxk.h" // QXK platform-independent public interface

View File

@ -1,7 +1,7 @@
;***************************************************************************** ;*****************************************************************************
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), TI-ARM assembler ; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), TI-ARM assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-07-14 ; Date of the Last Update: 2016-09-25
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -32,44 +32,46 @@
; mailto:info@state-machine.com ; mailto:info@state-machine.com
;***************************************************************************** ;*****************************************************************************
.global QXK_start_ ; start the QXK multitasking .global QXK_init ; initialze the QXK kernel
.global QXK_stackInit_ ; initialize the stack of each thread .global QXK_stackInit_ ; initialize the stack of an extended thread
.global PendSV_Handler ; CMSIS-compliant PendSV exception .global PendSV_Handler ; CMSIS-compliant PendSV exception name
.global NMI_Handler ; CMSIS-compliant NMI exception name
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__ .if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
.global QF_set_BASEPRI ; set BASEPRI register .global QF_set_BASEPRI ; set BASEPRI register
.endif ; M3/M4/M7 .endif ; M3/M4/M7
.global QXK_ISR_ENTRY ; inform QXK about interrupt entry .global QXK_get_IPSR ; get the IPSR
.global QXK_ISR_EXIT ; inform QXK about interrupt exit
.global assert_failed ; low-level assert handler .global assert_failed ; low-level assert handler
.ref QXK_attr_ ; QXK attribute structure .ref QXK_attr_ ; QXK attribute structure
.ref QXK_threadRet_ ; return from a thread function .ref QXK_threadRet_ ; return from an extended thread function
.ref QXK_sched_ ; external reference .ref QXK_activate_ ; external reference
.ref Q_onAssert ; external reference .ref Q_onAssert ; external reference
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!! ; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
QF_BASEPRI .equ (0xFF >> 2) QF_BASEPRI .equ (0xFF >> 2)
; NOTE: keep in synch with the QXK struct in "qxk.h" !!! ; NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!!
QXK_CURR .equ 0 QXK_CURR .equ 0
QXK_NEXT .equ 4 QXK_NEXT .equ 4
QXK_INT_NEST .equ 8 QXK_TOP_PRIO .equ 8
QXK_INT_NEST .equ 20
; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! ; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!!
QMACTIVE_OSOBJECT .equ 40 QMACTIVE_THREAD .equ 40
QMACTIVE_THREAD .equ 44 QMACTIVE_PRIO .equ 44
.text .text
.thumb .thumb
;***************************************************************************** ;*****************************************************************************
; The QXK_start_ function starts QXK multitasking. ; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
; The C signature: void QXK_start_(void); ; This operation is performed in a nestable critical section.
;
; NOTE: QXK_start_() must be called with interrupts disabled and
; returns with interrupts **enabled**.
;***************************************************************************** ;*****************************************************************************
QXK_start_: QXK_init: .asmfunc
MRS r0,PRIMASK ; store the state of the PRIMASK in r0
CPSID i ; disable interrupts (set PRIMASK)
LDR r1,SHPR_addr ; System Handler Priority Register LDR r1,SHPR_addr ; System Handler Priority Register
LDR r2,[r1,#8] ; load the System 12-15 Priority Register LDR r2,[r1,#8] ; load the System 12-15 Priority Register
MOVS r3,#0xFF MOVS r3,#0xFF
@ -77,123 +79,206 @@ QXK_start_:
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
STR r2,[r1,#8] ; write the System 12-15 Priority Register STR r2,[r1,#8] ; write the System 12-15 Priority Register
; set the MSP to the top of the C-STACK, which is the first entry MSR PRIMASK,r0 ; restore the original PRIMASK
; in the vector table. (This recovers any stack used so far by main().) BX lr ; return to the caller
LDR r0,VTOR_addr ; r0 := address of Vector Table Offset register .endasmfunc
LDR r0,[r0,#0] ; r0 := contents of VTOR
LDR r0,[r0] ; r0 := VT[0] (first entry is the top of stack)
MSR MSP,r0 ; main SP := initial top of stack
; set the current QXK thread to the next QXK thread ;*****************************************************************************
LDR r1,QXK_attr_addr ; The PendSV_Handler exception handler is used for handling context switch
LDR r2,[r1,#QXK_NEXT] ; r2 := QXK_attr_.next ; and asynchronous preemption in QXK. The use of the PendSV exception is
STR r2,[r1,#QXK_CURR] ; QXK_attr_.curr := r2 ; the recommended and most efficient method for performing context switches
; with ARM Cortex-M.
;
; The PendSV exception should have the lowest priority in the whole system
; (0xFF, see QXK_init). All other exceptions and interrupts should have higher
; priority. For example, for NVIC with 2 priority bits all interrupts and
; exceptions must have numerical value of priority lower than 0xC0. In this
; case the interrupt priority levels available to your applications are (in
; the order from the lowest urgency to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro, which triggers PendSV when it detects a need for
; a context switch or asynchronous preemption.
;
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK scheduler needs to
; check for the asynchronous preemption.
;*****************************************************************************
PendSV_Handler: .asmfunc
; get the top of stack of the current QXK thread ; Prepare some constants (an address and a bitmask) before entering
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP) ; a critical section...
; pop r4-r11 from the PSP LDR r3,QXK_attr_addr
MOVS r1,r0 ; r1 := top of stack LDR r2,ICSR_addr ; Interrupt Control and State Register
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 MOVS r1,#1
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts
.else ; M0/M0+
CPSID i ; disable interrupts (set PRIMASK)
.endif ; M0/M0+
; The PendSV exception handler can be preempted by an interrupt,
; which might pend PendSV exception again. The following write to
; ICSR[27] un-pends any such spurious instance of PendSV.
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; Check QXK_attr_.next, which contains the pointer to the next thread
; to run, which is set in QXK_ISR_EXIT(). This pointer must not be NULL.
LDR r0,[r3,#QXK_NEXT] ; r1 := QXK_attr_.next
CMP r0,#0 ; is (QXK_attr_.next == 0)?
BEQ PendSV_error ; branch if (QXK_attr_.next == 0)
; Load pointers into registers...
MOV r12,r0 ; save QXK_attr_.next in r12
LDR r2,[r0,#QMACTIVE_THREAD] ; r2 := QXK_attr_.next->thread
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr
CMP r1,#0 ; (QXK_attr_.curr != 0)?
BNE PendSV_save_ex ; branch if (current thread is extended)
CMP r2,#0 ; (QXK_attr_.next->thread != 0)?
BNE PendSV_save_ao ; branch if (next tread is extended)
PendSV_activate:
.if __TI_VFP_SUPPORT__ ; if VFP available...
PUSH {r0,lr} ; ... push lr (EXC_RETURN) plus stack-aligner
.endif ; VFP available
; The QXK activator must be called in a thread context, while this code
; executes in the handler contex of the PendSV exception. The switch
; to the Thread mode is accomplished by returning from PendSV using
; a fabricated exception stack frame, where the return address is the
; QXK activator QXK_activate_().
;
; NOTE: the QXK activator is called with interrupts DISABLED and also
; it returns with interrupts DISABLED.
MOVS r3,#1
LSLS r3,r3,#24 ; r3:=(1 << 24), set the T bit (new xpsr)
LDR r2,QXK_activate_addr ; address of the QXK activator (new pc)
LDR r1,Thread_ret_addr ; return address after the call (new lr)
SUB sp,sp,#8*4 ; reserve space for exception stack frame
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
STM r0!,{r1-r3} ; save xpsr,pc,lr
MOVS r0,#6
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
BX r0 ; exception-return to the QXK activator
;-------------------------------------------------------------------------
PendSV_error:
LDR r3,assert_failed_addr
BX r3 ; long-branch to the assertion-handler
;=========================================================================
; Saving AO-thread before crossing to eXtended-thread
; expected register contents:
; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_save_ao:
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
PUSH {r4-r11} ; save r4-r11 on top of the exception frame
.if __TI_VFP_SUPPORT__ ; if VFP available...
TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero...
VSTMDBEQ sp!,{s16-s31} ; ... save VFP registers s16..s31
PUSH {r0,lr} ; save the "aligner" and the EXC_RETURN value
.endif ; VFP available
.else ; M0/M0+
PUSH {r4-r7} ; save the low registers
MOV r4,r8 ; move the high registers to low registers...
MOV r5,r9
MOV r6,r10
MOV r7,r11
PUSH {r4-r7} ; save the high registers
.endif ; M0/M0+
CMP r2,#0
BNE PendSV_restore_ex ; branch if (QXK_attr_.next->thread != 0)
; otherwise continue to restoring next AO-thread...
;-------------------------------------------------------------------------
; Restoring AO-thread after crossing from eXtended-thread
; expected register contents:
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_restore_ao:
MOVS r0,#0
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := 0 (QXK_attr_.next)
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
.if __TI_VFP_SUPPORT__ ; if VFP available...
POP {r0,lr} ; restore alighner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if EXC_RETURN[4] is zero...
VLDMIAEQ sp!,{s16-s31} ; ... restore VFP registers s16..s31
.else
BIC lr,lr,#(1 << 2) ; make sure MSP is used
.endif ; VFP available
POP {r4-r11} ; restore r4-r11 from the next thread's stack
.else ; M0/M0+
MOV r0,sp ; r0 := top of stack
MOV r1,r0
ADDS r1,r1,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r1!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r0!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack ADD sp,sp,#(8*4) ; remove 8 registers from the stack
MSR PSP,r0 ; set PSP to the thread's SP MOVS r1,#6
ISB ; flush the instruction pipeline MVNS r1,r1 ; r2 := ~6 == 0xFFFFFFF9
MOV lr,r1 ; make sure MSP is used
.endif ; M0/M0+
; switch CPU to using the Process Stack Pointer (PSP) MOV r0,r12 ; r0 := QXK_attr_.next
MRS r0,CONTROL LDR r0,[r0,#QMACTIVE_PRIO] ; r0 := QXK_attr_.next->prio
MOVS r1,#(1 << 1) LDR r1,[r3,#QXK_TOP_PRIO] ; r1 := QXK_attr_.topPrio
ORRS r0,r0,r1 ; set the Active Stack Pointer CMP r1,r0
MSR CONTROL,r0 ; switch to PSP BCC PendSV_activate ; if (next->prio > topPrio) activate the next AO
DSB ; make sure all data access completes
ISB ; flush the instruction pipeline
; fake return from an exception... ; otherwise re-enable interrupts and return to the preempted AO-thread
POP {r0-r3} ; pop R0..R3
; NOTE: R0 holds the 'par' argument of the
; thread function
POP {r1,r2} ; pop R12 and LR into low registers r1,r2
MOV r12,r1
MOV lr,r2
POP {r1,r2} ; pop PC to R1 and xPSR to R2
; NOTE: it's OK to clobber R1 and R2
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__ .if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
.if __TI_VFP_SUPPORT__ ; if VFP available... MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
; enable VFP by enabling CP10 and CP11 coprocessors .else ; M0/M0+
LDR.W r3,CPACR_addr ; r3 := &CPACR
LDR r2,[r3] ; r2 := CPACR
ORR r2,r2,#(0xF << 20); r2 := r2 | (CP10 | CP11)
STR r2,[r3] ; CPACR = r2
DSB ; make sure all data access completes
ISB ; reset the instruction pipeline
; enable Automatic State Preservation and Lazy Stacking in the VFP
LDR r3,FPCCR_addr ; r3 := &FPCCR
LDR r2,[r3] ; r2 := FPCCR
ORR r2,r2,#(3 << 30) ; set ASPEN | LSPEN
STR r2,[r3] ; FPCCR &= ~(ASPEN | LSPEN)
; clear the VFP Context Active (FPCA) bit in CONTROL
MRS r2,CONTROL ; r2 := CONTROL (NOTE: it's OK to clobber R2)
BICS r2,r2,#(1 << 2) ; r2 := r2 & ~(1 << 2) (FPCA bit)
MSR CONTROL,r2 ; CONTROL := r2 (clear CONTROL[2] FPCA bit)
.endif ; VFP available
MOVS r2,#0 ; NOTE: it's OK to clobber R2
MSR BASEPRI,r2 ; enable interrupts (clear BASEPRI)
.else ; M0/M0+/M1
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
.endif ; M0/M0+/M1 .endif ; M0/M0+
; >>>>>>>>>>>>>>>>>>>>>>>> CRITICAL SECTION END >>>>>>>>>>>>>>>>>>>>>>>>>>
BX r1 ; return to the "interrupted" thread (PC) BX lr ; return to the preempted AO-thread
.endasmfunc
;*****************************************************************************
; The PendSV_Handler exception handler is used for context switching in QXK.
; The use of the PendSV exception is the recommended and most efficient
; method for performing context switches with ARM Cortex-M.
;
; The PendSV exception should have the lowest priority in the whole system
; (0xFF, see QXK_start_). All other exceptions and interrupts should have
; higher priority. For example, for NVIC with 2 priority bits all interrupts
; and exceptions must have numerical value of priority lower than 0xC0.
; In this case the interrupt priority levels available to your applications
; are (in the order from the lowest to the highest urgency): 0x80, 0x40, 0x00.
;
; Also, *all* "kernel-aware" ISRs in the QXK application must call the
; QXK_ISR_EXIT() macro to trigger the PendSV exception.
;
; The C signature (CMSIS): void PendSV_Handler(void);
;
; NOTE:
; Due to tail-chaining and its lowest priority, the PendSV exception will be
; entered immediately after the exit from the *last* nested interrupt (or
; exception). In QXK, this is exactly the time when the QXK context switch
; must occur.
;*****************************************************************************
PendSV_Handler: .asmfunc
;-------------------------------------------------------------------------
; Saving extended-thread before crossing to AO-thread
; expected register contents:
; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_save_ex:
MRS r0,PSP ; r0 := Process Stack Pointer MRS r0,PSP ; r0 := Process Stack Pointer
LDR r2,ICSR_addr ; Interrupt Control and State Register
LDR r3,QXK_attr_addr
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__ .if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame
.if __TI_VFP_SUPPORT__ ; if VFP available... .if __TI_VFP_SUPPORT__ ; if VFP available...
TST lr,#(1 << 4) ; is it return with the VFP exception frame? TST lr,#(1 << 4) ; is it return with the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31 VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
.endif ; VFP available .endif ; VFP available
MOVS r1,#QF_BASEPRI .else ; M0/M0+
MSR BASEPRI,r1 ; selectively disable interrupts
.else ; M0/M0+/M1
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11 SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!) MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
STMIA r1!,{r4-r7} ; save the low registers STMIA r1!,{r4-r7} ; save the low registers
@ -202,77 +287,124 @@ PendSV_Handler: .asmfunc
MOV r6,r10 MOV r6,r10
MOV r7,r11 MOV r7,r11
STMIA r1!,{r4-r7} ; save the high registers STMIA r1!,{r4-r7} ; save the high registers
; NOTE: at this point r0 still holds the top of stack ; NOTE: at this point r0 holds the top of stack
CPSID i ; disable interrupts (set PRIMASK)
.endif ; M0/M0+/M1
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
.endif ; M0/M0+
; NOTE: This PendSV exception handler can be preempted by an ; store the SP of the current extended-thread
; interrupt, which might pend PendSV exception again. This STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
; would be a problem, because the QK scheduler would run again MOV r0,r12 ; QXK_attr_.next (restore value)
; after this PendSV instance, so the same AO would be scheduled
; twice. The following write to ICSR[7] un-pends any such spurious
; instance of PendSV.
MOVS r1,#1
LSLS r1,r1,#27 ; r1 := (1 << 27) (UNPENDSVSET bit)
STR r1,[r2] ; ICSR[27] := 1 (unpend PendSV)
; store the SP of the current QXK thread, CMP r2,#0
; which was set in QXK_ISR_EXIT(). BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
LDR r2,[r3,#QXK_CURR] ; otherwise continue to restoring next extended-thread...
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
.if __TI_VFP_SUPPORT__ ; if VFP available... ;-------------------------------------------------------------------------
; store the LR of the current QXK thread... ; Restoring extended-thread after crossing from AO-thread
STR lr,[r2,#QMACTIVE_OSOBJECT] ; QXK_attr_.curr->osObject := lr ; expected register contents:
.endif ; VFP available ; r0 -> QXK_attr_.next
; r1 -> QXK_attr_.curr
; r2 -> QXK_attr_.next->thread (SP)
; r3 -> &QXK_attr_
; r12 -> QXK_attr_.next
PendSV_restore_ex:
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := r0 (QXK_attr_.next)
; set current to the next...
LDR r2,[r3,#QXK_NEXT] ; r2 := QXK_attr_.next
STR r2,[r3,#QXK_CURR] ; QXK_attr_.curr := r2
; restore the SP of the next thread...
LDR r0,[r2,#QMACTIVE_THREAD] ; r0 := QXK_attr_.next->thread (SP)
.if __TI_VFP_SUPPORT__ ; if VFP available...
; restore the LR of the next thread...
LDR lr,[r2,#QMACTIVE_OSOBJECT] ; lr := QXK_attr_.next->osObject
.endif ; VFP available
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
; exit the critical section ; exit the critical section
MOVS r3,#0 .if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI) MOVS r1,#1
MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
.if __TI_VFP_SUPPORT__ ; if VFP available... .if __TI_VFP_SUPPORT__ ; if VFP available...
LDMIA r2!,{r1,lr} ; restore aligner and EXC_RETURN into lr
TST lr,#(1 << 4) ; is it return to the VFP exception frame? TST lr,#(1 << 4) ; is it return to the VFP exception frame?
IT EQ ; if lr[4] is zero... IT EQ ; if lr[4] is zero...
VLDMIAEQ r0!,{s16-s31} ; ... restore VFP registers s16..s31 VLDMIAEQ r2!,{s16-s31} ; ... restore VFP registers s16..s31
.else
ORR lr,lr,#(1 << 2) ; make sure PSP is used
.endif ; VFP available .endif ; VFP available
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack .else ; M0/M0+
.else ; M0/M0+/M1
; exit the critical section
CPSIE i ; enable interrupts (clear PRIMASK) CPSIE i ; enable interrupts (clear PRIMASK)
MOVS r1,r0 ; r1 := top of stack MOVS r0,r2 ; r2 := top of stack
ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11 ADDS r0,r0,#(4*4) ; point r0 to the 4 high registers r7-r11
LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers LDMIA r0!,{r4-r7} ; pop the 4 high registers into low registers
MOV r8,r4 ; move low registers into high registers MOV r8,r4 ; move low registers into high registers
MOV r9,r5 MOV r9,r5
MOV r10,r6 MOV r10,r6
MOV r11,r7 MOV r11,r7
LDMIA r1!,{r4-r7} ; pop the low registers LDMIA r2!,{r4-r7} ; pop the low registers
; NOTE: at this point r0 holds the new top of stack MOVS r2,r0 ; r2 := holds the new top of stack
.endif ; M0/M0+/M1
MOVS r1,#2
MVNS r1,r1 ; r1 := ~2 == 0xFFFFFFFD
MOV lr,r1 ; make sure PSP is used
.endif ; M0/M0+
; set the PSP to the next thread's SP ; set the PSP to the next thread's SP
MSR PSP,r0 ; Process Stack Pointer := r0 MSR PSP,r2 ; Process Stack Pointer := r2
BX lr ; return to the next thread BX lr ; return to the next extended-thread
.endasmfunc .endasmfunc
;*****************************************************************************
; Thread_ret is a helper function executed when the QXK activator returns.
;
; NOTE: Thread_ret does not execute in the PendSV context!
; NOTE: Thread_ret executes entirely with interrupts DISABLED.
;*****************************************************************************
Thread_ret: .asmfunc ; to ensure that the label is THUMB
; After the QXK activator returns, we need to resume the preempted
; thread. However, this must be accomplished by a return-from-exception,
; while we are still in the thread context. The switch to the exception
; contex is accomplished by triggering the NMI exception.
; before triggering the NMI exception, make sure that the
; VFP stack frame will NOT be used...
.if __TI_VFP_SUPPORT__ ; if VFP available...
MRS r0,CONTROL ; r0 := CONTROL
BICS r0,r0,#4 ; r0 := r0 & ~4 (FPCA bit)
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
.endif ; VFP available
; trigger NMI to return to preempted task...
; NOTE: The NMI exception is triggered with nterrupts DISABLED
LDR r0,ICSR_addr ; Interrupt Control and State Register
MOVS r1,#1
LSLS r1,r1,#31 ; r0 := (1 << 31) (NMI bit)
STR r1,[r0] ; ICSR[31] := 1 (pend NMI)
Thread_wait_NMI:
B Thread_wait_NMI ; wait for preemption by NMI
.endasmfunc
;*****************************************************************************
; The NMI_Handler exception handler is used for returning back to the
; interrupted task. The NMI exception simply removes its own interrupt
; stack frame from the stack and returns to the preempted task using the
; interrupt stack frame that must be at the top of the stack.
;
; NOTE: The NMI exception is entered with interrupts DISABLED, so it needs
; to re-enable interrupts before it returns to the preempted task.
;*****************************************************************************
NMI_Handler: .asmfunc
ADD sp,sp,#(8*4) ; remove one 8-register exception frame
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
.if __TI_VFP_SUPPORT__ ; if VFP available...
POP {r0,pc} ; pop stack "aligner" and EXC_RETURN to PC
.else
BX lr ; return to the preempted task
.endif
.else ; Cortex-M0/M0+/M1 ?
CPSIE i ; enable interrupts (clear PRIMASK)
BX lr ; return to the preempted task
.endif ; M0/M0+/M1
.endasmfunc
;***************************************************************************** ;*****************************************************************************
@ -297,8 +429,9 @@ QXK_stackInit_: .asmfunc
; r2 - begining of stack ; r2 - begining of stack
; r3 - size of stack [bytes] ; r3 - size of stack [bytes]
MOV r12,r0 ; temporarily save r0 in r12 (act)
STR r1,[r0,#QMACTIVE_THREAD] ; temporarily save the thread routine
ADDS r3,r2,r3 ; r3 := end of stack (top of stack) ADDS r3,r2,r3 ; r3 := end of stack (top of stack)
MOV r12,r0 ; save r0 in r12
; round up the beginning of stack to the 8-byte boundary ; round up the beginning of stack to the 8-byte boundary
; r2 := (((r2 -1) >> 3) + 1) << 3; ; r2 := (((r2 -1) >> 3) + 1) << 3;
@ -312,80 +445,85 @@ QXK_stackInit_: .asmfunc
LSRS r0,r3,#3 LSRS r0,r3,#3
LSLS r3,r0,#3 LSLS r3,r0,#3
; make room for the thread's stack frame...
SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame SUBS r3,r3,#16*4 ; r3 := top of the 16-register stack frame
.if __TI_VFP_SUPPORT__ ; if VFP available...
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
.endif ; VFP available
MOV r0,r12 ; pre-fill the unused part of the stack with 0xDEADBEEF...................
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
; pre-fill the stack with STACK_FILL_const
LDR r0,STACK_FILL_const LDR r0,STACK_FILL_const
MOV r1,r0 MOV r1,r0
QXK_stackInit_fill: QXK_stackInit_fill:
STMIA r2!,{r0,r1} STMIA r2!,{r0,r1}
CMP r2,r3 CMP r2,r3
BLT.N QXK_stackInit_fill BLT.N QXK_stackInit_fill
; prepare the standard exception (without VFP) stack frame... ; prepare the standard exception (without VFP) stack frame................
MOV r0,r12 ; restore r0 from r12 (act)
MOV r0,r12 ; restore r0 from r12 LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
MOVS r2,#0x04
STR r2,[r3,#0*4] ; r4
MOVS r2,#0x05
STR r2,[r3,#1*4] ; r5
MOVS r2,#0x06
STR r2,[r3,#2*4] ; r6
MOVS r2,#0x07
STR r2,[r3,#3*4] ; r7
MOVS r2,#0x08
STR r2,[r3,#4*4] ; r8
MOVS r2,#0x09
STR r2,[r3,#5*4] ; r9
MOVS r2,#0x0A
STR r2,[r3,#6*4] ; r10
MOVS r2,#0x0B
STR r2,[r3,#7*4] ; r11
STR r0,[r3,#8*4] ; r0 (argument to thread routine, act pointer)
MOVS r2,#0x01
STR r2,[r3,#9*4] ; r1
MOVS r2,#0x02
STR r2,[r3,#10*4] ; r2
MOVS r2,#0x03
STR r2,[r3,#11*4] ; r3
MOVS r2,#0x0C
STR r2,[r3,#12*4] ; r12
LDR r2,QXK_threadRet_addr
STR r2,[r3,#13*4] ; LR (return address)
LDR r1,[r0,#QMACTIVE_OSOBJECT] ; r1 := saved thread routine
STR r1,[r3,#14*4] ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STR r2,[r3,#15*4] ; xPSR
STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack STR r3,[r0,#QMACTIVE_THREAD] ; act->thread := top of stack
.if __TI_VFP_SUPPORT__ ; if VFP available...
MOVS r2,#0
STMIA r3!,{r2} ; stack "aligner"
; synthesize EXC_RETURN for return to Thread mode with no FPU-state
MOVS r2,#2 MOVS r2,#2
MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD MVNS r2,r2 ; r2 := ~2 == 0xFFFFFFFD
STR r2,[r0,#QMACTIVE_OSOBJECT] ; act->thread.osObject := lr STMIA r3!,{r2} ; save EXC_RETURN
.endif ; VFP available
MOVS r2,#0x04
STMIA r3!,{r2} ; r4
MOVS r2,#0x05
STMIA r3!,{r2} ; r5
MOVS r2,#0x06
STMIA r3!,{r2} ; r6
MOVS r2,#0x07
STMIA r3!,{r2} ; r7
MOVS r2,#0x08
STMIA r3!,{r2} ; r8
MOVS r2,#0x09
STMIA r3!,{r2} ; r9
MOVS r2,#0x0A
STMIA r3!,{r2} ; r10
MOVS r2,#0x0B
STMIA r3!,{r2} ; r11
STMIA r3!,{r0} ; r0 (argument to thread routine, me pointer)
MOVS r2,#0x01
STMIA r3!,{r2} ; r1
MOVS r2,#0x02
STMIA r3!,{r2} ; r2
MOVS r2,#0x03
STMIA r3!,{r2} ; r3
MOVS r2,#0x0C
STMIA r3!,{r2} ; r12
LDR r2,QXK_threadRet_addr
STMIA r3!,{r2} ; LR (return address)
STMIA r3!,{r1} ; PC (entry point, thread routine)
MOVS r2,#1
LSLS r2,r2,#24 ; r2 := 0x01000000
STMIA r3!,{r2} ; xPSR
BX lr ; return to the caller BX lr ; return to the caller
.endasmfunc .endasmfunc
;***************************************************************************** ;*****************************************************************************
@ -400,91 +538,18 @@ QXK_stackInit_fill:
QF_set_BASEPRI: .asmfunc QF_set_BASEPRI: .asmfunc
MSR BASEPRI,r0 ; set BASEPRI MSR BASEPRI,r0 ; set BASEPRI
BX lr ; return to the caller BX lr ; return to the caller
.endasmfunc .endasmfunc
.endif ; M3/M4/M7 .endif ; M3/M4/M7
;***************************************************************************** ;*****************************************************************************
; Inform QXK about interrupt entry. ; The QXK_get_IPSR function gets the IPSR register and returns it in r0.
; C prototype: void QXK_ISR_ENTRY(void); ; C prototype: uint32_t QXK_get_IPSR(void);
; C pseudocode:
; void QXK_ISR_ENTRY(void) {
; int_disable();
; ++QXK_attr_.intNest;
; int_enable();
; }
;***************************************************************************** ;*****************************************************************************
QXK_ISR_ENTRY: .asmfunc QXK_get_IPSR: .asmfunc
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__ MRS r0,ipsr ; r0 := IPSR
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts
.else ; M0/M0+/M1
CPSID i ; disable interrupts (set PRIMASK)
.endif ; M0/M0+/M1
; ++QXK_attr_.intNest
LDR r1,QXK_attr_addr ; address of the QXK_attr
LDR r0,[r1,#QXK_INT_NEST]
ADDS r0,r0,#1
STR r0,[r1,#QXK_INT_NEST]
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; set BASEPRI (disable interrupts)
.else ; M0/M0+/M1
CPSIE i ; enable interrupts (set PRIMASK)
.endif ; M0/M0+/M1
BX lr ; return to the caller BX lr ; return to the caller
.endasmfunc .endasmfunc
;*****************************************************************************
; Inform QXK about interrupt exit.
; C prototype: void QXK_ISR_EXIT(void);
; C pseudocode:
; void QXK_ISR_EXIT(void) {
; int_disable();
; if (--QXK_attr_.intNest == 0) {
; QXK_sched_();
; }
; int_enable();
; }
;*****************************************************************************
QXK_ISR_EXIT: .asmfunc
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#QF_BASEPRI
MSR BASEPRI,r0 ; selectively disable interrupts
.else ; M0/M0+/M1
CPSID i ; disable interrupts (set PRIMASK)
.endif ; M0/M0+/M1
; --QXK_attr_.intNest
LDR r1,QXK_attr_addr ; address of the QXK_attr
LDR r0,[r1,#QXK_INT_NEST]
SUBS r0,r0,#1
STR r0,[r1,#QXK_INT_NEST]
; if QXK_attr_.intNest != 0, branch to QK_ISR_EXIT_ret
CMP r0,#0
BNE QXK_ISR_EXIT_ret
PUSH {r0,lr} ; push lr (return address) plus stack "aligner"
BL QXK_sched_ ; call QXK scheduler
POP {r0,r1} ; pop the stack "aligner" and the saved LR
MOV lr,r1
QXK_ISR_EXIT_ret:
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
MOVS r0,#0
MSR BASEPRI,r0 ; set BASEPRI (disable interrupts)
.else ; M0/M0+/M1
CPSIE i ; enable interrupts (set PRIMASK)
.endif ; M0/M0+/M1
BX lr ; return to the caller
.endasmfunc
;***************************************************************************** ;*****************************************************************************
@ -497,9 +562,9 @@ assert_failed: .asmfunc
LDR r0,[r0,#0] ; r0 := contents of VTOR LDR r0,[r0,#0] ; r0 := contents of VTOR
LDR r0,[r0] ; r0 := VT[0] (first entry is the top of stack) LDR r0,[r0] ; r0 := VT[0] (first entry is the top of stack)
MSR MSP,r0 ; main SP := initial top of stack MSR MSP,r0 ; main SP := initial top of stack
BL Q_onAssert BL Q_onAssert
.endasmfunc .endasmfunc
;***************************************************************************** ;*****************************************************************************
; Constants for PC-relative LDR ; Constants for PC-relative LDR
@ -507,15 +572,14 @@ assert_failed: .asmfunc
SHPR_addr: .word 0xE000ED18 SHPR_addr: .word 0xE000ED18
ICSR_addr: .word 0xE000ED04 ICSR_addr: .word 0xE000ED04
VTOR_addr: .word 0xE000ED08 VTOR_addr: .word 0xE000ED08
CPACR_addr: .word 0xE000ED88
FPCCR_addr: .word 0xE000EF34
STACK_FILL_const: .word 0xDEADBEEF STACK_FILL_const: .word 0xDEADBEEF
;***************************************************************************** ;*****************************************************************************
; Addresses for PC-relative LDR ; Addresses for PC-relative LDR
;***************************************************************************** ;*****************************************************************************
QXK_attr_addr: .word QXK_attr_ QXK_attr_addr: .word QXK_attr_
QXK_sched_addr: .word QXK_sched_ QXK_activate_addr: .word QXK_activate_
QXK_threadRet_addr: .word QXK_threadRet_ QXK_threadRet_addr: .word QXK_threadRet_
Thread_ret_addr: .word Thread_ret
assert_failed_addr: .word assert_failed

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, GNU-ARM toolset /// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, GNU-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -77,9 +77,8 @@
#define QK_IRQ_END() \ #define QK_IRQ_END() \
} --QK_attr_.intNest; \ } --QK_attr_.intNest; \
if (QK_attr_.intNest == (uint_fast8_t)0) { \ if (QK_attr_.intNest == (uint_fast8_t)0) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != (uint_fast8_t)0) { \
if (p != (uint_fast8_t)0) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
__asm volatile (" POP {R3, LR}\n" \ __asm volatile (" POP {R3, LR}\n" \
@ -96,9 +95,8 @@
#define QK_IRQ_END() \ #define QK_IRQ_END() \
} --QK_attr_.intNest; \ } --QK_attr_.intNest; \
if (QK_attr_.intNest == (uint_fast8_t)0) { \ if (QK_attr_.intNest == (uint_fast8_t)0) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != (uint_fast8_t)0) { \
if (p != (uint_fast8_t)0) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
__asm volatile (" POP {R3, LR}\n" \ __asm volatile (" POP {R3, LR}\n" \

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, IAR-ARM toolset /// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, IAR-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -73,9 +73,8 @@
#define QK_IRQ_END() \ #define QK_IRQ_END() \
} --QK_attr_.intNest; \ } --QK_attr_.intNest; \
if (QK_attr_.intNest == (uint_fast8_t)0) { \ if (QK_attr_.intNest == (uint_fast8_t)0) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != (uint_fast8_t)0) { \
if (p != (uint_fast8_t)0) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
__asm(" POP {R3, LR}\n" \ __asm(" POP {R3, LR}\n" \
@ -92,9 +91,8 @@
#define QK_IRQ_END() \ #define QK_IRQ_END() \
} --QK_attr_.intNest; \ } --QK_attr_.intNest; \
if (QK_attr_.intNest == (uint_fast8_t)0) { \ if (QK_attr_.intNest == (uint_fast8_t)0) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != (uint_fast8_t)0) { \
if (p != (uint_fast8_t)0) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
__asm(" POP {R3, LR}\n" \ __asm(" POP {R3, LR}\n" \

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, TI-ARM toolset /// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, TI-ARM toolset
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.7.0 /// Last updated for version 5.7.2
/// Last updated on 2016-08-21 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -79,9 +79,8 @@
#define QK_IRQ_END() \ #define QK_IRQ_END() \
} --QK_attr_.intNest; \ } --QK_attr_.intNest; \
if (QK_attr_.intNest == (uint_fast8_t)0) { \ if (QK_attr_.intNest == (uint_fast8_t)0) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != (uint_fast8_t)0) { \
if (p != (uint_fast8_t)0) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
} }

View File

@ -1,7 +1,7 @@
/***************************************************************************** /*****************************************************************************
* Product: QK port to ARM7-9, GNU-ARM assembler * Product: QK port to ARM7-9, GNU-ARM assembler
* Last Updated for Version: 5.7.0 * Last Updated for Version: 5.7.2
* Date of the Last Update: 2016-07-11 * Date of the Last Update: 2016-09-26
* *
* Q u a n t u m L e a P s * Q u a n t u m L e a P s
* --------------------------- * ---------------------------
@ -133,15 +133,15 @@ QK_irq:
STR r12,[r0,#QK_INT_NEST] /* store the value in QK_attr_.intNest */ STR r12,[r0,#QK_INT_NEST] /* store the value in QK_attr_.intNest */
BNE QK_irq_exit /* branch if interrupt nesting not zero */ BNE QK_irq_exit /* branch if interrupt nesting not zero */
LDR r12,=QK_schedPrio_
MOV lr,pc /* copy the return address to link register */
BX r12 /* call QK_schedPrio_ (ARM/THUMB) */
CMP r0,#0 /* check the returned priority */
BEQ QK_irq_exit /* branch if priority zero */
LDR r12,=QK_sched_ LDR r12,=QK_sched_
MOV lr,pc /* copy the return address to link register */ MOV lr,pc /* copy the return address to link register */
BX r12 /* call QK_sched_ (ARM/THUMB) */ BX r12 /* call QK_sched_ (ARM/THUMB) */
CMP r0,#0 /* check the returned priority */
BEQ QK_irq_exit /* branch if priority zero */
LDR r12,=QK_activate_
MOV lr,pc /* copy the return address to link register */
BX r12 /* call QK_activate_ (ARM/THUMB) */
QK_irq_exit: QK_irq_exit:
/* IRQ exit {{{ */ /* IRQ/FIQ disabled--return from scheduler */ /* IRQ exit {{{ */ /* IRQ/FIQ disabled--return from scheduler */

View File

@ -1,7 +1,7 @@
;----------------------------------------------------------------------------- ;-----------------------------------------------------------------------------
; Product: QK port to ARM7/9, IAR-ARM Assembler ; Product: QK port to ARM7/9, IAR-ARM Assembler
; Last Updated for Version: 5.7.0 ; Last Updated for Version: 5.7.2
; Date of the Last Update: 2016-07-11 ; Date of the Last Update: 2016-09-26
; ;
; Q u a n t u m L e a P s ; Q u a n t u m L e a P s
; --------------------------- ; ---------------------------
@ -71,7 +71,7 @@ QK_init:
SECTION .textrw:DATA:NOROOT(2) SECTION .textrw:DATA:NOROOT(2)
PUBLIC QK_irq PUBLIC QK_irq
EXTERN BSP_irq EXTERN BSP_irq
EXTERN QK_attr_, QK_schedPrio_, QK_sched_ EXTERN QK_attr_, QK_sched_, QK_activate_
CODE32 CODE32
QK_irq: QK_irq:
@ -114,15 +114,15 @@ QK_irq:
STR r12,[r0,#QK_INT_NEST] ; store the value in QK_attr_.intNest STR r12,[r0,#QK_INT_NEST] ; store the value in QK_attr_.intNest
BNE QK_irq_exit ; branch if interrupt nesting not zero BNE QK_irq_exit ; branch if interrupt nesting not zero
LDR r12,=QK_schedPrio_
MOV lr,pc ; copy the return address to link register
BX r12 ; call QK_schedPrio_ (ARM/THUMB)
CMP r0,#0 ; check the returned priority
BEQ QK_irq_exit ; branch if priority zero
LDR r12,=QK_sched_ LDR r12,=QK_sched_
MOV lr,pc ; copy the return address to link register MOV lr,pc ; copy the return address to link register
BX r12 ; call QK_sched_ (ARM/THUMB) BX r12 ; call QK_sched_ (ARM/THUMB)
CMP r0,#0 ; check the returned priority
BEQ QK_irq_exit ; branch if priority zero
LDR r12,=QK_activate_
MOV lr,pc ; copy the return address to link register
BX r12 ; call QK_activate_ (ARM/THUMB)
QK_irq_exit: QK_irq_exit:
; IRQ exit {{{ ; IRQ/FIQ disabled--return from scheduler ; IRQ exit {{{ ; IRQ/FIQ disabled--return from scheduler

View File

@ -80,17 +80,17 @@ void QF_setEmbOsTaskAttr(QMActive *act, uint32_t attr);
#ifdef QP_IMPL #ifdef QP_IMPL
// embOS-specific scheduler locking, see NOTE3 // embOS-specific scheduler locking, see NOTE3
#define QF_SCHED_STAT_TYPE_ struct { uint_fast8_t m_lockPrio; } #define QF_SCHED_STAT_
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \ #define QF_SCHED_LOCK_(dummy) do { \
if (OS_InInt != (OS_U8)0) { \ if (OS_InInt == static_cast<OS_U8>(0)) { \
(pLockStat_)->m_lockPrio = \
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
} else { \
(pLockStat_)->m_lockPrio = (prio_); \
OS_EnterRegion(); \ OS_EnterRegion(); \
} \ } \
} while (0) } while (false)
#define QF_SCHED_UNLOCK_(dummy) OS_LeaveRegion() #define QF_SCHED_UNLOCK_() do { \
if (OS_InInt == static_cast<OS_U8>(0)) { \
OS_LeaveRegion(); \
} \
} while (false)
// native QF event pool operations... // native QF event pool operations...
#define QF_EPOOL_TYPE_ QMPool #define QF_EPOOL_TYPE_ QMPool

View File

@ -2,7 +2,7 @@
:: =========================================================================== :: ===========================================================================
:: Product: QP/C++ build script for PC-Lint(TM), Standard C++ compiler :: Product: QP/C++ build script for PC-Lint(TM), Standard C++ compiler
: Last Updated for Version: 5.6.0 : Last Updated for Version: 5.6.0
:: Date of the Last Update: 2015-12-14 :: Date of the Last Update: 2015-12-14
:: ::
:: Q u a n t u m L e a P s :: Q u a n t u m L e a P s
:: --------------------------- :: ---------------------------

View File

@ -1,7 +1,7 @@
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
// Product: PC-Lint 9.x option file for linting QP/C++ // Product: PC-Lint 9.x option file for linting QP/C++
// Last updated for version 5.7.0 // Last updated for version 5.7.2
// Last updated on 2016-09-08 // Last updated on 2016-09-28
// //
// Q u a n t u m L e a P s // Q u a n t u m L e a P s
// --------------------------- // ---------------------------
@ -77,6 +77,10 @@ qpcpp.lnt // QP/C++ options
act) act)
-efunc(661, *execTatbl_) // Possible access of out-of-bounds pointer -efunc(661, *execTatbl_) // Possible access of out-of-bounds pointer
-efunc(644, *enterHistory_) // Variable 'entry' may not be initialized -efunc(644, *enterHistory_) // Variable 'entry' may not be initialized
-esym(1511, // Member hides non-virtual member
QP::QMsm::isInState,
QP::QMsm::stateObj,
QP::QMsm::childStateObj)
// QF // QF
-emacro(1960, QF_EVT_CONST_CAST_) // 5-2-5 attempt to cast away const -emacro(1960, QF_EVT_CONST_CAST_) // 5-2-5 attempt to cast away const
@ -107,8 +111,7 @@ qpcpp.lnt // QP/C++ options
QF_PTR_RANGE_, QF_PTR_RANGE_,
QF_PTR_INC_, QF_PTR_INC_,
QF_QACTIVE_TO_QHSM_CAST_) QF_QACTIVE_TO_QHSM_CAST_)
-esym(1923, -esym(1923, // 16-2-2 Macro could become const variable
QF_SCHED_STAT_TYPE_, // 16-2-2 Macro could become const variable
QF_SCHED_LOCK_, QF_SCHED_LOCK_,
QF_SCHED_UNLOCK_) QF_SCHED_UNLOCK_)
-emacro(740, // 5-2-7 Unusual pointer cast (incompatible indirect types) -emacro(740, // 5-2-7 Unusual pointer cast (incompatible indirect types)

View File

@ -54,7 +54,7 @@
/// and your own application code. The consistency is guaranteed if you define /// and your own application code. The consistency is guaranteed if you define
/// this macro only once in the qf_port.h header file and henceforth include /// this macro only once in the qf_port.h header file and henceforth include
/// this header file in all builds. /// this header file in all builds.
#define QF_MAX_ACTIVE 63 #define QF_MAX_ACTIVE 32
//! The maximum number of event pools in the application. //! The maximum number of event pools in the application.
/// ///

View File

@ -2,8 +2,8 @@
/// @brief QK/C++ port to Lint, Generic C++ compiler /// @brief QK/C++ port to Lint, Generic C++ compiler
/// @cond /// @cond
///*************************************************************************** ///***************************************************************************
/// Last updated for version 5.6.0 /// Last updated for version 5.7.2
/// Last updated on 2015-12-29 /// Last updated on 2016-09-26
/// ///
/// Q u a n t u m L e a P s /// Q u a n t u m L e a P s
/// --------------------------- /// ---------------------------
@ -73,8 +73,7 @@
/// QK ports will not define this macro, but instead will provide ISR /// QK ports will not define this macro, but instead will provide ISR
/// skeleton code in assembly. /// skeleton code in assembly.
#define QK_ISR_ENTRY() do { \ #define QK_ISR_ENTRY() do { \
++QK_intNest_; \ ++QK_attr_.intNest; \
QF_QS_ISR_ENTRY(QK_intNest_, QK_currPrio_); \
} while (false) } while (false)
@ -85,11 +84,10 @@
/// QK ports will not define this macro, but instead will provide ISR /// QK ports will not define this macro, but instead will provide ISR
/// skeleton code in assembly. /// skeleton code in assembly.
#define QK_ISR_EXIT() do { \ #define QK_ISR_EXIT() do { \
--QK_intNest_; \ --QK_attr_.intNest; \
if (QK_intNest_ == static_cast<uint_fast8_t>(0)) { \ if (QK_attr_.intNest == static_cast<uint_fast8_t>(0)) { \
uint_fast8_t p = QK_schedPrio_(); \ if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
if (p != static_cast<uint_fast8_t>(0)) { \ QK_activate_(); \
QK_sched_(p); \
} \ } \
} \ } \
else { \ else { \

View File

@ -60,7 +60,7 @@
/// and your own application code. The consistency is guaranteed if you define /// and your own application code. The consistency is guaranteed if you define
/// this macro only once in the qf_port.h header file and henceforth include /// this macro only once in the qf_port.h header file and henceforth include
/// this header file in all builds. /// this header file in all builds.
#define QF_MAX_ACTIVE 63 #define QF_MAX_ACTIVE 32
/// \brief The maximum number of event pools in the application. /// \brief The maximum number of event pools in the application.
/// ///

View File

@ -54,7 +54,7 @@
/// and your own application code. The consistency is guaranteed if you define /// and your own application code. The consistency is guaranteed if you define
/// this macro only once in the qf_port.h header file and henceforth include /// this macro only once in the qf_port.h header file and henceforth include
/// this header file in all builds. /// this header file in all builds.
#define QF_MAX_ACTIVE 63 #define QF_MAX_ACTIVE 32
//! The maximum number of event pools in the application. //! The maximum number of event pools in the application.
/// ///

Some files were not shown because too many files have changed in this diff Show More