Previous - Up - Next

8.2   Scripting Using Python

Simics provides support for the script language Python ( By using Python the user can extend Simics, and control it in greater detail. Python can interface with Simics using functions in the Simics API.

8.2.1   Python in Simics

Python is normally hidden from the user by the command line interface (CLI). But since CLI is implemented in Python, it also provides simple access to the Python environment, making it easy to write your own functions and scripts.

Note: All commands in Simics are implemented as Python functions; the source code of the commands is available in the distribution.

To execute some Python code from the command line, the @ character is used to prefix the line. Example:

simics> @print "This is a Python line"
This is a Python line

For code spanning more than one line, the prompt will change to ....... and more code can be inserted until an empty line is entered. The full code block will then be executed. (Note that whitespace is significant in Python.) Example:

simics> @if SIM_number_processors() > 1:
.......     print "Wow, an MP system!"
....... else:
.......     print "Only single pro :-("
Wow, an MP system!

Entering more than one line is useful for defining Python functions. It is also possible to execute Python code from a file, which is done with the run-python-file command.

If the Python code is an expression that should return a value to the CLI, the python command can be used, or the expression can be back-quoted. The following example selects a file with Python commands to execute depending on the number of processors in the system:

simics> run-python-file `"" % SIM_number_processors()`

If the system has 2 processors, the file will be executed.

8.2.2   Accessing CLI Variables from Python
CLI variables can be accessed from Python via the simenv name space, for example:
simics> $cpu = "processor"
simics> @simenv.cpu = simenv.cpu.capitalize()
simics> $cpu
8.2.3   Accessing the Configuration from Python   Configuration Objects

All configuration objects are visible as objects in Python. The global Python module conf holds all such objects. Attribute values can be both read and written using attributes in Python. Example: (print the pci_devices attribute in a pci-bus object that is called pcibus25B in the machine where the example was taken from)

simics> @print conf.pcibus25B.pci_devices
[[2, 0, 'glm0']]

To try this example in an arbitrary configuration, run list-objects pci-bus to find possible pci-bus objects to use instead of pcibus25B.

Any '-' (dash) character in the object name, or in an attribute name, is replaced by '_' (underscore).

Indexed attributes can be accessed using [] indexing in Python. It is also possible to index other list attributes this way, but it is inefficient since the full list is converted to a Python list before the element is extracted. Here are some examples of indexed attributes access (sb0 is a scsi-bus object, and phys_mem0 a memory-space object):

simics> @print conf.sb0.scsi_phases[1]

simics> @print conf.phys_mem0.memory[0x100000:0x10000f]
(157, 227, 191, 80, 3, 0, 0, 0, 130, 16, 96, 0, 131, 40, 112)

simics> @conf.phys_mem0.memory[0x100000:0x100003] = (100,101,102)

Warning: Python only supports 32 bit integers in keys when doing sliced indexing (no long integers). However, the Simics API treats [m:n] synonymous to [ [m, n-1] ], so instead of


(which won't work), write

conf.phys_mem0.memory[[0x1fff80082f0,0x1fff80082f7]]   Creating Configurations in Python

In addition to using .conf files, it is also possible to create and load configurations from Python. The main advantage is that the configuration can be parameterized without the need of multiple .conf files. A part of a configuration in Python, typically written as part of a low-level machine setup, may look like:

scsi_bus = pre_conf_object('sb5', 'scsi-bus')

sd_image = pre_conf_object('sd5_image', 'image')
sd_image.size = 2128486400

sd = pre_conf_object('sd5', 'scsi-disk')
sd.image = sd_image
sd.scsi_bus = scsi_bus
sd.scsi_target = 1
sd.geometry = [4157200, 1, 1]

scsi_bus.targets = [[1, sd, 0]]

This will create one scsi-disk, one image and one scsi-bus pre-configuration object with the names sd5, sd5_image and sb5. The pre-configuration objects are Python objects that are used to build a configuration before it is actually loaded in Simics.

When all relevant pre-configuration objects have been added, they can be loaded into Simics using the SIM_add_configuration() function.

SIM_add_configuration((sd_image, sd, scsi_bus), None)
This example can be run from a Python (.py) file.

Most configurations supplied with Simics are component based and written in Python. However, the way configurations are created differs between targets. Refer to the corresponding Simics Target Guide for more information. Python files that are used to create configurations can be found in the file:
[simics]/<host>/lib/<architecture> and, to some extent, in each target architecture directory.

8.2.4   Accessing Command-Line Commands from Python

At times, it can be useful to access command-line commands from a Python script file. This is done using the run_command(cli_string) function, which takes a string which is then evaluated by the command-line front-end. For example, write run_command("pregs") to execute the pregs command. Any return value from the command is returned to Python.

8.2.5   The Simics API

The Simics API is a set of functions that provide access to Simics functionality from loadable modules (i.e., devices and extensions), and Python scripts. All functions in the Simics API have a name that starts with "SIM_". They are described in details in the Simics Reference Manual.

By using the api-help and api-apropos commands you can get the declarations for API functions and data types. api-help identifier will print the declaration of identifier. api-apropos identifier lists all declarations where identifier appears.

The Simics API functions are available in the sim_core Python module. This module is imported into the Python environment in the frontend when Simics starts; for user-written .py files however, the module must be imported explicitly, i.e.,

    from sim_core import *

Errors in API functions are reported back to the caller using frontend exceptions. The exception is thrown together with a string that describes the problem more in detail. Examples of exceptions are General, Memory, Index, IOError...

For the Python environment, Simics defines an exception subclass for each of its defined exceptions in the sim_core module. These are raised to indicate exceptions inside the API functions. When errors occur in the interface between Python and the underlying C API function, the standard Python exceptions are used; e.g., if the C API function requires an int argument, and the Python function is called with a tuple, a Python TypeError exception is raised.

8.2.6   Haps

A hap is an event or occurrence in Simics with some specific semantic meaning, either related to the target or to the internals of the simulator.

Examples of simulation haps are:

There are also haps which are related to the simulator, e.g., (re)starting the simulation or stopping it and returning to prompt.

Note: In Simics documentation, the word event is used exclusively for events that occur at a specific point in simulated time, and hap for those that happen in response to other specific conditions (like a state change in the simulator or in the simulated machine).

Callback functions from any supported language can be tied to a certain hap. The callback can be invoked for all occurrences of the hap, or for a specified range. This range can be a register number, an address, or an exception number, depending on the hap.

A complete reference of the haps available in Simics can be found in the Simics Reference Manual.   Example of Python Callback on a Hap

This example uses functions from the Simics API to install a callback on the hap that occurs when a control register is written. It is intended to be part of a .simics script, that extends an UltraSPARC machine setup. The SIM_hap_add_callback_index() function sets the index of the control register to listen to, in this case the %pil register in an UltraSPARC processor.

@pil_reg_no = SIM_get_register_number(conf.cpu0, "pil")

# print the new value when %pil is changed
@def ctrl_write_pil(user_arg, cpu, reg, val):
    print "[%s] Write to %%pil: 0x%x" % (, val)

# install the callback
                            ctrl_write_pil, None, pil_reg_no)

In CLI, the same example would look like:

script-branch {
    local $reg = ((current-processor).register-number pil)
    while 1 {
        wait-for-hap Core_Control_Register_Write $reg info
        echo "[" + $info[0] + "] Write to %pil: " + $info[2]

Previous - Up - Next