43 Memory Tracing and Timing 45 Time Synchronization Library
Model Builder User's Guide  /  VII Extending Simics  / 

44 Connecting to the External World

This section discusses how Simics extensions can support communication with real hardware or external tools such as debuggers, test generators, Integrated Development Environments (IDEs) and other simulators.

For example, Simics itself supports communication with the external world in a number of ways:

The following sub-chapters discusses how to make these types of extensions deterministic, portable and use the appropriate APIs for various circumstances. Chapter 46 also discusses how to link external libraries with Simics extensions, or how to link Simics into another application.

44.1 Recording External Input

Being able to connect the simulated world with the real one is useful in many situations, for example to get keyboard input into a simulated console or to be able to access real file servers from a simulated system. At the same time, one of the most important and powerful characteristics of Simics, the determinism, is at risk. A deterministic simulation can be rerun over and over again with the same result. This greatly simplifies debugging of hard-to-catch bugs, since once an incorrect behavior is triggered in Simics, it can always be reproduced by rerunning the simulation.

To keep the determinism even when there are real world connections, Simics has support for recording all incoming asynchronous input. The recording is done in the bridges between the simulation and the real world. The devices in the simulated system are not aware of the recording, and model developers do not have to implement recording support. It is only when creating connections to the real world that recording has to be considered.

44.1.1 Recording and Simulator Interaction

Simics currently only records data received for the simulated system. It does not record user interaction with the simulator itself, for example from a debugger connection. This means that when a session is replayed, for example by using reverse execution, such interaction has to be reproduced as well. But this also enables the user to try alternative futures by manually changing some value and rerun the same session. It is possible that Simics in the future will provide ways to record and replay interactions with the simulator itself as well.

44.1.2 Interfacing the Recorder

The recorder class provides the needed functionality for recording and replaying input. A typically configured system in Simics has one recorder object for each cell, since the recorder itself only can be accessed from the thread that it belongs to. The recorder is used to support reverse execution and session checkpoints, and any extension that interfaces to the outside world should record asynchronous inputs using the recorder.

To implement recording support in a class (the extension) that bridges the real and the simulated worlds, the extension should first have an attribute called recorder that takes an object. The object assigned through this interface will implement the recorder_v2 interface and is typically the standard Simics recorder object. The extension also has to implement the recorded interface, and its input method.

When a machine configuration is created in Simics, the configuration system will automatically create one recorder for each cell, and assign this recorder to all objects with a recorder attribute. If an extension is created as part of a component-based system setup, the recorder will thus be automatically set. If an extension is created outside of the component system, it is necessary to manually assign the recorder attribute.

When the extension class receives asynchronous input, it should always send it through the recorder, no matter if recording is enabled or not. It is actually only the recorder itself that knows if recording or playback is active. The extension does not have to care, and the code flow is identical in all runs. The extension implementation should simply send all received input to the recorder using the record method.

The record method gets the extension object itself as an argument, and will immediately call the extension using the input method in the recorded interface of the extension. The simulation work related to handling the input has to be done in the input method. When input is replayed, the extension will only get a sequence of calls to input, based on the recorded data.

For the few cases when a real network connection has to know if the input it receives is a recording or live data, it can query the recorder using the playback method in the recorder_v2 interface.

44.2 Socket Programming in Simics

When integrating Simics with another tool it is not uncommon that some kind of communication between the two is needed in run time. A common way to communicate between processes is to use sockets. On Linux hosts the Berkeley (BSD) socket API is used and on Windows the similar Winsock interface exists. While creating sockets using the socket() call and reading/writing data on the socket is similar on all systems, it is often difficult to wait for events on several sockets in different independent parts of a program simultaneously without using threads. Simics simplifies this in an operating system independent way by offering a single interface for waiting on sockets events:

void
SIM_notify_on_socket(socket_t sock, notify_mode_t mode, int run_in_thread,
                     void (*callback)(lang_void *data), lang_void *data);

The SIM_notify_on_socket() function tells Simics to wait for some activity on a specified socket sock, previously created using the system API function socket(). Once the socket requires attention, for example that there is data available to read, the user function identified by callback is called by Simics with data as argument.

To be able to wait for events on sockets while at the same time have the simulation executing, Simics keeps a separate polling thread that is only used to wait for external events. It is possible to tell Simics to always run the user-installed callback in this polling thread as soon as the socket event occurs by supplying a non-zero run_in_thread, but in most cases it is better to run the user callback in a simulation thread once it is safe to do so. A callback run in the polling thread may not access the Simics API for example, with the exception of SIM_thread_safe_callback().

