This chapter describes the Time Synchronization Library, a system for synchronising Simics with external programs and exchange information in a deterministic way. It consists of a library that can be linked into the external application exposing a simple programming interface and a standard configuration object on the Simics sidee.
A synchronised setup consists of a configuration on the Simics side and a follower process. Each follower needs four objects in Simics:
clock
class. The clock approximates the time of the follower for Simics.leader
class). This manages the connection to the follower and keeps it in sync with the clock.The clock, leader and agent all belong to the same cell, which is not used for any other simulation objects.
The follower is the user-provided external simulator. It links to libfollower, which contains a simple API to help it staying in sync with Simics and to communicate with the agent.
All interfaces use the same time type, follower_time_t
, for specifying points in simulated time. All such times are absolute, and local to the follower. A follower_time_t may be converted to and from picoseconds (as integers). It can also be converted to and from seconds (as a floating point value), although this is not without loss. In Python, a follower_time_t
is represented as an integer, counting picoseconds.
The follower's time starts at zero each time it connects to Simics.
The follower must keep track of its own simulation time. It must also listen for messages from Simics by calling handle_simics_message()
, either periodically or when the descriptor used by libfollower (simics_follower_descriptor()
) is readable, using poll()
or select()
.
In handle_simics_message()
, any of the supplied functions may be called zero or more times:
proceed_to()
Gives the follower a point in time (a limit) it is not allowed to go past. Implicitly, it allows simulation to go on until that limit.
report_at()
Tells the follower to report to libfollower (simics_follower_report()
) when a specific point in time has been reached or passed. The follower can report at that time or later, but the earlier the report, the better for simulation performance. The time should be later than any previous reports.
accept_message()
Delivers a message (byte string) from the agent. The message will be delivered at the indicated time. If multiple messages arrive for delivery at the same time, they should be processed in the order of their secondary sorting keys (skey), which is an incrementing integer.
accept_async_message()
Delivers an asynchronous (indeterministic) message from the agent. This message only has a byte string as payload; it is intended for communication that is not directly related to the simulation.
bye()
Tells the follower that Simics has quit.
The param
argument to handle_simics_message()
is passed on unchanged to the callbacks above.
When the follower reaches its current time limit, given by proceed_to()
, it must go no further. Before waiting for new instructions from Simics, it should report its current time.
The leader must be configured before the follower can connect. The leader can be set to use a specific port number, or (the default) it can pick an available port which can be read out when the configuration is complete.
To send a deterministic message to the agent, the follower calls simics_follower_send_message()
with its current time and the message data. This message will be passed to the agent's .accept()
method.
To send an asynchronous (non-deterministic) message to the agent, the follower calls simics_follower_send_async_message()
. This message will be passed to the agent's .accept_async()
method.
The agent must implement the follower_agent
interface. Its .accept()
method is called with a message sent from the follower, and the agent can do whatever it wants with it. When that method is sent, the clock is on the same point in time that the follower was on when it sent the message.
Typically, the agent will be connected to links to other parts of the Simics configuration, and send link messages as instructed by the follower.
Any link endpoint connected to the follower must have its indirect_delivery
attribute set to true. This causes all link messages from that endpoint to be sent immediately, instead of on time. Instead, in the method receiving a link message, the agent should retrieve the delivery time and a secondary sorting key from the endpoint by calling .delivery_time()
and .delivery_skey()
in the link_endpoint
interface.
The leader implements the leader_message
interface, and the agent will call the .send()
method therein to send back information to the follower. It supplies the delivery time and skey it got from the endpoint when the agent received the message from the link.
For asynchronous (non-deterministic) communication with the follower, the agent implements the .accept_async()
method and can send data using the .send_async()
method implemented by the leader.
Asynchronous messages are delivered in order and as soon as possible, with no attempts to synchronise them with any simulated time. They should therefore only be used where this is acceptable, such as in configuration, set-up, interactive control, logging, debugging, and so on.
Contrary to .accept()
, .accept_async()
may be called to deliver a message from the follower even when Simics is standing still.
Any messages, deterministic or non-deterministic, sent with no follower connected are silently dropped.
When a configuration is saved, no information about attached followers is included. The user is responsible for saving and restoring the state of the follower; such information could be managed by the agent, for example.
When a configuration is restored, the leader will attempt to use the same TCP port number, and if it is not available, this will result in an error.
To run the provided code example, first start Simics and run the follower-test-with-link.py
script. It will print the selected port to the console.
Next, start follower-example:
$ bin/follower-example HOSTNAME PORT
where HOSTNAME
is the machine running Simics (probably localhost), and PORT
is the selected port.
$ bin/follower-example localhost 12345
The follower will start running as soon as it connects, but no further than allowed by Simics. Type "c" in Simics to go on. The follower will run as fast as it is allowed and send a "hello" message to its agent once every second (see the source code for details).
The sample script follower-test-with-link.py
works in the same way, but with a link-attached text console to receive the messages from the follower. Typing in this console will send characters back to the follower.