Event Driven framework

An introduction to the EventFramework

Import libraryEventFramework

EventFramework library allows the creation of an event-driven infrastructure in which small "threads" can handle events in a multithreaded execution context. The EventFramework can be configured to act as a cooperative or a fully-preemptive kernel with fixed-priority scheduling. Furthermore, this kernel matches run-to-completion semantics, and hence a single-stack configuration is enough to keep running this multithreaded execution environment. As running threads shares global stack, a huge quantity of RAM is saved in contrast with traditional RTOSes.

The EventFramework library has been designed having in mind those developers who don’t really need a full traditional RTOS but still need a bit of underlying management to implement a lightweight multithreaded application. The most important feature against other traditional RTOSes is the way in which memory is optimized. While traditional RTOSes need to keep a stack region per task, in this case, the EventFramework uses processor’s global stack to save threads’ context when a higher priority thread is ready for execution.

/media/uploads/raulMrello/rtos_vs_rtc_kernel.png

In a common application, suppose a GPS-enabled device with a file system in an external SD memory and a communication infrastructure like TCP/IP or similar; can count with around 8-12 tasks. In a traditional RTOS, this is translated in a memory allocation around 10KB (1KB per task) for RTOS context saving. In a 20Kbytes RAM microcontroller this implies more than 50% of RAM memory, limiting the user’s application to other 50% (around 10Kbytes) for variables, buffers, memory pools, etc… Nevertheless, using a single-stack-based EventFramework like this, we don’t need individual stacks for each thread. You can adjust processor’s global stack to meet its worst case and use the rest of memory for user’s variables, saving a huge quantity of RAM. Using a single stack for all threads, implies that high priority threads will block the execution of low priority ones if they do not finish their execution. For this reason, threads must be executed in an event-driven manner. By default, threads are in a “Sleep” state waiting for some kind of event. Once the event is raised, the EventFramework scheduler activates that thread to process the event. Once processed, the thread is deactivated to its default “Sleep” state again, allowing the execution of other pending low priority threads.

/media/uploads/raulMrello/rtc_thread_states.png

EventFramework Priority scheme In order to carry out multithreading, this EventFramework has a priority scheme inherent. In contrast with traditional RTOSes in which tasks use to have fixed priorities, in this case a dual priority scheme has been implemented:

  • Event priority: each event has a priority in the range 0 - 65535 where 0 is the highest and 65535 the lowest.
  • Thread priority: each thread (also known as EventHandler) also has a priority in the same range. On the other hand, several threads can be registered as listeners of the same event, and hence a scheduling policy must be defined. In a first stage, during the scheduling mechanism, the EventFramework scheduler will evaluate events priorities. Highest priority event will be dispatched first. Once selected the highest priority event, its internal listener list is evaluated and the highest priority thread is invoked to handle the event.

/media/uploads/raulMrello/dispatching_order.png

EventFramework Scheduling mechanism The EventFramework will schedule pending events in a similar way than traditional RTOSes:

  • Cooperative scheduling: An active low priority event will be handled by all its listeners and then a high priority pending event will be handled. There is no preemption.

/media/uploads/raulMrello/cooperative.png

  • Fully-preemptive scheduling: An active low priority event handling process will be blocked by a high priority event arrival. Once finished the handling process of the high priority event (by all its installed listeners), the low priority event will be resumed to finish its handling.

/media/uploads/raulMrello/preemptive.png

Events Events are the inter-process communication mechanism of the EventFramework. It will be used to signal some software/hardware situation and also they can attach data to be processed accordingly with their event type. They are formed by several properties:

  • Priority
  • Attached data, to be processed by its listeners (handlers)
  • An EventHandler list (list of listeners that must be invoked to handle the event)
  • Base reference (NULL for registered events or a registered event reference according with its priority). A similar concept than inheritance in OOP, but not the same.

/media/uploads/raulMrello/event.png

The EventFramework manages two event queues:

  • Registered event queue: is a queue formed by all the events that the EventFramework can manage. Each event is added to this queue according with its priority, through the interface EventFramework::AddEvent(). The highest priority event will be the first event of the queue and the lowest priority event will be the last one.
  • Published event queue: when a software entity raises an event, it must “Publish” it to the framework (through EventFramework::PublishEvent), and after scheduling, its listeners will be invoked to handle it. All published events are queued according with their priority. Highest first and lowest last. Each published event must match with one of the registered events. This matching mechanism is managed by a private event property named “base”. So a published event will always have a registered event base reference accordingly with its priority. Next example shows a new event published based on Event2 type.

