GithubHelp home page GithubHelp logo

quantumleaps / qpcpp Goto Github PK

View Code? Open in Web Editor NEW
363.0 22.0 80.0 87.5 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++ 87.14% Batchfile 0.38% C 4.82% Makefile 5.34% CMake 0.97% Python 1.34%
active-object actor actor-model arm arm-cortex-m0 arm-cortex-m3 arm-cortex-m4f arm-cortex-m7 embedded-systems embedded-c

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  avatar

qpcpp's Issues

'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

[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?

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

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

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.

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

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?

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?

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

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.

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.

__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.

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?

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)

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.

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.