This commit is contained in:
QL 2020-12-04 09:14:59 -05:00
parent 835c342e12
commit 0c7578d75f
13 changed files with 1003 additions and 10 deletions

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${src::qmsmtst.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${src::qmsmtst.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::calc1_sub.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::calc1_sub.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::history.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::history.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::qmsmtst.c} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -14,7 +14,7 @@
* Product(s) :
* This license is available only for evaluation purposes and
* the generated code is still licensed under the terms of GPL.
* Please submit request for extension of the evaluaion period at:
* Please submit request for extension of the evaluation period at:
* <www.state-machine.com/licensing/#RequestForm>
*/
/*.$endhead${.::qmsmtst.h} ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^*/

View File

@ -5,7 +5,7 @@
* @cond
******************************************************************************
* Last updated for version 6.9.1
* Last updated on 2020-09-30
* Last updated on 2020-12-04
*
* Q u a n t u m L e a P s
* ------------------------
@ -666,7 +666,7 @@ enum {
#elif (QS_OBJ_PTR_SIZE == 4U)
#define QS_OBJ(obj_) (QS_u32_fmt_(QS_OBJ_T, (uint32_t)(obj_)))
#elif (QS_OBJ_PTR_SIZE == 8U)
#define QS_OBJ(obj_) (QS_u64(QS_OBJ_T, (uint64_t)(obj_)))
#define QS_OBJ(obj_) (QS_u64_fmt_(QS_OBJ_T, (uint64_t)(obj_)))
#else
/*! Output formatted object pointer to the QS record */
#define QS_OBJ(obj_) (QS_u32_fmt_(QS_OBJ_T, (uint32_t)(obj_)))

View File

@ -0,0 +1,48 @@
/**
* @file
* @brief QEP/C port, generic C99 compiler
* @ingroup ports
* @cond
******************************************************************************
* Last updated for version 6.9.1
* Last updated on 2020-11-25
*
* 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 <www.gnu.org/licenses>.
*
* Contact information:
* <www.state-machine.com/licensing>
* <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 "qep.h" /* QEP platform-independent public interface */
#endif /* QEP_PORT_H */

View File

@ -0,0 +1,625 @@
/**
* @file
* @brief QF/C port to FreeRTOS-ESP32 adaptation
* @ingroup ports
* @cond
******************************************************************************
* Last updated for version 6.9.1
* Last updated on 2020-11-28
*
* 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 <www.gnu.org/licenses/>.
*
* Contact information:
* <www.state-machine.com/licensing>
* <info@state-machine.com>
******************************************************************************
* @endcond
*/
#define QP_IMPL /* this is QP implementation */
#include "qf_port.h" /* QF port */
#include "qf_pkg.h"
#include "qassert.h"
#ifdef Q_SPY /* QS software tracing enabled? */
#include "qs_port.h" /* QS port */
#include "qs_pkg.h" /* QS package-scope internal interface */
#else
#include "qs_dummy.h" /* disable the QS software tracing */
#endif /* Q_SPY */
#include <esp_log.h>
Q_DEFINE_THIS_MODULE("qf_port")
//static const char *TAG = "qf_port";
#if ( configSUPPORT_STATIC_ALLOCATION == 0 )
#error "This QP/C port to FreeRTOS requires configSUPPORT_STATIC_ALLOCATION "
#endif
#if ( configMAX_PRIORITIES < QF_MAX_ACTIVE )
#error "FreeRTOS configMAX_PRIORITIES must not be less than QF_MAX_ACTIVE"
#endif
/* Global objects ----------------------------------------------------------*/
PRIVILEGED_DATA portMUX_TYPE QF_esp32mux = portMUX_INITIALIZER_UNLOCKED;
/* Local objects -----------------------------------------------------------*/
static void task_function(void *pvParameters); /* FreeRTOS task signature */
int_t qf_run_active = 0;
/*==========================================================================*/
void QF_init(void) {
/* empty for FreeRTOS */
/*portMUX_TYPE QF_esp32mux = portMUX_INITIALIZER_UNLOCKED;*/
}
/*..........................................................................*/
int_t QF_run(void) {
//QF_onStartup(); /* the startup callback (configure/enable interrupts) */
//vTaskStartScheduler(); /* start the FreeRTOS scheduler */
//Q_ERROR_ID(110); /* the FreeRTOS scheduler should never return */
qf_run_active = 100;
return 0; /* dummy return to make the compiler happy */
}
/*..........................................................................*/
void QF_stop(void) {
QF_onCleanup(); /* cleanup callback */
}
/*..........................................................................*/
void QActive_start_(QActive * const me, uint_fast8_t prio,
QEvt const * * const qSto, uint_fast16_t const qLen,
void * const stkSto, uint_fast16_t const stkSize,
void const * const par)
{
TaskHandle_t thr;
/* task name provided by the user in QF_setTaskName() or default name */
char_t const *taskName = (me->thread.pxDummy1 != (void *)0)
? (char_t const *)me->thread.pxDummy1
: (char_t const *)"AO";
Q_REQUIRE_ID(200, (0U < prio)
&& (prio <= QF_MAX_ACTIVE) /* in range */
&& (qSto != (QEvt const **)0) /* queue storage must be provided */
&& (qLen > 0U) /* queue size must be provided */
&& (stkSto != (void *)0) /* stack storage must be provided */
&& (stkSize > 0U)); /* stack size must be provided */
/* create the event queue for the AO */
QEQueue_init(&me->eQueue, qSto, qLen);
me->prio = prio; /* save the QF priority */
QF_add_(me); /* make QF aware of this active object */
QHSM_INIT(&me->super, par, me->prio); /* the top-most initial tran. */
QS_FLUSH(); /* flush the QS trace buffer to the host */
/* statically create the FreeRTOS task for the AO */
thr = xTaskCreateStaticPinnedToCore(
&task_function, /* the task function */
taskName , /* the name of the task */
stkSize/sizeof(portSTACK_TYPE), /* stack size */
(void *)me, /* the 'pvParameters' parameter */
(UBaseType_t)(prio + tskIDLE_PRIORITY), /* FreeRTOS priority */
(StackType_t *)stkSto, /* stack storage */
&me->thread, /* task buffer */
1); /* CPU number */
Q_ENSURE_ID(210, thr != (TaskHandle_t)0); /* must be created */
}
/*..........................................................................*/
void QActive_setAttr(QActive *const me, uint32_t attr1, void const *attr2) {
/* this function must be called before QACTIVE_START(),
* which implies that me->thread.pxDummy1 must not be used yet;
*/
Q_REQUIRE_ID(300, me->thread.pxDummy1 == (void *)0);
switch (attr1) {
case TASK_NAME_ATTR:
/* temporarily store the name */
me->thread.pxDummy1 = (void *)attr2; /* cast 'const' away */
break;
/* ... */
}
}
/*..........................................................................*/
static void task_function(void *pvParameters) { /* FreeRTOS task signature */
QActive *act = (QActive *)pvParameters;
/* event-loop */
for (;;) { /* for-ever */
QEvt const *e = QActive_get_(act);
QHSM_DISPATCH(&act->super, e, act->prio);
QF_gc(e); /* check if the event is garbage, and collect it if so */
}
}
/*==========================================================================*/
/* The "FromISR" QP APIs for the FreeRTOS port... */
#ifdef Q_SPY
bool IRAM_ATTR QActive_postFromISR_(QActive * const me, QEvt const * const e,
uint_fast16_t const margin,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender)
#else
bool IRAM_ATTR QActive_postFromISR_(QActive * const me, QEvt const * const e,
uint_fast16_t const margin,
BaseType_t * const pxHigherPriorityTaskWoken)
#endif
{
QEQueueCtr nFree; /* temporary to avoid UB for volatile access */
bool status;
/** @pre event pointer must be valid */
Q_REQUIRE_ID(400, e != (QEvt *)0);
portENTER_CRITICAL_ISR(&QF_esp32mux);
nFree = me->eQueue.nFree; /* get volatile into the temporary */
if (margin == QF_NO_MARGIN) {
if (nFree > 0U) {
status = true; /* can post */
}
else {
status = false; /* cannot post */
Q_ERROR_ID(410); /* must be able to post the event */
}
}
else if (nFree > (QEQueueCtr)margin) {
status = true; /* can post */
}
else {
status = false; /* cannot post */
}
if (status) { /* can post the event? */
QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST, me->prio)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(sender); /* the sender object */
QS_SIG_PRE_(e->sig); /* the signal of the event */
QS_OBJ_PRE_(me); /* this active object (recipient) */
QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
QS_EQC_PRE_(nFree); /* number of free entries */
QS_EQC_PRE_(me->eQueue.nMin); /* min number of free entries */
QS_END_NOCRIT_PRE_()
/* is it a pool event? */
if (e->poolId_ != 0U) {
QF_EVT_REF_CTR_INC_(e); /* increment the reference counter */
}
--nFree; /* one free entry just used up */
me->eQueue.nFree = nFree; /* update the volatile */
if (me->eQueue.nMin > nFree) {
me->eQueue.nMin = nFree; /* update minimum so far */
}
/* empty queue? */
if (me->eQueue.frontEvt == (QEvt *)0) {
me->eQueue.frontEvt = e; /* deliver event directly */
portEXIT_CRITICAL_ISR(&QF_esp32mux);
/* signal the event queue */
vTaskNotifyGiveFromISR((TaskHandle_t)&me->thread,
pxHigherPriorityTaskWoken);
}
/* queue is not empty, insert event into the ring-buffer */
else {
/* insert event into the ring buffer (FIFO) */
QF_PTR_AT_(me->eQueue.ring, me->eQueue.head) = e;
if (me->eQueue.head == 0U) { /* need to wrap head? */
me->eQueue.head = me->eQueue.end; /* wrap around */
}
--me->eQueue.head; /* advance the head (counter clockwise) */
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
}
else {
QS_BEGIN_NOCRIT_PRE_(QS_QF_ACTIVE_POST_ATTEMPT, me->prio)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(sender); /* the sender object */
QS_SIG_PRE_(e->sig); /* the signal of the event */
QS_OBJ_PRE_(me); /* this active object (recipient) */
QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
QS_EQC_PRE_(nFree); /* number of free entries */
QS_EQC_PRE_(margin); /* margin requested */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
QF_gcFromISR(e); /* recycle the event to avoid a leak */
}
return status;
}
/*..........................................................................*/
#ifdef Q_SPY
void IRAM_ATTR QF_publishFromISR_(QEvt const * const e,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender)
#else
void IRAM_ATTR QF_publishFromISR_(QEvt const * const e,
BaseType_t * const pxHigherPriorityTaskWoken)
#endif
{
QPSet subscrList; /* local, modifiable copy of the subscriber list */
/** @pre the published signal must be within the configured range */
Q_REQUIRE_ID(500, e->sig < (QSignal)QF_maxPubSignal_);
portENTER_CRITICAL_ISR(&QF_esp32mux);
QS_BEGIN_NOCRIT_PRE_(QS_QF_PUBLISH, 0U)
QS_TIME_PRE_(); /* the timestamp */
QS_OBJ_PRE_(sender); /* the sender object */
QS_SIG_PRE_(e->sig); /* the signal of the event */
QS_2U8_PRE_(e->poolId_, e->refCtr_);/* pool-Id & ref-Count */
QS_END_NOCRIT_PRE_()
/* is it a dynamic event? */
if (e->poolId_ != 0U) {
/* NOTE: The reference counter of a dynamic event is incremented to
* prevent premature recycling of the event while the multicasting
* is still in progress. At the end of the function, the garbage
* collector step (QF_gcFromISR()) decrements the reference counter and
* recycles the event if the counter drops to zero. This covers the
* case when the event was published without any subscribers.
*/
QF_EVT_REF_CTR_INC_(e);
}
/* make a local, modifiable copy of the subscriber list */
subscrList = QF_PTR_AT_(QF_subscrList_, e->sig);
portEXIT_CRITICAL_ISR(&QF_esp32mux);
if (QPSet_notEmpty(&subscrList)) { /* any subscribers? */
uint_fast8_t p;
QPSet_findMax(&subscrList, p); /* the highest-prio subscriber */
/* no need to lock the scheduler in the ISR context */
do { /* loop over all subscribers */
/* the prio of the AO must be registered with the framework */
Q_ASSERT_ID(510, QF_active_[p] != (QActive *)0);
/* QACTIVE_POST_FROM_ISR() asserts if the queue overflows */
QACTIVE_POST_FROM_ISR(QF_active_[p], e,
pxHigherPriorityTaskWoken, sender);
QPSet_remove(&subscrList, p); /* remove the handled subscriber */
if (QPSet_notEmpty(&subscrList)) { /* still more subscribers? */
QPSet_findMax(&subscrList, p); /* highest-prio subscriber */
}
else {
p = 0U; /* no more subscribers */
}
} while (p != 0U);
/* no need to unlock the scheduler in the ISR context */
}
/* The following garbage collection step decrements the reference counter
* and recycles the event if the counter drops to zero. This covers both
* cases when the event was published with or without any subscribers.
*/
QF_gcFromISR(e);
}
/*..........................................................................*/
#ifdef Q_SPY
void IRAM_ATTR QF_tickXFromISR_(uint_fast8_t const tickRate,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender)
#else
void IRAM_ATTR QF_tickXFromISR_(uint_fast8_t const tickRate,
BaseType_t * const pxHigherPriorityTaskWoken)
#endif
{
QTimeEvt *prev = &QF_timeEvtHead_[tickRate];
portENTER_CRITICAL_ISR(&QF_esp32mux);
QS_BEGIN_NOCRIT_PRE_(QS_QF_TICK, 0U)
++prev->ctr;
QS_TEC_PRE_(prev->ctr); /* tick ctr */
QS_U8_PRE_(tickRate); /* tick rate */
QS_END_NOCRIT_PRE_()
/* scan the linked-list of time events at this rate... */
for (;;) {
QTimeEvt *t = prev->next; /* advance down the time evt. list */
/* end of the list? */
if (t == (QTimeEvt *)0) {
/* any new time events armed since the last run of QF_tickX_()? */
if (QF_timeEvtHead_[tickRate].act != (void *)0) {
/* sanity check */
Q_ASSERT_ID(610, prev != (QTimeEvt *)0);
prev->next = (QTimeEvt *)QF_timeEvtHead_[tickRate].act;
QF_timeEvtHead_[tickRate].act = (void *)0;
t = prev->next; /* switch to the new list */
}
else {
break; /* all currently armed time evts. processed */
}
}
/* time event scheduled for removal? */
if (t->ctr == 0U) {
prev->next = t->next;
/* mark time event 't' as NOT linked */
t->super.refCtr_ &= (uint8_t)(~TE_IS_LINKED);
/* do NOT advance the prev pointer */
/* exit crit. section to reduce latency */
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
else {
--t->ctr;
/* is time event about to expire? */
if (t->ctr == (QTimeEvtCtr)0) {
QActive *act = (QActive *)t->act; /* temp. for volatile */
/* periodic time evt? */
if (t->interval != 0U) {
t->ctr = t->interval; /* rearm the time event */
prev = t; /* advance to this time event */
}
/* one-shot time event: automatically disarm */
else {
prev->next = t->next;
/* mark time event 't' as NOT linked */
t->super.refCtr_ &= (uint8_t)(~TE_IS_LINKED);
/* do NOT advance the prev pointer */
QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_AUTO_DISARM, act->prio)
QS_OBJ_PRE_(t); /* this time event object */
QS_OBJ_PRE_(act); /* the target AO */
QS_U8_PRE_(tickRate); /* tick rate */
QS_END_NOCRIT_PRE_()
}
QS_BEGIN_NOCRIT_PRE_(QS_QF_TIMEEVT_POST, act->prio)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(t); /* the time event object */
QS_SIG_PRE_(t->super.sig); /* signal of this time event */
QS_OBJ_PRE_(act); /* the target AO */
QS_U8_PRE_(tickRate); /* tick rate */
QS_END_NOCRIT_PRE_()
/* exit critical section before posting */
portEXIT_CRITICAL_ISR(&QF_esp32mux);
/* QACTIVE_POST_FROM_ISR() asserts if the queue overflows */
QACTIVE_POST_FROM_ISR(act, &t->super,
pxHigherPriorityTaskWoken,
sender);
}
else {
prev = t; /* advance to this time event */
/* exit crit. section to reduce latency */
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
}
/* re-enter crit. section to continue */
portENTER_CRITICAL_ISR(&QF_esp32mux);
}
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
/*..........................................................................*/
QEvt IRAM_ATTR *QF_newXFromISR_(uint_fast16_t const evtSize,
uint_fast16_t const margin, enum_t const sig)
{
QEvt *e;
uint_fast8_t idx;
/* find the pool index that fits the requested event size ... */
for (idx = 0U; idx < QF_maxPool_; ++idx) {
if (evtSize <= QF_EPOOL_EVENT_SIZE_(QF_pool_[idx])) {
break;
}
}
/* cannot run out of registered pools */
Q_ASSERT_ID(710, idx < QF_maxPool_);
/* get e -- platform-dependent */
#ifdef Q_SPY
e = QMPool_getFromISR(&QF_pool_[idx],
((margin != QF_NO_MARGIN) ? margin : 0U),
(uint_fast8_t)QS_EP_ID + idx + 1U);
#else
e = QMPool_getFromISR(&QF_pool_[idx],
((margin != QF_NO_MARGIN) ? margin : 0U), 0U);
#endif
/* was e allocated correctly? */
if (e != (QEvt *)0) {
e->sig = (QSignal)sig; /* set signal for this event */
e->poolId_ = (uint8_t)(idx + 1U); /* store the pool ID */
e->refCtr_ = 0U; /* set the reference counter to 0 */
#ifdef Q_SPY
portENTER_CRITICAL_ISR(&QF_esp32mux);
QS_BEGIN_PRE_(QS_QF_NEW, (uint_fast8_t)QS_EP_ID + e->poolId_)
QS_TIME_PRE_(); /* timestamp */
QS_EVS_PRE_(evtSize); /* the size of the event */
QS_SIG_PRE_(sig); /* the signal of the event */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
#endif /* Q_SPY */
}
/* event cannot be allocated */
else {
/* must tolerate bad alloc. */
Q_ASSERT_ID(720, margin != QF_NO_MARGIN);
#ifdef Q_SPY
portENTER_CRITICAL_ISR(&QF_esp32mux);
QS_BEGIN_PRE_(QS_QF_NEW_ATTEMPT, (uint_fast8_t)QS_EP_ID + idx + 1U)
QS_TIME_PRE_(); /* timestamp */
QS_EVS_PRE_(evtSize); /* the size of the event */
QS_SIG_PRE_(sig); /* the signal of the event */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
#endif /* Q_SPY */
}
return e; /* can't be NULL if we can't tolerate bad allocation */
}
/*..........................................................................*/
void IRAM_ATTR QF_gcFromISR(QEvt const * const e) {
/* is it a dynamic event? */
if (e->poolId_ != 0U) {
portENTER_CRITICAL_ISR(&QF_esp32mux);
/* isn't this the last ref? */
if (e->refCtr_ > 1U) {
QF_EVT_REF_CTR_DEC_(e); /* decrements the ref counter */
QS_BEGIN_NOCRIT_PRE_(QS_QF_GC_ATTEMPT, (uint_fast8_t)e->poolId_)
QS_TIME_PRE_(); /* timestamp */
QS_SIG_PRE_(e->sig); /* the signal of the event */
QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
/* this is the last reference to this event, recycle it */
else {
uint_fast8_t idx = (uint_fast8_t)e->poolId_ - 1U;
QS_BEGIN_NOCRIT_PRE_(QS_QF_GC, (uint_fast8_t)e->poolId_)
QS_TIME_PRE_(); /* timestamp */
QS_SIG_PRE_(e->sig); /* the signal of the event */
QS_2U8_PRE_(e->poolId_, e->refCtr_); /* pool Id & ref Count */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
/* pool ID must be in range */
Q_ASSERT_ID(810, idx < QF_maxPool_);
#ifdef Q_SPY
/* cast 'const' away in (QEvt *)e is OK,
* because it's a pool event */
QMPool_putFromISR(&QF_pool_[idx], (QEvt *)e,
(uint_fast8_t)QS_EP_ID + e->poolId_);
#else
QMPool_putFromISR(&QF_pool_[idx], (QEvt *)e, 0U);
#endif
}
}
}
/*..........................................................................*/
void IRAM_ATTR QMPool_putFromISR(QMPool * const me, void *b,
uint_fast8_t const qs_id)
{
/** @pre # free blocks cannot exceed the total # blocks and
* the block pointer must be from this pool.
*/
Q_REQUIRE_ID(900, (me->nFree < me->nTot)
&& QF_PTR_RANGE_(b, me->start, me->end));
(void)qs_id; /* unused parameter (outside Q_SPY build configuration) */
portENTER_CRITICAL_ISR(&QF_esp32mux);
((QFreeBlock *)b)->next = (QFreeBlock *)me->free_head;/* link into list */
me->free_head = b; /* set as new head of the free list */
++me->nFree; /* one more free block in this pool */
QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_PUT, qs_id)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(me->start); /* the memory managed by this pool */
QS_MPC_PRE_(me->nFree); /* # free blocks in the pool */
QS_END_NOCRIT_PRE_()
portEXIT_CRITICAL_ISR(&QF_esp32mux);
}
/*..........................................................................*/
void *QMPool_getFromISR(QMPool * const me, uint_fast16_t const margin,
uint_fast8_t const qs_id)
{
QFreeBlock *fb;
(void)qs_id; /* unused parameter (outside Q_SPY build configuration) */
portENTER_CRITICAL_ISR(&QF_esp32mux);
/* have more free blocks than the requested margin? */
if (me->nFree > (QMPoolCtr)margin) {
void *fb_next;
fb = (QFreeBlock *)me->free_head; /* get a free block */
/* the pool has some free blocks, so a free block must be available */
Q_ASSERT_ID(910, fb != (QFreeBlock *)0);
fb_next = fb->next; /* put volatile to a temporary to avoid UB */
/* is the pool becoming empty? */
--me->nFree; /* one less free block */
if (me->nFree == (QMPoolCtr)0) {
/* pool is becoming empty, so the next free block must be NULL */
Q_ASSERT_ID(920, fb_next == (QFreeBlock *)0);
me->nMin = (QMPoolCtr)0; /* remember that the pool got empty */
}
else {
/* pool is not empty, so the next free block must be in range
*
* NOTE: the next free block pointer can fall out of range
* when the client code writes past the memory block, thus
* corrupting the next block.
*/
Q_ASSERT_ID(930, QF_PTR_RANGE_(fb_next, me->start, me->end));
/* is the number of free blocks the new minimum so far? */
if (me->nMin > me->nFree) {
me->nMin = me->nFree; /* remember the new minimum */
}
}
me->free_head = fb_next; /* set the head to the next free block */
QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET, qs_id)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(me->start); /* the memory managed by this pool */
QS_MPC_PRE_(me->nFree); /* # free blocks in the pool */
QS_MPC_PRE_(me->nMin); /* min # free blocks ever in the pool */
QS_END_NOCRIT_PRE_()
}
/* don't have enough free blocks at this point */
else {
fb = (QFreeBlock *)0;
QS_BEGIN_NOCRIT_PRE_(QS_QF_MPOOL_GET_ATTEMPT, qs_id)
QS_TIME_PRE_(); /* timestamp */
QS_OBJ_PRE_(me->start); /* the memory managed by this pool */
QS_MPC_PRE_(me->nFree); /* # free blocks in the pool */
QS_MPC_PRE_(margin); /* the requested margin */
QS_END_NOCRIT_PRE_()
}
portEXIT_CRITICAL_ISR(&QF_esp32mux);
return fb; /* return the pointer to memory block or NULL to the caller */
}

View File

@ -0,0 +1,258 @@
/**
* @file
* @brief QF/C port to FreeRTOS-ESP32 (Espressif ESP32-IDF 4.x) adaptation
* @ingroup ports
* @cond
******************************************************************************
* Last updated for version 6.9.1
* Last updated on 2020-11-28
*
* 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 <www.gnu.org/licenses>.
*
* Contact information:
* <www.state-machine.com/licensing>
* <info@state-machine.com>
******************************************************************************
* @endcond
*/
#ifndef QF_PORT_H
#define QF_PORT_H
/* FreeRTOS-ESP32 event queue and thread types, see NOTE0 */
#define QF_EQUEUE_TYPE QEQueue
#define QF_THREAD_TYPE StaticTask_t
/* The maximum number of active objects in the application, see NOTE1 */
#define QF_MAX_ACTIVE 8U
/* QF critical section for FreeRTOS-ESP32 (task level), see NOTE2 */
/* #define QF_CRIT_STAT_TYPE not defined */
#define QF_CRIT_ENTRY(dummy) portENTER_CRITICAL(&QF_esp32mux)
#define QF_CRIT_EXIT(dummy) portEXIT_CRITICAL(&QF_esp32mux)
#include "FreeRTOS.h" /* FreeRTOS master include file, see NOTE4 */
#include "task.h" /* FreeRTOS task management */
#include "qep_port.h" /* QEP port */
#include "qequeue.h" /* this QP port uses the native QF event queue */
#include "qmpool.h" /* this QP port uses the native QF memory pool */
#include "qf.h" /* QF platform-independent public interface */
/* global spinlock "mutex" for all critical sections in QF (see NOTE3) */
extern PRIVILEGED_DATA portMUX_TYPE QF_esp32mux;
/* the "FromISR" versions of the QF APIs, see NOTE4 */
#ifdef Q_SPY
#define QACTIVE_POST_FROM_ISR(me_, e_, pxHigherPrioTaskWoken_, sender_) \
((void)QActive_postFromISR_((me_), (e_), QF_NO_MARGIN, \
(pxHigherPrioTaskWoken_), (sender_)))
#define QACTIVE_POST_X_FROM_ISR(me_, e_, margin_, \
pxHigherPrioTaskWoken_, sender_) \
(QActive_postFromISR_((me_), (e_), (margin_), \
(pxHigherPrioTaskWoken_), (sender_)))
#define QF_PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, sender_) \
(QF_publishFromISR_((e_), (pxHigherPrioTaskWoken_), \
(void const *)(sender_)))
#define QF_TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, sender_) \
(QF_tickXFromISR_((tickRate_), (pxHigherPrioTaskWoken_), (sender_)))
/* this function only to be used through macros QACTIVE_POST_FROM_ISR()
* and QACTIVE_POST_X_FROM_ISR().
*/
bool QActive_postFromISR_(QActive * const me, QEvt const * const e,
uint_fast16_t const margin,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender);
void QF_publishFromISR_(QEvt const * const e,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender);
void QF_tickXFromISR_(uint_fast8_t const tickRate,
BaseType_t * const pxHigherPriorityTaskWoken,
void const * const sender);
#else
#define QACTIVE_POST_FROM_ISR(me_, e_, pxHigherPrioTaskWoken_, dummy) \
((void)QActive_postFromISR_((me_), (e_), QF_NO_MARGIN, \
(pxHigherPrioTaskWoken_)))
#define QACTIVE_POST_X_FROM_ISR(me_, e_, margin_, \
pxHigherPrioTaskWoken_, dummy) \
(QActive_postFromISR_((me_), (e_), (margin_), \
(pxHigherPrioTaskWoken_)))
#define QF_PUBLISH_FROM_ISR(e_, pxHigherPrioTaskWoken_, dummy) \
(QF_publishFromISR_((e_), (pxHigherPrioTaskWoken_)))
#define QF_TICK_X_FROM_ISR(tickRate_, pxHigherPrioTaskWoken_, dummy) \
(QF_tickXFromISR_((tickRate_), (pxHigherPrioTaskWoken_)))
bool QActive_postFromISR_(QActive * const me, QEvt const * const e,
uint_fast16_t const margin,
BaseType_t * const pxHigherPriorityTaskWoken);
void QF_publishFromISR_(QEvt const * const e,
BaseType_t * const pxHigherPriorityTaskWoken);
void QF_tickXFromISR_(uint_fast8_t const tickRate,
BaseType_t * const pxHigherPriorityTaskWoken);
#endif
#define QF_TICK_FROM_ISR(pxHigherPrioTaskWoken_, sender_) \
QF_TICK_X_FROM_ISR(0U, pxHigherPrioTaskWoken_, sender_)
#ifdef Q_EVT_CTOR /* Shall the ctor for the ::QEvt class be provided? */
#define Q_NEW_FROM_ISR(evtT_, sig_, ...) \
(evtT_##_ctor((evtT_ *)QF_newXFromISR_(sizeof(evtT_), \
QF_NO_MARGIN, 0), (sig_), ##__VA_ARGS__))
#define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_, ...) do { \
(e_) = (evtT_ *)QF_newXFromISR_(sizeof(evtT_), \
(margin_), 0); \
if ((e_) != (evtT_ *)0) { \
evtT_##_ctor((e_), (sig_), ##__VA_ARGS__); \
} \
} while (false)
#else
#define Q_NEW_FROM_ISR(evtT_, sig_) \
((evtT_ *)QF_newXFromISR_((uint_fast16_t)sizeof(evtT_), \
QF_NO_MARGIN, (sig_)))
#define Q_NEW_X_FROM_ISR(e_, evtT_, margin_, sig_) ((e_) = \
(evtT_ *)QF_newXFromISR_((uint_fast16_t)sizeof(evtT_), \
(margin_), (sig_)))
#endif /* Q_EVT_CTOR */
void QF_gcFromISR(QEvt const * const e);
/* this function only to be used through macros Q_NEW_FROM_ISR() and
* Q_NEW_X_FROM_ISR().
*/
QEvt *QF_newXFromISR_(uint_fast16_t const evtSize,
uint_fast16_t const margin, enum_t const sig);
void *QMPool_getFromISR(QMPool * const me, uint_fast16_t const margin,
uint_fast8_t const qs_id);
void QMPool_putFromISR(QMPool * const me, void *b, uint_fast8_t const qs_id);
enum FreeRTOS_TaskAttrs {
TASK_NAME_ATTR
};
/* FreeRTOS hooks prototypes (not provided by FreeRTOS) */
#if (configUSE_IDLE_HOOK > 0)
void vApplicationIdleHook(void);
#endif
#if (configUSE_TICK_HOOK > 0)
void vApplicationTickHook(void);
#endif
#if (configCHECK_FOR_STACK_OVERFLOW > 0)
void vApplicationStackOverflowHook(TaskHandle_t xTask, char *pcTaskName);
#endif
#if (configSUPPORT_STATIC_ALLOCATION > 0)
void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
StackType_t **ppxIdleTaskStackBuffer,
uint32_t *pulIdleTaskStackSize );
#endif
/*****************************************************************************
* interface used only inside QF, but not in applications
*/
#ifdef QP_IMPL
/* FreeRTOS blocking for event queue implementation (task level) */
#define QACTIVE_EQUEUE_WAIT_(me_) \
while ((me_)->eQueue.frontEvt == (QEvt *)0) { \
QF_CRIT_X_(); \
ulTaskNotifyTake(pdTRUE, portMAX_DELAY); \
QF_CRIT_E_(); \
}
/* FreeRTOS signaling (unblocking) for event queue (task level) */
#define QACTIVE_EQUEUE_SIGNAL_(me_) do { \
QF_CRIT_X_(); \
xTaskNotifyGive((TaskHandle_t)&(me_)->thread); \
QF_CRIT_E_(); \
} while (false)
#define QF_SCHED_STAT_
#define QF_SCHED_LOCK_(dummy) vTaskSuspendAll()
#define QF_SCHED_UNLOCK_() xTaskResumeAll()
/* native QF event pool operations */
#define QF_EPOOL_TYPE_ QMPool
#define QF_EPOOL_INIT_(p_, poolSto_, poolSize_, evtSize_) \
(QMPool_init(&(p_), (poolSto_), (poolSize_), (evtSize_)))
#define QF_EPOOL_EVENT_SIZE_(p_) ((uint_fast16_t)(p_).blockSize)
#define QF_EPOOL_GET_(p_, e_, m_, qs_id_) \
((e_) = (QEvt *)QMPool_get(&(p_), (m_), (qs_id_)))
#define QF_EPOOL_PUT_(p_, e_, qs_id_) \
(QMPool_put(&(p_), (e_), (qs_id_)))
#endif /* ifdef QP_IMPL */
/*****************************************************************************
* NOTE0:
* FreeRTOS-ESP32 is a significantly changed version of the FreeRTOS kernel
* developed by Espressif to support the ESP32 multi-core CPUs (see ESP32 IDF).
* FreeRTOS-ESP32 is NOT compatible with the baseine FreeRTOS and it needs to
* be treated as a separate RTOS kernel. According to the comments in the source
* code, FreeRTOS-ESP32 is based on FreeRTOS V8.2.0, but apparently FreeRTOS-ESP32
* has been updated with the newer features introduced to the original FreeRTOS
* in the later versions. For example, FreeRTOS-ESP32 supports the "static
* allocation", first introduced in baseline FreeRTOS V9.x. This prot to
* QP-FreeRTOS-ESP32 takes advantage of the "static allocation".
*
* NOTE1:
* The maximum number of active objects QF_MAX_ACTIVE can be increased to 64,
* inclusive, but it can be reduced to save some memory. Also, the number of
* active objects cannot exceed the number of FreeRTOS task priorities,
* because each QP active object requires a unique priority level.
*
* NOTE2:
* The critical section definition applies only to the FreeRTOS "task level"
* APIs. The "FromISR" APIs are defined separately.
*
* NOTE3:
* This QF port to FreeRTOS-ESP32 uses the FreeRTOS-ESP32 spin lock "mutex",
* similar to the internal implementation of FreeRTOS-ESP32 (see tasks.c).
* However, the QF port uses its own "mutex" object QF_esp32mux.
*
* NOTE4:
* The design of FreeRTOS requires using different APIs inside the ISRs
* (the "FromISR" variant) than at the task level. Accordingly, this port
* provides the "FromISR" variants for QP functions and "FROM_ISR" variants
* for QP macros to be used inside ISRs. ONLY THESE "FROM_ISR" VARIANTS
* ARE ALLOWED INSIDE ISRs AND CALLING THE TASK-LEVEL APIs IS AN ERROR.
*/
#endif /* QF_PORT_H */

View File

@ -0,0 +1,62 @@
/**
* @file
* @brief QS/C port to a 32-bit CPU and a generic C compiler.
* @ingroup qs
* @cond
******************************************************************************
* Last updated for version 6.9.1
* Last updated on 2020-11-25
*
* 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 <www.gnu.org/licenses>.
*
* Contact information:
* <www.state-machine.com/licensing>
* <info@state-machine.com>
******************************************************************************
* @endcond
*/
#ifndef QS_PORT_H
#define QS_PORT_H
/* QS time-stamp size in bytes */
#define QS_TIME_SIZE 4
/* object pointer size in bytes */
#define QS_OBJ_PTR_SIZE 4
/* 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 Q_ROM, 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 */