#43 Active Objects in Real-Time Part-1: Run-to-Completion and RMS/RMA

Поделиться
HTML-код
  • Опубликовано: 7 фев 2022
  • This lesson goes back to event-driven programming and Active Objects in particular. You've been using event-driven Active Objects since lesson #34, but perhaps it's not quite clear to you what advantages they provide in real-time programming. So, this lesson ties together the concepts of event-driven programming, active objects, state machines, and real-time operating system (RTOS).
    Contents with Timestamps:
    -------------------------
    1:04 Real-time behavior of traditional blocking threads
    2:55 Logic analyzer view of the traditional threads
    3:06 Inexpensive logic analyzer hardware, software, and resources
    3:37 Rate Monotonic Scheduling (RMS) of traditional threads
    4:21 Can Active Objects be hard real-time?
    4:34 Converting traditional threads to Active Objects
    7:57 Allocating active object instances and event-queues
    8:27 No need for private stacks in the QXK kernel
    8:25 Basic threads and extended threads in QXK
    9:30 Starting Active Objects (basic threads) under QXK
    10:25 Declaring event signals for Active Objects
    10:52 Declaring global pointers to Active Objects
    11:20 Posting immutable events to Active Objets
    11:46 Running the Active Object code with logic analyzer
    12:00 Comparison between Active Objets and traditional threads
    12:37 Run-to-Completion != Monopolizing the CPU
    13:03 Summary
    -------
    Resources:
    Companion web page for this video course:
    www.state-machine.com/quickstart
    GitHub repository for projects for this video course:
    github.com/QuantumLeaps/moder...
    Transcript of this lesson:
    www.state-machine.com/course/...
    Inexpensive Logic Analyzer:
    HiLetgo USB Logic Analyzer on Amazon.com:
    www.amazon.com/dp/B077LSG5P2/...
    sigrok/PulseView software:
    sigrok.org
    github.com/sigrokproject/puls...
    HiLetGo Digital Logic Analyzer and PulseView video:
    • HiLetGo Digital Logic ...
    SparkFun Tutorial: "Using the USB Logic Analyzer with sigrok PulseView":
    learn.sparkfun.com/tutorials/...
    Music credits:
    The background music comes from:
    www.bensound.com/royalty-free...
  • НаукаНаука

