Menu

How to pause an AO while DMA completes an SPI transfer.

Jim
2024-07-19
2024-07-23
  • Jim

    Jim - 2024-07-19

    Hello
    In my current STM32F407 design I have a Master Hub communicating with 6 Slave Endpoints via SPI. The Hub has 2 AOs. One to handle the upper level of interface with the 6 Endpoints (I’ll call this Hub AO) and another to handle the lower level SPI via DMA and interrupts (I’ll call this Spi AO). The Spi AO is higher priority than the Hub AO and handles SPI transfers consisting of 10 to 180 bytes.

    The Hub AO posts many different events to the Spi AO with all of the information needed for the Spi AO to do its job. The Spi AO posts events back to the Hub AO with the results when the SPI transfer is complete.

    The issue is the framework begins processing the next Spi AO event while the DMA is still running in hardware from the previous event. This clobbers the currently running DMA transfer.

    I am trying to use event deferral but the Hub AO has 6 orthogonal components each sending their own specific events to the Spi AO. Even after combining many different events, I still need over 10 different deferral queues and most all events except for the very first event are deferred. Also recalling the deferred events changes the order that they originally arrived in and starves some of the orthogonal components depending on the deferral queue recall order. I see no efficient way to further combine the many different SPI transfers.

    My question is this. Is there a way to prevent the framework from processing the next event in the event queue for the Spi AO until the DMA transfer is complete? A pause function that only pauses event processing by the Spi AO. I need the Hub AO to continue processing and posting events to the Spi AO while the DMA is taking place. Maybe a flag that an AO can set when doing low level hardware transfers that the framework has no knowledge of. This pause flag could be set when the event processing is started and reset by the SPI complete interrupt.

    I believe that currently event deferral is the only way to handle this situation. If that is the case, I would like to request that this AO pause feature be added to the framework.

    I’m open to any suggestions.
    Thanks in advance.

     
    • Kevin

      Kevin - 2024-07-22

      Here's another design approach, you may need to provide more details on the state machines and/or event structures if there is something I am missing.

      I think the main challenge you are trying to resolve is how to route the response event from the SPI AO to the specific component within the Hub AO?
      One way I've handled this in the past is with a unique component ID parameter that is provided in every event that is published to the SPI AO. Not sure if using the actual HSM address for the component ID would be too risky, but would allow a direct dispatch instead of using another switch/case. There is a single deferral queue in the SPI AO where events are deferred while DMA active, recalled on entry to an idle state when DMA completes. The unique ID is copied into a parameter in the response event that is published from the SPI AO and used in the Hub AO to route the event to the correct component.

       
  • Harry Rostovtsev

    I don't understand how this feature would even work. If you pause the AO, how do you unpause it? You can't send an event to it since it's paused and not handling any events.

    I think your approach with deferral queues is the correct one, assuming you're not posting so many events that your system simply can't keep up with it.

     
  • Jim

    Jim - 2024-07-22

    The SPI AO would pause on entry and un-pause in the transfer complete interrupt. The pause functions would be simple function calls into the framework with no events involved.

     
    • Harry Rostovtsev

      But now you're going to have to create some sort of way to keep state in your ISR to know that you've processed the right number of events or some other flag to know when you can unpause. You're just creating a state machine when you already have one. This seems wrong.

       
  • Panopticon

    Panopticon - 2024-07-22

    Jim, I should confess I should go back and re-read your original post, but can it be accomplished with something like this:

    • whoever knows the SPI AO should be paused (maybe it's the SPI AO itself) does a LIFO post saying "Pause yourself", SPI AO goes to PAUSE state, while in PAUSE state other events are deferred until TX COMPLETE interrrupt, which posts UNPAUSE to SPI AO, then SPI AO resumes.

    I know there are probably some corner cases and concerns about event ordering, but I've often found the framework already gives me the mechanisms to achieve the behaviors I need, just wondering if something similar might work here?

    Just a thought, apologies if you've already worked through this...

     
  • Jim

    Jim - 2024-07-22

    Hi Panoption

    I have been working on something like this. In my original post I had combined the many different events into 10 event types so I only needed 10 different defer queues and this has drastically increased the spaghetti code to make this happen. It has become a huge cluster.

    All that is needed is for the framework to provide a pause function so the the AO can still queue events as they come but not process any of then until the SPI hardware has completed its current DMA transfer and un-pauses. The AO then gets the next event from the queue sets up the hardware and pauses. Rinse and repeat.

     
  • Harry Rostovtsev

    Is the system working correctly with deferral queues? This seems like the correct solution assuming you're not posting so many events that you run out of room.

     
  • Quantum Leaps

    Quantum Leaps - 2024-07-22

    Hi Jim,
    I'm trying to understand what is it that you're requesting, so I'm trying to build a mental model of how this could be implemented. It seems to me that your "pause function" should be equivalent to just deferring all events to a single deferred queue, isn't it? If so, this is how you could implement it without any upheaval to the framework itself (?)

    Also, why do you need two AOs for handling the SPI channels (orthogonal components)? I mean, if the two have to message each other so heaviliy, it means that there is a high coupling between them (so they really want to be one AO). Wouldn't one AO suffice? Wouldn't that simplify the design?

    --MMS

     
  • Jim

    Jim - 2024-07-22

    Hi Miro

    I started with a single AO but found that because the actual hardware DMA transfer with the SPI was not part of the run to completion step the system would almost always start the next transfer before the current transfer was complete thus destroying it. Also, even at 5mhz the SPI is by far the slowest part of the communication process and it was not being fully utilized. So, I put it into a separate AO with higher priority and that seemed to have helped that issue. Now when the current RTC step completes the SPI AO is checked first.

    The issue with having only one deferred queue is determining how to cast the events back to their original type so that their function and parameters are available.
    I’m sure I’m not the first person to make extensive use of both DMA and your framework (which is awesome by the way). Anyone else out there using DMA see a need to pause the AO responsible for the DMA?

    What I am suggesting is a way to pause an AO while DMA is in progress thus making the DMA transfer part of the RTC step without blocking. In this case an SPI AO receives an event, sets up the hardware, freezes the SPI AO dispatch function, starts the transfer, and un-freezes the AO when the DMA complete interrupt occurs. This makes the hardware DMA part of the RTC step without blocking and allows the processor to handle other events while waiting for the results of the SPI transfer.

    Thank you in advance.
    Jim

     
    • Quantum Leaps

      Quantum Leaps - 2024-07-23

      Hi Jim,

      I’m sure I’m not the first person to make extensive use of both DMA

      Abosultely, the I/O has always been much slower than the CPU, so that's normal and expected that most data transfers won't finish in a single non-blocking RTC step (with DMA or any other method). That's why you have a state machine to pick up in the next RTC step where you left off from the previous one. I mean, your DMA will fire an interrupt when it's done, so you can post an event to trigger that next RTC step, right?

      ... the system would almost always start the next transfer before the current transfer was complete thus destroying it.

      Frankly, I don't understand such a design. If a system is in the "busy" state, it should NOT start any transfers. Instead, it should defer the event (or discard). Only when DMA_DONE event arrives, it triggers a transition out of the "busy" state, which can recall a deferred event and start a new transfer. This is precisely how the motivating example for the "Deferred Event" pattern works.

      Now when the current RTC step completes the SPI AO is checked first.

      Again, I'm a lost why tinkering with AO priorities made such a difference. Even if you're using a non-preemptive kernel, priorities should not be a matter of correctenss.

      Anyone else out there using DMA see a need to pause the AO responsible for the DMA?

      No. "Pausing" an AO is a euphemism for blocking. There are many problems with blocking, but one of them is that it always sweeps the whole lot of issues under the rug. Specifically, blocking indiscriminately makes the thread unresponsive to events, including events that might need attention while blocked.

      The issue with having only one deferred queue is determining how to cast the events back to their original type so that their function and parameters are available.

      The information about the event type should be unambiguosuly represented in the event signal. If this is not the case in your design, you should correct this.

      What I am suggesting is a way to pause an AO while DMA is in progress thus making the DMA transfer part of the RTC step without blocking.

      This is impossible because pausing is blocking. But it is also unnecesary. You just need to embrace the event-driven, asynchronous programming mindset...

      I sincerly hope that my comments make sense to you.

      --MMS

       

      Last edit: Quantum Leaps 2024-07-23
  • Jim

    Jim - 2024-07-23

    Thanks Miro
    I do not believe that I have done a good job explaining what I’m doing. I also believe that I need more state machine education. I will spend some more time reviewing my code, rereading your app notes and watching your training videos. I hope to return to this in the future with some concrete logic analyzer traces that will help me describe what I’m doing. In any case thanks to everyone who had input. It’s appreciated.
    Jim

     

Log in to post a comment.