improved Zephyr port and examples
This commit is contained in:
MMS 2022-08-12 15:24:06 -04:00
parent f16303644b
commit 4cdb28e10d
10 changed files with 119 additions and 89 deletions

View File

@ -7,7 +7,7 @@
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "QP/C++"
PROJECT_NUMBER = 7.0.1
PROJECT_NUMBER = 7.0.2
PROJECT_BRIEF = "Real-Time Embedded Framework"
PROJECT_LOGO = ../../ql-doxygen/images/logo_ql.png
OUTPUT_DIRECTORY =

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -34,7 +34,7 @@ The [QP/Spy software tracing](https://www.state-machine.com/qtools/qpspy.html)
is supported in this example and can be enabled by providing the command-line
option `-DQSPY=ON` to the build process:
Specific example
Specific example
```
west build -b nucleo_h743zi -- -DQSPY=ON
```

View File

@ -1,8 +1,12 @@
# QPCPP Zephyr application
# SPDX-License-Identifier: GPL-3.0-or-later OR LicenseRef-QL-commercial
CONFIG_QPCPP=y
CONFIG_CPLUSPLUS=y
CONFIG_ASSERT=y
CONFIG_ASSERT_LEVEL=2
CONFIG_NUM_COOP_PRIORITIES=29
CONFIG_NUM_PREEMPT_PRIORITIES=40
CONFIG_GPIO=y
CONFIG_REBOOT=y
# for QSPY...
CONFIG_SERIAL=y
CONFIG_UART_INTERRUPT_DRIVEN=y

View File

@ -22,8 +22,8 @@
// <www.state-machine.com>
// <info@state-machine.com>
//============================================================================
//! @date Last updated on: 2022-08-05
//! @version Last updated for: @ref qpcpp_7_0_1
//! @date Last updated on: 2022-08-12
//! @version Last updated for: @ref qpcpp_7_0_2
//!
//! @file
//! @brief DPP example (BSP)
@ -68,7 +68,7 @@ static struct k_timer QF_tick_timer;
PHILO_STAT = QP::QS_USER,
PAUSED_STAT,
COMMAND_STAT,
CONTEXT_SW
TEST_MSG
};
#endif
@ -103,22 +103,20 @@ void BSP::init(void) {
// object dictionaries...
QS_OBJ_DICTIONARY(AO_Table);
QS_OBJ_DICTIONARY(AO_Philo[0]);
QS_OBJ_DICTIONARY(AO_Philo[1]);
QS_OBJ_DICTIONARY(AO_Philo[2]);
QS_OBJ_DICTIONARY(AO_Philo[3]);
QS_OBJ_DICTIONARY(AO_Philo[4]);
QS_OBJ_DICTIONARY(&timerID);
for (int n = 0; n < N_PHILO; ++n) {
QS_OBJ_ARR_DICTIONARY(AO_Philo[n], n);
}
QS_USR_DICTIONARY(PHILO_STAT);
QS_USR_DICTIONARY(PAUSED_STAT);
QS_USR_DICTIONARY(COMMAND_STAT);
QS_USR_DICTIONARY(CONTEXT_SW);
QS_USR_DICTIONARY(TEST_MSG);
// setup the QS filters...
QS_GLB_FILTER(QP::QS_SM_RECORDS); // state machine records
QS_GLB_FILTER(QP::QS_AO_RECORDS); // active object records
QS_GLB_FILTER(QP::QS_UA_RECORDS); // all user records
QS_GLB_FILTER(TEST_MSG);
}
//............................................................................
void BSP::ledOn(void) {
@ -136,6 +134,7 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
else {
ledOff();
}
Q_PRINTK("Philo[%d]->%s\n", n, stat);
QS_BEGIN_ID(PHILO_STAT, AO_Philo[n]->m_prio) // app-specific record begin
QS_U8(1, n); // Philosopher number
@ -144,7 +143,7 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
}
//............................................................................
void BSP::displayPaused(uint8_t paused) {
static_cast<void>(paused); // unused parameter
Q_UNUSED_PAR(paused);
}
//............................................................................
uint32_t BSP::random(void) { // a very cheap pseudo-random-number generator
@ -167,7 +166,7 @@ void BSP::randomSeed(uint32_t seed) {
//............................................................................
void BSP::terminate(int16_t result) {
(void)result;
Q_UNUSED_PAR(result);
}
} // namespace DPP
@ -179,9 +178,11 @@ namespace QP {
// QF callbacks ==============================================================
void QF::onStartup(void) {
k_timer_start(&QF_tick_timer, K_MSEC(1), K_MSEC(1));
Q_PRINTK("QF::onStartup()");
}
//............................................................................
void QF::onCleanup(void) {
Q_PRINTK("QF::onCleanup()");
}
// QS callbacks ==============================================================
@ -189,52 +190,53 @@ void QF::onCleanup(void) {
#include <drivers/uart.h>
static const struct device *uart_console_dev;
// select the Zephyr shell UART
// NOTE: you can change this to other UART peripheral if desired
static struct device const *uart_dev =
DEVICE_DT_GET(DT_CHOSEN(zephyr_shell_uart));
//............................................................................
static K_THREAD_STACK_DEFINE(qspy_stack, 1024); // stack storage
static k_thread qspy_thread_handler;
static void qspy_thread(void *p1, void *p2, void *p3){
while (true) {
// transmit bytes...
std::uint16_t len = 0xFFFFU; // get as many bytes as available
QS_CRIT_STAT_
QS_CRIT_E_();
std::uint8_t const *buf = QS::getBlock(&len);
QS_CRIT_X_();
for (; len != 0U; --len, ++buf) {
uart_poll_out(uart_console_dev, *buf);
}
void QS::doOutput(void) {
std::uint16_t len = 0xFFFFU; // big number to get all available bytes
// receive bytes...
std::uint8_t b;
while (uart_poll_in(uart_console_dev, &b) == 0) {
QS::rxPut(b);
}
QS::rxParse();
QS_CRIT_STAT_
QS_CRIT_E_();
std::uint8_t const *buf = QS::getBlock(&len);
QS_CRIT_X_();
// transmit the bytes via the UART...
for (; len != 0U; --len, ++buf) {
uart_poll_out(uart_dev, *buf);
}
}
//............................................................................
static void uart_cb(const struct device *dev, void *user_data) {
if (!uart_irq_update(uart_dev)) {
return;
}
if (uart_irq_rx_ready(uart_dev)) {
std::uint8_t buf[32];
int n = uart_fifo_read(uart_dev, buf, sizeof(buf));
for (std::uint8_t const *p = buf; n > 0; --n, ++p) {
QS::rxPut(*p);
}
}
}
//............................................................................
bool QS::onStartup(void const *arg) {
static uint8_t qsTxBuf[2*1024]; // buffer for QS transmit channel
static uint8_t qsRxBuf[256]; // buffer for QS receive channel
Q_REQUIRE(uart_dev != NULL);
initBuf (qsTxBuf, sizeof(qsTxBuf));
rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
uart_console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
Q_ASSERT(uart_console_dev != NULL);
k_thread_create(&qspy_thread_handler,
qspy_stack,
K_THREAD_STACK_SIZEOF(qspy_stack),
&qspy_thread,
nullptr, // p1
nullptr, // p2
nullptr, // p3
QF_MAX_ACTIVE, // lowest priority
K_ESSENTIAL, // thread options
K_NO_WAIT); // start immediately
//TODO assert if could not create thread
// configure interrupt and callback to receive data
uart_irq_callback_user_data_set(uart_dev, &uart_cb, nullptr);
uart_irq_rx_enable(uart_dev);
return true; // return success
}
//............................................................................
@ -242,19 +244,17 @@ void QS::onCleanup(void) {
}
//............................................................................
QSTimeCtr QS::onGetTime(void) { // NOTE: invoked with interrupts DISABLED
return k_uptime_get_32();
return k_cycle_get_32();
}
//............................................................................
void QS::onFlush(void) {
uint16_t len = 0xFFFFU; // big number to get as many bytes as available
uint8_t const *buf = QS::getBlock(&len); // get continguous block of data
while (buf != nullptr) { // data available?
for(auto i = 0;i!=len;i++)
{
uart_poll_out(uart_console_dev,buf[i]);
}
len = 0xFFFFU; // big number to get as many bytes as available
buf = QS::getBlock(&len); // try to get more data
std::uint16_t len = 0xFFFFU; // to get as many bytes as available
std::uint8_t const *buf;
while ((buf = QS::getBlock(&len)) != nullptr) { // QS-TX data available?
for (; len != 0U; --len, ++buf) {
uart_poll_out(uart_dev, *buf);
}
len = 0xFFFFU; // to get as many bytes as available
}
}
//............................................................................
@ -268,10 +268,10 @@ extern "C" void assert_failed(char const *module, int loc);
void QS::onCommand(uint8_t cmdId, uint32_t param1,
uint32_t param2, uint32_t param3)
{
(void)cmdId;
(void)param1;
(void)param2;
(void)param3;
Q_UNUSED_PAR(cmdId);
Q_UNUSED_PAR(param1);
Q_UNUSED_PAR(param2);
Q_UNUSED_PAR(param3);
}
#endif // Q_SPY
@ -286,8 +286,8 @@ Q_NORETURN Q_onAssert(char const * const module, int_t const loc) {
//
// NOTE: add here your application-specific error handling
//
printk("\nASSERTION in %s:%d\n", module, loc);
QS_ASSERTION(module, loc, static_cast<uint32_t>(10000U));
Q_PRINTK("\nASSERTION in %s:%d\n", module, loc);
#ifndef NDEBUG
k_panic(); // debug build: halt the system for error search...

View File

@ -32,7 +32,8 @@ zephyr_library_sources(
${QPCPP_DIR}/zephyr/qf_port.cpp
)
if(CONFIG_QSPY)
# QSPY option...
if(QSPY)
target_compile_definitions(app PUBLIC Q_SPY)
zephyr_library_compile_definitions(Q_SPY)
@ -46,6 +47,6 @@ zephyr_library_sources(
${QPCPP_DIR}/include/qstamp.cpp
)
endif() # CONFIG_QSPY
endif() # QSPY
endif() # CONFIG_QPCPP

View File

@ -9,12 +9,6 @@ menuconfig QPCPP
if QPCPP
config QSPY
bool "QSPY Tracing"
default n
help
Enables the QSPY Tracing for QP
module = QPCPP
module-str = QPCPP

View File

@ -1,4 +1,6 @@
# qpcpp Zephyr Module
[![QP Zephyr Module](../doxygen/images/qp-zephyr.jpg)](https://www.state-machine.com/qpcpp/zephyr.html)
# About the QPCPP Zephyr Module
This directory defines the
[QP/C++ Real-Time Embedded Framework](https://github.com/QuantumLeaps/qpcpp)
as a [Zephyr module](https://docs.zephyrproject.org/latest/develop/modules.html).
@ -7,22 +9,32 @@ as a [Zephyr module](https://docs.zephyrproject.org/latest/develop/modules.html)
Example of use is provided in the related repository
[qpcpp-zephyr-app](https://github.com/QuantumLeaps/qpcpp-zephyr-app)
To create your own QP-Zephyr project, you can clone that repository
and customize it to your needs:
```bash
git clone https://github.com/QuantumLeaps/qpcpp-zephyr-app <my-project> --recurse-submodules --depth 1
```
where `<my-project>` is the name of your project.
## Configuring the QPCPP Zephyr Module
The `Kconfig` file provides configuration `CONFIG_QPCPP` to activate
the QPCPP module in Zephyr. To do so, you need to add the following
line to your `prj.conf`:
The `Kconfig` file provides configuration `CONFIG_QPCPP` to activate the QPCPP module
in Zephyr. To do so, you need to add the following line to your `prj.conf`:
```ini
CONFIG_QPCPP=y
```
## Configuring the QSPY Software Tracing
If you wish to enable
[QSPY Software Tracing](https://www.state-machine.com/qtools/qpspy.html),
`Kconfig` file provides configuration `CONFIG_QSPY`, which you can
use in your `prj.conf`:
## Option for Activating QSPY Software Tracing
The QP/C++ Zephyr Module supports the
[QSPY Software Tracing](https://www.state-machine.com/qtools/qpspy.html)
option and will add the appropriate macros and files to build the "QSPY"
configuration.
```ini
CONFIG_QSPY=y
If you wish to enable "QSPY" you can provide the option "QSPY"
in the command-line for the build. For example:
```bash
west build -b nucleo_h743zi -- -DQSPY=ON
```

View File

@ -22,8 +22,8 @@
// <www.state-machine.com>
// <info@state-machine.com>
//============================================================================
//! @date Last updated on: 2022-08-06
//! @version Last updated for: @ref qpcpp_7_0_1
//! @date Last updated on: 2022-08-12
//! @version Last updated for: @ref qpcpp_7_0_2
//!
//! @file
//! @brief QF/C++ port to Zephyr RTOS kernel, all supported compilers
@ -67,7 +67,18 @@ void QF::init(void) {
//............................................................................
int_t QF::run(void) {
onStartup();
#ifdef Q_SPY
// lower the priority of the main thread to the level of idle
k_thread_priority_set(k_current_get(),
CONFIG_NUM_PREEMPT_PRIORITIES - 1);
// perform QS work
while (true) {
QS::rxParse(); // parse any QS-RX bytes
QS::doOutput(); // perform the QS-TX output
}
#else
return 0; // return from the main Zephyr thread
#endif
}
//............................................................................
void QF::stop(void) {

View File

@ -22,8 +22,8 @@
// <www.state-machine.com>
// <info@state-machine.com>
//============================================================================
//! @date Last updated on: 2022-08-06
//! @version Last updated for: @ref qpcpp_7_0_1
//! @date Last updated on: 2022-08-12
//! @version Last updated for: @ref qpcpp_7_0_2
//!
//! @file
//! @brief QF/C++ port to Zephyr RTOS kernel, all supported compilers
@ -61,6 +61,14 @@ extern struct k_spinlock spinlock;
} // namespace QF
} // namespace QP
// Q_PRINTK() macro to avoid conflicts with Zephyr's printk()
// when Q_SPY configuation is used
#ifndef Q_SPY
#define Q_PRINTK(fmt_, ...) printk(fmt_, ##__VA_ARGS__)
#else
#define Q_PRINTK(dummy, ...) (static_cast<void>(0))
#endif
//============================================================================
// interface used only inside QF, but not in applications
//