qpcpp/source/qs_rx.cpp
Quantum Leaps 89834cf23b 5.9.0
2017-05-17 13:15:09 -04:00

1218 lines
42 KiB
C++

/// @file
/// @brief QS receive channel services
/// @ingroup qs
/// @cond
///***************************************************************************
/// Last updated for version 5.9.0
/// Last updated on 2017-05-17
///
/// Q u a n t u m L e a P s
/// ---------------------------
/// innovating embedded systems
///
/// Copyright (C) 2005-2017 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:
/// https://state-machine.com
/// mailto:info@state-machine.com
///***************************************************************************
/// @endcond
#define QP_IMPL // this is QP implementation
#include "qs_port.h" // QS port
#include "qs_pkg.h" // QS package-scope internal interface
#include "qassert.h" // QP assertions
namespace QP {
Q_DEFINE_THIS_MODULE("qs_rx")
//****************************************************************************
QS::QSrxPriv QS::rxPriv_; // QS-RX private data
//****************************************************************************
#if (QS_OBJ_PTR_SIZE == 1)
typedef uint8_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 2)
typedef uint16_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 4)
typedef uint32_t QSObj;
#elif (QS_OBJ_PTR_SIZE == 8)
typedef uint64_t QSObj;
#endif
#if (QS_FUN_PTR_SIZE == 1)
typedef uint8_t QSFun;
#elif (QS_FUN_PTR_SIZE == 2)
typedef uint16_t QSFun;
#elif (QS_FUN_PTR_SIZE == 4)
typedef uint32_t QSFun;
#elif (QS_FUN_PTR_SIZE == 8)
typedef uint64_t QSFun;
#endif
/// @cond
/// Exlcude the following internals from the Doxygen documentation
/// Extended-state variables used for parsing various QS-RX Records
struct CmdVar {
uint32_t param1;
uint32_t param2;
uint32_t param3;
uint8_t idx;
uint8_t cmdId;
};
struct TickVar {
uint_fast8_t rate;
};
struct PeekVar {
uint16_t offs;
uint8_t size;
uint8_t num;
uint8_t idx;
};
struct PokeVar {
uint32_t data;
uint16_t offs;
uint8_t size;
uint8_t num;
uint8_t idx;
uint8_t fill;
};
struct GFltVar {
uint8_t data[16];
uint8_t idx;
};
struct ObjVar {
QSObj addr;
uint8_t idx;
uint8_t kind; // see qs.h, enum QSpyObjKind
uint8_t recId;
};
struct TPVar { // Test-Probe
QSFun addr;
uint32_t data;
uint8_t idx;
};
struct AFltVar {
uint8_t prio;
};
struct EvtVar {
QEvt *e;
uint8_t *p;
QSignal sig;
uint16_t len;
uint8_t prio;
uint8_t idx;
};
// extended-state variables for the current QS-RX state
static struct ExtState {
union Variant {
CmdVar cmd;
TickVar tick;
PeekVar peek;
PokeVar poke;
GFltVar gFlt;
AFltVar aFlt;
ObjVar obj;
EvtVar evt;
TPVar tp;
} var;
uint8_t state;
uint8_t esc;
uint8_t seq;
uint8_t chksum;
} l_rx;
enum RxStateEnum {
WAIT4_SEQ,
WAIT4_REC,
WAIT4_INFO_FRAME,
WAIT4_CMD_ID,
WAIT4_CMD_PARAM1,
WAIT4_CMD_PARAM2,
WAIT4_CMD_PARAM3,
WAIT4_CMD_FRAME,
WAIT4_RESET_FRAME,
WAIT4_TICK_RATE,
WAIT4_TICK_FRAME,
WAIT4_PEEK_OFFS,
WAIT4_PEEK_SIZE,
WAIT4_PEEK_NUM,
WAIT4_PEEK_FRAME,
WAIT4_POKE_OFFS,
WAIT4_POKE_SIZE,
WAIT4_POKE_NUM,
WAIT4_POKE_DATA,
WAIT4_POKE_FRAME,
WAIT4_FILL_DATA,
WAIT4_FILL_FRAME,
WAIT4_GLB_FILTER_LEN,
WAIT4_GLB_FILTER_DATA,
WAIT4_GLB_FILTER_FRAME,
WAIT4_AO_FILTER_PRIO,
WAIT4_AO_FILTER_FRAME,
WAIT4_OBJ_KIND,
WAIT4_OBJ_ADDR,
WAIT4_OBJ_FRAME,
WAIT4_EVT_PRIO,
WAIT4_EVT_SIG,
WAIT4_EVT_LEN,
WAIT4_EVT_PAR,
WAIT4_EVT_FRAME,
WAIT4_TEST_SETUP_FRAME,
WAIT4_TEST_TEARDOWN_FRAME,
WAIT4_TEST_PROBE_DATA,
WAIT4_TEST_PROBE_ADDR,
WAIT4_TEST_PROBE_FRAME,
WAIT4_TEST_CONTINUE_FRAME,
ERROR_STATE
};
#ifdef Q_UTEST
static struct TestData {
TPVar tpBuf[16]; // buffer of Test-Probes received so far
uint8_t tpNum; // current number of Test-Probes
QSTimeCtr testTime; // test time (tick counter)
} l_testData;
#endif // Q_UTEST
// internal helper functions...
static void rxParseData_(uint8_t b);
static void rxHandleGoodFrame_(uint8_t state);
static void rxHandleBadFrame_(uint8_t state);
static void rxReportAck_(enum QSpyRxRecords recId);
static void rxReportError_(uint8_t code);
static void rxReportDone_(enum QSpyRxRecords recId);
static void rxPoke_(void);
static uint8_t const l_QS_RX = static_cast<uint8_t>(0); // QS source ID
//! Internal QS-RX function to take a transition in the QS-RX FSM
static inline void tran_(RxStateEnum const target) {
l_rx.state = static_cast<uint8_t>(target);
}
/// @endcond
//****************************************************************************
/// @description
/// This function should be called from QS::onStartup() to provide QS-RX with
/// the receive data buffer.
///
/// @param[in] sto[] the address of the memory block
/// @param[in] stoSize the size of this block [bytes]. The size of the
/// QS RX buffer cannot exceed 64KB.
///
/// @note QS-RX can work with quite small data buffers, but you will start
/// losing data if the buffer is not drained fast enough in the idle task.
///
/// @note If the data input rate exceeds the QS-RX processing rate, the data
/// will be lost, but the QS protocol will notice that:
/// (1) that the checksum in the incomplete QS records will fail; and
/// (2) the sequence counter in QS records will show discontinuities.
///
/// The QS-RX channel will report any data errors by sending the
/// QS_RX_DATA_ERROR trace record.
///
void QS::rxInitBuf(uint8_t sto[], uint16_t const stoSize) {
rxPriv_.buf = &sto[0];
rxPriv_.end = static_cast<QSCtr>(stoSize) - static_cast<QSCtr>(1);
rxPriv_.head = static_cast<QSCtr>(0);
rxPriv_.tail = static_cast<QSCtr>(0);
rxPriv_.currObj[QS::SM_OBJ] = static_cast<void *>(0);
rxPriv_.currObj[QS::AO_OBJ] = static_cast<void *>(0);
rxPriv_.currObj[QS::MP_OBJ] = static_cast<void *>(0);
rxPriv_.currObj[QS::EQ_OBJ] = static_cast<void *>(0);
rxPriv_.currObj[QS::TE_OBJ] = static_cast<void *>(0);
rxPriv_.currObj[QS::AP_OBJ] = static_cast<void *>(0);
tran_(WAIT4_SEQ);
l_rx.esc = static_cast<uint8_t>(0);
l_rx.seq = static_cast<uint8_t>(0);
l_rx.chksum = static_cast<uint8_t>(0);
beginRec(static_cast<uint_fast8_t>(QS_OBJ_DICT));
QS_OBJ_(&l_QS_RX);
QS_STR_("QS_RX");
endRec();
// no QS_REC_DONE(), because QS is not running yet
#ifdef Q_UTEST
l_testData.tpNum = static_cast<uint8_t>(0);
l_testData.testTime = static_cast<QSTimeCtr>(0);
#endif // Q_UTEST
}
//****************************************************************************
/// @description
/// This function is intended to be called from the ISR that reads the QS-RX
/// bytes from the QSPY application. The function returns the conservative
/// number of free bytes currently available in the buffer, assuming that
/// the head pointer is not being moved concurrently. The tail pointer might
/// be moving, meaning that bytes can be concurrently removed from the buffer.
///
uint16_t QS::rxGetNfree(void) {
uint16_t nFree;
if (rxPriv_.head == rxPriv_.tail) {
nFree = static_cast<uint16_t>(rxPriv_.end);
}
else if (rxPriv_.head < rxPriv_.tail) {
nFree = static_cast<uint16_t>(rxPriv_.tail - rxPriv_.head);
}
else {
nFree = static_cast<uint16_t>((rxPriv_.tail + rxPriv_.end)
- rxPriv_.head);
}
return nFree;
}
//****************************************************************************
void QS::rxParse(void) {
while (rxPriv_.head != rxPriv_.tail) { // QS-RX buffer not empty?
uint8_t b = QS_PTR_AT_(rxPriv_.buf, rxPriv_.tail);
if (rxPriv_.tail != static_cast<QSCtr>(0)) {
--rxPriv_.tail;
}
else {
rxPriv_.tail = rxPriv_.end;
}
if (l_rx.esc != static_cast<uint8_t>(0)) { // escaped byte arrived?
l_rx.esc = static_cast<uint8_t>(0);
b ^= QS_ESC_XOR;
l_rx.chksum += b;
rxParseData_(b);
}
else if (b == QS_ESC) {
l_rx.esc = static_cast<uint8_t>(1);
}
else if (b == QS_FRAME) {
// get ready for the next frame
b = l_rx.state; // save the current state in b
l_rx.esc = static_cast<uint8_t>(0);
tran_(WAIT4_SEQ);
if (l_rx.chksum == QS_GOOD_CHKSUM) {
l_rx.chksum = static_cast<uint8_t>(0);
rxHandleGoodFrame_(b);
}
else { // bad checksum
l_rx.chksum = static_cast<uint8_t>(0);
rxReportError_(static_cast<uint8_t>(0x00));
rxHandleBadFrame_(b);
}
}
else {
l_rx.chksum += b;
rxParseData_(b);
}
}
}
//****************************************************************************
static void rxParseData_(uint8_t const b) {
switch (l_rx.state) {
case WAIT4_SEQ: {
++l_rx.seq;
if (l_rx.seq != b) { // not the expected sequence?
rxReportError_(static_cast<uint8_t>(0x42));
l_rx.seq = b; // update the sequence
}
tran_(WAIT4_REC);
break;
}
case WAIT4_REC: {
switch (b) {
case QS_RX_INFO:
tran_(WAIT4_INFO_FRAME);
break;
case QS_RX_COMMAND:
tran_(WAIT4_CMD_ID);
break;
case QS_RX_RESET:
tran_(WAIT4_RESET_FRAME);
break;
case QS_RX_TICK:
tran_(WAIT4_TICK_RATE);
break;
case QS_RX_PEEK:
if (QS::rxPriv_.currObj[QS::AP_OBJ]
!= static_cast<void *>(0))
{
l_rx.var.peek.offs = static_cast<uint16_t>(0);
l_rx.var.peek.idx = static_cast<uint8_t>(0);
tran_(WAIT4_PEEK_OFFS);
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_PEEK));
tran_(ERROR_STATE);
}
break;
case QS_RX_POKE:
case QS_RX_FILL:
l_rx.var.poke.fill =
(b == static_cast<uint8_t>(QS_RX_FILL))
? static_cast<uint8_t>(1)
: static_cast<uint8_t>(0);
if (QS::rxPriv_.currObj[QS::AP_OBJ]
!= static_cast<void *>(0))
{
l_rx.var.poke.offs = static_cast<uint16_t>(0);
l_rx.var.poke.idx = static_cast<uint8_t>(0);
tran_(WAIT4_POKE_OFFS);
}
else {
rxReportError_(
(l_rx.var.poke.fill != static_cast<uint8_t>(0))
? static_cast<uint8_t>(QS_RX_FILL)
: static_cast<uint8_t>(QS_RX_FILL));
tran_(ERROR_STATE);
}
break;
case QS_RX_GLB_FILTER:
tran_(WAIT4_GLB_FILTER_LEN);
break;
case QS_RX_LOC_FILTER:
l_rx.var.obj.recId =
static_cast<uint8_t>(QS_RX_LOC_FILTER);
tran_(WAIT4_OBJ_KIND);
break;
case QS_RX_AO_FILTER:
tran_(WAIT4_AO_FILTER_PRIO);
break;
case QS_RX_CURR_OBJ:
l_rx.var.obj.recId = static_cast<uint8_t>(QS_RX_CURR_OBJ);
tran_(WAIT4_OBJ_KIND);
break;
case QS_RX_EVENT:
tran_(WAIT4_EVT_PRIO);
break;
#ifdef Q_UTEST
case QS_RX_TEST_SETUP:
tran_(WAIT4_TEST_SETUP_FRAME);
break;
case QS_RX_TEST_TEARDOWN:
tran_(WAIT4_TEST_TEARDOWN_FRAME);
break;
case QS_RX_TEST_CONTINUE:
tran_(WAIT4_TEST_CONTINUE_FRAME);
break;
case QS_RX_TEST_PROBE:
if (l_testData.tpNum
< static_cast<uint8_t>(
(sizeof(l_testData.tpBuf)
/ sizeof(l_testData.tpBuf[0]))))
{
l_rx.var.tp.data = static_cast<uint32_t>(0);
l_rx.var.tp.idx = static_cast<uint8_t>(0);
tran_(WAIT4_TEST_PROBE_DATA);
}
else { // the number of Test-Probes exceeded
rxReportError_(
static_cast<uint8_t>(QS_RX_TEST_PROBE));
tran_(ERROR_STATE);
}
break;
#endif // Q_UTEST
default:
rxReportError_(static_cast<uint8_t>(0x43U));
tran_(ERROR_STATE);
break;
}
break;
}
case WAIT4_INFO_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_CMD_ID: {
l_rx.var.cmd.cmdId = b;
l_rx.var.cmd.idx = static_cast<uint8_t>(0);
l_rx.var.cmd.param1 = static_cast<uint32_t>(0);
l_rx.var.cmd.param2 = static_cast<uint32_t>(0);
l_rx.var.cmd.param3 = static_cast<uint32_t>(0);
tran_(WAIT4_CMD_PARAM1);
break;
}
case WAIT4_CMD_PARAM1: {
l_rx.var.cmd.param1 |=
(static_cast<uint32_t>(b) << l_rx.var.cmd.idx);
l_rx.var.cmd.idx += static_cast<uint8_t>(8);
if (l_rx.var.cmd.idx == static_cast<uint8_t>((8*4))) {
l_rx.var.cmd.idx = static_cast<uint8_t>(0);
tran_(WAIT4_CMD_PARAM2);
}
break;
}
case WAIT4_CMD_PARAM2: {
l_rx.var.cmd.param2 |=
static_cast<uint32_t>(b) << l_rx.var.cmd.idx;
l_rx.var.cmd.idx += static_cast<uint8_t>(8);
if (l_rx.var.cmd.idx == static_cast<uint8_t>((8*4))) {
l_rx.var.cmd.idx = static_cast<uint8_t>(0);
tran_(WAIT4_CMD_PARAM3);
}
break;
}
case WAIT4_CMD_PARAM3: {
l_rx.var.cmd.param3 |=
static_cast<uint32_t>(b) << l_rx.var.cmd.idx;
l_rx.var.cmd.idx += static_cast<uint8_t>(8);
if (l_rx.var.cmd.idx == static_cast<uint8_t>((8*4))) {
l_rx.var.cmd.idx = static_cast<uint8_t>(0);
tran_(WAIT4_CMD_FRAME);
}
break;
}
case WAIT4_CMD_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_RESET_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_TICK_RATE: {
l_rx.var.tick.rate = static_cast<uint_fast8_t>(b);
tran_(WAIT4_TICK_FRAME);
break;
}
case WAIT4_TICK_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_PEEK_OFFS: {
if (l_rx.var.peek.idx == static_cast<uint8_t>(0)) {
l_rx.var.peek.offs = static_cast<uint16_t>(b);
l_rx.var.peek.idx += static_cast<uint8_t>(8);
}
else {
l_rx.var.peek.offs |= static_cast<uint16_t>(
static_cast<uint16_t>(b) << 8);
tran_(WAIT4_PEEK_SIZE);
}
break;
}
case WAIT4_PEEK_SIZE: {
if ((b == static_cast<uint8_t>(1))
|| (b == static_cast<uint8_t>(2))
|| (b == static_cast<uint8_t>(4)))
{
l_rx.var.peek.size = b;
tran_(WAIT4_PEEK_NUM);
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_PEEK));
tran_(ERROR_STATE);
}
break;
}
case WAIT4_PEEK_NUM: {
l_rx.var.peek.num = b;
tran_(WAIT4_PEEK_FRAME);
break;
}
case WAIT4_PEEK_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_POKE_OFFS: {
if (l_rx.var.poke.idx == static_cast<uint8_t>(0)) {
l_rx.var.poke.offs = static_cast<uint16_t>(b);
l_rx.var.poke.idx = static_cast<uint8_t>(1);
}
else {
l_rx.var.poke.offs |= static_cast<uint16_t>(
static_cast<uint16_t>(b) << 8);
tran_(WAIT4_POKE_SIZE);
}
break;
}
case WAIT4_POKE_SIZE: {
if ((b == static_cast<uint8_t>(1))
|| (b == static_cast<uint8_t>(2))
|| (b == static_cast<uint8_t>(4)))
{
l_rx.var.poke.size = b;
tran_(WAIT4_POKE_NUM);
}
else {
rxReportError_((l_rx.var.poke.fill != static_cast<uint8_t>(0))
? static_cast<uint8_t>(QS_RX_FILL)
: static_cast<uint8_t>(QS_RX_POKE));
tran_(ERROR_STATE);
}
break;
}
case WAIT4_POKE_NUM: {
if (b > static_cast<uint8_t>(0)) {
l_rx.var.poke.num = b;
l_rx.var.poke.data = static_cast<uint32_t>(0);
l_rx.var.poke.idx = static_cast<uint8_t>(0);
tran_((l_rx.var.poke.fill != static_cast<uint8_t>(0))
? WAIT4_FILL_DATA
: WAIT4_POKE_DATA);
}
else {
rxReportError_((l_rx.var.poke.fill != static_cast<uint8_t>(0))
? static_cast<uint8_t>(QS_RX_FILL)
: static_cast<uint8_t>(QS_RX_POKE));
tran_(ERROR_STATE);
}
break;
}
case WAIT4_FILL_DATA: {
l_rx.var.poke.data |=
static_cast<uint32_t>(b) << l_rx.var.poke.idx;
l_rx.var.poke.idx += static_cast<uint8_t>(8);
if ((l_rx.var.poke.idx >> 3) == l_rx.var.poke.size) {
tran_(WAIT4_FILL_FRAME);
}
break;
}
case WAIT4_POKE_DATA: {
l_rx.var.poke.data |=
static_cast<uint32_t>(b) << l_rx.var.poke.idx;
l_rx.var.poke.idx += static_cast<uint8_t>(8);
if ((l_rx.var.poke.idx >> 3) == l_rx.var.poke.size) {
rxPoke_();
--l_rx.var.poke.num;
if (l_rx.var.poke.num == static_cast<uint8_t>(0)) {
tran_(WAIT4_POKE_FRAME);
}
}
break;
}
case WAIT4_FILL_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_POKE_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_GLB_FILTER_LEN: {
if (b == static_cast<uint8_t>(sizeof(l_rx.var.gFlt.data))) {
l_rx.var.gFlt.idx = static_cast<uint8_t>(0);
tran_(WAIT4_GLB_FILTER_DATA);
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_GLB_FILTER));
tran_(ERROR_STATE);
}
break;
}
case WAIT4_GLB_FILTER_DATA: {
l_rx.var.gFlt.data[l_rx.var.gFlt.idx] = b;
++l_rx.var.gFlt.idx;
if (l_rx.var.gFlt.idx
== static_cast<uint8_t>(sizeof(l_rx.var.gFlt.data)))
{
tran_(WAIT4_GLB_FILTER_FRAME);
}
break;
}
case WAIT4_GLB_FILTER_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_OBJ_KIND: {
if (b <= static_cast<uint8_t>(6)) {
l_rx.var.obj.kind = b;
l_rx.var.obj.addr = static_cast<QSObj>(0);
l_rx.var.obj.idx = static_cast<uint8_t>(0);
tran_(WAIT4_OBJ_ADDR);
}
else {
rxReportError_(l_rx.var.obj.recId);
tran_(ERROR_STATE);
}
break;
}
case WAIT4_OBJ_ADDR: {
l_rx.var.obj.addr |=
static_cast<uint32_t>(b) << l_rx.var.obj.idx;
l_rx.var.obj.idx += static_cast<uint8_t>(8);
if (l_rx.var.obj.idx
== static_cast<uint8_t>((8*QS_OBJ_PTR_SIZE)))
{
tran_(WAIT4_OBJ_FRAME);
}
break;
}
case WAIT4_OBJ_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_AO_FILTER_PRIO: {
l_rx.var.aFlt.prio = b;
tran_(WAIT4_AO_FILTER_FRAME);
break;
}
case WAIT4_AO_FILTER_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_EVT_PRIO: {
l_rx.var.evt.prio = b;
l_rx.var.evt.sig = static_cast<QSignal>(0);
l_rx.var.evt.idx = static_cast<uint8_t>(0);
tran_(WAIT4_EVT_SIG);
break;
}
case WAIT4_EVT_SIG: {
l_rx.var.evt.sig |= static_cast<QSignal>(
static_cast<uint32_t>(b) << l_rx.var.evt.idx);
l_rx.var.evt.idx += static_cast<uint8_t>(8);
if (l_rx.var.evt.idx == static_cast<uint8_t>((8*Q_SIGNAL_SIZE))) {
l_rx.var.evt.len = static_cast<uint16_t>(0);
l_rx.var.evt.idx = static_cast<uint8_t>(0);
tran_(WAIT4_EVT_LEN);
}
break;
}
case WAIT4_EVT_LEN: {
l_rx.var.evt.len |= static_cast<uint16_t>(
static_cast<uint32_t>(b) << l_rx.var.evt.idx);
l_rx.var.evt.idx += static_cast<uint8_t>(8);
if (l_rx.var.evt.idx == static_cast<uint8_t>((8*2))) {
if ((l_rx.var.evt.len + static_cast<uint16_t>(sizeof(QEvt)))
<= static_cast<uint16_t>(QF::poolGetMaxBlockSize()))
{
// report Ack before generating any other QS records
rxReportAck_(QS_RX_EVENT);
l_rx.var.evt.e = QF::newX_(
(static_cast<uint_fast16_t>(l_rx.var.evt.len)
+ static_cast<uint_fast16_t>(sizeof(QEvt))),
static_cast<uint_fast16_t>(1), // margin
static_cast<enum_t>(l_rx.var.evt.sig));
// event allocated?
if (l_rx.var.evt.e != static_cast<QEvt *>(0)) {
l_rx.var.evt.p =
reinterpret_cast<uint8_t *>(l_rx.var.evt.e);
l_rx.var.evt.p += sizeof(QEvt);
if (l_rx.var.evt.len > static_cast<uint16_t>(0)) {
tran_(WAIT4_EVT_PAR);
}
else {
tran_(WAIT4_EVT_FRAME);
}
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_EVENT));
tran_(ERROR_STATE);
}
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_EVENT));
tran_(ERROR_STATE);
}
}
break;
}
case WAIT4_EVT_PAR: { // event parameters
*l_rx.var.evt.p = b;
++l_rx.var.evt.p;
--l_rx.var.evt.len;
if (l_rx.var.evt.len == static_cast<uint16_t>(0)) {
tran_(WAIT4_EVT_FRAME);
}
break;
}
case WAIT4_EVT_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
#ifdef Q_UTEST
case WAIT4_TEST_SETUP_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_TEST_TEARDOWN_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_TEST_CONTINUE_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
case WAIT4_TEST_PROBE_DATA: {
l_rx.var.tp.data |= static_cast<uint32_t>(b) << l_rx.var.tp.idx;
l_rx.var.tp.idx += static_cast<uint8_t>(8);
if (l_rx.var.tp.idx
== static_cast<uint8_t>(8U*sizeof(uint32_t)))
{
l_rx.var.tp.addr = static_cast<uint32_t>(0);
l_rx.var.tp.idx = static_cast<uint8_t>(0);
tran_(WAIT4_TEST_PROBE_ADDR);
}
break;
}
case WAIT4_TEST_PROBE_ADDR: {
l_rx.var.tp.addr |= static_cast<uint32_t>(b) << l_rx.var.tp.idx;
l_rx.var.tp.idx += static_cast<uint8_t>(8);
if (l_rx.var.tp.idx == static_cast<uint8_t>(8*QS_FUN_PTR_SIZE)) {
tran_(WAIT4_TEST_PROBE_FRAME);
}
break;
}
case WAIT4_TEST_PROBE_FRAME: {
// keep ignoring the data until a frame is collected
break;
}
#endif // Q_UTEST
case ERROR_STATE: {
// keep ignoring the data until a good frame is collected
break;
}
default: { // unexpected or unimplemented state
rxReportError_(static_cast<uint8_t>(0x45));
tran_(ERROR_STATE);
break;
}
}
}
//****************************************************************************
static void rxHandleGoodFrame_(uint8_t state) {
uint8_t i;
uint8_t *ptr;
switch (state) {
case WAIT4_INFO_FRAME: {
// no need to report Ack or Done
QS_target_info_(static_cast<uint8_t>(0)); // send only Target info
break;
}
case WAIT4_RESET_FRAME: {
// no need to report Ack or Done, because Target resets
QS::onReset(); // reset the Target
break;
}
case WAIT4_CMD_PARAM1: // intentionally fall-through
case WAIT4_CMD_PARAM2: // intentionally fall-through
case WAIT4_CMD_PARAM3: // intentionally fall-through
case WAIT4_CMD_FRAME: {
rxReportAck_(QS_RX_COMMAND);
QS::onCommand(l_rx.var.cmd.cmdId, l_rx.var.cmd.param1,
l_rx.var.cmd.param2, l_rx.var.cmd.param3);
rxReportDone_(QS_RX_COMMAND);
break;
}
case WAIT4_TICK_FRAME: {
rxReportAck_(QS_RX_TICK);
QF::tickX_(l_rx.var.tick.rate, &l_QS_RX);
rxReportDone_(QS_RX_TICK);
break;
}
case WAIT4_PEEK_FRAME: {
// no need to report Ack or Done
QS::beginRec(static_cast<uint_fast8_t>(QS_PEEK_DATA));
ptr = (static_cast<uint8_t *>(QS::rxPriv_.currObj[QS::AP_OBJ])
+ l_rx.var.peek.offs);
QS_TIME_(); // timestamp
QS_U16_(l_rx.var.peek.offs); // data offset
QS_U8_(l_rx.var.peek.size); // data size
QS_U8_(l_rx.var.peek.num); // number of data items
for (i =static_cast<uint8_t>(0); i < l_rx.var.peek.num; ++i) {
switch (l_rx.var.peek.size) {
case 1:
QS_U8_(*(ptr + i));
break;
case 2:
QS_U16_(*(reinterpret_cast<uint16_t *>(ptr) + i));
break;
case 4:
QS_U32_(*(reinterpret_cast<uint32_t *>(ptr) + i));
break;
default:
break;
}
}
QS::endRec();
QS_REC_DONE();
break;
}
case WAIT4_POKE_DATA: {
// received less than expected poke data items
rxReportError_(static_cast<uint8_t>(QS_RX_POKE));
break;
}
case WAIT4_POKE_FRAME: {
rxReportAck_(QS_RX_POKE);
// no need to report done
break;
}
case WAIT4_FILL_FRAME: {
rxReportAck_(QS_RX_FILL);
ptr = (static_cast<uint8_t *>(QS::rxPriv_.currObj[QS::AP_OBJ])
+ l_rx.var.poke.offs);
for (i = static_cast<uint8_t>(0); i < l_rx.var.poke.num; ++i) {
switch (l_rx.var.poke.size) {
case 1:
*(ptr + i) = static_cast<uint8_t>(l_rx.var.poke.data);
break;
case 2:
*(reinterpret_cast<uint16_t *>(ptr) + i) =
static_cast<uint16_t>(l_rx.var.poke.data);
break;
case 4:
*(reinterpret_cast<uint32_t *>(ptr) + i) =
l_rx.var.poke.data;
break;
default:
break;
}
}
break;
}
case WAIT4_GLB_FILTER_FRAME: {
rxReportAck_(QS_RX_GLB_FILTER);
// never disable the non-maskable records
l_rx.var.gFlt.data[0] |= static_cast<uint8_t>(0x01);
l_rx.var.gFlt.data[7] |= static_cast<uint8_t>(0xFC);
l_rx.var.gFlt.data[8] |= static_cast<uint8_t>(0x3F);
// never disable the last 3 records on (0x7D, 0x7E, 0x7F)
l_rx.var.gFlt.data[15] &= static_cast<uint8_t>(0xE0);
for (i = static_cast<uint8_t>(0);
i < static_cast<uint8_t>(sizeof(QS::priv_.glbFilter)); ++i)
{
QS::priv_.glbFilter[i] = l_rx.var.gFlt.data[i];
}
// no need to report Done
break;
}
case WAIT4_OBJ_FRAME: {
if (l_rx.var.obj.kind < static_cast<uint8_t>(QS::MAX_OBJ)) {
if (l_rx.var.obj.recId
== static_cast<uint8_t>(QS_RX_LOC_FILTER))
{
QS::priv_.locFilter[l_rx.var.obj.kind]
= reinterpret_cast<void *>(l_rx.var.obj.addr);
}
else {
QS::rxPriv_.currObj[l_rx.var.obj.kind] =
reinterpret_cast<void *>(l_rx.var.obj.addr);
}
rxReportAck_(
static_cast<enum QSpyRxRecords>(l_rx.var.obj.recId));
}
// both SM and AO
else if (l_rx.var.obj.kind == static_cast<uint8_t>(6)) {
if (l_rx.var.obj.recId
== static_cast<uint8_t>(QS_RX_LOC_FILTER))
{
QS::priv_.locFilter[QS::SM_OBJ] =
reinterpret_cast<void *>(l_rx.var.obj.addr);
QS::priv_.locFilter[QS::AO_OBJ] =
reinterpret_cast<void *>(l_rx.var.obj.addr);
}
else {
QS::rxPriv_.currObj[QS::SM_OBJ] =
reinterpret_cast<void *>(l_rx.var.obj.addr);
QS::rxPriv_.currObj[QS::AO_OBJ] =
reinterpret_cast<void *>(l_rx.var.obj.addr);
}
rxReportAck_(
static_cast<enum QSpyRxRecords>(l_rx.var.obj.recId));
}
else {
rxReportError_(l_rx.var.obj.recId);
}
break;
}
case WAIT4_AO_FILTER_FRAME: {
rxReportAck_(QS_RX_AO_FILTER);
if (l_rx.var.aFlt.prio <= static_cast<uint8_t>(QF_MAX_ACTIVE)) {
rxReportAck_(QS_RX_AO_FILTER);
QS::priv_.locFilter[QS::AO_OBJ] =
QF::active_[l_rx.var.aFlt.prio];
QS::priv_.locFilter[QS::SM_OBJ] =
QF::active_[l_rx.var.aFlt.prio];
}
else {
rxReportError_(static_cast<uint8_t>(QS_RX_AO_FILTER));
}
// no need to report Done
break;
}
case WAIT4_EVT_FRAME: {
// NOTE: Ack was already reported in the WAIT4_EVT_LEN state
#ifdef Q_UTEST
QS::onTestEvt(l_rx.var.evt.e); // "massage" the event, if needed
#endif // Q_UTEST
// use 'i' as status, 0 == success,no-recycle
i = static_cast<uint8_t>(0);
if (l_rx.var.evt.prio == static_cast<uint8_t>(0)) { // publish
QF::PUBLISH(l_rx.var.evt.e, &l_QS_RX);
rxReportDone_(QS_RX_EVENT);
}
else if (l_rx.var.evt.prio < static_cast<uint8_t>(QF_MAX_ACTIVE))
{
if (!QF::active_[l_rx.var.evt.prio]->POST_X(
l_rx.var.evt.e,
static_cast<uint_fast16_t>(1), // margin
&l_QS_RX))
{
// failed QACTIVE_POST() recycles the event
i = static_cast<uint8_t>(0x80); // failure, no recycle
}
}
else if (l_rx.var.evt.prio == static_cast<uint8_t>(255)) {
// dispatch to the current SM object
if (QS::rxPriv_.currObj[QS::SM_OBJ]
!= static_cast<void *>(0))
{
static_cast<QHsm *>(QS::rxPriv_.currObj[QS::SM_OBJ])
->dispatch(l_rx.var.evt.e);
i = static_cast<uint8_t>(0x01); // success, recycle
}
else {
i = static_cast<uint8_t>(0x81); // failure, recycle
}
}
else if (l_rx.var.evt.prio == static_cast<uint8_t>(254)) {
// init the current SM object"
if (QS::rxPriv_.currObj[QS::SM_OBJ] != static_cast<void *>(0))
{
static_cast<QHsm *>(QS::rxPriv_.currObj[QS::SM_OBJ])
->init(l_rx.var.evt.e);
i = static_cast<uint8_t>(0x01); // success, recycle
}
else {
i = static_cast<uint8_t>(0x81); // failure, recycle
}
}
else if (l_rx.var.evt.prio == static_cast<uint8_t>(253)) {
// post to the current AO
if (QS::rxPriv_.currObj[QS::AO_OBJ] != static_cast<void *>(0))
{
if (!static_cast<QActive *>(
QS::rxPriv_.currObj[QS::AO_OBJ])->POST_X(
l_rx.var.evt.e,
static_cast<uint_fast16_t>(1), // margin
&l_QS_RX))
{
// failed QACTIVE_POST() recycles the event
i = static_cast<uint8_t>(0x80); // failure, no recycle
}
}
else {
i = static_cast<uint8_t>(0x81); // failure, recycle
}
}
else {
i = static_cast<uint8_t>(0x81); // failure, recycle
}
// recycle needed?
if ((i & static_cast<uint8_t>(0x01)) != static_cast<uint8_t>(0)) {
QF::gc(l_rx.var.evt.e);
}
// failure?
if ((i & static_cast<uint8_t>(0x80)) != static_cast<uint8_t>(0)) {
rxReportError_(static_cast<uint8_t>(QS_RX_EVENT));
}
else {
rxReportDone_(QS_RX_EVENT);
}
break;
}
#ifdef Q_UTEST
case WAIT4_TEST_SETUP_FRAME: {
rxReportAck_(QS_RX_TEST_SETUP);
l_testData.tpNum = static_cast<uint8_t>(0); // clear Test-Probes
l_testData.testTime = static_cast<QSTimeCtr>(0); //clear time tick
// don't clear current objects
QS::onTestSetup(); // application-specific test setup
// no need to report Done
break;
}
case WAIT4_TEST_TEARDOWN_FRAME: {
rxReportAck_(QS_RX_TEST_TEARDOWN);
QS::onTestTeardown(); // application-specific test teardown
// no need to report Done
break;
}
case WAIT4_TEST_CONTINUE_FRAME: {
rxReportAck_(QS_RX_TEST_CONTINUE);
QS::rxPriv_.inTestLoop = false; // exit the QUTest loop
// no need to report Done
break;
}
case WAIT4_TEST_PROBE_FRAME: {
rxReportAck_(QS_RX_TEST_PROBE);
Q_ASSERT_ID(815, l_testData.tpNum
< static_cast<uint8_t>(sizeof(l_testData.tpBuf)
/sizeof(l_testData.tpBuf[0])));
l_testData.tpBuf[l_testData.tpNum] = l_rx.var.tp;
++l_testData.tpNum;
// no need to report Done
break;
}
#endif // Q_UTEST
case ERROR_STATE: {
// keep ignoring all bytes until new frame
break;
}
default: {
rxReportError_(static_cast<uint8_t>(0x47));
break;
}
}
}
//****************************************************************************
static void rxHandleBadFrame_(uint8_t state) {
rxReportError_(static_cast<uint8_t>(0x50)); // error for all bad frames
switch (state) {
case WAIT4_EVT_FRAME: {
Q_ASSERT_ID(910, l_rx.var.evt.e != (QEvt *)0);
QF::gc(l_rx.var.evt.e); // don't leak an allocated event
break;
}
default: {
break;
}
}
}
/****************************************************************************/
static void rxReportAck_(enum QSpyRxRecords recId) {
QS::beginRec(static_cast<uint_fast8_t>(QS_RX_STATUS));
QS_U8_(recId); // record ID
QS::endRec();
QS_REC_DONE();
}
//****************************************************************************
static void rxReportError_(uint8_t const code) {
QS::beginRec(static_cast<uint_fast8_t>(QS_RX_STATUS));
QS_U8_(static_cast<uint8_t>(0x80U | code)); // error code
QS::endRec();
QS_REC_DONE();
}
/****************************************************************************/
static void rxReportDone_(enum QSpyRxRecords recId) {
QS::beginRec(static_cast<uint_fast8_t>(QS_TARGET_DONE));
QS_TIME_(); // timestamp
QS_U8_(recId); // record ID
QS::endRec();
QS_REC_DONE();
}
/****************************************************************************/
static void rxPoke_(void) {
uint8_t *ptr = ((uint8_t *)QS::rxPriv_.currObj[QS::AP_OBJ]
+ l_rx.var.poke.offs);
switch (l_rx.var.poke.size) {
case 1:
*ptr = static_cast<uint8_t>(l_rx.var.poke.data);
break;
case 2:
*(uint16_t *)ptr = static_cast<uint16_t>(l_rx.var.poke.data);
break;
case 4:
*(uint32_t *)ptr = l_rx.var.poke.data;
break;
default:
Q_ERROR_ID(900);
break;
}
l_rx.var.poke.data = static_cast<uint32_t>(0);
l_rx.var.poke.idx = static_cast<uint8_t>(0);
l_rx.var.poke.offs += l_rx.var.poke.size;
}
//============================================================================
#ifdef Q_UTEST
//****************************************************************************
/// @description
/// This function obtains the Test-Probe for a given API.
///
/// @param[in] api_id the API-ID that requests its Test-Probe
///
/// @returs Test-Probe data that has been received for the given API
/// from the Host (running qutest). For any ginve API, the function returns
/// the Test-Probe data in the same order as it was received from the Host.
/// If there is no Test-Probe for a ginve API, or no more Test-Probes for
/// a given API, the function returns zero.
///
uint32_t QS::getTestProbe_(void (* const api)(void)) {
uint32_t data = static_cast<uint32_t>(0);
uint8_t i;
for (i = static_cast<uint8_t>(0); i < l_testData.tpNum; ++i) {
if (l_testData.tpBuf[i].addr == (QSFun)api) {
QS_CRIT_STAT_
data = l_testData.tpBuf[i].data;
--l_testData.tpNum;
// move all remaining entries in the buffer up by one
for (; i < l_testData.tpNum; ++i) {
l_testData.tpBuf[i] = l_testData.tpBuf[i + 1];
}
// i == l_testData.tpNum, which terminates the top loop
QS::beginRec(static_cast<uint_fast8_t>(QS_TEST_PROBE_GET));
QS_TIME_(); // timestamp
QS_FUN_(api); // the calling API
QS_U32_(data); // the Test-Probe data
QS::endRec();
QS_REC_DONE();
}
}
return data;
}
/****************************************************************************/
QSTimeCtr QS::onGetTime(void) {
return (++l_testData.testTime);
}
#endif // Q_UTEST
} // namespace QP