Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

eRPC on stm32f7 #638

Closed
kikass13 opened this issue Jun 2, 2021 · 28 comments
Closed

eRPC on stm32f7 #638

kikass13 opened this issue Jun 2, 2021 · 28 comments

Comments

@kikass13
Copy link

kikass13 commented Jun 2, 2021

Hi,

i wanted to let you know that eRPC is working with modm. I have changed erpc slightly and created an example which utilizes local FIFOs (could be any other protocol) and freertos to do RPC's.

My plan for the future is to implement a transport layer for CAN, UART and Ethernet, all of them utilizing the driver stuff modm already provides.

First question:

Is this of some use to anyone?

@salkinium
Copy link
Member

Yes, likely!

I'm looking to replace our rather underdocumented and unmaintained XPCC protocol with something more standardized. eRPC looks very useful for that purpose, especially with a modm transport layer port!

@kikass13
Copy link
Author

kikass13 commented Jun 3, 2021

Well I can't really say that erpc is superior, but it's lightweight enough and there are a lot of people doing stuff with it currently.

especially with a modm transport layer port

The problem is, that as far as I can see, ERPC expects drivers for the transport layers to be included in the transport as well. I rather hate the idea of writing transport layers for:

  • Modm UART
  • Modm TCP (with included freertos stack)
  • modm can
  • etc.

My next step would be to implement a generic UserChannelTransport, which utilizes callbacks from the user code to allow whatever driver behavior or action with the incoming or outgoing data.

Because we are planning to use ERPC and modm heavily in the future, I'll do some further development and will post possible use-cases/results here :)

@salkinium
Copy link
Member

Yeah, modm is a little too low level in that regard, we have very different interfaces for each peripheral and even separate types per instance. I typically create a higher-level virtual interface that is then implemented via template class wrapper, but it's not the best solution. See modm::amnb::DeviceWrapper as an example.

@kikass13
Copy link
Author

kikass13 commented Jun 3, 2021

well my idea was to just do something like this:

class erpc::UserChannel{
  // erpc transport api
  isConnected(){}
  readCallback(uint8_t* data, uint32_t size){
     // implement whatever protocol you want
     // and do whatever to read some data and fill the data* object
     // could be CAN, UART, etc
  }
  write()
     // implement whatever protocol you want
     // and do whatever to write some data
     // could be CAN, UART, etc
};

int main()
{
  erpc::UserChannel A();
  // init erpc and stuff ...
  transport = erpc::UserChannelTransport_init(&A)
  // do stuff to further initialize erpc things
}

so it is probably similar enough to your suggestion (correct me if im wrong). Would you rather implement every protocol as a device wrapper and push that into erpc then?

What is modm::Resumable<2> exactly?

@salkinium
Copy link
Member

so it is probably similar enough to your suggestion (correct me if im wrong).

Yes, of course. I just wanted to give you context how we solved it in the past.

Would you rather implement every protocol as a device wrapper and push that into erpc then?

Ideally the eRPC repo would not be modified and instead you would store a modified copy in modm and copy that instead via lbuild. But I'm not sure exactly how much changes are required, perhaps a real fork of eRPC is required instead of a few patches.

What is modm::Resumable<2> exactly?

The base class contains state continuations for running 2 coroutine functions in parallel without nesting. See https://modm.io/reference/module/modm-processing-resumable

The alternative is modm::NestedResumable<Levels> where you can run only one coroutine function at a time, but this function can then call other coroutines of the same class for Levels number of levels. This is useful for device drivers, were bus access (I2C, SPI etc) is mutually exclusive per method, but you still want to call common functions like modifyRegister(...).

@kikass13
Copy link
Author

kikass13 commented Jun 7, 2021

Ideally the eRPC repo would not be modified and instead you would store a modified copy in modm and copy that instead via lbuild. But I'm not sure exactly how much changes are required, perhaps a real fork of eRPC is required instead of a few patches.

That depends on what we would want to do. There are no changes necessary except for the "modm transport layer" but even that could be added to the original erpc via lbuild (and these changes are either part of modm or come from wherever).

I'm gonna play around and see where this leads me :)

@kikass13
Copy link
Author

kikass13 commented Jun 7, 2021

@salkinium is there a working example of https://modm.io/reference/module/modm-processing-protothread/ ?
That one does not seem to work (it does nothing because the timer is not progressing.

And the UART example you gave does not contain a means to instantiate and "use" that thing either (at least i could not find one). So I have no idea what do do with it :)

@rleh
Copy link
Member

rleh commented Jun 7, 2021

is there a working example of https://modm.io/reference/module/modm-processing-protothread/ ?

https://github.com/modm-io/modm/blob/develop/examples/stm32f4_discovery/protothreads/

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

@rleh
that example would'nt work either, because the timer does not expire at all.

			PT_WAIT_UNTIL(timeout.isExpired());

blocks indefinitely

@rleh
Copy link
Member

rleh commented Jun 8, 2021

Are you running the unmodified example on a STM32F4 Discovery board?

@rleh
Copy link
Member

rleh commented Jun 8, 2021

