Giter Site home page Giter Site logo

quantumleaps / qpcpp Goto Github PK

View Code? Open in Web Editor NEW
351.0 21.0 77.0 87.37 MB

QP/C++ Real-Time Embedded Framework/RTOS for embedded systems based on active objects (actors) and hierarchical state machines

Home Page: https://www.state-machine.com/products/qp

C++ 86.80% Batchfile 0.24% C 4.86% Makefile 5.78% CMake 0.98% Python 1.36%
active-object actor actor-model arm arm-cortex-m0 arm-cortex-m3 arm-cortex-m4f arm-cortex-m7 embedded-systems embedded-c

qpcpp's Introduction

QP Framework

What's New?

GitHub release (latest by date)

View QP/C++ Revision History at: https://www.state-machine.com/qpcpp/history.html

NOTE: If you're interested in the latest QP/C++ version from GitHub, it is highly recommended that you clone this repo like that:

git clone https://github.com/QuantumLeaps/qpcpp --recurse-submodules --depth 1

Alternatively, you can also download the latest QP/C++ Release.

Getting Started with QP/C++

The most recommended way of obtaining QP/C++ is by downloading the QP-bundle, which includes QP/C++ as well as the QM modeling tool and the QTools collection. The main advantage of obtaining QP/C++ bundled together like that is that you get all components, tools and examples ready to go.

Getting Started Resources

About QP/C++

QP/C++ (Quantum Platform in C++) is a lightweight, open source Real-Time Embedded Framework (RTEF) for building modern embedded software as systems of asynchronous, event-driven active objects (actors). The QP/C++ framework is a member of a QP family consisting of QP/C and QP/C++ frameworks, which are strictly quality controlled, thoroughly documented, and commercially licensable.

Safer Model of Concurrency

The QP framework family is based on the Active Object (actor) design pattern, which inherently supports and automatically enforces the following best practices of concurrent programming:

  • Keep data isolated and bound to active objects' threads. Threads should hide (encapsulate) their private data and other resources, and not share them with the rest of the system.

  • Communicate among active object threads asynchronously via event objects. Using asynchronous events keeps the threads running truly independently, without blocking on each other.

  • Active object threads should spend their lifetime responding to incoming events, so their mainline should consist of an event-loop that handles events one at a time (to completion), thus avoiding any concurrency hazards within an active object thread itself.

This architecture is generally safer, more responsive and easier to understand and maintain than the shared-state concurrency of a conventional RTOS. It also provides higher level of abstraction and the correct abstractions to effectively apply modeling and code generation to deeply embedded real-time systems.

Hierarchical State Machines

The behavior of active objects is specified in QP/C++ by means of Hierarchical State Machines (UML statecharts). The framework supports manual coding of UML state machines in C as well as automatic code generation by means of the free QM modeling tool.

Built-in Real-Time Kernels

The QP/C++ framework can run on bare-metal single-chip microcontrollers, completely replacing a traditional RTOS. The framework contains a selection of built-in real-time kernels, such as the cooperative QV kernel, the preemptive non-blocking QK kernel, and the preemptive, blocking QXK kernel that provides all the features you might expect from a traditional RTOS. Native QP ports and ready-to-use examples are provided for major CPUs, such as ARM Cortex-M (M0/M0+/M3/M4/M7).

Traditional RTOS/OS

QP/C++ can also work with a traditional RTOS, such as ThreadX, FreeRTOS, embOS, uC/OS-II and TI-RTOS, as well as with (embedded) Linux (POSIX) and Windows.

Popularity and Maturity

With 20 years of continuous development, over 350 commercial licensees, and many times more open source users worldwide, the QP™ frameworks are the most popular such offering on the market. They power countless electronic products ranging from implantable medical devices to complex weapon systems.

QP/C++ Licensing

QP/C++ is licensed under the sustainable dual licensing model, in which both the open source software distribution mechanism and traditional closed source software distribution models are combined.

NOTE: If your company has a policy forbidding open source in your product, all QP frameworks can be licensed commercially, in which case you don't use any open source license and you do not violate your policy.

QP/C++ Documentation

