Interface call-outs allow the user to capture execution control of the generated system running on a target.
Especially in the deeply embedded software development world, it may be necessary to tightly interface the xtUML system to the surrounding/containing system. MC-3020 provides callout routines that enable the user to easily interface code generated by the model compiler with other system code. These callout routines are empty when generated by the model compiler. It is up to the user to define additional functionality (if necessary) to be performed at these callout points.
MC-3020 tries to provide callout functions at as many key points of control as possible. It is the goal of the model compiler to make it easy for the user to interface to the generated code.
The user callout file sys_user_co.c
will initially be generated into the source directory
(/src
).
After being generated once, it is common to copy the file to the
/gen
folder to be edited. (Note that hand
edited/modified .c and .h
files in the /gen
folder get copied on top of
files in the /src
folder at build time.)
The user
should add invocations from this file into appropriate system
specific functionality.
MC-3020 generates hooks into the generated at key points
where these callouts are needed. In early versions of MC-3020,
these hooks represented real C instructions
even if the callouts where not being used actively. In
recent versions, the sys_user_co.h
defines the hooks as macros. Until they are activated,
they take no time or space inline with the generated code.
They are effectively invisible.
The comments in the sys_user_co files explain how to
activate and modify the callouts.
Edit sys_user_co.h
to activate
particular callout functions. Edit
sys_user_co.c
to add code to the defined
callout routines. It is important that both files be edited to
enable the callout capability.
This function is invoked at the immediate beginning of application initialization. It is the very first function to be executed at system startup. User supplied implementation of this function should be restricted to things like memory initialization, early hardware duties, etc.
UserInitializationCallout( | void) ; |
Example 6.1. Bring-up Initialization
void UserInitializationCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked immediately prior to executing any xtUML application initialization function.
UserPreOoaInitializationCallout( | void) ; |
Example 6.2. Pre-xtUML Initialization
void UserPreOoaInitializationCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked immediately after executing any/all xtUML application initialization function(s). When this callout function returns, the system dispatcher will allow the xtUML state models to start consuming events.
UserPostOoaInitializationCallout( | void) ; |
Example 6.3. Post-xtUML Initialization
void UserPostOoaInitializationCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked once during each loop execution of the system dispatcher. (This may be an excellent place to hang an invocation to a timer (TIM) tick routine.)
UserBackgroundProcessingCallout( | void) ; |
Example 6.4. Background Processing
void UserBackgroundProcessingCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked at termination of the system dispatcher, but prior to performing any xtUML application shutdown sequencing.
UserPreShutdownCallout( | void) ; |
Example 6.5. Pre-Shutdown
void UserPreShutdownCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked immediately before application exit.
UserPostShutdownCallout( | void) ; |
Example 6.6. Post-Shutdown Callout
void UserPostShutdownCalloutf() { /* Insert implementation specific code here. */ }
This function is invoked any time that an event is received that results in a ``can't happen'' transition.
UserEventCantHappenCallout( | const Escher_StateNumber_t current_state, |
const Escher_StateNumber_t next_state, | |
const Escher_EventNumber_t event_number) ; |
Where the input parameters are:
current_state | is the number of the state before the transition. |
next_state | is the number of the calculated next state. |
event_number | is the number of the event that is stimulating this transition. |
Example 6.7. Event Can't Happen
void UserEventCantHappenCallout( const Escher_StateNumber_t current_state, const Escher_StateNumber_t next_state, const Escher_EventNumber_t event_number ) { /* Insert implementation specific code here. */ }
This function is invoked any time that an event is received and there is no target instance to receive it. This often means that the instance was deleted while the event was in flight. Usually this indicates a modeling error. The default behavior without supplying a body to this function is simply to consume the event and go on.
UserEventNoInstanceCallout( | const Escher_EventNumber_t event_number) ; |
Where the input parameters are:
current_state | is the number of the state when event is dispatched. |
next_state | is the number of the calculated next state. |
event_number | is the number of the event that is landing on the missing object instance. |
Example 6.8. Event with No Instance
void UserEventNoInstanceCallout( const Escher_EventNumber_t event_number ) { /* Insert implementation specific code here. */ }
This function is invoked when an attempt is made to allocate an event, but there are no more left.
UserEventFreeListEmptyCallout( | void) ; |
Example 6.9. Event Free List Empty Handler
void UserEventFreeListEmptyCalloutf() { /* Insert implementation specific code here. */ }
When marked active, this function is invoked when an attempt is made to use an instance reference variable (handle) that is null (empty).
UserEmptyHandleDetectedCallout( | c_t * object_keyletters, |
c_t * string) ; |
Example 6.10. User Empty Handle Detection
void UserEmptyHandleDetectedCallout( const char * object_keyletters, const char * s ) { /* Insert implementation specific code here. */ }
This function is called from instance creation methods when an attempt is made to create an instance of an object and no allocation units are available.
UserObjectPoolEmptyCallout( | c_t * domain, |
c_t * object_name) ; |
Example 6.11. Object Pool Empty
void UserObjectPoolEmptyCallout( const char * domain, const char * object_name ) { /* Insert implementation specific code here. */ }
MC-3020 uses a collection of set ``containoids'' to link data items together in lists. These utility list nodes are used collecting extents, events and relationships. In the situation that an attempt is made to allocate a node, but none are available, this function will be called.
UserNodeListEmptyCallout( | void) ; |
Example 6.12. Node List Empty
void UserNodeListEmptyCalloutf() { /* Insert implementation specific code here. */ }
MC-3020 uses an array as a queue to manage invocations of interleaved bridges (bridge operations marked safe for interrupt invocation). UserInterleavedBridgeOverflowCallout is invoked when an attempt is made to post too many interleaved bridges. The depth of this list is defined by SYS_MAX_INTERLEAVED_BRIDGES (unless changed in the rule file).
UserInterleavedBridgeOverflowCallout( | void) ; |
Example 6.13. Overflow of Interleaved Bridge
void UserInterleavedBridgeOverflowCalloutf() { /* Insert implementation specific code here. */ }
If the user wishes to gain control of processing when
either of the two event queues are empty (no events to
be processed at the current time), two callouts are provided.
UserSelfEventQueueEmptyCallout
and
UserNonSelfEventQueueEmptyCallout
are
invoked (assuming they are enabled) each time the corresponding
event queue is interrogated and found to be empty.
UserSelfEventQueueEmptyCallout( | void) ; |
UserNonSelfEventQueueEmptyCallout( | void) ; |
Example 6.14. Event Queue Empty Notification
void UserSelfEventQueueEmptyCalloutf() { /* Insert implementation specific code here. */ } void UserNonSelfEventQueueEmptyCalloutf() { /* Insert implementation specific code here. */ }