diff --git a/doxygen/Doxyfile b/doxygen/Doxyfile index 5acb2687..613fb450 100644 --- a/doxygen/Doxyfile +++ b/doxygen/Doxyfile @@ -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 = diff --git a/doxygen/images/qp-zephyr.jpg b/doxygen/images/qp-zephyr.jpg new file mode 100644 index 00000000..16ef9346 Binary files /dev/null and b/doxygen/images/qp-zephyr.jpg differ diff --git a/examples/zephyr/dpp/README.md b/examples/zephyr/dpp/README.md index 0602a008..9fb09751 100644 --- a/examples/zephyr/dpp/README.md +++ b/examples/zephyr/dpp/README.md @@ -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 ``` diff --git a/examples/zephyr/dpp/prj.conf b/examples/zephyr/dpp/prj.conf index 441f6b5c..b55132ff 100644 --- a/examples/zephyr/dpp/prj.conf +++ b/examples/zephyr/dpp/prj.conf @@ -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 diff --git a/examples/zephyr/dpp/src/bsp.cpp b/examples/zephyr/dpp/src/bsp.cpp index f7a907f5..4ae3f9f2 100644 --- a/examples/zephyr/dpp/src/bsp.cpp +++ b/examples/zephyr/dpp/src/bsp.cpp @@ -22,8 +22,8 @@ // // //============================================================================ -//! @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(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 -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(10000U)); + Q_PRINTK("\nASSERTION in %s:%d\n", module, loc); #ifndef NDEBUG k_panic(); // debug build: halt the system for error search... diff --git a/zephyr/CMakeLists.txt b/zephyr/CMakeLists.txt index cf2995b6..04e2e636 100644 --- a/zephyr/CMakeLists.txt +++ b/zephyr/CMakeLists.txt @@ -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 diff --git a/zephyr/Kconfig b/zephyr/Kconfig index 4989808d..490543d8 100644 --- a/zephyr/Kconfig +++ b/zephyr/Kconfig @@ -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 diff --git a/zephyr/README.md b/zephyr/README.md index ad3f6003..d99e4b7b 100644 --- a/zephyr/README.md +++ b/zephyr/README.md @@ -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 --recurse-submodules --depth 1 +``` +where `` 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 ``` diff --git a/zephyr/qf_port.cpp b/zephyr/qf_port.cpp index f7d639b6..a382355f 100644 --- a/zephyr/qf_port.cpp +++ b/zephyr/qf_port.cpp @@ -22,8 +22,8 @@ // // //============================================================================ -//! @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) { diff --git a/zephyr/qf_port.hpp b/zephyr/qf_port.hpp index ca2a6798..e6fcdc6a 100644 --- a/zephyr/qf_port.hpp +++ b/zephyr/qf_port.hpp @@ -22,8 +22,8 @@ // // //============================================================================ -//! @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(0)) +#endif + //============================================================================ // interface used only inside QF, but not in applications //