The online HTML documentation for the latest version of QP/C++ is located at: https://www.state-machine.com/qpcpp

The offline HTML documentation for this particular version of QP/C++ is located in the sub-folder html. To view the offline documentation, open the file html/index.html in your web browser.

How to Get Help?

How to Help this Project?

If you like this project, please give it a star (in the upper-right corner of your browser window):

GitHub star

qpcpp's People

Contributors

quantum-leaps avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

qpcpp's Issues

Zephyr module name

I have been further working with zephyr modules and found out that by default the name of the module is its path.

https://github.com/zephyrproject-rtos/zephyr/blob/e0fb04458f086111b95237bb6060f95738981fcd/cmake/modules/extensions.cmake#L391

In some cases if the project path is too large some warnings might appear. I would suggest to avoid this and have an easier way to access the module from cmake to change

zephyr_library()

to

zephyr_library_named(qpcpp)

More info about this cmake function

https://github.com/zephyrproject-rtos/zephyr/blob/e0fb04458f086111b95237bb6060f95738981fcd/cmake/modules/extensions.cmake#L418

Best regards
Victor

Error on Line 169 include/qep.hpp

There is an error on Line 169 of include/qep.hpp. There is an extra bracket on the end of the line containing refCtr_(0U)).

The line should read refCtr_(0U)

https://github.com/QuantumLeaps/qpcpp/blob/master/include/qep.hpp#L169

include/qep.hpp

#ifdef Q_EVT_CTOR // Provide the constructor for the QEvt class?

    //************************************************************************
    class QEvt {
    public:
        //! public constructor (dynamic event)
        QEvt(QSignal const s) noexcept
          : sig(s)
          // poolId_/refCtr_ intentionally uninitialized
        {}
        enum StaticEvt : std::uint8_t { STATIC_EVT };

        //! public constructor (static event)
        QEvt(QSignal const s, StaticEvt /*dummy*/) noexcept
          : sig(s),
            poolId_(0U),
            refCtr_(0U))
        {}

Just thought someone should know.

CMakeLists.txt missing in 'ports'

Hi Miro,

for some reason the file ports/CMakeLists.txt didnt make it into the repository. Please copy the following into the file:

# CMake the qpcpp libraries for different targets
set(PORT_DIR ${PORT})
if((PORT STREQUAL win32) OR (PORT STREQUAL posix))
    if(QPCPP_CFG_UNIT_TEST)
        set(PORT_DIR ${PORT_DIR}-qutest)
    elseif(KERNEL STREQUAL qv)
        set(PORT_DIR ${PORT_DIR}-qv)
    endif()
endif()
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${PORT_DIR})
    message(STATUS "Found port dir - './${PORT_DIR}' for port ${PORT}, KERNEL ${KERNEL}")
else()
    message(FATAL_ERROR "Target port dir '${CMAKE_CURRENT_SOURCE_DIR}/${PORT_DIR}' not found!")
endif()

add_subdirectory(${PORT_DIR})

Thanks
Stefan

QHsm::isIn should support calls from super state

Assume there is a child state C and its super state S, and the state machine is in state C (leaf in the state machine graph). Calling isIn(C) and isIn(S) both return true and it's great. When calling super(S) in C and there is a call to isIn(C) from S, assert is called and the program crashes. Same thing for calling isIn(S) from S. Assert is called because of state stability check. However, calling isIn from super states is logically correct, since there is a temporary jump to another state and a transition didn't occur. In other words, being in a super state should not prevent me from calling isIn.

Missing (direct) documentation of QEVT_DYN_CTOR

I discovered QEVT_DYN_CTOR by looking at the definition of Q_NEW in the qp.hpp header.
It was really difficult for me to discover that it is explained in the documentation of Q_NEW.
I think at least a reference to Q_NEW in the documentation of QEVT_DYN_CTOR would be helpful.

PS: The links to the html folder in the README.md don’t work when viewing on GitHub. I have seen that the folder is present in the release download.
A hint about that would also be nice.

error: '_Noreturn' does not name a type

I have updated from tag v7.2.1 to the latest commit and my zephyr project throws the compiling error