@salkinium is there a working example of https://modm.io/reference/module/modm-processing-protothread/ ?

I just tested the code on an STM32F407 Discovery Board: Works perfectly!
Edit: Also works on Nucleo-F767ZI (9554d61)

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

My LED example still blocks

Edited: fixed errors

#pragma once

#include <modm/board.hpp>
#include <modm/processing/protothread.hpp>

using Led1 = Board::LedGreen;
using Led2 = Board::LedBlue;
class BlinkingLight : public modm::pt::Protothread
{
public:
    bool
    run()
    {
        PT_BEGIN();
        Led1::setOutput();
        Led2::setOutput();
        while (true){
            timeout.restart(100ms);
            Led1::toggle();
            Led2::toggle();
            PT_WAIT_UNTIL(timeout.isExpired());
            MODM_LOG_ERROR<< "NEVER REACH" << modm::endl;
        }
        PT_END();
    }

private:
    modm::ShortTimeout timeout;
};
BlinkingLight light;

int main()
{
   Board::initialize();

    Board::LedRed::setOutput();
    while(true){
        light.run();
        Board::LedRed::toggle();
        modm::delay(200ms);
    }
   return 0;
}
  • my red led is blinking (from main loop)
  • my green/blue leds are on all the time, because the second toggle-pair is not reached

@rleh
Copy link
Member

rleh commented Jun 8, 2021

Please provide more information for others to be able to reproduce the error, at least the target (dev board or MCU) your build setup and the project.xml config file.
Ideally create an example something similar so that other people can do git checkout and lbuild build && scons.


I can't reproduce you example, even after adding the necessary includes

#include <modm/board.hpp>
#include <modm/processing/timer.hpp>
#include <modm/processing/protothread.hpp>

because some code is missing:

main.cpp: In member function 'bool BlinkingLight::run()':
main.cpp:27:3: error: 'Led1' has not been declared
   27 |   Led1::setOutput();
      |   ^~~~

@rleh
Copy link
Member

rleh commented Jun 8, 2021

I created an example and tested it successfully on a Nucleo-F767ZI: 9554d61
Please test it with your build setup and hardware and report back if it works.

@rleh
Copy link
Member

rleh commented Jun 8, 2021

After removing the Led2 occurences and renaming Led1 to Led your example works perfectly fine, and NEVER REACH is reached every 200ms.
image

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

I found the problem:
is still have my erpc module in my projext.xml ... as soon as i remove that, the code works. I assume erpc uses a define which is also used by "something inside modm"?

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

I cant really find anything, any ideas on what could disrupt modm timers?

@rleh
Copy link
Member

rleh commented Jun 8, 2021

Probably related to the SysTick timer (hardware) and modm|s system clock. Have you checked if the system clock still works?

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

from https://docs.modm.io/develop/api/stm32l486zgt7/group__modm__architecture__clock.html

while(true)
{
    const auto start = modm::PreciseClock::now();
    modm::delay(100ms);
    const auto duration = modm::PreciseClock::now() - start;
    MODM_LOG_DEBUG << "Function took " << duration << modm::endl;
}

output:

Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us
Function took 0us

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

compared to

Function took 100023us

when i do not add

  <modules>
    <module>modm:erpc</module>
  </modules>

@salkinium
Copy link
Member

Does eRPC touch any SysTick registers? Perhaps it defines the SysTick_Handler IRQ itself?
For reference, this is what modm does to init SysTick.

@rleh
Copy link
Member

rleh commented Jun 8, 2021

Does eRPC touch any SysTick registers?

It doesn't look like: https://github.com/kikass13/erpc/search?q=SysTick


But maybe FreeRTOS (dependency of the erpc module) causes the trouble...?

@rleh
Copy link
Member

rleh commented Jun 8, 2021

But maybe FreeRTOS (dependency of the erpc module) causes the trouble...?

I can confirm: Adding the module modm:processing:rtos to my example 9554d61 prevents the LED from blinking.

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

yes, i had freertos as a module dependency in my erpc module.lb which probably caused these problems (in the sense that i included freertos accidentally as well)

@kikass13
Copy link
Author

kikass13 commented Jun 8, 2021

after removing the (useless) freertos dependency (i used freeRtos in my erpc example before and that worked with server + client) in from protothread example, the erpc server + led pthreads can run succesfully

@kikass13
Copy link
Author

kikass13 commented Jun 9, 2021

@salkinium @rleh is the use of protothreads even possible without writing intrusive code into erpc ?
As far as i can tell, the entry functions into erpc's api would have to be Resumable Functions as well ... which is probably a bad idea (one would have to rewrite erpc to be mostly stackless and also write a seperate resumable api for it).

aka:

is it possible to have a protothread and do a (wait for/yielding) loop somwhere internally without stealing the program control?
Currently my call hierarchy looks like this:

MyProtoThread1_Server
   - while(1)
        - erpc_poll()
             - erpc stuff()
             -  ....
                   - MyErpcTransportLayer 
                            - RF_CALL_BLOCKING() respective read/write() in MyDeviceWrapper
                                  - read: WAIT_UNTIL_DATA_IS_READY_OR_TIMEOUT [RF_WAIT_UNTIL]
                                  - write WAIT_UNTIL_DATA_IS_SENT_OR_TIMEOUT [RF_WAIT_UNTIL]
