Events
The event API is used to provides a common interface for pushing timed events on the event providers.
The API is defined in temu-c/Support/Events.h
.
The API provides functions for posting events on CPU objects, and provides the ability to post in three different time bases (cycles, nanoseconds and seconds), and the ability to decide if events are synchronized on a single CPU or the parent machine object.
When posting events to a CPU, nano-second events are converted to cycles. This means that you will actually not have NS accuracy for the events. The accuracy is a function of the clock frequency. I.e. for a 100 MHz CPU, the accuracy is 10ns, while a 50 MHz CPU has an event posting accuracy of 20ns.
When posting machine synchronized events, the delta time must be at least in the next time quanta. If not, the event will be slipped to the start of the next quanta (and a warning will be printed to the log).
In the case synchronized events are used, the machine scheduler will adjust its quanta length to ensure that CPUs do not execute longer than needed. Note that synchronized events are executed after a CPU has returned to the machine object, potentially executing non-synchronized events before the machine event, even if the strictly speaking have a trigger time after.
In addition to the different types of timed events, it is possible to stack post an event, in which case it will be executed after the current instruction finishes (in case of CPU synchronized events), or after the current time quanta finishes in case of machine synchronized events.
Events are prioritized as follows:
-
CPU synchronized stacked events (in LIFO order).
-
CPU synchronized timed events
-
Machine synchronized stacked events
-
Machine synchronized timed events
That means that machine synchronized events will not be executed until all the stacked events and the normal timer events have been executed.
int64_t temu_eventPublishStruct(const char *EvName,
temu_Event *Ev,
void *Obj,
void (*Func)(temu_Event*));
int64_t temu_eventPublish(const char *EvName, void *Obj,
void (*Func)(temu_Event*));
typedef enum {
teSE_Cpu, // Trigger event when CPU reaches timer
teSE_Machine, // Synchronise event on machine
} temu_SyncEvent;
void temu_eventPostCycles(void *Q, int64_t EvID, int64_t Delta,
temu_SyncEvent Sync);
void temu_eventPostNanos(void *Q, int64_t EvID, int64_t Delta,
temu_SyncEvent Sync);
void temu_eventPostSecs(void *Q, int64_t EvID, double Delta,
temu_SyncEvent Sync);
void temu_eventPostStack(void *Q, int64_t EvID,
temu_SyncEvent Sync);
Events from Other Threads
Note that it is in general not possible to post events from an other thread than the one controlling the execution of the emulator.
To solve this issue, the function temu_postCallback()
is available.
This function is thread safe, and posts an event to be executed alongside the rest of the emulator event queue executions.
The event will be called when the emulator thinks it is safe while it is running, which is when the CPU event timer expires (or when the machine quanta expires by other means). Note that a lone CPU will post a null-event to ensure that the event handlers are triggered at regular intervals and not just when model events are executed. For machines, each CPU runs at most a quanta of time, so the check can be done at quanta expiration.
A possible way to use this capability is when you integrate external hardware / models into your simulator. For example, a separate thread can run and wait on a file-descriptor or socket, when data is available, it reads out the data and posts a thread-safe callback function to be called by the main thread at a safe time. The callback can then take the data that was read from the file-descriptor and inject this over a virtual bus model or write it to emulated memory.
Note that in TEMU 2.2, specific APIs for doing file descriptor and timer monitoring has been added.
These are available as the temu_async*()
functions in the temu-c/Support/Events.h
-header.
Notifications
A special type of event is an event that does not have a triggering time. They are instead triggered due to some action or event detected due to other logic inside the emulator. These events are posted using a pub-sub mechanism, with strings as the event key. A model or any other user code can listen for an event which is requested with a specific name. Note that when an event is published, it is assigned an integer ID which is used for fast notifications.
The functions for posting and listening to notifications include:
-
temu_publishNotification()
-
temu_subscribeNotification()
-
temu_unsubscribeNotification()
-
temu_notify()
-
temu_notifyFast()
In order to avoid event namespace collisions, all events issued in TEMU are prefixed with temu.
or temu::
(the latter is the new style).
It is recommended that user published events use their own namespace.
The notification ID 0 is a special notification that is used for indicating "no event", this way it is easy to disable event emission and have the no-event base case be processed cheaply with a single compare and no function call needed.
The temu_notifyFast
-function is an inline function that allows the compiler to get rid of the function call in case the notification id is the null notification.
Current events include (but this is by no means an exhaustive list):
temu.cpuErrorMode
-
CPU entered error mode (halted)
temu.cpuTrapEntry
-
CPU trap taken
temu.cpuTrapExit
-
CPU trap handler returned (for targets supporting this, e.g.
rett
instruction on SPARC) temu.breakpoint
-
Breakpoint hit
temu.watchpointRead
-
Watchpoint read access
temu.watchpointWrite
-
Watchpoint write access
temu.mil1553Send
-
MIL1553 message sent, reported by bus object
temu.mil1553Stat
-
MIL1553 status report, reported by bus object