include/qassert.h:327:20: error: '_Noreturn' does not name a type
327 | #define Q_NORETURN _Noreturn void

Is there something I have to be aware between version 7.2.1 and 7.2.2 for the Zephyr Port?

Transition to new state from entry signal

I am refactoring a project with a polling state machine and I found an issue, which I am not sure if it's because I just started using the QP framework.

I have a state that should transition immediately to a state depending on some guards that were set/unset via the entry action. I tried the following:

bool DummyGuard;
Q_STATE_DEF(MyAO, State1) 
{
    QP::QState status_;
    switch (e->sig) {
        case Q_ENTRY_SIG: 
          {
                /*
                Here should be the logic that sets/unsets guard
               */
               if(DummyGuard)
               {
                    status_ = tran(&state2);
               }
               else
               {
                    status_ = tran(&state1);
               }
                break;
        }
        default: 
       {
            status_ = super(&MyOtherState);
            break;
        }
    }
    return status_;
}

This did not work, and the state that was executed afterward is the same state1. I started debugging the framework and I found that QHsm::m_temp.fun is assigned again the top state MyOtherState, which overrides the value assigned via tran(&state2) or tran(&state3).

What I ended up doing is creating an artificial signal that is executed in the entry action, and afterward in this signal is where I do the logic to change to the next state.

/* Abritriary number for Artificial signal.
  This number is actually in an enum with all the other signals
 */
int SIG_ARTIFICIAL=6; 
static constexpr QP::QEvt ArtificialEvt= {.sig=static_cast<QP::QSignal>(SIG_ARTIFICIAL),
                                                  .poolId_=0,
                                                  .refCtr_=0};
bool DummyGuard;
Q_STATE_DEF(MyAO, State1) 
{
    QP::QState status_;
    switch (e->sig) {
        case Q_ENTRY_SIG: 
          {
               POST(&ArtificialEvt,this);
                break;
        }
       case SIG_ARTIFICIAL:
               if(DummyGuard)
               {
                    status_ = tran(&state2);
               }
               else
               {
                    status_ = tran(&state1);
               }
               break;
        default: 
       {
            status_ = super(&MyOtherState);
            break;
        }
    }
    return status_;
}

Based on this small test I am not sure if this is the correct way to accomplish this objective with the QP Framework or if there is an easier way to do this without having to create a signal+event. I am also not sure if this would be UML compliant . Speaking about UML would I need to define a Signal to transition or could I have a transition that does not depend on a signal (e.g., the initial transition).

I am asking this because the project is based on a protocol specification that has some state machines with this type of state chart notation where the state does some logic in the entry action and then depending on guards it will change to another state.

Thanks in advance for any feedback on this discussion
Victor

Undefined reference to QP::QS::onCommand() method

The following error is at examples/workstation/blinky with make with CONF=spy

../../../src/qs/qs_rx.cpp:851: undefined reference to `QP::QS::onCommand(unsigned char, unsigned int, unsigned int, unsigned `int)'

Looks like a dummy implementation of onCommand() is missing from the source code. Shall I create a pull request with a fix?

QF::newRef_ Should Support Static Events (Non-dynamic Events)

Currently this function allows only dynamic events to add a new reference and increment the reference count. This is problematic because we have a use case where there are two possible sources for the same event - one sends a static event and one sends a dynamic one. The target AO needs to save the event pointer to use the event forever. The source is determined during run time. Current implementation forces me to check if the event is dynamic or not, instead of the function.

A suitable solution would be checking if the event is dynamic. If it is, increment the ref. count. Otherwise, do nothing.

Two onStartup() callbacks in qk.cpp run()

While stepping through the QK run, I noticed there were 2 onStartup() callbacks.
One on line 193 and another on line 204. The second is done with interrupts disabled.

Was this on purpose?

[Qpccp 6.9.2] test of assertion fail now.

Hello.
We just updated to QP 6.9.2 and one of our test on our target now fail.
I'm not an expert of qutest yet.

the test fail when we try to test and assertion.