MyProtoThread2_Client
   - while(1)
        - RPC_FUNCTION_CALL()
             - erpc stuff()
             -  ....
                   - MyErpcTransportLayer 
                            - RF_CALL_BLOCKING() respective read/write() in MyDeviceWrapper
                                  - write WAIT_UNTIL_DATA_IS_SENT_OR_TIMEOUT [RF_WAIT_UNTIL]
                                  - read: WAIT_UNTIL_DATA_IS_READY_OR_TIMEOUT [RF_WAIT_UNTIL]

obviously that does not work, what happens is:

  • The Client calls write()
  • The Client calls read() and blocks
  • The server cannot call read because the client is busy waiting ...
  • The client runs into receive timeout
  • The server can now call read()
  • The server reads the request
  • The server handles the requests and does write() response
  • The client existed with timeout error already, therefore the response is not received properly

@salkinium
Copy link
Member

is the use of protothreads even possible without writing intrusive code into erpc ?

No. Resumable Functions and the C++20 coroutines are language solutions and therefore require all caller function signature to "upgrade" to the async syntax. You've stumbled across the main problem with this approach: Calling a async function from inside a sync function. The only solution is to convert your code to explicit polling, ie. periodically polling a sync function like device->read() until it returns true and then continue. It seems like erpc_poll() is there for that purpose?

We want to introduce stackful fibers into modm as an alternative, see #439. I need to find the time to finish it though…

kikass13 added a commit to DroidDrive/erpc_modm_example that referenced this issue Jun 10, 2021
… all the code and cleaned it up a lot; Changed erpc transport layer to be the new ModmDeviceTransport, which is far superior in all ways modm-io/modm#638
kikass13 added a commit to kikass13/modm that referenced this issue Jun 10, 2021
…design; we use freertos threads for now because they make life easy; added erpc transport layer ModmDeviceTransport, which is far superior in all ways modm-io#638 because it is generic
@kikass13
Copy link
Author

kikass13 commented Jun 10, 2021

Update

The stm32F7 example i am using (using freerots tasks) can be seen here (it was set to public by my orga):

The modm fork containing the erpc module is here:

Other Stuff

There is also a proper FreeRtos Thread wrapper class construct in the example (if someone is interested) :)

  • it needs a FreeRTOSConfigLocal.h (required by modm)
    • containing #define configUSE_TRACE_FACILITY 1
#include <modm/processing/rtos.hpp>

class TaskBase {
public:
    TaskHandle_t handle_;
    ~TaskBase() {
        vTaskDelete(handle_);
    return;
  }
};
class Task : public TaskBase {
public:
    Task(char const*name, unsigned portBASE_TYPE priority, unsigned portSHORT stackDepth=configMINIMAL_STACK_SIZE) {
        xTaskCreate(&taskfun, (const char*)name, stackDepth, this, priority, &handle_);
    }
    virtual void task() = 0;
    static void taskfun(void* param) {
        static_cast<Task *>(param)->task();
        vTaskDelete(static_cast<Task *>(param)->handle_);
    }
};

class ModmTask : public Task {
public:
    ModmTask(char const*name, unsigned portBASE_TYPE priority, unsigned portSHORT stackDepth=configMINIMAL_STACK_SIZE) 
    : Task(name, priority, stackDepth){
        MODM_LOG_INFO << "[System *] Task '" << name << "' created" << modm::endl;
        updateStatus();
    }
    ~ModmTask(){
        MODM_LOG_INFO << "[System #] Task '" << status_.pcTaskName << "' removed" << modm::endl;
    }
    static void taskfun(void* param) {
    static_cast<Task *>(param)->task();
        vTaskDelete(static_cast<Task *>(param)->handle_);
    }
public:
    void sleep(TickType_t ticks){
        vTaskDelay(ticks);
    }
    void updateStatus()
    {
        /* Use the handle to obtain further information about the task. */
        vTaskGetInfo( 
            /* The handle of the task being queried. */
            this->handle_,
            /* The TaskStatus_t structure to complete with information on xTask. */
            &this->status_,
            /* Include the stack high water mark value in the
            TaskStatus_t structure. */
            pdTRUE,
            /* Include the task state in the TaskStatus_t structure. */
            eInvalid 
        );
    }
public:
    TaskStatus_t status_;
private:
};

and heres an example Task (blink led) using it

#include <modm/board.hpp>
#include <utils/Task.hpp>

template <class Gpio, int SleepTime>
class LedTask : public ModmTask{
public:
    LedTask() : ModmTask("LedTask", 6)
    {}
    void task()
    {
        Gpio::setOutput();
        while (true)
        {
            sleep(SleepTime * MILLISECONDS);
            Gpio::toggle();
            {
                static modm::rtos::Mutex lm;
                modm::rtos::MutexGuard m(lm);
            }
        }
    }
};

@modm-io modm-io locked and limited conversation to collaborators Sep 29, 2021

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Development

No branches or pull requests

3 participants