/media/uploads/raulMrello/pending_event_list.png

Interrupt Service Issues When fully-preemptive scheduling is selected for multithreading, there is an important issue to have in mind: interrupt nesting. Most traditional RTOSes don’t allow post messages from ISR context or inherits some mechanism to allow it without user’s intervention. In this case, when an event is published from ISR context it is necessary to control the interrupt nesting level. For such purpose, a couple of interfaces have been added:

  • EventFramework::SaveContext, this must be the first instruction of the ISR routine. It increments the interrupt nesting level of the framework. Scheduling is forbidden if there is a nesting level higher than 0.
  • EventFramework::RestoreContext, this must be the last instruction of the ISR routine. It decrements the interrupt nesting level of the framework. In this case, exiting from ISR context could imply the execution of the scheduler if a new event has been published while ISR context has been executed.

/media/uploads/raulMrello/isr.png

Next example shows how preemption is applied by the scheduler. A low priority event (ev2) is being handled by one of its installed listeners. In the middle of the process, ISR1 is raised. While ISR1 is serviced, a higher priority interrupt ISR2 is raised and serviced. Again a higher priority interrupt ISR3 is raised and serviced. Along ISR3 service, (ev1) event is published but thread preemption cannot be executed with a nesting level higher than 0. So ISR3 is exited, then ISR2 is completed and then ISR1. Once ISR1 finishes, nesting level comes back to 0 and the scheduler is invoked, causing thread preemption. Now event (ev1) is handled by all of its installed listeners. Once handled, (ev2) handler operation is resumed.

/media/uploads/raulMrello/preemption.png

Tips and tricks Using this framework is quite simple. You only have in mind next tips:

  • Assign priorities as you prefer. But be careful you will obtain different results depending on the selection you do.
  • Keep event handling routines as short as possible to keep the system highly reactive. Have in mind that only one event can be processed at a time.
  • Fully-preemptive scheduling consumes much more stack than cooperative. It depends on the number of events and eventhandlers. Have in mind that low level handlers can be stacked to service high priority ones. Nevertheless, preemptive scheduling ensures a high reactive response in contrast with cooperative.
  • You can use your preferred middleware above the EventFramework. For example a State Machine processor to develop your application as UML compliant state charts, or other inter-process mechanism as mutexes, semaphores, timers and more. Future improvements This framework is the first floor of a great building. I’ll work hard to support it and add plenty of tools: mutexes, semaphores, timers, UML compliant state processor and more.

Example A GPS-enabled device must be designed with these requirements:

  • The device must receive RMB NMEA frames from a GPS modem with UART interface.
  • NMEA sentences must be decoded and printed in a LCD display (i2c interface).
  • User can setup several configuration issues through a keyboard or remotely with a mobile APP via Bluetooth. Configuration changes must be displayed accordingly.
  • GPS data and user configuration must be stored in a FAT filesystem mounted on a SD flash (spi interface).
  • Also device can be connected to a host PC as a USB mass storage device and filesystem could be accessed via USB (usb device interface).
  • The device is cost critical, it must be a high volume mass production device. Selected processor is Cortex-M3 core with 24Kbytes RAM and 128Kbytes Flash.
  • Underlying RTOS is not an option here: few RAM resources, task stacks limits communication i/o buffers for USB and Bluetooth.

/media/uploads/raulMrello/gps_device.png

According with these requirements, in a first shot we can think in a system divided into several subsystems, forming a multithreading application. Each subsystem will process events related with the device or software they manage. So, we can use the EventFramework and divide such system into eight EventHandler threads as shown below:

/media/uploads/raulMrello/gps_system.png

Further readings Event-Driven systems is a wonderful world. If you like it you can start from here. I’m sure you won’t get bored. http://www.embedded.com/design/prototyping-and-development/4207786/Low-Cost-Cooperative-Multitasking-Part-1-Building-a-simple-FM-player- http://www.embedded.com/design/prototyping-and-development/4026990/Using-finite-state-machines-to-design-software-item-1 http://www.embedded.com/design/prototyping-and-development/4008247/A-crash-course-in-UML-state-machines-Part-1 http://www.state-machine.com/ http://www.eetimes.com/design/automotive-design/4006764/Embedded-multitasking-with-small-MCUs-Part-1--State-Machine-Constructs?cid=NL_Embedded&Ecosystem=embedded http://www.embedded.com/design/prototyping-and-development/4210574/Tracing-of-the-event-flow-in-state-based-designs


All wikipages