mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-14 05:42:57 +08:00
5.7.2
This commit is contained in:
parent
14dffcaf2b
commit
655608b020
@ -5,7 +5,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "QP/C++"
|
||||
PROJECT_NUMBER = "5.7.0"
|
||||
PROJECT_NUMBER = "5.7.2"
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO = images/header_logo_ql.png
|
||||
OUTPUT_DIRECTORY =
|
||||
|
@ -5,7 +5,7 @@
|
||||
#---------------------------------------------------------------------------
|
||||
DOXYFILE_ENCODING = UTF-8
|
||||
PROJECT_NAME = "QP/C++"
|
||||
PROJECT_NUMBER = "5.7.0"
|
||||
PROJECT_NUMBER = "5.7.2"
|
||||
PROJECT_BRIEF =
|
||||
PROJECT_LOGO = images/header_logo_ql.png
|
||||
OUTPUT_DIRECTORY =
|
||||
|
@ -2,6 +2,35 @@ namespace QP {
|
||||
|
||||
/** @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
|
||||
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.
|
||||
|
||||
|
@ -16,6 +16,17 @@ namespace QP {
|
||||
/// failures when the switch Q_NASSERT is defined.
|
||||
#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
|
||||
/// instrumentation in the code
|
||||
///
|
||||
|
@ -1,8 +1,8 @@
|
||||
@echo off
|
||||
:: ==========================================================================
|
||||
:: Product: QP/C++ script for generating Doxygen documentation
|
||||
:: Last Updated for Version: 5.7.0
|
||||
:: Date of the Last Update: 2016-08-31
|
||||
:: Last Updated for Version: 5.7.2
|
||||
:: Date of the Last Update: 2016-09-29
|
||||
::
|
||||
:: Q u a n t u m L e a P s
|
||||
:: ---------------------------
|
||||
@ -38,7 +38,7 @@ echo usage:
|
||||
echo make
|
||||
echo make -CHM
|
||||
|
||||
set VERSION=5.7.0
|
||||
set VERSION=5.7.2
|
||||
|
||||
:: Generate Resource Standard Metrics for QP/C++ .............................
|
||||
set DOXHOME="C:\tools\doxygen\bin"
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -25,7 +25,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
|
||||
#ifdef qxk_h
|
||||
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
|
||||
#endif // qxk_h
|
||||
|
@ -40,7 +40,8 @@ if (!registered) {
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
|
||||
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
|
||||
|
||||
me->subscribe(EAT_SIG);</action>
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);</action>
|
||||
<initial_glyph conn="2,3,5,1,20,5,-3">
|
||||
<action box="0,-2,6,2"/>
|
||||
</initial_glyph>
|
||||
@ -49,17 +50,22 @@ me->subscribe(EAT_SIG);</action>
|
||||
<entry>me->m_timeEvt.armX(think_time(), 0U);</entry>
|
||||
<exit>(void)me->m_timeEvt.disarm();</exit>
|
||||
<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"/>
|
||||
</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)->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"/>
|
||||
</tran_glyph>
|
||||
</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">
|
||||
<entry box="1,2,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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
me->m_fork[n] = FREE;
|
||||
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
</initial_glyph>
|
||||
</initial>
|
||||
<state name="active">
|
||||
<tran trig="TERMINATE">
|
||||
<action>BSP::terminate(0);</action>
|
||||
<tran trig="TEST">
|
||||
<tran_glyph conn="2,11,3,-1,14">
|
||||
<action box="0,-2,11,4"/>
|
||||
</tran_glyph>
|
||||
@ -318,7 +323,8 @@ me->m_fork[n] = FREE;</action>
|
||||
</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"/>
|
||||
<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>
|
||||
<directory name=".">
|
||||
<file name="dpp.h">
|
||||
@ -332,7 +338,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
|
||||
$declare(AOs::AO_Table)
|
||||
|
||||
#ifdef qxk_h
|
||||
$declare(AOs::XT_Test)
|
||||
$declare(AOs::XT_Test1)
|
||||
$declare(AOs::XT_Test2)
|
||||
#endif // qxk_h
|
||||
|
||||
#endif // dpp_h</text>
|
||||
|
@ -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
|
||||
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
return QM_TRAN_INIT(&tatbl_);
|
||||
}
|
||||
//${AOs::Philo::SM::thinking} ................................................
|
||||
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
// ${AOs::Philo::SM::thinking::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = QM_SUPER();
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)me; // avoid compiler warning in case 'me' is not used
|
||||
return status_;
|
||||
}
|
||||
//${AOs::Philo::SM::hungry} ..................................................
|
||||
|
@ -40,10 +40,8 @@ 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
|
||||
|
@ -1,7 +1,7 @@
|
||||
///***************************************************************************
|
||||
// Product: DPP example, EFM32-SLSTK3401A board, preemptive QXK kernel
|
||||
// Last Updated for Version: 5.6.5
|
||||
// Date of the Last Update: 2016-06-02
|
||||
// Last Updated for Version: 5.7.2
|
||||
// Date of the Last Update: 2016-09-29
|
||||
//
|
||||
// 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
|
||||
PHILO_STAT = QP::QS_USER,
|
||||
PAUSED_STAT,
|
||||
COMMAND_STAT
|
||||
};
|
||||
|
||||
@ -155,8 +156,10 @@ void GPIO_EVEN_IRQHandler(void) {
|
||||
QXK_ISR_ENTRY(); // inform QXK about entering an ISR
|
||||
|
||||
// for testing...
|
||||
DPP::AO_Table->POST(Q_NEW(QP::QEvt, DPP::MAX_PUB_SIG),
|
||||
&l_GPIO_EVEN_IRQHandler);
|
||||
//DPP::AO_Table->POST(Q_NEW(QP::QEvt, DPP::MAX_PUB_SIG),
|
||||
// &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
|
||||
}
|
||||
@ -217,6 +220,7 @@ void BSP::init(void) {
|
||||
QS_OBJ_DICTIONARY(&l_SysTick_Handler);
|
||||
QS_OBJ_DICTIONARY(&l_GPIO_EVEN_IRQHandler);
|
||||
QS_USR_DICTIONARY(PHILO_STAT);
|
||||
QS_USR_DICTIONARY(PAUSED_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) {
|
||||
if (paused != 0U) {
|
||||
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 {
|
||||
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
|
||||
@ -260,16 +273,16 @@ uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
|
||||
}
|
||||
//............................................................................
|
||||
void BSP::randomSeed(uint32_t seed) {
|
||||
l_rnd = seed;
|
||||
l_rndMutex.init(N_PHILO); // ceiling <== maximum Philo priority
|
||||
l_rnd = seed;
|
||||
}
|
||||
//............................................................................
|
||||
void BSP::ledOn(void) {
|
||||
GPIO->P[LED_PORT].DOUT |= (1U << LED0_PIN);
|
||||
GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN);
|
||||
}
|
||||
//............................................................................
|
||||
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) {
|
||||
@ -313,10 +326,14 @@ void QF::onCleanup(void) {
|
||||
//............................................................................
|
||||
void QXK::onIdle(void) {
|
||||
// toggle the User LED on and then off, see NOTE01
|
||||
QF_INT_DISABLE();
|
||||
GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN);
|
||||
GPIO->P[LED_PORT].DOUT &= ~(1U << LED1_PIN);
|
||||
QF_INT_ENABLE();
|
||||
// QF_INT_DISABLE();
|
||||
// GPIO->P[LED_PORT].DOUT |= (1U << LED1_PIN);
|
||||
// GPIO->P[LED_PORT].DOUT &= ~(1U << LED1_PIN);
|
||||
// QF_INT_ENABLE();
|
||||
|
||||
// Some flating point code is to exercise the VFP...
|
||||
float volatile x = 1.73205F;
|
||||
x = x * 1.73205F;
|
||||
|
||||
#ifdef Q_SPY
|
||||
QS::rxParse(); // parse all the received bytes
|
||||
|
@ -1,7 +1,7 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.2
|
||||
// Last updated on 2016-03-31
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
@ -38,23 +38,18 @@
|
||||
//............................................................................
|
||||
int main() {
|
||||
static QP::QEvt const *tableQueueSto[N_PHILO];
|
||||
static uint64_t tableStackSto[64];
|
||||
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 QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
|
||||
|
||||
// stack for the "naked" test thread
|
||||
static QP::QEvt const *testQueueSto[5];
|
||||
static uint64_t testStackSto[64];
|
||||
|
||||
// stack for the QXK's idle thread
|
||||
static uint64_t idleStackSto[32];
|
||||
|
||||
// stacks and queues for the extended test threads
|
||||
static QP::QEvt const *test1QueueSto[5];
|
||||
static uint64_t test1StackSto[64];
|
||||
static QP::QEvt const *test2QueueSto[5];
|
||||
static uint64_t test2StackSto[64];
|
||||
|
||||
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
|
||||
|
||||
// object dictionaries...
|
||||
@ -72,33 +67,42 @@ int main() {
|
||||
QP::QF::poolInit(smlPoolSto,
|
||||
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) {
|
||||
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
|
||||
Q_DIM(philoQueueSto[n]), // queue length [events]
|
||||
philoStackSto[n], // stack storage
|
||||
sizeof(philoStackSto[n]), // stack size [bytes]
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
static_cast<QP::QEvt *>(0)); // initialization event
|
||||
}
|
||||
|
||||
DPP::AO_Table->start(
|
||||
static_cast<uint_fast8_t>(N_PHILO + 1), // QP priority of the AO
|
||||
tableQueueSto, // event queue storage
|
||||
Q_DIM(tableQueueSto), // queue length [events]
|
||||
tableStackSto, // stack storage
|
||||
sizeof(tableStackSto), // stack size [bytes]
|
||||
// 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
|
||||
|
||||
// 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
|
||||
DPP::AO_Table->start(
|
||||
static_cast<uint_fast8_t>(N_PHILO + 3), // QP priority of the AO
|
||||
tableQueueSto, // event queue storage
|
||||
Q_DIM(tableQueueSto), // queue length [events]
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
static_cast<QP::QEvt *>(0)); // initialization event
|
||||
|
||||
return QP::QF::run(); // run the QF application
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.0
|
||||
// Last updated on 2015-12-28
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
// 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
|
||||
// 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/>.
|
||||
//
|
||||
// Contact information:
|
||||
// Web: www.state-machine.com
|
||||
// Email: info@state-machine.com
|
||||
// http://www.state-machine.com
|
||||
// mailto:info@state-machine.com
|
||||
//****************************************************************************
|
||||
#include "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
@ -37,26 +37,74 @@
|
||||
|
||||
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) {
|
||||
(void)par;
|
||||
static void Thread1_run(QP::QXThread * const /*me*/) {
|
||||
|
||||
l_mutex.init(3U);
|
||||
|
||||
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();
|
||||
|
||||
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 .........................................
|
||||
QP::QXThread * const XT_Test = &l_test;
|
||||
// subscribe to the test signal */
|
||||
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
|
||||
|
@ -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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
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 status_;
|
||||
switch (e->sig) {
|
||||
// ${AOs::Table::SM::active::TERMINATE}
|
||||
case TERMINATE_SIG: {
|
||||
BSP::terminate(0);
|
||||
// ${AOs::Table::SM::active::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
|
||||
#ifdef qxk_h
|
||||
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
|
||||
#endif // qxk_h
|
||||
|
@ -40,7 +40,8 @@ if (!registered) {
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
|
||||
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
|
||||
|
||||
me->subscribe(EAT_SIG);</action>
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);</action>
|
||||
<initial_glyph conn="2,3,5,1,20,5,-3">
|
||||
<action box="0,-2,6,2"/>
|
||||
</initial_glyph>
|
||||
@ -49,17 +50,22 @@ me->subscribe(EAT_SIG);</action>
|
||||
<entry>me->m_timeEvt.armX(think_time(), 0U);</entry>
|
||||
<exit>(void)me->m_timeEvt.disarm();</exit>
|
||||
<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"/>
|
||||
</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)->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"/>
|
||||
</tran_glyph>
|
||||
</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">
|
||||
<entry box="1,2,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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
me->m_fork[n] = FREE;
|
||||
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
</initial_glyph>
|
||||
</initial>
|
||||
<state name="active">
|
||||
<tran trig="TERMINATE">
|
||||
<action>BSP::terminate(0);</action>
|
||||
<tran trig="TEST">
|
||||
<tran_glyph conn="2,11,3,-1,14">
|
||||
<action box="0,-2,11,4"/>
|
||||
</tran_glyph>
|
||||
@ -318,7 +323,8 @@ me->m_fork[n] = FREE;</action>
|
||||
</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"/>
|
||||
<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>
|
||||
<directory name=".">
|
||||
<file name="dpp.h">
|
||||
@ -332,7 +338,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
|
||||
$declare(AOs::AO_Table)
|
||||
|
||||
#ifdef qxk_h
|
||||
$declare(AOs::XT_Test)
|
||||
$declare(AOs::XT_Test1)
|
||||
$declare(AOs::XT_Test2)
|
||||
#endif // qxk_h
|
||||
|
||||
#endif // dpp_h</text>
|
||||
|
@ -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
|
||||
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
return QM_TRAN_INIT(&tatbl_);
|
||||
}
|
||||
//${AOs::Philo::SM::thinking} ................................................
|
||||
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
// ${AOs::Philo::SM::thinking::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = QM_SUPER();
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)me; // avoid compiler warning in case 'me' is not used
|
||||
return status_;
|
||||
}
|
||||
//${AOs::Philo::SM::hungry} ..................................................
|
||||
|
@ -87,13 +87,28 @@
|
||||
</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_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"/>
|
||||
</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"/>
|
||||
<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">
|
||||
<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"/>
|
||||
<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"/>
|
||||
<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 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>
|
||||
<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=""/>
|
||||
|
@ -230,15 +230,14 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
|
||||
//............................................................................
|
||||
void BSP::displayPaused(uint8_t paused) {
|
||||
//GPIOF->DATA_Bits[LED_RED] = ((paused != 0U) ? 0xFFU : 0U);
|
||||
|
||||
static QP::QEvt const pauseEvt = { PAUSE_SIG, 0U, 0U};
|
||||
XT_Test->POST_X(&pauseEvt, 1U, (void *)0);
|
||||
//XT_Test->unblock(); //??? unblock the Test thread
|
||||
XT_Test2->POST_X(&pauseEvt, 1U, (void *)0);
|
||||
|
||||
// application-specific trace record
|
||||
QS_BEGIN(PAUSED_STAT, AO_Table)
|
||||
QS_U8(1, paused); // Paused status
|
||||
QS_END()
|
||||
|
||||
}
|
||||
//............................................................................
|
||||
uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
|
||||
|
@ -1,7 +1,7 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.2
|
||||
// Last updated on 2016-03-31
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
@ -38,23 +38,18 @@
|
||||
//............................................................................
|
||||
int main() {
|
||||
static QP::QEvt const *tableQueueSto[N_PHILO];
|
||||
static uint64_t tableStackSto[64];
|
||||
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 QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
|
||||
|
||||
// stack for the "naked" test thread
|
||||
static QP::QEvt const *testQueueSto[5];
|
||||
static uint64_t testStackSto[64];
|
||||
|
||||
// stack for the QXK's idle thread
|
||||
static uint64_t idleStackSto[32];
|
||||
|
||||
// stacks and queues for the extended test threads
|
||||
static QP::QEvt const *test1QueueSto[5];
|
||||
static uint64_t test1StackSto[64];
|
||||
static QP::QEvt const *test2QueueSto[5];
|
||||
static uint64_t test2StackSto[64];
|
||||
|
||||
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
|
||||
|
||||
// object dictionaries...
|
||||
@ -72,33 +67,42 @@ int main() {
|
||||
QP::QF::poolInit(smlPoolSto,
|
||||
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) {
|
||||
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
|
||||
Q_DIM(philoQueueSto[n]), // queue length [events]
|
||||
philoStackSto[n], // stack storage
|
||||
sizeof(philoStackSto[n]), // stack size [bytes]
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
static_cast<QP::QEvt *>(0)); // initialization event
|
||||
}
|
||||
|
||||
DPP::AO_Table->start(
|
||||
static_cast<uint_fast8_t>(N_PHILO + 1), // QP priority of the AO
|
||||
tableQueueSto, // event queue storage
|
||||
Q_DIM(tableQueueSto), // queue length [events]
|
||||
tableStackSto, // stack storage
|
||||
sizeof(tableStackSto), // stack size [bytes]
|
||||
// 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
|
||||
|
||||
// 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
|
||||
DPP::AO_Table->start(
|
||||
static_cast<uint_fast8_t>(N_PHILO + 3), // QP priority of the AO
|
||||
tableQueueSto, // event queue storage
|
||||
Q_DIM(tableQueueSto), // queue length [events]
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
static_cast<QP::QEvt *>(0)); // initialization event
|
||||
|
||||
return QP::QF::run(); // run the QF application
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.0
|
||||
// Last updated on 2015-12-28
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
// 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
|
||||
// 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/>.
|
||||
//
|
||||
// Contact information:
|
||||
// Web: www.state-machine.com
|
||||
// Email: info@state-machine.com
|
||||
// http://www.state-machine.com
|
||||
// mailto:info@state-machine.com
|
||||
//****************************************************************************
|
||||
#include "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
@ -37,26 +37,74 @@
|
||||
|
||||
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) {
|
||||
(void)par;
|
||||
static void Thread1_run(QP::QXThread * const /*me*/) {
|
||||
|
||||
l_mutex.init(3U);
|
||||
|
||||
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();
|
||||
|
||||
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 .........................................
|
||||
QP::QXThread * const XT_Test = &l_test;
|
||||
// subscribe to the test signal */
|
||||
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
|
||||
|
@ -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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
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 status_;
|
||||
switch (e->sig) {
|
||||
// ${AOs::Table::SM::active::TERMINATE}
|
||||
case TERMINATE_SIG: {
|
||||
BSP::terminate(0);
|
||||
// ${AOs::Table::SM::active::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -62,7 +62,12 @@ extern QP::QMActive * const AO_Table;
|
||||
#ifdef qxk_h
|
||||
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
|
||||
#endif // qxk_h
|
||||
|
@ -40,7 +40,8 @@ if (!registered) {
|
||||
QS_SIG_DICTIONARY(HUNGRY_SIG, me); // signal for each Philos
|
||||
QS_SIG_DICTIONARY(TIMEOUT_SIG, me); // signal for each Philos
|
||||
|
||||
me->subscribe(EAT_SIG);</action>
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);</action>
|
||||
<initial_glyph conn="2,3,5,1,20,5,-3">
|
||||
<action box="0,-2,6,2"/>
|
||||
</initial_glyph>
|
||||
@ -49,17 +50,22 @@ me->subscribe(EAT_SIG);</action>
|
||||
<entry>me->m_timeEvt.armX(think_time(), 0U);</entry>
|
||||
<exit>(void)me->m_timeEvt.disarm();</exit>
|
||||
<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"/>
|
||||
</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)->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"/>
|
||||
</tran_glyph>
|
||||
</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">
|
||||
<entry box="1,2,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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
me->m_fork[n] = FREE;
|
||||
@ -162,8 +168,7 @@ for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
</initial_glyph>
|
||||
</initial>
|
||||
<state name="active">
|
||||
<tran trig="TERMINATE">
|
||||
<action>BSP::terminate(0);</action>
|
||||
<tran trig="TEST">
|
||||
<tran_glyph conn="2,11,3,-1,14">
|
||||
<action box="0,-2,11,4"/>
|
||||
</tran_glyph>
|
||||
@ -318,7 +323,8 @@ me->m_fork[n] = FREE;</action>
|
||||
</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"/>
|
||||
<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>
|
||||
<directory name=".">
|
||||
<file name="dpp.h">
|
||||
@ -332,7 +338,7 @@ enum DPPSignals {
|
||||
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
|
||||
TEST_SIG, // published by BSP to test the application
|
||||
MAX_PUB_SIG, // the last published signal
|
||||
|
||||
HUNGRY_SIG, // posted direclty to Table from hungry Philo
|
||||
@ -351,7 +357,8 @@ $declare(AOs::AO_Philo[N_PHILO])
|
||||
$declare(AOs::AO_Table)
|
||||
|
||||
#ifdef qxk_h
|
||||
$declare(AOs::XT_Test)
|
||||
$declare(AOs::XT_Test1)
|
||||
$declare(AOs::XT_Test2)
|
||||
#endif // qxk_h
|
||||
|
||||
#endif // dpp_h</text>
|
||||
|
@ -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
|
||||
|
||||
me->subscribe(EAT_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
return QM_TRAN_INIT(&tatbl_);
|
||||
}
|
||||
//${AOs::Philo::SM::thinking} ................................................
|
||||
@ -182,11 +183,17 @@ QP::QState Philo::thinking(Philo * const me, QP::QEvt const * const e) {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
// ${AOs::Philo::SM::thinking::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
status_ = QM_SUPER();
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void)me; // avoid compiler warning in case 'me' is not used
|
||||
return status_;
|
||||
}
|
||||
//${AOs::Philo::SM::hungry} ..................................................
|
||||
|
@ -1,13 +1,13 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.0
|
||||
// Last updated on 2015-12-26
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
// 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
|
||||
// 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/>.
|
||||
//
|
||||
// Contact information:
|
||||
// Web: www.state-machine.com
|
||||
// Email: info@state-machine.com
|
||||
// http://www.state-machine.com
|
||||
// mailto:info@state-machine.com
|
||||
//****************************************************************************
|
||||
#include "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
@ -38,23 +38,18 @@
|
||||
//............................................................................
|
||||
int main() {
|
||||
static QP::QEvt const *tableQueueSto[N_PHILO];
|
||||
static uint64_t tableStackSto[32];
|
||||
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 QF_MPOOL_EL(DPP::TableEvt) smlPoolSto[2*N_PHILO]; // small pool
|
||||
|
||||
// stack for the "naked" test thread
|
||||
static QP::QEvt const *testQueueSto[5];
|
||||
static uint64_t testStackSto[32];
|
||||
|
||||
// stack for the QXK's idle thread
|
||||
static uint64_t idleStackSto[16];
|
||||
|
||||
// stacks and queues for the extended test threads
|
||||
static QP::QEvt const *test1QueueSto[5];
|
||||
static uint64_t test1StackSto[64];
|
||||
static QP::QEvt const *test2QueueSto[5];
|
||||
static uint64_t test2StackSto[64];
|
||||
|
||||
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
|
||||
|
||||
// object dictionaries...
|
||||
@ -72,35 +67,42 @@ int main() {
|
||||
QP::QF::poolInit(smlPoolSto,
|
||||
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) {
|
||||
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
|
||||
Q_DIM(philoQueueSto[n]), // queue length [events]
|
||||
philoStackSto[n], // stack storage
|
||||
sizeof(philoStackSto[n]), // stack size [bytes]
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
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(
|
||||
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
|
||||
Q_DIM(tableQueueSto), // queue length [events]
|
||||
tableStackSto, // stack storage
|
||||
sizeof(tableStackSto), // stack size [bytes]
|
||||
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
|
||||
static_cast<void *>(0), // no stack storage
|
||||
static_cast<uint_fast16_t>(0), // stack size [bytes]
|
||||
static_cast<QP::QEvt *>(0)); // initialization event
|
||||
|
||||
return QP::QF::run(); // run the QF application
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
//****************************************************************************
|
||||
// DPP example for QXK
|
||||
// Last updated for version 5.6.0
|
||||
// Last updated on 2015-12-28
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
// 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
|
||||
// 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/>.
|
||||
//
|
||||
// Contact information:
|
||||
// Web: www.state-machine.com
|
||||
// Email: info@state-machine.com
|
||||
// http://www.state-machine.com
|
||||
// mailto:info@state-machine.com
|
||||
//****************************************************************************
|
||||
#include "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
@ -37,26 +37,74 @@
|
||||
|
||||
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) {
|
||||
(void)par;
|
||||
static void Thread1_run(QP::QXThread * const /*me*/) {
|
||||
|
||||
l_mutex.init(3U);
|
||||
|
||||
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();
|
||||
|
||||
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 .........................................
|
||||
QP::QXThread * const XT_Test = &l_test;
|
||||
// subscribe to the test signal */
|
||||
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
|
||||
|
@ -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(PAUSE_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
|
||||
|
||||
me->subscribe(DONE_SIG);
|
||||
me->subscribe(PAUSE_SIG);
|
||||
me->subscribe(SERVE_SIG);
|
||||
me->subscribe(TERMINATE_SIG);
|
||||
me->subscribe(TEST_SIG);
|
||||
|
||||
for (uint8_t n = 0U; n < N_PHILO; ++n) {
|
||||
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 status_;
|
||||
switch (e->sig) {
|
||||
// ${AOs::Table::SM::active::TERMINATE}
|
||||
case TERMINATE_SIG: {
|
||||
BSP::terminate(0);
|
||||
// ${AOs::Table::SM::active::TEST}
|
||||
case TEST_SIG: {
|
||||
status_ = QM_HANDLED();
|
||||
break;
|
||||
}
|
||||
|
@ -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>
|
@ -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.
|
@ -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>
|
@ -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.
|
@ -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>
|
@ -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.
|
@ -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>
|
@ -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.
|
@ -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>
|
@ -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.
|
@ -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>
|
@ -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.
|
60
examples/win32/dpp.try/.dpp
Normal file
60
examples/win32/dpp.try/.dpp
Normal 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>
|
261
examples/win32/dpp.try/Makefile
Normal file
261
examples/win32/dpp.try/Makefile
Normal 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)
|
249
examples/win32/dpp.try/bsp.cpp
Normal file
249
examples/win32/dpp.try/bsp.cpp
Normal 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
|
52
examples/win32/dpp.try/bsp.h
Normal file
52
examples/win32/dpp.try/bsp.h
Normal 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
|
62
examples/win32/dpp.try/dpp.h
Normal file
62
examples/win32/dpp.try/dpp.h
Normal 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
|
448
examples/win32/dpp.try/dpp.qm
Normal file
448
examples/win32/dpp.try/dpp.qm
Normal 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(&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(&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);</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->m_timeEvt.armX(think_time(), 0U);</entry>
|
||||
<exit>(void)me->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)->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->philoNum = PHILO_ID(me);
|
||||
AO_Table->POST(pe, me);</entry>
|
||||
<tran trig="EAT">
|
||||
<choice target="../../../3">
|
||||
<guard>Q_EVT_CAST(TableEvt)->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)->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->m_timeEvt.armX(eat_time(), 0U);</entry>
|
||||
<exit>TableEvt *pe = Q_NEW(TableEvt, DONE_SIG);
|
||||
pe->philoNum = PHILO_ID(me);
|
||||
QP::QF::PUBLISH(pe, me);
|
||||
(void)me->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)->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(&Table::initial))
|
||||
|
||||
for (uint8_t n = 0U; n < 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(&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);
|
||||
}</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 < 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);
|
||||
}
|
||||
}</entry>
|
||||
<tran trig="HUNGRY">
|
||||
<action>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);</action>
|
||||
<choice>
|
||||
<guard brief="both free">(me->m_fork[m] == FREE) && (me->m_fork[n] == FREE)</guard>
|
||||
<action>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);</action>
|
||||
<choice_glyph conn="19,26,5,-1,10">
|
||||
<action box="1,0,10,2"/>
|
||||
</choice_glyph>
|
||||
</choice>
|
||||
<choice>
|
||||
<guard>else</guard>
|
||||
<action>me->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)->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);
|
||||
}</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)->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);</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)->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;</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 "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
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<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 ----------------------------------------------------------
|
||||
$define(AOs::Philo)</text>
|
||||
</file>
|
||||
<file name="table.cpp">
|
||||
<text>#include "qpcpp.h"
|
||||
#include "dpp.h"
|
||||
#include "bsp.h"
|
||||
|
||||
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<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
|
||||
|
||||
//............................................................................
|
||||
$define(AOs::Table)</text>
|
||||
</file>
|
||||
</directory>
|
||||
</model>
|
25
examples/win32/dpp.try/dpp.sln
Normal file
25
examples/win32/dpp.try/dpp.sln
Normal 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
|
165
examples/win32/dpp.try/dpp.vcxproj
Normal file
165
examples/win32/dpp.try/dpp.vcxproj
Normal 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>
|
22
examples/win32/dpp.try/dpp.vcxproj.filters
Normal file
22
examples/win32/dpp.try/dpp.vcxproj.filters
Normal 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>
|
77
examples/win32/dpp.try/main.cpp
Normal file
77
examples/win32/dpp.try/main.cpp
Normal 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
|
||||
}
|
303
examples/win32/dpp.try/philo.cpp
Normal file
303
examples/win32/dpp.try/philo.cpp
Normal 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
|
311
examples/win32/dpp.try/table.cpp
Normal file
311
examples/win32/dpp.try/table.cpp
Normal 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
|
@ -3,8 +3,8 @@
|
||||
/// @ingroup qep
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-09
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-25
|
||||
///
|
||||
/// 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
|
||||
// major version number, Y is a 1-digit minor version number, and Z is
|
||||
// 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
|
||||
// a 1-digit major version number, Y is a 1-digit minor version 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)
|
||||
#define QP_RELEASE 0xA02320D5U
|
||||
//! Tamperproof current QP release (5.7.2) and date (16-10-07)
|
||||
#define QP_RELEASE 0xA00845D3U
|
||||
|
||||
//****************************************************************************
|
||||
#ifndef Q_SIGNAL_SIZE
|
||||
@ -525,6 +525,16 @@ public:
|
||||
//! Obtain the current active child state of a given 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 constructor of a HSM
|
||||
QHsm(QStateHandler const initial);
|
||||
|
56
include/qf.h
56
include/qf.h
@ -3,8 +3,8 @@
|
||||
/// @ingroup qf
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-09-14
|
||||
/// Last updated for version 5.7.1
|
||||
/// Last updated on 2016-09-23
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -40,15 +40,14 @@
|
||||
#define qf_h
|
||||
|
||||
//****************************************************************************
|
||||
#ifndef qpset_h
|
||||
#include "qpset.h"
|
||||
#endif
|
||||
|
||||
#ifdef Q_EVT_CTOR
|
||||
#include <new> // for placement new
|
||||
#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
|
||||
//
|
||||
@ -155,6 +154,7 @@ class QMActive : public QMsm {
|
||||
QF_EQUEUE_TYPE m_eQueue;
|
||||
#endif
|
||||
|
||||
public: // for access from extern "C" functions
|
||||
#ifdef QF_OS_OBJECT_TYPE
|
||||
//! OS-dependent per-thread object.
|
||||
/// @description
|
||||
@ -313,6 +313,16 @@ public:
|
||||
protected:
|
||||
//! protected constructor (abstract class)
|
||||
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;
|
||||
#ifdef qxk_h
|
||||
friend class QXThread;
|
||||
friend void QXK_activate_(void);
|
||||
#endif // qxk_h
|
||||
};
|
||||
|
||||
|
||||
//****************************************************************************
|
||||
//! The size of the Subscriber list bit array
|
||||
/// @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
|
||||
//! Subscriber List
|
||||
/// @description
|
||||
/// 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.
|
||||
class QSubscrList {
|
||||
private:
|
||||
typedef QPSet QSubscrList;
|
||||
|
||||
//! 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.
|
||||
|
58
include/qk.h
58
include/qk.h
@ -3,8 +3,8 @@
|
||||
/// @ingroup qk
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-28
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -57,32 +57,31 @@
|
||||
extern "C" {
|
||||
|
||||
struct QK_Attr {
|
||||
uint_fast8_t volatile curr; //!< priority of the current executing AO
|
||||
uint_fast8_t volatile next; //!< priority of the next AO to execute
|
||||
void volatile *aux; //!< auxiliary attribute used in the port
|
||||
uint_fast8_t volatile actPrio; //!< prio of the active AO
|
||||
uint_fast8_t volatile nextPrio; //!< prio of the next AO to execute
|
||||
uint_fast8_t volatile lockPrio; //!< lock prio (0 == no-lock)
|
||||
uint_fast8_t volatile lockHolder; //!< prio of the lock holder
|
||||
#ifndef QK_ISR_CONTEXT_
|
||||
uint_fast8_t volatile intNest; //!< ISR nesting level
|
||||
#endif // QK_ISR_CONTEXT_
|
||||
#if (QF_MAX_ACTIVE <= 8)
|
||||
QP::QPSet8 readySet; //!< QK ready-set of AOs and "naked" threads
|
||||
#else
|
||||
QP::QPSet64 readySet; //!< QK ready-set of AOs and "naked" threads
|
||||
#endif
|
||||
QP::QPSet readySet; //!< QK ready-set of AOs and "naked" threads
|
||||
};
|
||||
|
||||
//! global attributes of the QK kernel
|
||||
extern QK_Attr QK_attr_;
|
||||
|
||||
//! 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);
|
||||
|
||||
//! QK scheduler
|
||||
void QK_sched_(uint_fast8_t p);
|
||||
//! QK scheduler finds the highest-priority thread ready to run
|
||||
uint_fast8_t QK_sched_(void);
|
||||
|
||||
//! Find the highest-priority task ready to run
|
||||
uint_fast8_t QK_schedPrio_(void);
|
||||
//! QK activator activates the next active object. The activated AO preempts
|
||||
// the currently executing AOs.
|
||||
void QK_activate_(void);
|
||||
|
||||
} // extern "C"
|
||||
|
||||
@ -152,24 +151,28 @@ private:
|
||||
(QK_attr_.intNest != static_cast<uint_fast8_t>(0))
|
||||
#endif // QK_ISR_CONTEXT_
|
||||
|
||||
// QF-specific scheduler locking
|
||||
//! Internal port-specific macro to represent the scheduler lock status
|
||||
// QK-specific scheduler locking
|
||||
//! Internal macro to represent the scheduler lock status
|
||||
// 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.
|
||||
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \
|
||||
//! Internal macro for selective scheduler locking.
|
||||
#define QF_SCHED_LOCK_(prio_) do { \
|
||||
if (QK_ISR_CONTEXT_()) { \
|
||||
(pLockStat_)->m_lockPrio = \
|
||||
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
|
||||
schedLock_.m_lockPrio = static_cast<uint_fast8_t>(0); \
|
||||
} else { \
|
||||
(pLockStat_)->init((prio_)); \
|
||||
(pLockStat_)->lock(); \
|
||||
schedLock_.init((prio_)); \
|
||||
schedLock_.lock(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
//! Internal port-specific macro for selective scheduler unlocking.
|
||||
#define QF_SCHED_UNLOCK_(pLockStat_) (pLockStat_)->unlock()
|
||||
//! 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_) \
|
||||
@ -178,9 +181,8 @@ private:
|
||||
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
|
||||
QK_attr_.readySet.insert((me_)->m_prio); \
|
||||
if (!QK_ISR_CONTEXT_()) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
} while (false)
|
||||
|
@ -1,7 +1,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Product: PC-Lint 9.x option file for linting QP/C++ applications
|
||||
// Last updated for version 5.7.0
|
||||
// Last updated on 2016-08-21
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-26
|
||||
//
|
||||
// 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
|
||||
QP::QMActive::m_osObject,
|
||||
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
|
||||
QF_MAX_ACTIVE,
|
||||
QF_MAX_EPOOL,
|
||||
@ -260,9 +263,8 @@
|
||||
|
||||
// QK
|
||||
-estring(1960, // 11-0-1(req) non-private data member
|
||||
curr,
|
||||
next,
|
||||
aux,
|
||||
actPrio,
|
||||
nextPrio,
|
||||
lockPrio,
|
||||
lockHolder,
|
||||
intNest,
|
||||
|
248
include/qpset.h
248
include/qpset.h
@ -3,8 +3,8 @@
|
||||
/// @ingroup qf
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-26
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -39,197 +39,195 @@
|
||||
#ifndef 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 {
|
||||
|
||||
//****************************************************************************
|
||||
// useful lookup tables
|
||||
|
||||
/****************************************************************************/
|
||||
/* Log-base-2 calculations ...*/
|
||||
#ifndef QF_LOG2
|
||||
|
||||
//! Macro to return (log2(n_) + 1), where @p 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.
|
||||
//! Lookup table for (log2(n) + 1), where n = 0..255 */
|
||||
///
|
||||
/// @description
|
||||
/// This lookup delivers the 1-based number of the most significant 1-bit
|
||||
/// of a byte.
|
||||
extern uint8_t const QF_log2Lkup[256];
|
||||
|
||||
#define QF_LOG2LKUP 1
|
||||
#endif // QF_LOG2
|
||||
//! function that returns (log2(x) + 1), where @p x is uint32_t */
|
||||
///
|
||||
/// @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.
|
||||
/// @note Index range n = 0..64. The first index (n == 0) should never
|
||||
/// be used.
|
||||
extern uint8_t const QF_pwr2Lkup[65];
|
||||
if ((x >> 16) != static_cast<uint32_t>(0)) {
|
||||
if ((x >> 24) != static_cast<uint32_t>(0)) {
|
||||
i = static_cast<uint_fast8_t>(x >> 24);
|
||||
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.
|
||||
/// @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];
|
||||
#endif // QF_LOG2
|
||||
|
||||
//****************************************************************************
|
||||
//! Priority Set of up to 8 elements for building various schedulers,
|
||||
//! but also useful as a general set of up to 8 elements of any kind.
|
||||
/// @description
|
||||
#if (QF_MAX_ACTIVE <= 32)
|
||||
//! Priority Set of up to 32 elements */
|
||||
///
|
||||
/// The priority set represents the set of active objects that are ready to
|
||||
/// run and need to be considered by scheduling processing. The set is capable
|
||||
/// of storing up to 8 priority levels.
|
||||
class QPSet8 {
|
||||
/// run and need to be considered by the scheduling algorithm. The set is
|
||||
/// capable of storing up to 32 priority levels.
|
||||
///
|
||||
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:
|
||||
|
||||
//! the function evaluates to TRUE if the set is empty, which means that
|
||||
//! no active objects are ready to run.
|
||||
bool isEmpty(void) const {
|
||||
return (m_bits == static_cast<uint_fast8_t>(0));
|
||||
//! Makes the priority set @p me_ empty.
|
||||
void setEmpty(void) {
|
||||
m_bits = static_cast<uint32_t>(0);
|
||||
}
|
||||
|
||||
//! the function evaluates to TRUE if the set has elements, which means
|
||||
//! that some active objects are ready to run.
|
||||
//! Evaluates to true if the priority set is empty
|
||||
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 {
|
||||
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.
|
||||
bool hasElement(uint_fast8_t const n) const {
|
||||
return
|
||||
((m_bits & static_cast<uint_fast8_t>(QF_pwr2Lkup[n]))
|
||||
!= static_cast<uint_fast8_t>(0));
|
||||
return (m_bits & (static_cast<uint32_t>(1)
|
||||
<< (n - static_cast<uint_fast8_t>(1))))
|
||||
!= static_cast<uint32_t>(0);
|
||||
}
|
||||
|
||||
//! insert element @p n into the set, n = 1..8
|
||||
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
|
||||
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
|
||||
uint_fast8_t findMax(void) const {
|
||||
return static_cast<uint_fast8_t>(QF_LOG2(m_bits));
|
||||
return QF_LOG2(m_bits);
|
||||
}
|
||||
};
|
||||
|
||||
//****************************************************************************
|
||||
//! 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.
|
||||
/// @description
|
||||
#else // QF_MAX_ACTIVE > 32
|
||||
|
||||
//! Priority Set of up to 64 elements
|
||||
///
|
||||
/// The priority set represents the set of active objects that are ready to
|
||||
/// run and need to be considered by scheduling processing. The set is capable
|
||||
/// of storing up to 64 priority levels.@n
|
||||
/// @n
|
||||
/// The priority set allows to build cooperative multitasking schedulers
|
||||
/// to manage up to 64 tasks. It is also used in the Quantum Kernel (QK)
|
||||
/// preemptive scheduler.
|
||||
class QPSet64 {
|
||||
/// run and need to be considered by the scheduling algorithm. The set is
|
||||
/// capable of storing up to 64 priority levels.
|
||||
///
|
||||
class QPSet {
|
||||
|
||||
//! bimask representing 8-element subsets of the set
|
||||
/// @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];
|
||||
uint32_t volatile m_bits[2]; //!< two bitmasks with a bit for each element
|
||||
|
||||
public:
|
||||
|
||||
//! the function evaluates to TRUE if the set is empty, which means
|
||||
//! that no active objects are ready to run.
|
||||
bool isEmpty(void) const {
|
||||
return (m_bytes == static_cast<uint_fast8_t>(0));
|
||||
//! Makes the priority set @p me_ empty.
|
||||
void setEmpty(void) {
|
||||
m_bits[0] = static_cast<uint32_t>(0);
|
||||
m_bits[1] = static_cast<uint32_t>(0);
|
||||
}
|
||||
|
||||
//! the function evaluates to TRUE if the set has elements, which means
|
||||
//! that some active objects are ready to run.
|
||||
//! Evaluates to true if the priority set is empty
|
||||
// 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 {
|
||||
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.
|
||||
bool hasElement(uint_fast8_t const n) const {
|
||||
uint_fast8_t const m =
|
||||
static_cast<uint_fast8_t>(QF_div8Lkup[n]);
|
||||
return ((m_bits[m]
|
||||
& static_cast<uint_fast8_t>(QF_pwr2Lkup[n]))
|
||||
!= static_cast<uint_fast8_t>(0));
|
||||
return (n <= static_cast<uint_fast8_t>(32))
|
||||
? ((m_bits[0] & (static_cast<uint32_t>(1)
|
||||
<< (n - static_cast<uint_fast8_t>(1))))
|
||||
!= static_cast<uint32_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
|
||||
void insert(uint_fast8_t const n) {
|
||||
uint_fast8_t m =
|
||||
static_cast<uint_fast8_t>(QF_div8Lkup[n]);
|
||||
m_bits[m] |= static_cast<uint_fast8_t>(QF_pwr2Lkup[n]);
|
||||
m_bytes |=
|
||||
static_cast<uint_fast8_t>(QF_pwr2Lkup[m
|
||||
+ static_cast<uint_fast8_t>(1)]);
|
||||
if (n <= static_cast<uint_fast8_t>(32)) {
|
||||
m_bits[0] |= (static_cast<uint32_t>(1)
|
||||
<< (n - static_cast<uint_fast8_t>(1)));
|
||||
}
|
||||
else {
|
||||
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
|
||||
void remove(uint_fast8_t const n) {
|
||||
uint_fast8_t m =
|
||||
static_cast<uint_fast8_t>(QF_div8Lkup[n]);
|
||||
m_bits[m] &= static_cast<uint_fast8_t>(QF_invPwr2Lkup[n]);
|
||||
if (m_bits[m] == static_cast<uint_fast8_t>(0)) {
|
||||
m_bytes &= static_cast<uint_fast8_t>(
|
||||
QF_invPwr2Lkup[m + static_cast<uint_fast8_t>(1)]);
|
||||
if (n <= static_cast<uint_fast8_t>(32)) {
|
||||
(m_bits[0] &= ~(static_cast<uint32_t>(1)
|
||||
<< (n - static_cast<uint_fast8_t>(1))));
|
||||
}
|
||||
else {
|
||||
(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
|
||||
uint_fast8_t findMax(void) const {
|
||||
uint_fast8_t n;
|
||||
if (m_bytes != static_cast<uint_fast8_t>(0)) {
|
||||
n = static_cast<uint_fast8_t>(
|
||||
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;
|
||||
return (m_bits[1] != static_cast<uint32_t>(0))
|
||||
? (QF_LOG2(m_bits[1]) + static_cast<uint_fast8_t>(32)) \
|
||||
: (QF_LOG2(m_bits[0]));
|
||||
}
|
||||
};
|
||||
|
||||
#endif // QF_MAX_ACTIVE
|
||||
|
||||
} // namespace QP
|
||||
|
||||
#endif // qpset_h
|
||||
|
20
include/qv.h
20
include/qv.h
@ -3,8 +3,8 @@
|
||||
/// @ingroup qv
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.2
|
||||
/// Last updated on 2016-03-31
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-28
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -92,12 +92,7 @@ public:
|
||||
|
||||
//****************************************************************************
|
||||
extern "C" {
|
||||
|
||||
#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 QP::QPSet QV_readySet_; //!< ready set of AOs
|
||||
} // extern "C"
|
||||
|
||||
//****************************************************************************
|
||||
@ -105,11 +100,10 @@ extern "C" {
|
||||
|
||||
#ifdef QP_IMPL
|
||||
|
||||
// QF-specific scheduler locking (not needed in QV)
|
||||
#define QF_SCHED_STAT_TYPE_ struct { uint_fast8_t m_lockPrio; }
|
||||
#define QF_SCHED_LOCK_(pLockStat_, dummy) ((pLockStat_)->m_lockPrio \
|
||||
= static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1))
|
||||
#define QF_SCHED_UNLOCK_(dummy) ((void)0)
|
||||
// QV-specific scheduler locking (not needed in QV)
|
||||
#define QF_SCHED_STAT_
|
||||
#define QF_SCHED_LOCK_(dummy) ((void)0)
|
||||
#define QF_SCHED_UNLOCK_() ((void)0)
|
||||
|
||||
// native event queue operations...
|
||||
#define QACTIVE_EQUEUE_WAIT_(me_) \
|
||||
|
105
include/qxk.h
105
include/qxk.h
@ -4,8 +4,8 @@
|
||||
/// @ingroup qxk
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.2
|
||||
/// Last updated on 2016-03-31
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-28
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -53,18 +53,12 @@
|
||||
///
|
||||
#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
|
||||
/// @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" {
|
||||
@ -73,33 +67,38 @@ extern "C" {
|
||||
struct QXK_Attr {
|
||||
void *curr; //!< currently executing thread
|
||||
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 lockHolder; //!< prio of the lock holder
|
||||
#ifndef QXK_ISR_CONTEXT_
|
||||
uint_fast8_t volatile intNest; //!< ISR nesting level
|
||||
uint_fast8_t volatile intNest; //!< ISR nesting level
|
||||
#endif // QXK_ISR_CONTEXT_
|
||||
#if (QF_MAX_ACTIVE <= 8)
|
||||
QP::QPSet8 readySet; //!< QXK ready-set of AOs and "naked" threads
|
||||
#else
|
||||
QP::QPSet64 readySet; //!< QXK ready-set of AOs and "naked" threads
|
||||
#endif
|
||||
QP::QPSet readySet; //!< ready-set of basic- and extended-threads
|
||||
};
|
||||
|
||||
//! global attributes of the QXK kernel
|
||||
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"
|
||||
|
||||
//****************************************************************************
|
||||
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.
|
||||
/// @description
|
||||
@ -164,14 +163,6 @@ private:
|
||||
} // 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
|
||||
|
||||
@ -186,36 +177,44 @@ void QXK_sched_(void);
|
||||
(QXK_attr_.intNest != static_cast<uint_fast8_t>(0))
|
||||
#endif // QXK_ISR_CONTEXT_
|
||||
|
||||
// QF-specific scheduler locking
|
||||
#define QF_SCHED_STAT_TYPE_ QXMutex
|
||||
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \
|
||||
if (QXK_ISR_CONTEXT_()) { \
|
||||
(pLockStat_)->m_lockPrio = \
|
||||
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
|
||||
} else { \
|
||||
(pLockStat_)->init((prio_)); \
|
||||
(pLockStat_)->lock(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define QF_SCHED_UNLOCK_(pLockStat_) (pLockStat_)->unlock()
|
||||
// QXK-specific scheduler locking
|
||||
//! Internal macro to represent the scheduler lock status
|
||||
// that needs to be preserved to allow nesting of locks.
|
||||
//
|
||||
#define QF_SCHED_STAT_ QXMutex schedLock_;
|
||||
|
||||
//! 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_) \
|
||||
if ((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_(); \
|
||||
}
|
||||
Q_ASSERT_ID(0, (me_)->m_eQueue.m_frontEvt != static_cast<QEvt *>(0))
|
||||
|
||||
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
|
||||
QXK_attr_.readySet.insert((me_)->m_prio); \
|
||||
if (!QXK_ISR_CONTEXT_()) { \
|
||||
QXK_sched_(); \
|
||||
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QXK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
} 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...
|
||||
#define QF_EPOOL_TYPE_ QMPool
|
||||
|
@ -1,10 +1,10 @@
|
||||
/// @file
|
||||
/// @brief QXK/C++ naked (blocking) thread
|
||||
/// @brief QXK/C++ extended (blocking) thread
|
||||
/// @ingroup qxk
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-27
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -41,13 +41,15 @@
|
||||
|
||||
namespace QP {
|
||||
|
||||
class QXThread; // forward declaration
|
||||
|
||||
//! 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
|
||||
/// @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
|
||||
/// corresponding QP::QXThread instance
|
||||
///
|
||||
@ -69,13 +71,7 @@ public:
|
||||
//! public constructor
|
||||
QXThread(QXThreadHandler const handler, uint_fast8_t const tickRate);
|
||||
|
||||
//! block (suspend) the current "naked" thread
|
||||
static void block(void);
|
||||
|
||||
//! unblock (resume) a given thread
|
||||
void unblock(void) const;
|
||||
|
||||
//! delay (block) the current "naked" thread for a specified # ticks
|
||||
//! delay (block) the current extended thread for a specified # ticks
|
||||
static bool delay(uint_fast16_t const nTicks,
|
||||
uint_fast8_t const tickRate);
|
||||
|
||||
@ -83,7 +79,7 @@ public:
|
||||
bool delayCancel(void);
|
||||
|
||||
//! 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);
|
||||
|
||||
// virtual function overrides...
|
||||
@ -94,7 +90,7 @@ public:
|
||||
//! Dispatches an event to QMsm
|
||||
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.
|
||||
virtual void start(uint_fast8_t const prio,
|
||||
QEvt const *qSto[], uint_fast16_t const qLen,
|
||||
@ -111,7 +107,7 @@ public:
|
||||
}
|
||||
|
||||
#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.
|
||||
virtual bool post_(QEvt const * const e, uint_fast16_t const margin);
|
||||
#else
|
||||
@ -156,11 +152,7 @@ public:
|
||||
|
||||
private:
|
||||
uint_fast16_t m_count;
|
||||
#if (QF_MAX_ACTIVE <= 8)
|
||||
QPSet8 m_waitSet; //!< set of "naked" threads waiting on this semaphore
|
||||
#else
|
||||
QPSet64 m_waitSet; //!< set of "naked" threads waiting on this semaphore
|
||||
#endif
|
||||
QPSet m_waitSet; //!< set of extended threads waiting on this semaphore
|
||||
};
|
||||
|
||||
} // namespace QP
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-M, ARM-KEIL toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
#define QK_ISR_ENTRY() ((void)0)
|
||||
|
||||
#define QK_ISR_EXIT() do { \
|
||||
uint_fast8_t nextPrio_; \
|
||||
QF_INT_DISABLE(); \
|
||||
nextPrio_ = QK_schedPrio_(); \
|
||||
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_attr_.next = nextPrio_; \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-08-08
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-26
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -34,18 +34,14 @@
|
||||
|
||||
EXPORT QK_init
|
||||
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
|
||||
|
||||
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
|
||||
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
|
||||
THUMB
|
||||
|
||||
@ -53,8 +49,7 @@ QK_AUX EQU 8
|
||||
|
||||
;*****************************************************************************
|
||||
; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; Also, it initializes QK_attr_.aux to zero. Both these operations
|
||||
; are performed in a critical section.
|
||||
; This operation is performed in a critical section.
|
||||
;*****************************************************************************
|
||||
QK_init
|
||||
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
|
||||
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
|
||||
BX lr ; return to the caller
|
||||
|
||||
@ -101,49 +87,8 @@ QK_init
|
||||
; check for the asynchronous preemption.
|
||||
;*****************************************************************************
|
||||
PendSV_Handler
|
||||
|
||||
; Check QK_attr_.aux to determine the purpose of this 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.
|
||||
;
|
||||
; 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
|
||||
; Prepare some constants in registers before entering critical section
|
||||
LDR r3,=0xE000ED04 ; Interrupt Control and State Register
|
||||
MOVS r1,#1
|
||||
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)?
|
||||
CPSID i ; disable interrupts (set PRIMASK)
|
||||
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
|
||||
MSR BASEPRI,r0 ; selectively disable interrupts
|
||||
ENDIF ; M3/M4/M7
|
||||
@ -158,38 +106,21 @@ PendSV_check_next
|
||||
; 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)
|
||||
STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
|
||||
|
||||
; Check QK_attr_.next, which contains the priority of the next thread
|
||||
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero
|
||||
; (QK_attr_.next != 0), this next thread needs to be scheduled.
|
||||
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to
|
||||
; simply return without activating the QK scheduler.
|
||||
; The QK activator must be called in a Thread mode, while this code
|
||||
; executes in the Handler mode 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
|
||||
; 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
|
||||
; 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)
|
||||
; NOTE: the QK activator is called with interrupts DISABLED and also
|
||||
; returns with interrupts DISABLED.
|
||||
LSRS r3,r1,#3 ; r3 := (1 << 24), set the T bit (new xpsr)
|
||||
LDR r2,=QK_activate_ ; address of the QK 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
|
||||
STR r0,[sp] ; save the prio argument (new r0)
|
||||
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
|
||||
STM r0!,{r1-r3} ; save xpsr,pc,lr
|
||||
|
||||
@ -197,44 +128,22 @@ PendSV_call_sched
|
||||
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
|
||||
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
|
||||
ELSE ; VFP not available...
|
||||
BX lr ; return to the preempted task
|
||||
ENDIF ; VFP available
|
||||
|
||||
; NOTE: the following code does not execute in the PendSV context!
|
||||
;=========================================================================
|
||||
; NOTE: QK scheduler returns with interrupts DISABLED.
|
||||
Thread_sched_ret
|
||||
;*****************************************************************************
|
||||
; 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 QK scheduler returns, we need to resume the preempted
|
||||
; task. However, this must be accomplished by a return-from-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 {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
|
||||
; 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
|
||||
@ -242,14 +151,38 @@ Thread_sched_ret
|
||||
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
|
||||
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
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit)
|
||||
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV)
|
||||
B . ; wait for preemption by PendSV
|
||||
LSLS r1,r1,#31 ; r0 := (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
|
||||
BX lr ; return to the preempted task
|
||||
ENDIF ; VFP available ENDIF
|
||||
ENDIF ; M3/M4/M7
|
||||
|
||||
ALIGN ; make sure the END is properly aligned
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, GNU-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
#define QK_ISR_ENTRY() ((void)0)
|
||||
|
||||
#define QK_ISR_EXIT() do { \
|
||||
uint_fast8_t nextPrio_; \
|
||||
QF_INT_DISABLE(); \
|
||||
nextPrio_ = QK_schedPrio_(); \
|
||||
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_attr_.next = nextPrio_; \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
* Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), GNU-ARM assembler
|
||||
* Last Updated for Version: 5.7.0
|
||||
* Date of the Last Update: 2016-08-08
|
||||
* Last Updated for Version: 5.7.2
|
||||
* Date of the Last Update: 2016-09-26
|
||||
*
|
||||
* 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" !!! */
|
||||
.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).
|
||||
* Also, it initializes QK_attr_.aux to zero. Both these operations
|
||||
* are performed in a critical section.
|
||||
* This operation is performed in a critical section.
|
||||
*****************************************************************************/
|
||||
.section .text.QK_init
|
||||
.global QK_init
|
||||
@ -64,16 +58,6 @@ QK_init:
|
||||
ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */
|
||||
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 */
|
||||
BX lr /* return to the caller */
|
||||
.size QK_init, . - QK_init
|
||||
@ -102,62 +86,21 @@ QK_init:
|
||||
.section .text.PendSV_Handler
|
||||
.global PendSV_Handler /* CMSIS-compliant exception name */
|
||||
.type PendSV_Handler, %function
|
||||
.type Thread_sched_ret, %function /* to ensure the label is THUMB */
|
||||
|
||||
PendSV_Handler:
|
||||
|
||||
/* Check QK_attr_.aux to determine the purpose of this 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.
|
||||
*
|
||||
* 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 */
|
||||
/* Prepare some constants in registers before entering critical section */
|
||||
LDR r3,=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 */
|
||||
.ifdef __FPU_PRESENT /* if VFP available... */
|
||||
PUSH {r0,lr} /* ... push EXC_RETURN plus stack-aligner */
|
||||
.endif /* VFP */
|
||||
MOVS r0,#QF_BASEPRI
|
||||
MSR BASEPRI,r0 /* selectively disable interrupts */
|
||||
.endif /* M3/M4/M7 */
|
||||
@ -166,36 +109,20 @@ PendSV_check_next:
|
||||
* 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) */
|
||||
STR r1,[r3] /* ICSR[27] := 1 (unpend PendSV) */
|
||||
|
||||
/* Check QK_attr_.next, which contains the priority of the next thread
|
||||
* to run, which is set in QK_ISR_EXIT(). If this priority is non-zero
|
||||
* (QK_attr_.next != 0), this next thread needs to be scheduled.
|
||||
* Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to
|
||||
* simply return without activating the QK scheduler.
|
||||
*/
|
||||
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_().
|
||||
/* The QK activator must be called in a Thread mode, while this code
|
||||
* executes in the Handler mode 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
|
||||
* QK_activate_().
|
||||
*
|
||||
* NOTE: the QK scheduler is called with interrupts DISABLED and also
|
||||
* it returns with interrupts DISABLED.
|
||||
* NOTE: the QK activator is called with interrupts DISABLED and also
|
||||
* 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) */
|
||||
LSRS r3,r1,#3 /* r3 := (1 << 24), set the T bit (new xpsr) */
|
||||
LDR r2,=QK_activate_ /* address of the QK 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 */
|
||||
STR r0,[sp] /* save the prio argument (new r0) */
|
||||
@ -205,45 +132,26 @@ PendSV_call_sched:
|
||||
MOVS r0,#6
|
||||
MVNS r0,r0 /* r0 := ~6 == 0xFFFFFFF9 */
|
||||
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 */
|
||||
.else
|
||||
BX lr /* return to the preempted task */
|
||||
.endif /* VFP */
|
||||
/*****************************************************************************
|
||||
* 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
|
||||
|
||||
/* NOTE: the following code does not execute in the PendSV context!
|
||||
*=========================================================================
|
||||
* NOTE: QK scheduler returns with interrupts DISABLED.
|
||||
*/
|
||||
Thread_sched_ret:
|
||||
Thread_ret:
|
||||
/* After the QK scheduler returns, we need to resume the preempted
|
||||
* task. However, this must be accomplished by a return-from-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
|
||||
* VFP stack frame will NOT be used...
|
||||
@ -254,14 +162,44 @@ Thread_sched_ret:
|
||||
MSR CONTROL,r0 /* CONTROL := r0 (clear CONTROL[2] FPCA bit) */
|
||||
.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 */
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#28 /* r0 := (1 << 28) (PendSV bit) */
|
||||
STR r1,[r0] /* ICSR[28] := 1 (pend PendSV) */
|
||||
B . /* wait for preemption by PendSV */
|
||||
.size PendSV_Handler, . - PendSV_Handler
|
||||
LSLS r1,r1,#31 /* r0 := (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
|
||||
|
||||
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
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, IAR-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -43,14 +43,12 @@
|
||||
|
||||
/* QK interrupt entry and exit */
|
||||
#define QK_ISR_ENTRY() ((void)0)
|
||||
|
||||
#define QK_ISR_EXIT() do { \
|
||||
uint_fast8_t nextPrio_; \
|
||||
QF_INT_DISABLE(); \
|
||||
nextPrio_ = QK_schedPrio_(); \
|
||||
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_attr_.next = nextPrio_; \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR ARM assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-08-08
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-26
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -34,23 +34,18 @@
|
||||
|
||||
PUBLIC QK_init
|
||||
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
|
||||
|
||||
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
|
||||
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
|
||||
QF_BASEPRI EQU (0xFF >> 2)
|
||||
|
||||
RSEG CODE:CODE:NOROOT(2)
|
||||
;*****************************************************************************
|
||||
; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; Also, it initializes QK_attr_.aux to zero. Both these operations
|
||||
; are performed in a critical section.
|
||||
; This operation is performed in a nestable critical section.
|
||||
;*****************************************************************************
|
||||
QK_init:
|
||||
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
|
||||
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
|
||||
BX lr ; return to the caller
|
||||
|
||||
@ -97,49 +83,8 @@ QK_init:
|
||||
; check for the asynchronous preemption.
|
||||
;*****************************************************************************
|
||||
PendSV_Handler:
|
||||
|
||||
; Check QK_attr_.aux to determine the purpose of this 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.
|
||||
;
|
||||
; 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
|
||||
; Prepare some constants in registers before entering critical section
|
||||
LDR r3,=0xE000ED04 ; Interrupt Control and State Register
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
|
||||
|
||||
@ -147,6 +92,9 @@ PendSV_check_next:
|
||||
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
|
||||
CPSID i ; disable interrupts (set PRIMASK)
|
||||
#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
|
||||
MSR BASEPRI,r0 ; selectively disable interrupts
|
||||
#endif ; M3/M4/M7
|
||||
@ -154,38 +102,21 @@ PendSV_check_next:
|
||||
; 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)
|
||||
STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
|
||||
|
||||
; Check QK_attr_.next, which contains the priority of the next thread
|
||||
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero
|
||||
; (QK_attr_.next != 0), this next thread needs to be scheduled.
|
||||
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to
|
||||
; simply return without activating the QK scheduler.
|
||||
; The QK activator must be called in a Thread mode, while this code
|
||||
; executes in the Handler mode 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
|
||||
; 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
|
||||
; 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)
|
||||
; NOTE: the QK activator is called with interrupts DISABLED and also
|
||||
; returns with interrupts DISABLED.
|
||||
LSRS r3,r1,#3 ; r3 := (1 << 24), set the T bit (new xpsr)
|
||||
LDR r2,=QK_activate_ ; address of the QK 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
|
||||
STR r0,[sp] ; save the prio argument (new r0)
|
||||
ADD r0,sp,#5*4 ; r0 := 5 registers below the top of stack
|
||||
STM r0!,{r1-r3} ; save xpsr,pc,lr
|
||||
|
||||
@ -193,44 +124,22 @@ PendSV_call_sched:
|
||||
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
|
||||
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
|
||||
#else ; VFP not available...
|
||||
BX lr ; return to the preempted task
|
||||
#endif ; VFP
|
||||
|
||||
; NOTE: the following code does not execute in the PendSV context!
|
||||
;=========================================================================
|
||||
; NOTE: QK scheduler returns with interrupts DISABLED.
|
||||
Thread_sched_ret:
|
||||
;*****************************************************************************
|
||||
; 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 QK scheduler returns, we need to resume the preempted
|
||||
; task. However, this must be accomplished by a return-from-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 (__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
|
||||
; 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
|
||||
@ -238,14 +147,38 @@ Thread_sched_ret:
|
||||
MSR CONTROL,r0 ; CONTROL := r0 (clear CONTROL[2] FPCA bit)
|
||||
#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
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit)
|
||||
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV)
|
||||
B . ; wait for preemption by PendSV
|
||||
LSLS r1,r1,#31 ; r0 := (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
|
||||
|
||||
ALIGNROM 2,0xFF ; make sure the END is properly aligned
|
||||
|
||||
|
@ -60,8 +60,8 @@
|
||||
#define QF_INT_DISABLE() QF_set_BASEPRI(QF_BASEPRI)
|
||||
#define QF_INT_ENABLE() QF_set_BASEPRI(0U)
|
||||
|
||||
// the intrinsic function _norm() generates the CLZ instruction
|
||||
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_)))
|
||||
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
|
||||
#define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
|
||||
|
||||
// assembly function for setting the BASEPRI register
|
||||
extern "C" void QF_set_BASEPRI(unsigned basePri);
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-M, preemptive QK kernel, TI-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -43,14 +43,12 @@
|
||||
|
||||
// QK interrupt entry and exit
|
||||
#define QK_ISR_ENTRY() ((void)0)
|
||||
|
||||
#define QK_ISR_EXIT() do { \
|
||||
uint_fast8_t nextPrio_; \
|
||||
QF_INT_DISABLE(); \
|
||||
nextPrio_ = QK_schedPrio_(); \
|
||||
if (nextPrio_ != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_attr_.next = nextPrio_; \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
((*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
static_cast<uint32_t>(1U << 28))); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (0)
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QK port to ARM Cortex-M (M0,M0+,M3,M4,M7), TI-ARM assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-08-08
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-26
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -34,30 +34,29 @@
|
||||
|
||||
.global QK_init
|
||||
.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
|
||||
.endif ; M3/M4/M7
|
||||
.global QK_get_IPSR ; get the IPSR
|
||||
.global assert_failed ; low-level assert handler
|
||||
|
||||
.ref QK_sched_ ; external reference
|
||||
.ref QK_attr_ ; external reference
|
||||
.ref QK_activate_ ; external reference
|
||||
|
||||
.ref Q_onAssert ; external reference
|
||||
|
||||
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
|
||||
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
|
||||
.thumb
|
||||
|
||||
;*****************************************************************************
|
||||
; The QK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; Also, it initializes QK_attr_.aux to zero. Both these operations
|
||||
; are performed in a critical section.
|
||||
; This operation is performed in a nestable critical section.
|
||||
;*****************************************************************************
|
||||
QK_init: .asmfunc
|
||||
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
|
||||
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
|
||||
BX lr ; return to the caller
|
||||
.endasmfunc
|
||||
@ -105,53 +95,16 @@ QK_init: .asmfunc
|
||||
;*****************************************************************************
|
||||
PendSV_Handler: .asmfunc
|
||||
|
||||
; Check QK_attr_.aux to determine the purpose of this 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.
|
||||
;
|
||||
; 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
|
||||
; Prepare some constants in registers before entering critical section
|
||||
LDR r3,ICSR_addr ; Interrupt Control and State Register
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#27 ; r0 := (1 << 27) (UNPENDSVSET bit)
|
||||
|
||||
; <<<<<<<<<<<<<<<<<<<<<<< CRITICAL SECTION BEGIN <<<<<<<<<<<<<<<<<<<<<<<<<
|
||||
.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
|
||||
MSR BASEPRI,r0 ; selectively disable interrupts
|
||||
.else ; Cortex-M0/M0+/M1 ?
|
||||
@ -161,82 +114,45 @@ PendSV_check_next:
|
||||
; 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)
|
||||
STR r1,[r3] ; ICSR[27] := 1 (unpend PendSV)
|
||||
|
||||
; Check QK_attr_.next, which contains the priority of the next thread
|
||||
; to run, which is set in QK_ISR_EXIT(). If this priority is non-zero
|
||||
; (QK_attr_.next != 0), this next thread needs to be scheduled.
|
||||
; Otherwise, if (QK_attr_.next == 0), this PendSV exception needs to
|
||||
; simply return without activating the QK scheduler.
|
||||
; The QK activator must be called in a Thread mode, while this code
|
||||
; executes in the Handler mode 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
|
||||
; 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
|
||||
; 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)
|
||||
; NOTE: the QK activator is called with interrupts DISABLED and also
|
||||
; returns with interrupts DISABLED.
|
||||
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)
|
||||
LDR r1,Thread_ret_addr ; return address after the call (new lr)
|
||||
|
||||
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
|
||||
STM r0!,{r1-r3} ; save xpsr,pc,lr
|
||||
|
||||
MOVS r0,#6
|
||||
MVNS r0,r0 ; r0 := ~6 == 0xFFFFFFF9
|
||||
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
|
||||
|
||||
; NOTE: the following code does not execute in the PendSV context!
|
||||
;=========================================================================
|
||||
; NOTE: QK scheduler returns with interrupts DISABLED.
|
||||
Thread_sched_ret: .asmfunc ; to ensure that the label is THUMB
|
||||
|
||||
;*****************************************************************************
|
||||
; 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 QK scheduler returns, we need to resume the preempted
|
||||
; task. However, this must be accomplished by a return-from-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_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
|
||||
; 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
|
||||
@ -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)
|
||||
.endif ; VFP
|
||||
|
||||
.else ; Cortex-M0/M0+/M1 ?
|
||||
CPSIE i ; enable interrupts (clear PRIMASK)
|
||||
.endif ; M0/M0+/M1
|
||||
|
||||
; trigger PendSV to return to preempted thread...
|
||||
; trigger NMI to return to preempted thread...
|
||||
LDR r0,ICSR_addr ; Interrupt Control and State Register
|
||||
MOVS r1,#1
|
||||
LSLS r1,r1,#28 ; r0 := (1 << 28) (PendSV bit)
|
||||
STR r1,[r0] ; ICSR[28] := 1 (pend PendSV)
|
||||
Thread_wait_PendSV:
|
||||
B Thread_wait_PendSV ; wait for preemption by PendSV
|
||||
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
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
; The QF_set_BASEPRI function sets the BASEPRI register to the value
|
||||
; 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);
|
||||
;*****************************************************************************
|
||||
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
|
||||
QF_set_BASEPRI: .asmfunc
|
||||
MSR BASEPRI,r0 ; set BASEPRI
|
||||
BX lr ; return to the caller
|
||||
.endasmfunc
|
||||
.endif ; M3/M4/M7
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
@ -302,5 +246,5 @@ VTOR_addr: .word 0xE000ED08
|
||||
; Addresses for PC-relative LDR
|
||||
;*****************************************************************************
|
||||
QK_attr_addr: .word QK_attr_
|
||||
QK_sched_addr: .word QK_sched_
|
||||
Thread_sched_ret_addr .word Thread_sched_ret
|
||||
QK_activate_addr: .word QK_activate_
|
||||
Thread_ret_addr .word Thread_ret
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, GNU-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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))
|
||||
|
||||
// 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
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QF/C++ port to ARM Cortex-M, cooperative QV kernel, IAR-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.4.0
|
||||
/// Last updated on 2015-05-04
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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))
|
||||
|
||||
// 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
|
||||
|
||||
// QF critical section entry/exit...
|
||||
|
@ -3,8 +3,8 @@
|
||||
/// @brief QF/C++ port to Cortex-M, cooperative QV kernel, TI-ARM CCS toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.5.1
|
||||
/// Last updated on 2015-09-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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_ENABLE() QF_set_BASEPRI(0U)
|
||||
|
||||
// the intrinsic function _norm() generates the CLZ instruction
|
||||
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_)))
|
||||
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
|
||||
#define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
|
||||
|
||||
// assembly function for setting the BASEPRI register
|
||||
extern "C" void QF_set_BASEPRI(unsigned basePri);
|
||||
|
@ -1,48 +1,45 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QEP/C port, generic C99 compiler
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* 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
|
||||
* ---------------------------
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QEP/C++ port to ARM Cortex-M, generic C++ compiler
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.4.0
|
||||
/// Last updated on 2015-03-14
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
/// innovating embedded systems
|
||||
///
|
||||
/// Copyright (C) Quantum Leaps, 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:
|
||||
/// Web: www.state-machine.com
|
||||
/// Email: info@state-machine.com
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef qep_port_h
|
||||
#define qep_port_h
|
||||
|
||||
#include <stdint.h> /* Exact-width types. WG14/N843 C99 Standard */
|
||||
#include <stdbool.h> /* Boolean type. WG14/N843 C99 Standard */
|
||||
#include <stdint.h> // Exact-width types. 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
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QF/C++ port to ARM Cortex-M, preemptive QXK kernel, ARM-KEIL toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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))
|
||||
|
||||
// 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
|
||||
static __inline void QF_set_BASEPRI(unsigned basePri) {
|
||||
|
@ -1,62 +1,54 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QS/C port to a 32-bit CPU and a generic C compiler.
|
||||
* @ingroup qs
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* 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
|
||||
* ---------------------------
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QS/C++ port to ARM Cortex-M, generic compiler
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
/// innovating embedded systems
|
||||
///
|
||||
/// Copyright (C) Quantum Leaps. 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
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef 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 */
|
||||
#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 */
|
||||
#endif // qs_port_h
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, ARM-KEIL toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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) = \
|
||||
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_EXIT() do { \
|
||||
QF_INT_DISABLE(); \
|
||||
QXK_sched_(); \
|
||||
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QXK_CONTEXT_SWITCH_(); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (false)
|
||||
|
||||
|
||||
#include "qxk.h" // QXK platform-independent public interface
|
||||
|
||||
#endif // qxk_port_h
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), ARM-Keil assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-07-14
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-25
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -32,23 +32,27 @@
|
||||
; mailto:info@state-machine.com
|
||||
;*****************************************************************************
|
||||
|
||||
EXPORT QXK_start_ ; start the QXK multitasking
|
||||
EXPORT QXK_stackInit_ ; initialize the stack of each thread
|
||||
EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception
|
||||
EXPORT QXK_init ; initialze the QXK kernel
|
||||
EXPORT QXK_stackInit_ ; initialize the stack of an extended thread
|
||||
EXPORT PendSV_Handler ; CMSIS-compliant PendSV exception name
|
||||
EXPORT NMI_Handler ; CMSIS-compliant NMI exception name
|
||||
|
||||
IMPORT QXK_attr_ ; QXK attribute structure
|
||||
IMPORT QXK_activate_ ; external reference
|
||||
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" !!!
|
||||
QF_BASEPRI EQU (0xFF:SHR:2)
|
||||
|
||||
; NOTE: keep in synch with the QXK struct in "qxk.h" !!!
|
||||
QXK_CURR EQU 0
|
||||
QXK_NEXT EQU 4
|
||||
; NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!!
|
||||
QXK_CURR EQU 0
|
||||
QXK_NEXT EQU 4
|
||||
QXK_TOP_PRIO EQU 8
|
||||
|
||||
; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!!
|
||||
QMACTIVE_OSOBJECT EQU 40
|
||||
QMACTIVE_THREAD EQU 44
|
||||
QMACTIVE_THREAD EQU 40
|
||||
QMACTIVE_PRIO EQU 44
|
||||
|
||||
|
||||
AREA |.text|, CODE, READONLY
|
||||
@ -57,13 +61,13 @@ QMACTIVE_THREAD EQU 44
|
||||
PRESERVE8 ; this code preserves 8-byte stack alignment
|
||||
|
||||
;*****************************************************************************
|
||||
; The QXK_start_ function starts QXK multitasking.
|
||||
; The C signature: void QXK_start_(void);
|
||||
;
|
||||
; NOTE: QXK_start_() must be called with interrupts disabled and
|
||||
; returns with interrupts **enabled**.
|
||||
; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; This operation is performed in a nestable critical section.
|
||||
;*****************************************************************************
|
||||
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 r2,[r1,#8] ; load the System 12-15 Priority Register
|
||||
MOVS r3,#0xFF
|
||||
@ -71,112 +75,195 @@ QXK_start_
|
||||
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
|
||||
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
|
||||
; in the vector table. (This recovers any stack used so far by main().)
|
||||
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
|
||||
MSR PRIMASK,r0 ; restore the original PRIMASK
|
||||
BX lr ; return to the caller
|
||||
|
||||
; 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 registers
|
||||
;*****************************************************************************
|
||||
; The PendSV_Handler exception handler is used for handling context switch
|
||||
; and asynchronous preemption 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_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 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
|
||||
LDMIA r0!,{r4-r7} ; pop the low registers
|
||||
ADD sp,sp,#(8*4) ; remove 8 registers from the stack
|
||||
|
||||
MSR PSP,r0 ; set PSP to the thread's SP
|
||||
ISB ; flush the instruction pipeline
|
||||
MOVS r1,#6
|
||||
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)
|
||||
MRS r0,CONTROL
|
||||
MOVS r1,#(1:SHL: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
|
||||
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 the next AO
|
||||
|
||||
; fake return from an exception...
|
||||
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
|
||||
; otherwise re-enable interrupts and return to the preempted AO-thread
|
||||
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
|
||||
CPSIE i ; enable interrupts (clear PRIMASK)
|
||||
ELSE ; M3/M4/M7
|
||||
|
||||
IF {TARGET_FPU_VFP} == {TRUE} ; if VFP available...
|
||||
; 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)
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
;-------------------------------------------------------------------------
|
||||
; 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
|
||||
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)?
|
||||
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
|
||||
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
|
||||
@ -186,83 +273,128 @@ PendSV_Handler
|
||||
MOV r6,r10
|
||||
MOV r7,r11
|
||||
STMIA r1!,{r4-r7} ; save the high registers
|
||||
; NOTE: at this point r0 still holds the top of stack
|
||||
CPSID i ; disable interrupts (set PRIMASK)
|
||||
; NOTE: at this point r0 holds the top of stack
|
||||
|
||||
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
|
||||
ELSE ; M3/M4/M7
|
||||
STMDB r0!,{r4-r11} ; save r4-r11 on top of the exception frame
|
||||
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...
|
||||
VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31
|
||||
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
|
||||
ENDIF ; VFP available
|
||||
|
||||
MOVS r1,#QF_BASEPRI
|
||||
MSR BASEPRI,r1 ; selectively disable interrupts
|
||||
ENDIF ; M3/M4/M7
|
||||
|
||||
; NOTE: This PendSV exception handler can be preempted by an
|
||||
; interrupt, which might pend PendSV exception again. This
|
||||
; would be a problem, because the QK scheduler would run again
|
||||
; 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 extended-thread
|
||||
STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
MOV r0,r12 ; QXK_attr_.next (restore value)
|
||||
|
||||
; store the SP of the current QXK thread,
|
||||
; which was set in QXK_ISR_EXIT().
|
||||
LDR r2,[r3,#QXK_CURR]
|
||||
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
CMP r2,#0
|
||||
BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
|
||||
; otherwise continue to restoring next extended-thread...
|
||||
|
||||
IF {TARGET_FPU_VFP} == {TRUE} ; 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)
|
||||
|
||||
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
|
||||
;-------------------------------------------------------------------------
|
||||
; 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
|
||||
PendSV_restore_ex
|
||||
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := r0 (QXK_attr_.next)
|
||||
|
||||
; exit the critical section
|
||||
IF {TARGET_ARCH_THUMB} == 3 ; Cortex-M0/M0+/M1 (v6-M, v6S-M)?
|
||||
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
|
||||
LDMIA r0!,{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 r1!,{r4-r7} ; pop the low registers
|
||||
; NOTE: at this point r0 holds the new top of stack
|
||||
LDMIA r2!,{r4-r7} ; pop the low registers
|
||||
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
|
||||
MOVS r3,#0
|
||||
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI)
|
||||
|
||||
MOVS r1,#1
|
||||
MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
|
||||
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...
|
||||
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
|
||||
|
||||
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
|
||||
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
ENDIF ; M3/M4/M7
|
||||
|
||||
; 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
|
||||
; 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)
|
||||
MOV r12,r0 ; save r0 in r12
|
||||
|
||||
; round up the beginning of stack to the 8-byte boundary
|
||||
; r2 := (((r2 -1) >> 3) + 1) << 3;
|
||||
@ -302,77 +435,82 @@ QXK_stackInit_
|
||||
LSRS r0,r3,#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
|
||||
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
|
||||
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
|
||||
|
||||
; pre-fill the stack with 0xDEADBEEF
|
||||
; pre-fill the unused part of the stack with 0xDEADBEEF...................
|
||||
LDR r0,=0xDEADBEEF
|
||||
MOV r1,r0
|
||||
|
||||
QXK_stackInit_fill
|
||||
STMIA r2!,{r0,r1}
|
||||
CMP r2,r3
|
||||
BLT.N QXK_stackInit_fill
|
||||
|
||||
; prepare the standard exception (without VFP) stack frame...
|
||||
|
||||
MOV r0,r12 ; restore r0 from r12
|
||||
|
||||
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
|
||||
; prepare the standard exception (without VFP) stack frame................
|
||||
MOV r0,r12 ; restore r0 from r12 (act)
|
||||
LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -1,48 +1,45 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QEP/C port, generic C99 compiler
|
||||
* @ingroup ports
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* 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
|
||||
* ---------------------------
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QEP/C++ port to ARM Cortex-M, generic C++ compiler
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.4.0
|
||||
/// Last updated on 2015-03-14
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
/// innovating embedded systems
|
||||
///
|
||||
/// Copyright (C) Quantum Leaps, 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:
|
||||
/// Web: www.state-machine.com
|
||||
/// Email: info@state-machine.com
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef qep_port_h
|
||||
#define qep_port_h
|
||||
|
||||
#include <stdint.h> /* Exact-width types. WG14/N843 C99 Standard */
|
||||
#include <stdbool.h> /* Boolean type. WG14/N843 C99 Standard */
|
||||
#include <stdint.h> // Exact-width types. 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
|
||||
|
@ -1,60 +1,59 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QF/C port to Cortex-M, preemptive QXK kernel, GNU-ARM toolset
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last Updated for Version: 5.6.0
|
||||
* Date of the Last Update: 2015-12-11
|
||||
*
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QF/C++ port to ARM Cortex-M, dual-mode QXK kernel, GNU-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef 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
|
||||
|
||||
/* The maximum number of system clock tick rates */
|
||||
// The maximum number of system clock tick rates
|
||||
#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 */
|
||||
|
||||
#define QF_INT_DISABLE() __asm volatile ("cpsid i")
|
||||
#define QF_INT_ENABLE() __asm volatile ("cpsie i")
|
||||
|
||||
/* QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2 */
|
||||
#define QF_AWARE_ISR_CMSIS_PRI 0
|
||||
// QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE2
|
||||
#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 (\
|
||||
"movs r0,%0 \n\t" \
|
||||
@ -62,68 +61,69 @@
|
||||
#define QF_INT_DISABLE() QF_SET_BASEPRI(QF_BASEPRI)
|
||||
#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)
|
||||
|
||||
/* 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))
|
||||
|
||||
/* Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2 */
|
||||
#define QF_LOG2(n_) ((uint8_t)(32U - __builtin_clz(n_)))
|
||||
// Cortex-M3/M4/M4F provide the CLZ instruction for fast LOG2
|
||||
#define QF_LOG2(x_) \
|
||||
(static_cast<uint_fast8_t>(32U - __builtin_clz(x_)))
|
||||
|
||||
#endif
|
||||
|
||||
/* QF critical section entry/exit */
|
||||
/* QF_CRIT_STAT_TYPE not defined: unconditional interrupt disabling" policy */
|
||||
// QF critical section entry/exit
|
||||
// QF_CRIT_STAT_TYPE not defined: unconditional interrupt disabling" policy
|
||||
#define QF_CRIT_ENTRY(dummy) QF_INT_DISABLE()
|
||||
#define QF_CRIT_EXIT(dummy) QF_INT_ENABLE()
|
||||
#define QF_CRIT_EXIT_NOP() __asm volatile ("isb")
|
||||
|
||||
#include "qep_port.h" /* QEP port */
|
||||
#include "qxk_port.h" /* QXK port */
|
||||
#include "qf.h" /* QF platform-independent public interface */
|
||||
#include "qxthread.h" /* QXK naked thread */
|
||||
#include "qep_port.h" // QEP port
|
||||
#include "qxk_port.h" // QXK port
|
||||
#include "qf.h" // QF platform-independent public interface
|
||||
#include "qxthread.h" // QXK naked thread
|
||||
|
||||
/*****************************************************************************
|
||||
* NOTE1:
|
||||
* 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.
|
||||
*
|
||||
* NOTE2:
|
||||
* On Cortex-M0/M0+/M1 (architecture v6-M, v6S-M), the interrupt disabling
|
||||
* policy uses the PRIMASK register to disable interrupts globally. The
|
||||
* QF_AWARE_ISR_CMSIS_PRI level is zero, meaning that all interrupts are
|
||||
* "kernel-aware".
|
||||
*
|
||||
* NOTE3:
|
||||
* 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
|
||||
* interrupts only with priority lower than the level specified by the
|
||||
* QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e.,
|
||||
* 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
|
||||
* services, because QF is not aware of these interrupts. Coversely, only
|
||||
* "QF-aware" interrupts, with numerical values of priorities eqal to or
|
||||
* higher than QF_BASEPRI, can call QF services.
|
||||
*
|
||||
* NOTE4:
|
||||
* 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
|
||||
* (lowest possible numerical value) for the guaranteed 3 priority bits
|
||||
* implemented in the NVIC.
|
||||
*
|
||||
* NOTE5:
|
||||
* 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
|
||||
* 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 can be passed directly to the CMSIS function
|
||||
* NVIC_SetPriority(), which shifts them by (8 - __NVIC_PRIO_BITS) into the
|
||||
* 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 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
|
||||
* on the number of implemented priority bits in the NVIC.
|
||||
*/
|
||||
//****************************************************************************
|
||||
// NOTE1:
|
||||
// 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.
|
||||
//
|
||||
// NOTE2:
|
||||
// On Cortex-M0/M0+/M1 (architecture v6-M, v6S-M), the interrupt disabling
|
||||
// policy uses the PRIMASK register to disable interrupts globally. The
|
||||
// QF_AWARE_ISR_CMSIS_PRI level is zero, meaning that all interrupts are
|
||||
// "kernel-aware".
|
||||
//
|
||||
// NOTE3:
|
||||
// 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
|
||||
// interrupts only with priority lower than the level specified by the
|
||||
// QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e.,
|
||||
// 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
|
||||
// services, because QF is not aware of these interrupts. Coversely, only
|
||||
// "QF-aware" interrupts, with numerical values of priorities eqal to or
|
||||
// higher than QF_BASEPRI, can call QF services.
|
||||
//
|
||||
// NOTE4:
|
||||
// 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
|
||||
// (lowest possible numerical value) for the guaranteed 3 priority bits
|
||||
// implemented in the NVIC.
|
||||
//
|
||||
// NOTE5:
|
||||
// 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
|
||||
// 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 can be passed directly to the CMSIS function
|
||||
// NVIC_SetPriority(), which shifts them by (8 - __NVIC_PRIO_BITS) into the
|
||||
// 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 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
|
||||
// on the number of implemented priority bits in the NVIC.
|
||||
//
|
||||
|
||||
#endif /* qf_port_h */
|
||||
#endif // qf_port_h
|
||||
|
@ -1,62 +1,54 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QS/C port to a 32-bit CPU and a generic C compiler.
|
||||
* @ingroup qs
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* 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
|
||||
* ---------------------------
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QS/C++ port to ARM Cortex-M, generic compiler
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
/// innovating embedded systems
|
||||
///
|
||||
/// Copyright (C) Quantum Leaps. 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
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef 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 */
|
||||
#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 */
|
||||
#endif // qs_port_h
|
||||
|
@ -1,46 +1,45 @@
|
||||
/**
|
||||
* @file
|
||||
* @brief QXK/C port to ARM Cortex-M, GNU-ARM compiler
|
||||
* @cond
|
||||
******************************************************************************
|
||||
* Last Updated for Version: 5.6.0
|
||||
* Date of the Last Update: 2015-12-09
|
||||
*
|
||||
* 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
|
||||
******************************************************************************
|
||||
* @endcond
|
||||
*/
|
||||
/// @file
|
||||
/// @brief QXK/C++ port to ARM Cortex-M, GNU-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
///***************************************************************************
|
||||
/// @endcond
|
||||
|
||||
#ifndef qxk_port_h
|
||||
#define qxk_port_h
|
||||
|
||||
/* determination if the code executes in the ISR context */
|
||||
#define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != (uint32_t)0)
|
||||
// determination if the code executes in the ISR context
|
||||
#define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != static_cast<uint32_t>(0))
|
||||
|
||||
__attribute__((always_inline))
|
||||
static inline uint32_t QXK_get_IPSR(void) {
|
||||
@ -49,18 +48,23 @@ static inline uint32_t QXK_get_IPSR(void) {
|
||||
return regIPSR;
|
||||
}
|
||||
|
||||
// trigger the PendSV exception to pefrom the 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_EXIT() do { \
|
||||
QF_INT_DISABLE(); \
|
||||
QXK_sched_(); \
|
||||
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QXK_CONTEXT_SWITCH_(); \
|
||||
} \
|
||||
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
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
* Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), GNU-ARM assembler
|
||||
* Last Updated for Version: 5.7.0
|
||||
* Date of the Last Update: 2016-07-14
|
||||
* Last Updated for Version: 5.7.2
|
||||
* Date of the Last Update: 2016-09-25
|
||||
*
|
||||
* 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" !!! */
|
||||
.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_NEXT,4
|
||||
.equ QXK_TOP_PRIO,8
|
||||
|
||||
/* NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!! */
|
||||
.equ QMACTIVE_OSOBJECT,40
|
||||
.equ QMACTIVE_THREAD,44
|
||||
|
||||
.equ QMACTIVE_THREAD,40
|
||||
.equ QMACTIVE_PRIO,44
|
||||
|
||||
/*****************************************************************************
|
||||
* The QXK_start_ function starts QXK multitasking.
|
||||
* The C signature: void QXK_start_(void);
|
||||
*
|
||||
* NOTE: QXK_start_() must be called with interrupts disabled and
|
||||
* returns with interrupts **enabled**.
|
||||
* The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
* This operation is performed in a nestable critical section.
|
||||
*****************************************************************************/
|
||||
.section .text.QXK_start_
|
||||
.global QXK_start_
|
||||
.type QXK_start_, %function
|
||||
.section .text.QXK_init
|
||||
.global QXK_init
|
||||
.type QXK_init, %function
|
||||
.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 r2,[r1,#8] /* load the System 12-15 Priority Register */
|
||||
MOVS r3,#0xFF
|
||||
@ -66,205 +66,355 @@ QXK_start_:
|
||||
ORRS r2,r3 /* set PRI_14 (PendSV) to 0xFF */
|
||||
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
|
||||
* in the vector table. (This recovers any stack used so far by main()).
|
||||
*/
|
||||
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_
|
||||
MSR PRIMASK,r0 /* restore the original PRIMASK */
|
||||
BX lr /* return to the caller */
|
||||
.size QXK_init, . - QXK_init
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* 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_Handler exception handler is used for handling context switch
|
||||
* and asynchronous preemption 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.
|
||||
* (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 to trigger the PendSV exception.
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
* exception). In QXK, this is exactly the time when the QXK scheduler needs to
|
||||
* check for the asynchronous preemption.
|
||||
*****************************************************************************/
|
||||
.section .text.PendSV_Handler
|
||||
.global PendSV_Handler /* CMSIS-compliant exception name */
|
||||
.type PendSV_Handler, %function
|
||||
|
||||
PendSV_Handler:
|
||||
|
||||
MRS r0,PSP /* r0 := Process Stack Pointer */
|
||||
LDR r2,=0xE000ED04 /* Interrupt Control and State Register */
|
||||
/* 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 __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)? */
|
||||
SUBS r0,r0,#(8*4) /* make room for 8 registers r4-r11 */
|
||||
MOVS r1,r0 /* r1 := temporary PSP (do not clobber r0!) */
|
||||
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 r6,r10
|
||||
MOV r7,r11
|
||||
STMIA r1!,{r4-r7} /* save the high registers */
|
||||
/* NOTE: at this point r0 still holds the top of stack */
|
||||
CPSID i /* disable interrupts (set PRIMASK) */
|
||||
/* NOTE: at this point r0 holds the top of stack */
|
||||
|
||||
LDR r1,[r3,#QXK_CURR] /* r1 := QXK_attr_.curr (restore value) */
|
||||
.else /* M3/M4/M7 */
|
||||
STMDB r0!,{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 r0!,{s16-s31} /* ... save VFP registers s16..s31 */
|
||||
STMDB r0!,{r1,lr} /* save the "aligner" and the EXC_RETURN */
|
||||
.endif /* VFP available */
|
||||
MOVS r1,#QF_BASEPRI
|
||||
MSR BASEPRI,r1 /* selectively disable interrupts */
|
||||
.endif /* M3/M4/M7 */
|
||||
|
||||
/* NOTE: This PendSV exception handler can be preempted by an
|
||||
* interrupt, which might pend PendSV exception again. This
|
||||
* would be a problem, because the QK scheduler would run again
|
||||
* 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.
|
||||
/* store the SP of the current extended-thread */
|
||||
STR r0,[r1,#QMACTIVE_THREAD] /* QXK_attr_.curr->thread := r0 */
|
||||
MOV r0,r12 /* QXK_attr_.next (restore value) */
|
||||
|
||||
CMP r2,#0
|
||||
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
|
||||
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... */
|
||||
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 */
|
||||
PendSV_restore_ex:
|
||||
STR r0,[r3,#QXK_CURR] /* QXK_attr_.curr := r0 (QXK_attr_.next) */
|
||||
|
||||
/* exit the critical section */
|
||||
.if __ARM_ARCH == 6 /* Cortex-M0/M0+/M1 (v6-M, v6S-M)? */
|
||||
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 */
|
||||
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 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 */
|
||||
.else /* M3/M4/M7 */
|
||||
MOVS r3,#0
|
||||
MSR BASEPRI,r3 /* enable interrupts (clear BASEPRI) */
|
||||
LDMIA r2!,{r4-r7} /* pop the low registers */
|
||||
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 */
|
||||
MOVS r1,#1
|
||||
MSR BASEPRI,r1 /* enable interrupts (clear BASEPRI) */
|
||||
.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? */
|
||||
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 */
|
||||
|
||||
LDMIA r0!,{r4-r11} /* restore r4-r11 from next thread's stack */
|
||||
LDMIA r2!,{r4-r11} /* restore r4-r11 from the next thread's stack*/
|
||||
.endif /* M3/M4/M7 */
|
||||
|
||||
/* 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
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* 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.
|
||||
*
|
||||
@ -293,92 +443,100 @@ QXK_stackInit_:
|
||||
* 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) */
|
||||
MOV r12,r0 /* save r0 in r12 */
|
||||
|
||||
/* round up the beginning of stack to the 8-byte boundary */
|
||||
/* r2 := (((r2 -1) >> 3) + 1) << 3 */
|
||||
/* round up the beginning of stack to the 8-byte boundary
|
||||
* r2 := (((r2 -1) >> 3) + 1) << 3;
|
||||
*/
|
||||
SUBS r0,r2,#1
|
||||
LSRS r0,r0,#3
|
||||
ADDS r0,r0,#1
|
||||
LSLS r2,r0,#3
|
||||
|
||||
/* round down the end of stack to the 8-byte boundary */
|
||||
/* r3 := (r3 >> 3) << 3 */
|
||||
/* round down the end of stack to the 8-byte boundary
|
||||
* r3 := (r3 >> 3) << 3;
|
||||
*/
|
||||
LSRS r0,r3,#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 */
|
||||
.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
|
||||
STR r1,[r0,#QMACTIVE_OSOBJECT] /* temporarily save thread routine */
|
||||
|
||||
/* pre-fill the stack with 0xDEADBEEF */
|
||||
/* pre-fill the unused part of the stack with 0xDEADBEEF................*/
|
||||
LDR r0,=0xDEADBEEF
|
||||
MOV r1,r0
|
||||
|
||||
QXK_stackInit_fill:
|
||||
STMIA r2!,{r0,r1}
|
||||
CMP r2,r3
|
||||
BLT.N QXK_stackInit_fill
|
||||
|
||||
/* prepare the standard exception (without VFP) stack frame... */
|
||||
|
||||
MOV r0,r12 /* restore r0 from r12 */
|
||||
|
||||
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 */
|
||||
/* prepare the standard exception (without VFP) stack frame.............*/
|
||||
MOV r0,r12 /* restore r0 from r12 (act) */
|
||||
LDR r1,[r0,#QMACTIVE_THREAD] /* restore the thread routine */
|
||||
|
||||
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
|
||||
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 */
|
||||
.size QXK_stackInit_, . - QXK_stackInit_
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QF/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-28
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
#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_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)
|
||||
|
||||
// QF-aware ISR priority for CMSIS function NVIC_SetPriority(), NOTE5
|
||||
#define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
|
||||
|
||||
// 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
|
||||
|
||||
// QF critical section entry/exit...
|
||||
@ -78,7 +78,7 @@
|
||||
#include "qep_port.h" // QEP port
|
||||
#include "qxk_port.h" // QXK port
|
||||
#include "qf.h" // QF platform-independent public interface
|
||||
#include "qxthread.h" // QXK naked thread
|
||||
#include "qxthread.h" // QXK extended thread interface
|
||||
|
||||
//****************************************************************************
|
||||
// NOTE1:
|
||||
@ -92,7 +92,7 @@
|
||||
// "kernel-aware".
|
||||
//
|
||||
// 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
|
||||
// interrupts only with priority lower than the level specified by the
|
||||
// QF_BASEPRI macro. The interrupts with priorities above QF_BASEPRI (i.e.,
|
||||
@ -103,7 +103,7 @@
|
||||
// higher than QF_BASEPRI, can call QF services.
|
||||
//
|
||||
// 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
|
||||
// (lowest possible numerical value) for the guaranteed 3 priority bits
|
||||
// implemented in the NVIC.
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QXK/C++ port to ARM Cortex-M, QXK kernel, IAR-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-28
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -46,12 +46,14 @@
|
||||
(*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_EXIT() do { \
|
||||
QF_INT_DISABLE(); \
|
||||
QXK_sched_(); \
|
||||
if (QXK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QXK_CONTEXT_SWITCH_(); \
|
||||
} \
|
||||
QF_INT_ENABLE(); \
|
||||
} while (false)
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), IAR ARM assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-07-14
|
||||
; Product: QXK port to ARM Cortex-M (M0,M0+,M3,M4,M7), IAR-ARM assembler
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-25
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -32,34 +32,38 @@
|
||||
; mailto:info@state-machine.com
|
||||
;*****************************************************************************
|
||||
|
||||
PUBLIC QXK_start_ ; start the QXK multitasking
|
||||
PUBLIC QXK_stackInit_ ; initialize the stack of each thread
|
||||
PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception
|
||||
PUBLIC QXK_init ; initialze the QXK kernel
|
||||
PUBLIC QXK_stackInit_ ; initialize the stack of an extended thread
|
||||
PUBLIC PendSV_Handler ; CMSIS-compliant PendSV exception name
|
||||
PUBLIC NMI_Handler ; CMSIS-compliant NMI exception name
|
||||
|
||||
EXTERN QXK_attr_ ; QXK attribute structure
|
||||
EXTERN QXK_activate_ ; external reference
|
||||
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" !!!
|
||||
QF_BASEPRI EQU (0xFF >> 2)
|
||||
QF_BASEPRI EQU (0xFF >> 2)
|
||||
|
||||
; NOTE: keep in synch with the QXK struct in "qxk.h" !!!
|
||||
QXK_CURR EQU 0
|
||||
QXK_NEXT EQU 4
|
||||
; NOTE: keep in synch with the QXK_Attr struct in "qxk.h" !!!
|
||||
QXK_CURR EQU 0
|
||||
QXK_NEXT EQU 4
|
||||
QXK_TOP_PRIO EQU 8
|
||||
|
||||
; NOTE: keep in synch with the QMActive struct in "qf.h/qxk.h" !!!
|
||||
QMACTIVE_OSOBJECT EQU 40
|
||||
QMACTIVE_THREAD EQU 44
|
||||
QMACTIVE_THREAD EQU 40
|
||||
QMACTIVE_PRIO EQU 44
|
||||
|
||||
|
||||
RSEG CODE:CODE:NOROOT(2)
|
||||
;*****************************************************************************
|
||||
; The QXK_start_ function starts QXK multitasking.
|
||||
; The C signature: void QXK_start_(void);
|
||||
;
|
||||
; NOTE: QXK_start_() must be called with interrupts disabled and
|
||||
; returns with interrupts **enabled**.
|
||||
; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; This operation is performed in a nestable critical section.
|
||||
;*****************************************************************************
|
||||
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 r2,[r1,#8] ; load the System 12-15 Priority Register
|
||||
MOVS r3,#0xFF
|
||||
@ -67,112 +71,195 @@ QXK_start_:
|
||||
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
|
||||
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
|
||||
; in the vector table. (This recovers any stack used so far by main().)
|
||||
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
|
||||
MSR PRIMASK,r0 ; restore the original PRIMASK
|
||||
BX lr ; return to the caller
|
||||
|
||||
; 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 registers
|
||||
;*****************************************************************************
|
||||
; The PendSV_Handler exception handler is used for handling context switch
|
||||
; and asynchronous preemption 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_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 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
|
||||
LDMIA r0!,{r4-r7} ; pop the low registers
|
||||
ADD sp,sp,#(8*4) ; remove 8 registers from the stack
|
||||
|
||||
MSR PSP,r0 ; set PSP to the thread's SP
|
||||
ISB ; flush the instruction pipeline
|
||||
MOVS r1,#6
|
||||
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)
|
||||
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
|
||||
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 the next AO
|
||||
|
||||
; fake return from an exception...
|
||||
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
|
||||
; otherwise re-enable interrupts and return to the preempted AO-thread
|
||||
#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 >>>>>>>>>>>>>>>>>>>>>>>>>>
|
||||
BX lr ; return to the preempted AO-thread
|
||||
|
||||
#ifdef __ARMVFP__ ; if VFP available...
|
||||
; enable 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)
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
; 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:
|
||||
|
||||
;-------------------------------------------------------------------------
|
||||
; 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
|
||||
LDR r2,=0xE000ED04 ; Interrupt Control and State Register
|
||||
LDR r3,=QXK_attr_
|
||||
|
||||
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
|
||||
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
|
||||
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
|
||||
@ -182,83 +269,128 @@ PendSV_Handler:
|
||||
MOV r6,r10
|
||||
MOV r7,r11
|
||||
STMIA r1!,{r4-r7} ; save the high registers
|
||||
; NOTE: at this point r0 still holds the top of stack
|
||||
CPSID i ; disable interrupts (set PRIMASK)
|
||||
; NOTE: at this point r0 holds the top of stack
|
||||
|
||||
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
|
||||
#else ; M3/M4/M7
|
||||
STMDB r0!,{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 r0!,{s16-s31} ; ... save VFP registers s16..s31
|
||||
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
|
||||
#endif ; VFP available
|
||||
|
||||
MOVS r1,#QF_BASEPRI
|
||||
MSR BASEPRI,r1 ; selectively disable interrupts
|
||||
#endif ; M3/M4/M7
|
||||
|
||||
; NOTE: This PendSV exception handler can be preempted by an
|
||||
; interrupt, which might pend PendSV exception again. This
|
||||
; would be a problem, because the QK scheduler would run again
|
||||
; 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 extended-thread
|
||||
STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
MOV r0,r12 ; QXK_attr_.next (restore value)
|
||||
|
||||
; store the SP of the current QXK thread,
|
||||
; which was set in QXK_ISR_EXIT().
|
||||
LDR r2,[r3,#QXK_CURR]
|
||||
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
CMP r2,#0
|
||||
BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
|
||||
; otherwise continue to restoring next extended-thread...
|
||||
|
||||
#ifdef __ARMVFP__ ; 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 __ARMVFP__ ; if VFP available...
|
||||
; restore the LR of the next thread...
|
||||
LDR lr,[r2,#QMACTIVE_OSOBJECT] ; lr := QXK_attr_.next->osObject
|
||||
#endif ; VFP available
|
||||
;-------------------------------------------------------------------------
|
||||
; 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
|
||||
PendSV_restore_ex:
|
||||
STR r0,[r3,#QXK_CURR] ; QXK_attr_.curr := r0 (QXK_attr_.next)
|
||||
|
||||
; exit the critical section
|
||||
#if (__CORE__ == __ARM6M__) ; Cortex-M0/M0+/M1 ?
|
||||
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
|
||||
LDMIA r0!,{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 r1!,{r4-r7} ; pop the low registers
|
||||
; NOTE: at this point r0 holds the new top of stack
|
||||
#else ; M3/M4/M7
|
||||
MOVS r3,#0
|
||||
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI)
|
||||
LDMIA r2!,{r4-r7} ; pop the low registers
|
||||
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
|
||||
MOVS r1,#1
|
||||
MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
|
||||
#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?
|
||||
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
|
||||
|
||||
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
|
||||
#endif ; M3/M4/M7
|
||||
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
#endif ; M3/M4/M7
|
||||
|
||||
; 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
|
||||
; 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)
|
||||
MOV r12,r0 ; save r0 in r12
|
||||
|
||||
; round up the beginning of stack to the 8-byte boundary
|
||||
; r2 := (((r2 -1) >> 3) + 1) << 3;
|
||||
@ -298,77 +431,82 @@ QXK_stackInit_:
|
||||
LSRS r0,r3,#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
|
||||
#ifdef __ARMVFP__ ; if VFP available...
|
||||
SUBS r3,r3,#2*4 ; r3 := top of the 18-register stack frame
|
||||
#endif ; VFP available
|
||||
|
||||
MOV r0,r12
|
||||
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
|
||||
|
||||
; pre-fill the stack with 0xDEADBEEF
|
||||
; pre-fill the unused part of the stack with 0xDEADBEEF...................
|
||||
LDR r0,=0xDEADBEEF
|
||||
MOV r1,r0
|
||||
|
||||
QXK_stackInit_fill:
|
||||
STMIA r2!,{r0,r1}
|
||||
CMP r2,r3
|
||||
BLT.N QXK_stackInit_fill
|
||||
|
||||
; prepare the standard exception (without VFP) stack frame...
|
||||
|
||||
MOV r0,r12 ; restore r0 from r12
|
||||
|
||||
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
|
||||
; prepare the standard exception (without VFP) stack frame................
|
||||
MOV r0,r12 ; restore r0 from r12 (act)
|
||||
LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/// @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
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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_ENABLE() QF_set_BASEPRI(0U)
|
||||
|
||||
// the intrinsic function _norm() generates the CLZ instruction
|
||||
#define QF_LOG2(n_) ((uint8_t)(32U - _norm(n_)))
|
||||
// Cortex-M3/M4/M7 provide the CLZ instruction for fast LOG2
|
||||
#define QF_LOG2(x_) (static_cast<uint_fast8_t>(32U - __clz(x_)))
|
||||
|
||||
// assembly function for setting the BASEPRI register
|
||||
extern "C" void QF_set_BASEPRI(unsigned basePri);
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QXK/C++ port to ARM Cortex-M, preemptive QXK kernel, TI-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-30
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -38,15 +38,29 @@
|
||||
#ifndef qxk_port_h
|
||||
#define qxk_port_h
|
||||
|
||||
// QXK context switch (trigger PendSV exception)
|
||||
#define QXK_CONTEXT_SWITCH_() \
|
||||
(*Q_UINT2PTR_CAST(uint32_t, 0xE000ED04U) = (uint32_t)(1U << 28))
|
||||
// determination if the code executes in the ISR context
|
||||
#define QXK_ISR_CONTEXT_() (QXK_get_IPSR() != static_cast<uint32_t>(0))
|
||||
|
||||
// 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" {
|
||||
void QXK_ISR_ENTRY(void);
|
||||
void QXK_ISR_EXIT(void);
|
||||
} // extern "C"
|
||||
// get the IPSR defined in assembly
|
||||
uint32_t QXK_get_IPSR(void);
|
||||
}
|
||||
|
||||
#include "qxk.h" // QXK platform-independent public interface
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
;*****************************************************************************
|
||||
; Product: QXK port to ARM Cortex-M (M0,M0+,M1,M3,M4,M7), TI-ARM assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-07-14
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-25
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -32,44 +32,46 @@
|
||||
; mailto:info@state-machine.com
|
||||
;*****************************************************************************
|
||||
|
||||
.global QXK_start_ ; start the QXK multitasking
|
||||
.global QXK_stackInit_ ; initialize the stack of each thread
|
||||
.global PendSV_Handler ; CMSIS-compliant PendSV exception
|
||||
.global QXK_init ; initialze the QXK kernel
|
||||
.global QXK_stackInit_ ; initialize the stack of an extended thread
|
||||
.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
|
||||
.endif ; M3/M4/M7
|
||||
.global QXK_ISR_ENTRY ; inform QXK about interrupt entry
|
||||
.global QXK_ISR_EXIT ; inform QXK about interrupt exit
|
||||
.global QXK_get_IPSR ; get the IPSR
|
||||
.global assert_failed ; low-level assert handler
|
||||
|
||||
.ref QXK_attr_ ; QXK attribute structure
|
||||
.ref QXK_threadRet_ ; return from a thread function
|
||||
.ref QXK_sched_ ; external reference
|
||||
.ref QXK_threadRet_ ; return from an extended thread function
|
||||
.ref QXK_activate_ ; external reference
|
||||
.ref Q_onAssert ; external reference
|
||||
|
||||
; NOTE: keep in synch with QF_BASEPRI value defined in "qf_port.h" !!!
|
||||
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_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" !!!
|
||||
QMACTIVE_OSOBJECT .equ 40
|
||||
QMACTIVE_THREAD .equ 44
|
||||
QMACTIVE_THREAD .equ 40
|
||||
QMACTIVE_PRIO .equ 44
|
||||
|
||||
.text
|
||||
.thumb
|
||||
|
||||
;*****************************************************************************
|
||||
; The QXK_start_ function starts QXK multitasking.
|
||||
; The C signature: void QXK_start_(void);
|
||||
;
|
||||
; NOTE: QXK_start_() must be called with interrupts disabled and
|
||||
; returns with interrupts **enabled**.
|
||||
; The QXK_init() function sets the priority of PendSV to 0xFF (lowest).
|
||||
; This operation is performed in a nestable critical section.
|
||||
;*****************************************************************************
|
||||
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 r2,[r1,#8] ; load the System 12-15 Priority Register
|
||||
MOVS r3,#0xFF
|
||||
@ -77,123 +79,206 @@ QXK_start_:
|
||||
ORRS r2,r3 ; set PRI_14 (PendSV) to 0xFF
|
||||
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
|
||||
; in the vector table. (This recovers any stack used so far by main().)
|
||||
LDR r0,VTOR_addr ; 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
|
||||
MSR PRIMASK,r0 ; restore the original PRIMASK
|
||||
BX lr ; return to the caller
|
||||
.endasmfunc
|
||||
|
||||
; set the current QXK thread to the next QXK thread
|
||||
LDR r1,QXK_attr_addr
|
||||
LDR r2,[r1,#QXK_NEXT] ; r2 := QXK_attr_.next
|
||||
STR r2,[r1,#QXK_CURR] ; QXK_attr_.curr := r2
|
||||
;*****************************************************************************
|
||||
; The PendSV_Handler exception handler is used for handling context switch
|
||||
; and asynchronous preemption 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_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
|
||||
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 registers
|
||||
; Prepare some constants (an address and a bitmask) before entering
|
||||
; a critical section...
|
||||
LDR r3,QXK_attr_addr
|
||||
LDR r2,ICSR_addr ; Interrupt Control and State Register
|
||||
MOVS r1,#1
|
||||
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 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
|
||||
LDMIA r0!,{r4-r7} ; pop the low registers
|
||||
ADD sp,sp,#(8*4) ; remove 8 registers from the stack
|
||||
|
||||
MSR PSP,r0 ; set PSP to the thread's SP
|
||||
ISB ; flush the instruction pipeline
|
||||
MOVS r1,#6
|
||||
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)
|
||||
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
|
||||
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 the next AO
|
||||
|
||||
; fake return from an exception...
|
||||
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
|
||||
; otherwise re-enable interrupts and return to the preempted AO-thread
|
||||
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
|
||||
|
||||
.if __TI_VFP_SUPPORT__ ; if VFP available...
|
||||
; enable VFP by enabling CP10 and CP11 coprocessors
|
||||
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
|
||||
MOVS r0,#0
|
||||
MSR BASEPRI,r0 ; enable interrupts (clear BASEPRI)
|
||||
.else ; M0/M0+
|
||||
CPSIE i ; enable interrupts (clear PRIMASK)
|
||||
.endif ; M0/M0+/M1
|
||||
|
||||
BX r1 ; return to the "interrupted" thread (PC)
|
||||
.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
|
||||
.endif ; M0/M0+
|
||||
; >>>>>>>>>>>>>>>>>>>>>>>> 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
|
||||
LDR r2,ICSR_addr ; Interrupt Control and State Register
|
||||
LDR r3,QXK_attr_addr
|
||||
|
||||
|
||||
.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...
|
||||
TST lr,#(1 << 4) ; is it return with the VFP exception frame?
|
||||
IT EQ ; if lr[4] is zero...
|
||||
VSTMDBEQ r0!,{s16-s31} ; ... save VFP registers s16..s31
|
||||
STMDB r0!,{r1,lr} ; save the "aligner" and the EXC_RETURN value
|
||||
.endif ; VFP available
|
||||
MOVS r1,#QF_BASEPRI
|
||||
MSR BASEPRI,r1 ; selectively disable interrupts
|
||||
.else ; M0/M0+/M1
|
||||
.else ; M0/M0+
|
||||
SUBS r0,r0,#(8*4) ; make room for 8 registers r4-r11
|
||||
MOVS r1,r0 ; r1 := temporary PSP (do not clobber r0!)
|
||||
STMIA r1!,{r4-r7} ; save the low registers
|
||||
@ -202,77 +287,124 @@ PendSV_Handler: .asmfunc
|
||||
MOV r6,r10
|
||||
MOV r7,r11
|
||||
STMIA r1!,{r4-r7} ; save the high registers
|
||||
; NOTE: at this point r0 still holds the top of stack
|
||||
CPSID i ; disable interrupts (set PRIMASK)
|
||||
.endif ; M0/M0+/M1
|
||||
; NOTE: at this point r0 holds the top of stack
|
||||
|
||||
LDR r1,[r3,#QXK_CURR] ; r1 := QXK_attr_.curr (restore value)
|
||||
.endif ; M0/M0+
|
||||
|
||||
; NOTE: This PendSV exception handler can be preempted by an
|
||||
; interrupt, which might pend PendSV exception again. This
|
||||
; would be a problem, because the QK scheduler would run again
|
||||
; 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 extended-thread
|
||||
STR r0,[r1,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
MOV r0,r12 ; QXK_attr_.next (restore value)
|
||||
|
||||
; store the SP of the current QXK thread,
|
||||
; which was set in QXK_ISR_EXIT().
|
||||
LDR r2,[r3,#QXK_CURR]
|
||||
STR r0,[r2,#QMACTIVE_THREAD] ; QXK_attr_.curr->thread := r0
|
||||
CMP r2,#0
|
||||
BEQ PendSV_restore_ao ; branch if (QXK_attr_.next->thread == 0)
|
||||
; otherwise continue to restoring next extended-thread...
|
||||
|
||||
.if __TI_VFP_SUPPORT__ ; if VFP available...
|
||||
; store the LR of the current QXK thread...
|
||||
STR lr,[r2,#QMACTIVE_OSOBJECT] ; QXK_attr_.curr->osObject := lr
|
||||
.endif ; VFP available
|
||||
;-------------------------------------------------------------------------
|
||||
; 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
|
||||
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
|
||||
MOVS r3,#0
|
||||
MSR BASEPRI,r3 ; enable interrupts (clear BASEPRI)
|
||||
|
||||
.if __TI_TMS470_V7M3__ | __TI_TMS470_V7M4__ ; | __TI_TMS470_V7M7__
|
||||
MOVS r1,#1
|
||||
MSR BASEPRI,r1 ; enable interrupts (clear BASEPRI)
|
||||
.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?
|
||||
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
|
||||
|
||||
LDMIA r0!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
|
||||
.else ; M0/M0+/M1
|
||||
; exit the critical section
|
||||
LDMIA r2!,{r4-r11} ; restore r4-r11 from the next thread's stack
|
||||
.else ; M0/M0+
|
||||
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
|
||||
LDMIA r0!,{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 r1!,{r4-r7} ; pop the low registers
|
||||
; NOTE: at this point r0 holds the new top of stack
|
||||
.endif ; M0/M0+/M1
|
||||
LDMIA r2!,{r4-r7} ; pop the low registers
|
||||
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
|
||||
.endif ; M0/M0+
|
||||
|
||||
; 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
|
||||
.endasmfunc
|
||||
BX lr ; return to the next extended-thread
|
||||
.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
|
||||
; 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)
|
||||
MOV r12,r0 ; save r0 in r12
|
||||
|
||||
; round up the beginning of stack to the 8-byte boundary
|
||||
; r2 := (((r2 -1) >> 3) + 1) << 3;
|
||||
@ -312,80 +445,85 @@ QXK_stackInit_: .asmfunc
|
||||
LSRS r0,r3,#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
|
||||
.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
|
||||
STR r1,[r0,#QMACTIVE_OSOBJECT] ; temporarily save the thread routine
|
||||
|
||||
; pre-fill the stack with STACK_FILL_const
|
||||
; pre-fill the unused part of the stack with 0xDEADBEEF...................
|
||||
LDR r0,STACK_FILL_const
|
||||
MOV r1,r0
|
||||
|
||||
QXK_stackInit_fill:
|
||||
STMIA r2!,{r0,r1}
|
||||
CMP r2,r3
|
||||
BLT.N QXK_stackInit_fill
|
||||
|
||||
; prepare the standard exception (without VFP) stack frame...
|
||||
|
||||
MOV r0,r12 ; restore r0 from r12
|
||||
|
||||
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
|
||||
; prepare the standard exception (without VFP) stack frame................
|
||||
MOV r0,r12 ; restore r0 from r12 (act)
|
||||
LDR r1,[r0,#QMACTIVE_THREAD] ; restore the thread routine
|
||||
|
||||
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
|
||||
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
|
||||
.endasmfunc
|
||||
.endasmfunc
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
@ -400,91 +538,18 @@ QXK_stackInit_fill:
|
||||
QF_set_BASEPRI: .asmfunc
|
||||
MSR BASEPRI,r0 ; set BASEPRI
|
||||
BX lr ; return to the caller
|
||||
.endasmfunc
|
||||
.endasmfunc
|
||||
.endif ; M3/M4/M7
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
; Inform QXK about interrupt entry.
|
||||
; C prototype: void QXK_ISR_ENTRY(void);
|
||||
; C pseudocode:
|
||||
; void QXK_ISR_ENTRY(void) {
|
||||
; int_disable();
|
||||
; ++QXK_attr_.intNest;
|
||||
; int_enable();
|
||||
; }
|
||||
; The QXK_get_IPSR function gets the IPSR register and returns it in r0.
|
||||
; C prototype: uint32_t QXK_get_IPSR(void);
|
||||
;*****************************************************************************
|
||||
QXK_ISR_ENTRY: .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]
|
||||
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
|
||||
|
||||
QXK_get_IPSR: .asmfunc
|
||||
MRS r0,ipsr ; r0 := IPSR
|
||||
BX lr ; return to the caller
|
||||
.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
|
||||
.endasmfunc
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
@ -497,9 +562,9 @@ assert_failed: .asmfunc
|
||||
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
|
||||
|
||||
BL Q_onAssert
|
||||
.endasmfunc
|
||||
.endasmfunc
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
; Constants for PC-relative LDR
|
||||
@ -507,15 +572,14 @@ assert_failed: .asmfunc
|
||||
SHPR_addr: .word 0xE000ED18
|
||||
ICSR_addr: .word 0xE000ED04
|
||||
VTOR_addr: .word 0xE000ED08
|
||||
CPACR_addr: .word 0xE000ED88
|
||||
FPCCR_addr: .word 0xE000EF34
|
||||
STACK_FILL_const: .word 0xDEADBEEF
|
||||
|
||||
|
||||
;*****************************************************************************
|
||||
; Addresses for PC-relative LDR
|
||||
;*****************************************************************************
|
||||
QXK_attr_addr: .word QXK_attr_
|
||||
QXK_sched_addr: .word QXK_sched_
|
||||
QXK_attr_addr: .word QXK_attr_
|
||||
QXK_activate_addr: .word QXK_activate_
|
||||
QXK_threadRet_addr: .word QXK_threadRet_
|
||||
|
||||
Thread_ret_addr: .word Thread_ret
|
||||
assert_failed_addr: .word assert_failed
|
||||
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, GNU-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -77,9 +77,8 @@
|
||||
#define QK_IRQ_END() \
|
||||
} --QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == (uint_fast8_t)0) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != (uint_fast8_t)0) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != (uint_fast8_t)0) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
__asm volatile (" POP {R3, LR}\n" \
|
||||
@ -96,9 +95,8 @@
|
||||
#define QK_IRQ_END() \
|
||||
} --QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == (uint_fast8_t)0) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != (uint_fast8_t)0) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != (uint_fast8_t)0) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
__asm volatile (" POP {R3, LR}\n" \
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, IAR-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -73,9 +73,8 @@
|
||||
#define QK_IRQ_END() \
|
||||
} --QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == (uint_fast8_t)0) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != (uint_fast8_t)0) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != (uint_fast8_t)0) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
__asm(" POP {R3, LR}\n" \
|
||||
@ -92,9 +91,8 @@
|
||||
#define QK_IRQ_END() \
|
||||
} --QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == (uint_fast8_t)0) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != (uint_fast8_t)0) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != (uint_fast8_t)0) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
__asm(" POP {R3, LR}\n" \
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to ARM Cortex-R, preemptive QK kernel, TI-ARM toolset
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.7.0
|
||||
/// Last updated on 2016-08-21
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// Q u a n t u m L e a P s
|
||||
/// ---------------------------
|
||||
@ -79,9 +79,8 @@
|
||||
#define QK_IRQ_END() \
|
||||
} --QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == (uint_fast8_t)0) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != (uint_fast8_t)0) { \
|
||||
QK_sched_(p); \
|
||||
if (QK_sched_() != (uint_fast8_t)0) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
/*****************************************************************************
|
||||
* Product: QK port to ARM7-9, GNU-ARM assembler
|
||||
* Last Updated for Version: 5.7.0
|
||||
* Date of the Last Update: 2016-07-11
|
||||
* Last Updated for Version: 5.7.2
|
||||
* Date of the Last Update: 2016-09-26
|
||||
*
|
||||
* 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 */
|
||||
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_
|
||||
MOV lr,pc /* copy the return address to link register */
|
||||
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:
|
||||
/* IRQ exit {{{ */ /* IRQ/FIQ disabled--return from scheduler */
|
||||
|
@ -1,7 +1,7 @@
|
||||
;-----------------------------------------------------------------------------
|
||||
; Product: QK port to ARM7/9, IAR-ARM Assembler
|
||||
; Last Updated for Version: 5.7.0
|
||||
; Date of the Last Update: 2016-07-11
|
||||
; Last Updated for Version: 5.7.2
|
||||
; Date of the Last Update: 2016-09-26
|
||||
;
|
||||
; Q u a n t u m L e a P s
|
||||
; ---------------------------
|
||||
@ -71,7 +71,7 @@ QK_init:
|
||||
SECTION .textrw:DATA:NOROOT(2)
|
||||
PUBLIC QK_irq
|
||||
EXTERN BSP_irq
|
||||
EXTERN QK_attr_, QK_schedPrio_, QK_sched_
|
||||
EXTERN QK_attr_, QK_sched_, QK_activate_
|
||||
CODE32
|
||||
|
||||
QK_irq:
|
||||
@ -114,15 +114,15 @@ QK_irq:
|
||||
STR r12,[r0,#QK_INT_NEST] ; store the value in QK_attr_.intNest
|
||||
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_
|
||||
MOV lr,pc ; copy the return address to link register
|
||||
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:
|
||||
; IRQ exit {{{ ; IRQ/FIQ disabled--return from scheduler
|
||||
|
@ -80,17 +80,17 @@ void QF_setEmbOsTaskAttr(QMActive *act, uint32_t attr);
|
||||
#ifdef QP_IMPL
|
||||
|
||||
// embOS-specific scheduler locking, see NOTE3
|
||||
#define QF_SCHED_STAT_TYPE_ struct { uint_fast8_t m_lockPrio; }
|
||||
#define QF_SCHED_LOCK_(pLockStat_, prio_) do { \
|
||||
if (OS_InInt != (OS_U8)0) { \
|
||||
(pLockStat_)->m_lockPrio = \
|
||||
static_cast<uint_fast8_t>(QF_MAX_ACTIVE + 1); \
|
||||
} else { \
|
||||
(pLockStat_)->m_lockPrio = (prio_); \
|
||||
#define QF_SCHED_STAT_
|
||||
#define QF_SCHED_LOCK_(dummy) do { \
|
||||
if (OS_InInt == static_cast<OS_U8>(0)) { \
|
||||
OS_EnterRegion(); \
|
||||
} \
|
||||
} while (0)
|
||||
#define QF_SCHED_UNLOCK_(dummy) OS_LeaveRegion()
|
||||
} while (false)
|
||||
#define QF_SCHED_UNLOCK_() do { \
|
||||
if (OS_InInt == static_cast<OS_U8>(0)) { \
|
||||
OS_LeaveRegion(); \
|
||||
} \
|
||||
} while (false)
|
||||
|
||||
// native QF event pool operations...
|
||||
#define QF_EPOOL_TYPE_ QMPool
|
||||
|
@ -2,7 +2,7 @@
|
||||
:: ===========================================================================
|
||||
:: Product: QP/C++ build script for PC-Lint(TM), Standard C++ compiler
|
||||
: 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
|
||||
:: ---------------------------
|
||||
|
@ -1,7 +1,7 @@
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
// Product: PC-Lint 9.x option file for linting QP/C++
|
||||
// Last updated for version 5.7.0
|
||||
// Last updated on 2016-09-08
|
||||
// Last updated for version 5.7.2
|
||||
// Last updated on 2016-09-28
|
||||
//
|
||||
// Q u a n t u m L e a P s
|
||||
// ---------------------------
|
||||
@ -77,6 +77,10 @@ qpcpp.lnt // QP/C++ options
|
||||
act)
|
||||
-efunc(661, *execTatbl_) // Possible access of out-of-bounds pointer
|
||||
-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
|
||||
-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_INC_,
|
||||
QF_QACTIVE_TO_QHSM_CAST_)
|
||||
-esym(1923,
|
||||
QF_SCHED_STAT_TYPE_, // 16-2-2 Macro could become const variable
|
||||
-esym(1923, // 16-2-2 Macro could become const variable
|
||||
QF_SCHED_LOCK_,
|
||||
QF_SCHED_UNLOCK_)
|
||||
-emacro(740, // 5-2-7 Unusual pointer cast (incompatible indirect types)
|
||||
|
@ -54,7 +54,7 @@
|
||||
/// 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 header file in all builds.
|
||||
#define QF_MAX_ACTIVE 63
|
||||
#define QF_MAX_ACTIVE 32
|
||||
|
||||
//! The maximum number of event pools in the application.
|
||||
///
|
||||
|
@ -2,8 +2,8 @@
|
||||
/// @brief QK/C++ port to Lint, Generic C++ compiler
|
||||
/// @cond
|
||||
///***************************************************************************
|
||||
/// Last updated for version 5.6.0
|
||||
/// Last updated on 2015-12-29
|
||||
/// Last updated for version 5.7.2
|
||||
/// Last updated on 2016-09-26
|
||||
///
|
||||
/// 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
|
||||
/// skeleton code in assembly.
|
||||
#define QK_ISR_ENTRY() do { \
|
||||
++QK_intNest_; \
|
||||
QF_QS_ISR_ENTRY(QK_intNest_, QK_currPrio_); \
|
||||
++QK_attr_.intNest; \
|
||||
} while (false)
|
||||
|
||||
|
||||
@ -85,11 +84,10 @@
|
||||
/// QK ports will not define this macro, but instead will provide ISR
|
||||
/// skeleton code in assembly.
|
||||
#define QK_ISR_EXIT() do { \
|
||||
--QK_intNest_; \
|
||||
if (QK_intNest_ == static_cast<uint_fast8_t>(0)) { \
|
||||
uint_fast8_t p = QK_schedPrio_(); \
|
||||
if (p != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_sched_(p); \
|
||||
--QK_attr_.intNest; \
|
||||
if (QK_attr_.intNest == static_cast<uint_fast8_t>(0)) { \
|
||||
if (QK_sched_() != static_cast<uint_fast8_t>(0)) { \
|
||||
QK_activate_(); \
|
||||
} \
|
||||
} \
|
||||
else { \
|
||||
|
@ -60,7 +60,7 @@
|
||||
/// 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 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.
|
||||
///
|
||||
|
@ -54,7 +54,7 @@
|
||||
/// 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 header file in all builds.
|
||||
#define QF_MAX_ACTIVE 63
|
||||
#define QF_MAX_ACTIVE 32
|
||||
|
||||
//! 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
Loading…
x
Reference in New Issue
Block a user