There are more details on SIM_notify_on_socket() in the API Reference Manual.

In addition to socket notifications, Simics also provides two host dependent functions that simplify the task of waiting on external input. On Windows hosts it is possible to wait for waitable objects and on Linux systems on file descriptors. See the reference documentation for SIM_notify_on_object() and SIM_notify_on_descriptor().

44.3 Writing Portable Code

Simics device models are typically portable since they only use the Simics device API. However, extensions which communicates with the external world may need to use the operating system more directly. The means extensions need to consider portability across operating systems and host architectures, if this is desired.

Some general guidelines:

44.4 Writing Debugger Connections

Simics can be controlled and inspected from multiple sources, such as the Simics command line, or the GNU Debugger (GDB).

Additional ways to control Simics can be added, and this section explains what needs to be considered to make this work correctly. A good example can be found in the source code for the gdb-remote module, which is included in the Simics Base package. It implements the debugger connection for the GNU Debugger.

It is recommended that a new (debugger) connection is implemented in a new module.

The following chapters describe some of the things you need to take into account if you want to implement support for a new way to control Simics, with focus on supporting a new debugger.

44.4.1 Connect and Disconnect

It is recommended that the new module defines a new global command in its simics_start.py that starts listening to incoming connection requests, connects to an already running external program, or launches an external debugger and then connects to it.

In gdb-remote/simics_start.py, the command new-gdb-remote is defined, which starts listening to new connections from an external GDB session.

If your debugger connection is done using sockets (e.g., if it uses TCP/IP), you should read chapter 44.2.

44.4.2 Starting and Stopping the Simulation

When the user wants to start the simulation, you need to call SIM_continue. This needs to be done from Global Context, which means that you cannot do it in a callback from SIM_notify_on_socket, a hap callback, or similar. Instead, you need to call SIM_register_work, and from its callback function, you can call SIM_continue.

Note that SIM_continue will not return until the simulation stops for any reason. This is not a problem if you call it from SIM_register_work and use the Core_Simulation_Stopped hap to detect when the simulation stops (see below for more information).

Regardless of which API function you are calling, you need to pay careful attention to which execution context they are allowed to be called from.

You typically want to stop simulating as a result of hitting a breakpoint, after a certain amount of time or number of instructions (steps), the user of the external debugging interactively asking to break the simulation, or similar.

To break simulation when executing, reading, or writing some particular address, you should use SIM_breakpoint. Note that you usually want to use the Sim_Breakpoint_Simulation flag for breakpoints set this way. After installing a callback function using SIM_hap_add_callback_index, that callback should call SIM_break_simulation to actually stop the simulation.

Time breakpoints are best implemented using events. Post an event at the appropriate point in time using SIM_event_post_time, SIM_event_post_cycle, or SIM_event_post_step and call SIM_break_simulation from the event handler. For more information, see the two Events sections of the chapters Modeling with C and Modeling with Python, 14.7 and 15.10 respectively.

If you need to get a callback once the simulation really has stopped, install a handler on the Core_Simulation_Stopped hap. Note that Simics may stop simulating for a host of reasons that may not be related to your module. For example, the user may have asked Simics to stop from another user interface, or Simics may have hit an error that prevents it from continuing.

See the documentation for these functions in the API Reference Manual for further details.

44.4.3 Contexts and Process Tracking

To support debugging or inspection of a particular process or task on the simulated machine, your debugger support module will have to make use of OS awareness, which is the collection of techniques used to keep track of information about the simulated operating system.

Process tracking, one of the OS awareness techniques, is what Simics uses to track which processes are currently alive in the simulated machine, and to switch the current active context as CPUs start and stop running code belonging to different processes. See the Analyzer User's Guide for more information about process trackers and OS awareness.

To make your debugger module make use of information from the process trackers, you will need to set breakpoints on the correct context objects and enable or disable your debugger as the system starts or stops running the processes you care about.

For example, to support single-stepping one instruction in a particular process, you need to listen to the different haps triggered by the process trackers. These are called Core_Context_* and are described in the Simics Reference Manual.

44.5 Synchronizing Virtual Time

Connections to systems outside the simulation might have real-time requirements. For example, an external test generator might time-out if the required response has not been received with a certain time period.

