How to use it and how it works
The first and most important feature that must be appreciated is that applications written using the executive do not have a start and thread of execution in the conventional sense. There is no ‘main’ function (although this actually exists as part of the executive itself). Every item of processing that is performing is done in response to an event, with the exception of initialisation which is performed in an application-provided routine, called by the executive on start-up.
Being table driven, an application does not merely consist of executable code, but also data tables of various kinds, most of which are designed to run in ROM. In addition to event-handling functions, each state machine has a state transition table, which defines the operation of a particular state machine, and how it hangs together.
In addition to state transition tables which define the behaviour of state machines, there is a single main set of tables which define the events and state machines, and tie the system together. The system is also tied together by a set of definitions provided by the application header file (ExecApp.h).
Other than event-handling functions, there are a number of other routines that have to be supplied in order for the application to successfully link with the executive. These functions handle initialisation, timers, system idle and debug trace data forwarding.
The processing of an event and state transition can be illustrated as follows:
For each state machine there are a fixed number of states defined. Events are all pre-defined by the application, and each event feeds one (and only one) state machine. For each combination of state and event on each state machine, a state to change to is defined, and also a function to be called as part of the state transition.
No processing is performed without the posting of events, and a sequence of events is normally triggered by interrupt activity: the arrival of data of some sort. Each event is posted at a particular priority, and events are processed on a first-in-first-out basis and in order of priority.
In terms of execution priority, there are a configurable number of priority levels (up to 256). Each priority level has an event queue associated with it. Thus, if there were 8 priority levels, there would be 8 event queues.
If there is an event outstanding on a queue at a higher priority level, then the current event processing routine runs to completion, and after that processing is switched to the higher priority queue. All events are processed on that queue before returning to processing events on the original queue.
The system is potentially capable of supporting up to 255 (optionally 65535) events and 256 (optionally 65536) state machines. Each state machine can be disabled or in one of up to 255 states, and be uniquely connected to up to 256 events each.
All data structures that define a particular system structure (see section 2.5) are user-supplied. In terms of linkage, the names of the structures are referenced within the executive, and structure definitions exist within the executive header file, but the actual data is defined within the code of the application.
A timer system is included as part of the executive. Timers are statically allocated and are single shot (not periodic). A portion of the implementation of the timer sub-system is provided by the application since it is hardware dependent. The system operates optimally when it uses a 16-bit free-running timer in conjunction with a 16-bit compare register, triggering an interrupt upon match. The application example (see section 4.5) shows a typical implementation. There may be up to 256 (optionally 65536) timers in a system.
If an accurate periodic timer is required, then it is recommended to use a separate timer interrupt. Systems that use a sample cycle will often capture data in a periodic interrupt routine. The posting of an event from this interrupt routine is used to signal that data has been captured, and this is used as the basis of the main system sample period and scheduling.
To ensure the integrity of these data structures, it recommended to use manifest constants (#defines) to define sizes of arrays, etc.
Note that servicing of the watchdog is performed between the calling of event functions, and therefore should not be done in the application.
For further clarification as to the structuring of an application, see the application example in section 4.