here is the actual test: (q_assert_file_path is a variable with the file name, that's not the issue).

test("GetBitAndAdvance: output SHOULD be assert error WHEN GetBitAndAdvance() is called after constructing the bit " \
     "iterator object with the default constructor")
command(2, 0)
expect(f"@timestamp =ASSERT= Mod={q_assert_file_path},Loc=200")

the Qspy output with QP 6.9.1 that works.

           Trg-Ack  QS_RX_TEST_SETUP
           Trg-Ack  QS_RX_COMMAND
0000000001 =ASSERT= Mod=../src/utils/BitIterator.cpp,Loc=200
           Trg-Ack  QS_RX_TEST_TEARDOWN

and the output with 6.9.2 that does not work.

           Trg-Ack  QS_RX_TEST_SETUP
           Trg-Ack  QS_RX_COMMAND
0000000001 =ASSERT= Mod=../src/utils/BitIterator.cpp,Loc=200
           Trg-ERR  QS_RX_INFO
           Trg-ERR  0x50
           Trg-Ack  QS_RX_TEST_TEARDOWN

is the 2 lines "Trg-ERR" expected with 6.9.2? or did I miss something during the upgrade?

me' was not declared in this scope (Zephyr port)

When compiling in debug mode I got this error

qpcpp\ports\zephyr\qf_port.cpp:122:24: error: 'me' was not declared in this scope
  122 |     k_thread_name_set(&me->thread, name);
      |                        ^~

I think it should be m_thread

__NVIC_PRIO_BITS not declared in scope

Hi there!

I've encounter an error that looks like it is stemming from this repository. I was wondering if I could get some assistance.

I am working on a new port for Adafruit SeeSaw for a SAMD51 board. It depends on the State Machine found in this repository.

StackTrace

Building robohatmm1
lib/qp/extras/fw_log.cpp
In file included from ./lib/qp/include/qpcpp.h:65:0,
                 from ./include/bsp.h:34,
                 from lib/qp/extras/fw_log.cpp:32:
lib/qp/ports/arm-cm/qxk/gnu/qf_port.h:101:56: error: '__NVIC_PRIO_BITS' was not declared in this scope
     #define QF_AWARE_ISR_CMSIS_PRI (QF_BASEPRI >> (8 - __NVIC_PRIO_BITS))
                                                        ^
./lib/qp/include/qassert.h:301:36: note: in definition of macro 'Q_ASSERT_COMPILE'
     extern int_t Q_assert_compile[(test_) ? 1 : -1]
                                    ^~~~~
./include/bsp.h:45:50: note: in expansion of macro 'QF_AWARE_ISR_CMSIS_PRI'
 Q_ASSERT_COMPILE(MAX_KERNEL_UNAWARE_CMSIS_PRI <= QF_AWARE_ISR_CMSIS_PRI);

It looks like __NVIC_PRIO_BITS is not defined or included. I know that this comes from sam.h but unsure which file I should be including sam.h for qp to work correctly.

Any advise would be greatly appreciated.

Thanks in Advance.

'struct k_msgq' has no member named 'maxMsg' (Zephyr port QSPY)

I am trying to use QPCPP with Zephyr and want to implement QSPY. When using macro definition Q_SPY in my build I get the following error

qpcpp\ports\zephyr\qf_port.cpp:212:30: error: 'struct k_msgq' has no member named 'maxMsg'; did you mean 'max_msgs'?

I think the correct member variable should be max_msgs

See https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/kernel.h#L4214

In addition I think nofmsg needs to be replaced to used_msgs, see https://github.com/zephyrproject-rtos/zephyr/blob/main/include/zephyr/kernel.h#L4224

Stack top/bottom argument (uc-os-ii port)

I was checking the source code for the uc-os ii port and found a possible bug.

In particular, when you create the task for the active object

#if OS_STK_GROWTH
&static_cast<OS_STK *>(stkSto)[(stkSize/sizeof(OS_STK)) - 1], // ptos
#else
static_cast<OS_STK *>(stkSto), // ptos
#endif

shouldn't there be as well a conditional compilation macro for the bottom part of the stack?

static_cast<OS_STK *>(stkSto), // pbos

Zephyr QSpy port

I have made a preliminary port for qspy for zephyr and tested it with an NRF52832 board could you check it out if it works on your side? On my side it works, some packets are having errors but I suspect its because of it running at 115200 kbps, since I noticed that the transitions happen really fast (I suspect it could be a problem with the random timing generator (?) )

grafik

Attached as a text block is a patch the BSP of the zephyr DPP example, and in addition the Qspy sources must be enabled in the cmakelist.

diff --git a/examples/zephyr/dpp/src/bsp.cpp b/examples/zephyr/dpp/src/bsp.cpp
index 8b8a0b1..5f41518 100644
--- a/examples/zephyr/dpp/src/bsp.cpp
+++ b/examples/zephyr/dpp/src/bsp.cpp
@@ -33,6 +33,8 @@
 #include "bsp.hpp"
 
 #include <drivers/gpio.h>
+#include <drivers/uart.h>
+#include <sys/reboot.h>
 // add other drivers if necessary...
 
 // The devicetree node identifier for the "led0" alias.
@@ -135,7 +137,7 @@ void BSP::displayPhilStat(uint8_t n, char const *stat) {
     else {
         ledOff();
     }
-    printk("Philo[%d]->%s\n", n, stat);
+    //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
@@ -179,25 +181,63 @@ namespace QP {
 // QF callbacks ==============================================================
 void QF::onStartup(void) {
     k_timer_start(&QF_tick_timer, K_MSEC(1), K_MSEC(1));
-    printk("QF::onStartup\n");
+    //printk("QF::onStartup\n");
 }
 //............................................................................
 void QF::onCleanup(void) {
-    printk("QF::onCleanup\n");
+    //printk("QF::onCleanup\n");
 }
 
 // QS callbacks ==============================================================
 #ifdef Q_SPY
+static const struct device *uart_console_dev;
+
 //............................................................................
+constexpr size_t qspy_stack_size = 2048;
+K_THREAD_STACK_DEFINE(qspy_stack, qspy_stack_size); /* stack storage */
+k_thread qspy_thread_handler;
+static void qspy_thread(void *p1, void *p2, void *p3){
+    while(1){
+        uint16_t len;
+        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
+        }
+        unsigned char in_char;
+        const int res = uart_poll_in(uart_console_dev,&in_char);
+        if(res==0)
+        {
+            QS::rxPut(in_char);
+            QS::rxParse();
+        }
+    }
+}
+
 bool QS::onStartup(void const *arg) {
-    static uint8_t qsTxBuf[2*1024]; // buffer for QS transmit channel
+    static uint8_t qsTxBuf[4*2048]; // buffer for QS transmit channel
     static uint8_t qsRxBuf[100];    // buffer for QS receive channel
 
     initBuf  (qsTxBuf, sizeof(qsTxBuf));
     rxInitBuf(qsRxBuf, sizeof(qsRxBuf));
-
-    //TBD...
-
+    uart_console_dev = DEVICE_DT_GET(DT_CHOSEN(zephyr_console));
+    //TODO assert if nullptr
+    k_thread_create(&qspy_thread_handler,
+                    qspy_stack,
+                    qspy_stack_size,
+                    &qspy_thread,
+                    nullptr, // p1
+                    nullptr,    // p2
+                    nullptr,    // p3
+                    14,      // Zephyr priority */
+                    K_ESSENTIAL,        // thread options */
+                    K_NO_WAIT); // start immediately */
+
+    //TODO assert if could not create thread
     return true; // return success
 }
 //............................................................................
@@ -205,17 +245,25 @@ void QS::onCleanup(void) {
 }
 //............................................................................
 QSTimeCtr QS::onGetTime(void) {  // NOTE: invoked with interrupts DISABLED
-    //TBD...
-    return 0U;
+    return k_uptime_get_32();
 }
 //............................................................................
 void QS::onFlush(void) {
-    //TBD...
+    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
+    }
 }
 //............................................................................
 //! callback function to reset the target (to be implemented in the BSP)
 void QS::onReset(void) {
-    //???sys_reboot();
+    sys_reboot(SYS_REBOOT_COLD);
 }
 //............................................................................
 //! callback function to execute a user command (to be implemented in BSP)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.