mirror of
https://github.com/QuantumLeaps/qpcpp.git
synced 2025-01-28 06:02:56 +08:00
1218 lines
42 KiB
C++
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
|