Depending on the target system being modelled within Simics, the modelled target might execute much slower or much faster than real-time (wall-clock). For more information regarding Simics simulation performance consult the "Performance" section in the Simics User's Guide.

This section discusses some techniques on how to adapt the external world application to work with Simics concept of virtual time instead of wall-clock time. An alternative, when not only time has to be synchronized but also messages exchanged, is to use the Time Synchronization Library described in its own chapter.

The easiest way to handle virtual time is to run all the applications inside the simulation and not have any external systems. For example, a test generator can run on its own simulated machine within the simulation and connect to the system under test through a virtual network connection. To get the results of the tests out of the simulation you can use a more tolerant real world connection at the end of the test. For example, by using a real network connection or writing the results to a console. This way you avoid the issues involved in adapting the application to work with virtual time.

44.5.1 The Time Server and the Time Client

When interfacing Simics, timing may in some cases pose a problem. For example, test programs may specify a timeout, but since Simics can run both faster and slower than real time, the test may either timeout when it should not, or not timeout when it actually should.

The time server solves this by exporting the virtual time ("Simics time") to the outer world. Clients can communicate with the time server through TCP/IP, using the time client library that provides a C-interface to the time server.

44.5.1.1 Time Client Library

The client library provides non-threaded C primitives for all features of the time server protocol. Using the time client library, interfacing to the time server is as easy as a C function call.

The following primitives exists:

    typedef enum {
            Simtime_No_Error,
            Simtime_Socket_Error,         // errno contains last error
            Simtime_Timeout,              // global timeout (as specified in
                                          // simtime_connect)
            Simtime_Receive_Buffer_Full,  // the received message did not fit
                                          // in the buffer. It's probably a bug
                                          // in the time client library if it
                                          // happens
            Simtime_Parse_Error           // received message could not be
                                          // parsed
    } simtime_error_t;


    simtime_error_t simtime_query_time(simtime_context_t *ctx,
                                       double *time);

    simtime_error_t simtime_sleep(simtime_context_t *ctx,
                                  double seconds,
                                  double *time);

    simtime_error_t simtime_periodic_ping(simtime_context_t *ctx,
                                          double interval,
                                          double how_long,
                                          simtime_callback_t cb,
                                          void *user_data);

simtime_query_time will return the current virtual time. It is not expected to block very long (the time server will respond immediately when it receives the query).

Although tempting, simtime_query_time should not be used in a tight polling loop, as it will degrade Simics performance quite noticeably.

Depending on what the timing code looks like, one of the other primitives should be used instead.

The simtime_sleep function will block for a specified number of virtual seconds. Note that this function will never return if Simics is not simulating, unless a global timeout is specified (see the simtime_connect function). This is obviously because the virtual time is not progressing when the simulation is not running.

If the timeout is not known beforehand, it may not be possible to use simtime_sleep. In this case, simtime_periodic_ping might be the solution. It will cause Simics to send periodic pings to the client. The interval between each ping is in real seconds, i.e. host time, not Simics time. It is also possible to specify a duration, also in real seconds, after which Simics will stop sending ping messages. If duration is negative or zero, Simics will continue to send ping messages until told to stop (non-zero return value from ping message callback function).

For every ping message, the callback function cb will be called:

    typedef int (*simtime_callback_t)(void *data,
                                      simtime_context_t *ctx,
                                      int seq_no, double time);

seq_no contains the sequence number of the received ping message and time contains the current virtual time.

If the callback function returns a value other than zero, the periodic pings will be aborted, even though duration real seconds has not yet passed.

Note that simtime_periodic_ping will not return until duration real seconds has passed, or until the callback function returns a non-zero value. Also note that no simtime API calls should be made from the callback.

Before any of the above primitives can be used, a connection to the time server has to be established:

    simtime_context_t *simtime_connect(const char *host,
                                       int port,
                                       int global_timeout);
    void simtime_disconnect(simtime_context_t *ctx);

The time client will connect to a time server on host at port port. If global_timeout is larger than zero, all calls to simtime will timeout after global_timeout real seconds. This is useful to detect, for example, a crashed Simics session. But note that a call to e.g., simtime_sleep may take very long real time, depending on how fast Simics is simulating (and, of course, how long the sleep time is).

To disconnect from the time server, call the function simtime_disconnect.

If the client is threaded, care must be taken to not make more than one API call at a time. The time client library is not designed to handle simultaneous calls.

43 Memory Tracing and Timing 45 Time Synchronization Library