/** * @file * @brief QV-nano implementation. * @ingroup qvn * @cond ****************************************************************************** * Last updated for version 6.8.0 * Last updated on 2020-03-08 * * Q u a n t u m L e a P s * ------------------------ * Modern Embedded Software * * Copyright (C) 2005-2020 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 . * * Contact information: * * ****************************************************************************** * @endcond */ #include "qpn_conf.h" /* QP-nano configuration file (from the application) */ #include "qfn_port.h" /* QF-nano port from the port directory */ #include "qassert.h" /* embedded systems-friendly assertions */ Q_DEFINE_THIS_MODULE("qvn") /* protection against including this source file in a wrong project */ #ifndef QVN_H #error "Source file included in a project NOT based on the QV-nano kernel" #endif /* QVN_H */ /****************************************************************************/ /** * @description * QF_run() is typically called from your startup code after you initialize * the QF and start at least one active object with QActive_start(). * This implementation of QF_run() is for the cooperative Vanilla kernel. * * @returns QF_run() typically does not return in embedded applications. * However, when QP runs on top of an operating system, QF_run() might * return and in this case the return represents the error code (0 for * success). Typically the value returned from QF_run() is subsequently * passed on as return from main(). */ int_t QF_run(void) { uint_fast8_t p; QActive *a; #ifdef QF_MAX_ACTIVE /* deprecated constant provided? */ #if (QF_MAX_ACTIVE < 1) || (8 < QF_MAX_ACTIVE) #error "QF_MAX_ACTIVE not defined or out of range. Valid range is 1..8" #endif QF_maxActive_ = (uint_fast8_t)QF_MAX_ACTIVE; #else /** @pre the number of active objects must be initialized by calling: * QF_init(Q_DIM(QF_active)); */ Q_REQUIRE_ID(100, (1U <= QF_maxActive_) && (QF_maxActive_ <= 8U)); #endif /* set priorities all registered active objects... */ for (p = 1U; p <= QF_maxActive_; ++p) { a = QF_ROM_ACTIVE_GET_(p); /* QF_active[p] must be initialized */ Q_ASSERT_ID(810, a != (QActive *)0); a->prio = (uint8_t)p; /* set the priority of the active object */ } /* trigger initial transitions in all registered active objects... */ for (p = 1U; p <= QF_maxActive_; ++p) { a = QF_ROM_ACTIVE_GET_(p); QHSM_INIT(&a->super); /* take the initial transition in the SM */ } QF_onStartup(); /* invoke startup callback */ /* the event loop of the cooperative QV-nano kernel... */ QF_INT_DISABLE(); for (;;) { if (QF_readySet_ != 0U) { QActiveCB const Q_ROM *acb; #ifdef QF_LOG2 p = QF_LOG2(QF_readySet_); #else /* hi nibble non-zero? */ if ((QF_readySet_ & 0xF0U) != 0U) { p = (uint_fast8_t)Q_ROM_BYTE(QF_log2Lkup[QF_readySet_ >> 4]) + 4U; } else { /* hi nibble of QF_readySet_ is zero */ p = (uint_fast8_t)Q_ROM_BYTE(QF_log2Lkup[QF_readySet_]); } #endif /* QF_LOG2 */ acb = &QF_active[p]; a = QF_ROM_ACTIVE_GET_(p); /* some unsuded events must be available */ Q_ASSERT_ID(820, a->nUsed > 0U); --a->nUsed; Q_SIG(a) = QF_ROM_QUEUE_AT_(acb, a->tail).sig; #if (Q_PARAM_SIZE != 0U) Q_PAR(a) = QF_ROM_QUEUE_AT_(acb, a->tail).par; #endif if (a->tail == 0U) { /* wrap around? */ a->tail = Q_ROM_BYTE(acb->qlen); } --a->tail; QF_INT_ENABLE(); QHSM_DISPATCH(&a->super); /* dispatch to the HSM (RTC step) */ QF_INT_DISABLE(); /* empty queue? */ if (a->nUsed == 0U) { /* clear the bit corresponding to 'p' */ QF_readySet_ &= (uint_fast8_t)~(1U << (p - 1U)); } } else { /* QV_onIdle() must be called with interrupts DISABLED because * the determination of the idle condition (no events in the * queues) can change at any time by an interrupt posting events * to a queue. QV_onIdle() MUST enable interrupts internally, * perhaps at the same time as putting the CPU into a power-saving * mode. */ QV_onIdle(); QF_INT_DISABLE(); } } #ifdef __GNUC__ /* GNU compiler? */ return 0; #endif }