Комментарии • 27

  • @StateMachineCOM
    @StateMachineCOM  Год назад

    NOTE: The source code, as presented in the video, might cause compilation errors with the newer MDK-ARM / uVision toolsets. This is because the underlying compiler in MDK-ARM has been changed to "Compiler-6", while the older "Compiler-5" is considered now obsolete. The updated code that compiles cleanly with "Compiler-6" is available from the companion website at:
    www.state-machine.com/video-course
    and from GitHub:
    github.com/QuantumLeaps/modern-embedded-programming-course

  • @alexeyrealmer1566
    @alexeyrealmer1566 2 года назад +4

    Great!!! New great lesson to get. Thank you!!!!

  • @MahmoudAli-sv7fj
    @MahmoudAli-sv7fj 2 года назад

    Really Great Course

  • @toddmoore112
    @toddmoore112 2 года назад +2

    Thank you

  • @nolimangulabnan6101
    @nolimangulabnan6101 2 года назад +2

    Thanks Miro

  • @marcovimercati393
    @marcovimercati393 2 года назад +2

    Miro it would be nice if you could compare different schedulers normally available in RTOS (e.g RTEMS offers different kind of schedulers), and which one is more suitable to be used with RMS. Thx

  • @jorgis123
    @jorgis123 3 месяца назад +1

    Hello Miro,
    How does the kernel switch from one running one active object to the other, without the code in the uncompleted event giving back control to the kernel? Does the kernel do some scheduling after one event finishes, and then schedules an interrupt to take back control?
    And if it uses an interrupt, how does it work without stack queues?

    • @StateMachineCOM
      @StateMachineCOM  3 месяца назад +2

      I'm not sure if this is the question here, but QXK is a fully *preemptive* kernel (like most RTOS kernels are). This means that the scheduling is also performed at the end of every ISR. If such an ISR happens to fire during event processing in some AO, the kernel gains control during this "uncompleted event". Please also watch an earlier lesson #34 Event-Driven Programming Part-2, Active Object design pattern ( ruclips.net/video/l69ghMpsp6w/видео.htmlsi=sKQSbpE8a1O9-1tT ) . --MMS

    • @jorgis123
      @jorgis123 3 месяца назад +1

      @@StateMachineCOMAh, of course, how could I forget. 😅 This answers the question. Thank you :)

  • @ramadhanafif
    @ramadhanafif Год назад

    Hello mr Samek, thank you for such great lesson as always. A part that I still fail to understand is why you use event queue buffer to be an array of pointer to QEvt instead of array of QEvt value itself. Why did you use a pointer? Wouldn't it risks a race condirion, where the referenced event variable changed its value during event dispatching process?

    • @StateMachineCOM
      @StateMachineCOM  Год назад +1

      This is *the* central question of the next lesson-44 ( ruclips.net/video/e_Mfvat9mAA/видео.html ). In that next lesson, you will see that events can have additional data (event parameters), so events can have very different sizes. It is possible to copy such entire events into event queues and then out of the queues, but that is very expensive in memory and CPU time. Instead, the QP framework copies only pointers to events (which all have the same size). You can see events of different sizes even in this lesson. For example, in the Blinky1 active object receives time-events of the type QTimeEvt, which are bigger than QEvt (QTimeEvt is a subclass of QEvt). --MMS

    • @ramadhanafif
      @ramadhanafif Год назад

      @@StateMachineCOM thank you for the quick answer Mr Samek

  • @technics6215
    @technics6215 2 года назад

    Is it good idea to have big array (up to 2kB) of data in event class? I want to create event class to pass TCP client data in two very short event queues for incoming and outgoing data. I'm using STM32F7 microcontroller with 256kB RAM. I could store that data "outside" and pass pointers, but I want to avoid alloc and free housekeeping in the object that produces such events.

    • @StateMachineCOM
      @StateMachineCOM  2 года назад

      Yes, that's what events are for. The next lesson #44 talks exactly about the mutable events and the ownership rights to them, so please watch. But regarding your big TCP event, you can allocate such event, and fill it up over multiple RTC steps. Please note that you don't need any additional memory buffers. You produce data directly into the allocated event (so the event provides the buffer). Then you simply post of publish the event and the QP framework takes care of the rest, including the recycling of the event when it's no longer in use. As far as memory is concerned, you need to pre-allocate memory for a pool of such big events, and here you need to take into account the worst-case scenario (do you need double buffering or triple buffering?). Generally, the longer you can hang on the events, the more of them you'll need. But in practice, you can arrange priorities such that you don't need more than 2-3 such big events. All of this would fit comfortably in your 256KB of RAM. --MMS

    • @technics6215
      @technics6215 2 года назад

      @@StateMachineCOM "You produce data directly into the allocated event (so the event provides the buffer)." but I'm not using QP framework. Looks like I have to watch lesson 44 again.

    • @StateMachineCOM
      @StateMachineCOM  2 года назад

      @@technics6215 No harm in watching lesson #44. If you are not using QP but perhaps a traditional RTOS, you could use a message queue to send your events/messages. But then you'd have to allocate the messages from a deterministic memory pool (a.k.a. fixed-size heap) and pass pointers to the events. Then, you need to explicitly free the messages to avoid memory leak. --MMS

    • @technics6215
      @technics6215 2 года назад

      ​@@StateMachineCOM I realize that. So finally there is no way to avoid "memory housekeeping" without QP. I will buy it some day, when I understand everything :) Thank you!

  • @MahmoudAli-sv7fj
    @MahmoudAli-sv7fj Год назад

    Great Tutorial.
    I tried to read difference between Basic Thread and Extended Threads But i can't find any documentation on QPC Manual.
    so don't know when we should to use Basic over extended. and other thing why you made stack is null the documentation say the stack should be null in QK only. and how the same stack is used when the active objects preempt each other that's mean the task don't know where was the point she stopped

    • @StateMachineCOM
      @StateMachineCOM  Год назад +1

      This lesson provides just a first glimpse of the "basic threads" without really explaining them. This is because I plan to devote a whole lesson to the subject of preemptive, run-to-completion kernels. For now, you only need to know that "basic threads" are fully preemptive and don't need separate stacks, but cannot block. The "dual-mode" kernel, like the QXK used in this lesson, supports both "basic threads" and the traditional "extended threads". Active objects use the "basic threads" (so no private stacks are needed when starting AOs under the QXK kernel). Alongside AOs, in QXK you can also use traditional blocking threads (which do need private stacks). --MMS

    • @MahmoudAli-sv7fj
      @MahmoudAli-sv7fj Год назад

      ​@@StateMachineCOM okay but if there is only one stack for all AOs how the AO know the last point he was before it was preempted in the case of basic thread mode.

    • @StateMachineCOM
      @StateMachineCOM  Год назад +1

      @@MahmoudAli-sv7fj As I said, I will devote the whole lesson to "basic threads" and preemptive, non-blocking kernels. But the reason why only one stack is sufficient for all "basic threads" is the same why only one stack is sufficient to handle all nested hardware interrupts in the NVIC of the ARM Cortex-M processor (the NVIC is called *Nested* Vectored Interrupt Controller for a reason!). Also, please note that preemption is different than blocking. Preemption is forced and happens only when a higher-priority thread (or interrupt) becomes ready to run. The context of the preempted thread is pushed on the common stack, and is restored only after the high-priority thread returns and naturally removes itself from the stack. Blocking is different because it is initiated by the "extended" thread itself. It turns out that blocking requires an expensive private stack for each thread while preemption does NOT. But active objects (and state machines) exactly do NOT need blocking and can work with the efficient "basic" threads. This is beautiful, because "basic" threads also fully comply with RMA/RMS (as shown in this video lesson), while being much more efficient than "extended" threads. --MMS

    • @MahmoudAli-sv7fj
      @MahmoudAli-sv7fj Год назад

      @@StateMachineCOM Great thank you very much.

    • @MahmoudAli-sv7fj
      @MahmoudAli-sv7fj Год назад

      @@StateMachineCOM but what if we have three active object and they preempting each other the second will overwrite the common stack which will led to lose the stack of first preempted AO
      I Really tried to understand it but in vain.
      i tried to port this app on FreeRtos instead of QXK but i get some error so it would be nice if you make tutorial for that too.

  • @MahmoudAli-sv7fj
    @MahmoudAli-sv7fj Год назад

    I have checked the code and in active start method it check if stack is null or not. that's means it always should be null.
    Q_REQUIRE_ID(200, (!QXK_ISR_CONTEXT_())
    && (0U < prio) && (prio

    • @StateMachineCOM
      @StateMachineCOM  Год назад +1

      The QActive_start() API of the QP/C framework that you're referring to provides a general way of starting active objects under a wide variety of underlying real-time kernels. If you use one of the kernels built into the QP framework (QV, QK, or QXK), you should call QActive_start() function with the stack parameter set to NULL. This is because these kernels don't need to use separate stacks for the AOs. But QP/C framework can also run on top of traditional RTOS kernels (such as FreeRTOS, embOS, ThreadX, uC/OS-II, and Zephyr to name a few). In that case, AOs use the traditional blocking threads of those kernels, and these threads need private stacks. So, when you use one of the traditional RTOSes, you need to provide the stack memory to the QActive_start() calls. --MMS