2.3 Simics Scripting Environment 2.5 Moving Data in and out of the Simulation
Simics User's Guide  /  2 Feature Overview  / 

2.4 Configuration and Checkpointing

Simics includes a configuration system used to describe the state of the simulated machines.

Simics's configuration system is object-oriented: a simulated machine is represented as a set of objects that interact when the simulated time advances. Typically, processors, memories and devices are modeled as objects. Each object is defined by a number of properties that are called attributes. A processor object, for example, will have an attribute called freq_mhz to define its clock frequency (in MHz).

Simics's configuration system is flexible: it is possible to create and delete objects dynamically, as well as access the attributes of all objects for reading and writing at any time.

Simics's configuration system allows its elements to be saved so that a complete simulated machine state can be written to a set of files. This set of files is called a checkpoint.

This chapter describes the Simics configuration system as well as the different layers built on top of it to handle more specific tasks:

2.4.1 Basics

As mentioned above, Simics's configuration system is object-oriented. A Simics object is instantiated from a Simics class. The core of Simics defines some useful classes, but most of the classes (processors, device models, statistic gathering extensions) are provided by modules that are loaded by the simulator when requested.

For example, the x86-p4 module defines, not surprisingly, the x86-p4 class. Note that a module may define several classes. Since modules advertise the classes they define, Simics can load modules transparently as objects are instantiated.

A class defines attributes that represent both the static and dynamic state of the instantiated objects. The static state includes information that does not change during the simulation (like a version number in a register) while the dynamic state covers the part of the device that are affected by the simulation (registers, internal state, buffers, etc.).

Let us take the example of an x86-p4 processor and have a closer look at how it can be configured using attributes:

As you noticed, attributes may be of various types. A complete description is available in the next section.

2.4.2 Checkpointing

Simics's configuration system can save the complete state of a simulation in a portable way. This functionality is known as checkpointing, and the set of files that represent the elements of the systems are called a checkpoint.

Saving and restoring a checkpoint can be done from the command line with the write-configuration and read-configuration commands.

A checkpoint consists of the following files, collected under a directory:

Below is a portion of a checkpoint file showing an object. Saved objects are always represented as OBJECT object-name TYPE class-name { attributes }. In this case we have an instance of the DEC21143 class (fast Ethernet LAN controller interfacing the PCI bus) named dec0. The attribute pci_bus is used to connect the device to the PCI bus.

OBJECT dec0 TYPE DEC21143 {
        queue: cpu0
        component: eth_adapter_cmp0
        component_slot: "dec"
        object_id: "dec0"
        build_id: 0xbb9
        pci_bus: pci_bus0
        ...
}
OBJECT ... TYPE ... {
...

Objects are saved in the main checkpoint file in no specific order.

2.4.2.1 Compatibility

Simics maintains checkpoint compatibility with older versions, i.e. it is always possible to continue using checkpoints created in a previous version of Simics when upgrading to a new version. Compatibility is always maintained for one major version older than the oldest of the supported API versions. For checkpoints older than that, load the checkpoint with a newer version of Simics and create a new checkpoint.

The opposite is not true. Trying to load a checkpoint created in a newer version of Simics than the local version will typically not work. The same restriction may apply even between minor Simics releases. For example, a checkpoint created with Simics 3.2.2 is not guaranteed to load correctly in the older Simics 3.2.1 release.

2.4.2.2 Attributes

The short example of the dec0 description only uses a few types of attribute values: strings, objects, and hexadecimal integers. The possible attribute types are:

Each attribute belongs to one of the following categories. Note that only attributes of the first two categories are saved in checkpoints.

2.4.2.3 Images

Simics implements a special class called image for objects that potentially need to save a huge amount of state, like memories and disks. An image represents a big amount of raw data using pages and compression to minimize disk usage.

To save space and time, images do not necessarily save their entire state every time a checkpoint is written. They can work in several ways:

It is important to understand that when used in incremental mode, images create dependencies between checkpoints. A checkpoint can only be loaded if all previous checkpoints are intact.

As an example, let us have a look at an assumed disk image:

...
}
OBJECT disk0_image TYPE image {
        ...
	files: (("tango1-fedora5.craff", "ro", 0, 0x4c5abc000, 0),
                ("disk0_image.craff", "ro", 0, 0x4c5abc000, 0))
        ...
}
...

The checkpointed image is based on the file tango1-fedora5.craff, on top of which is added the file disk0_image.craff that contains the difference between the checkpoint and the initial state.

Files like disk0_image.craff are often called diff files because they contain the difference between the new state and the previous state.

2.4.2.3.1 Image Search Path

This section contains more in-depth explanations about image handling that you may skip when reading this guide for the first time.

When successive incremental checkpoints are saved, an image object may become dependent on several diff files present in different directories. To keep track of all files, Simics stores in the checkpoint a checkpoint path list that contains the absolute directory paths where image files may be found. Image file names are then saved as %n%\filename where %n% represents the number of the entry in the checkpoint path, counting from zero.

Simics's checkpoint path is different from Simics's search path (see section 2.2.4), although both will be used when looking for image files, as show below.

Independent checkpoints do not affect the checkpoint path.

To summarize, when loading a checkpoint or a new configuration, Simics looks for images in the following way:

  1. If the filename contains a checkpoint path marker (%n%), the marker is translated using Simics's checkpoint path and the file is looked up in the corresponding path.

    • Windows example;
      If Simics's checkpoint path contains
      C:\checkpoints\c1;C:\checkpoints\c2, the file %1%/image.craff will be translated into C:\checkpoints\c2\image.craff.
    • Linux example;
      If Simics's checkpoint path contains /home/joe/c1:/home/joe/c2, the file %1%/image.craff will be translated into /home/joe/c2/image.craff.

The reason why Simics's search path is involved in the process is that it makes writing new configurations easier. Adding a path to the place where all initial images are located allows you to just specify the image names.

2.4.2.4 Saving and Restoring Persistent Data

As an alternative to checkpointing, Simics allows you to only save the persistent state of a machine, i.e., data that survive when the machine is powered-down. This typically consists of disk images and flash memory or NVRAM contents. A persistent data checkpoint is handled exactly like any other checkpoint and contains the same file set, but only objects containing persistent data are saved. This persistent data checkpoint can be loaded on top of a corresponding configuration later on.

The commands save-persistent-state and load-persistent-state respectively save and load the persistent data in a configuration.

These commands are often used to save the state and reboot a machine after the disk contents have been modified. Remember that the target OS might have cached disk contents in memory. In order to have a clean disk that can be used at boot, you should synchronize the disk, for example by running init 0 on a Linux target system, or shutting down the operating system, before you issue the save-persistent-state command.

Another option is to use the command enable-writable-persistent-state. It saves a persistent state and also switches all saved files to be writable, so that the images are used in read-write mode, as described in section 2.4.2.3. All changes to the images done during the simulation are then automatically written to these files, without the need to explicitly save the state.

This command can also load a state which already exists, created by this command. It is meant to be used in a Simics script and facilitates easy handling of the persistent states, either creating a new state or loading an existing state.

The states created by enable-writable-persistent-state can be loaded using load-persistent-state, if read-write mode is no longer desired. States created by save-persistent-state are generally not usable by enable-writable-persistent-state, since it may not be possible to make them writable.

2.4.2.5 Modifying Checkpoints

Checkpoints are usually created by saving a configuration inside Simics, but it is possible to edit or even create checkpoints yourself. It may even be required to edit file paths in a checkpoint file if it is relocated.

Because a minimal checkpoint only has to include required attributes, creating a checkpoint from scratch works relatively well for small configurations. We suggest you use an existing checkpoint as a starting point if you wish to do that. Note that more advanced layers have been built on top of the configuration system to make the creation of a new machine much easier. Refer to section 2.4.6 for more information.

Modifying checkpoints require some extra care. Adding or removing devices may confuse the operating system, which does not expect devices to appear or disappear while the system is running, and cause it to crash.

Changing the processor frequency may be enough to confuse the operating system. Many operating systems check the CPU frequency at boot time, and base their waiting loops and timing on the value they got. Saving a checkpoint and changing the frequency after boot may affect the simulation and confuse the system. Devices that use processor frequency to trigger events at specific times may also behave strangely when the frequency suddenly changes.

2.4.2.6 Merging Checkpoints

If you want to make an incremental checkpoint independent from all previous checkpoints, for example to distribute it, you can use the small checkpoint-merge program in [simics]\bin from your system command line. It merges the checkpoint with all its ancestors to create a checkpoint that has no dependencies. Specify the checkpoint you want to distribute as the first parameter and the name of the new stand-alone checkpoint as the second. This tool can be used in both Linux and Windows environments.

Note that the merged checkpoint still depends on the base image. This differs from independent checkpoints, which are completely standalone.

2.4.3 Inspecting the Configuration

Object attributes that are of type integer, string or object are directly accessible at the command line with the notation object->attribute:

# reading the EAX register in an x86 processor
simics> cpu0->eax
0
# writing a new value to EAX
simics> cpu0->eax = 10
simics> cpu0->eax
10
simics>

More information about the command line and scripting is available in chapter 2.3.

Finally, objects and attributes (of all types) are also available when scripting Simics directly in Python. Configuration objects are available under the conf namespace:

# reading the EAX register in an x86 processor
simics> @conf.cpu0.eax
0
# writing a new value to EAX
simics> @conf.cpu0.eax = 10
simics> @conf.cpu0.eax
10
simics>

More information about scripting Simics in Python is available in chapter 2.3.

2.4.4 Components

All machines in [simics]\targets\architecture use components to create configurations. A component is typically the smallest hardware unit that can be used when configuring a real machine, and examples include motherboards, PCI cards, hard disks, and backplanes. Components are usually implemented in Simics using several configuration objects and can also contain subcomponents.

Components are intended to reduce the large configuration space provided by Simics's objects and attributes, by only allowing combinations that match real hardware. This greatly simplifies the creation of different systems by catching many misconfigurations.

Components themselves are also configuration objects in Simics. But to avoid confusion, they will always be referred to as components and the objects implementing the actual functionality will be called objects.

2.4.4.1 Component Definitions

The component is the basic building block in the component system. When a component is created, it is in a non-instantiated state. At this stage only the component itself exists, not the configuration objects that will implement the actual functionality. Once a complete configuration has been created, all included components can be instantiated. When this happens, all objects are created and their attributes are set.

Components are connected to each other with connectors. Each connector has a connector type which tells what kind of connector it is and a direction, which can be up, down, or any. A connector is either required or optional. If it is optional it does not need to be connected to anything. Unless a connector is specified as hotpluggable it cannot be connected or disconnected after the component is instantiated. If a connection is hotpluggable it must be optional.

Connectors can be connected to each other in connections. Each connection connects an up connector with a down connector. A connection can also include an any connector. If an any connector is connected to an up connector it works exactly like a down connector and if it is connected to a down connector it works exactly like an up connector. The connections in the system must not form a cycle. You can think of the components and connections in the system as a directed acyclic graph with the components as the vertices and the connections as the edges.

Each connected subgraph in the set of components is called a component hierarchy.

A component A is said to be above a component B if it can be reached through up connectors in one or more steps from component B. Analogously, component A is said to be below a component B if B is above A.

A root is a component without any components above it. A component's roots are the roots which are above it.

A component where the top_level attribute returns true is a top-level component. It is often a motherboard, backplane or system chassis. It must be a root.

A standalone component is a component without any required connectors. A typical example is a hotplug device, such as a PC Card (PCMCIA) or an Ethernet link.

To instantiate a set of components, each component which is not standalone or top-level must have a top-level component as a root.

Components are also namespaces and can be nested in a namespace hierarchy, which is separate from the component hierarchy. The root of the hierarchy is the global namespace, and this is the only namespace which is not a component. Each configuration object (including components) lives in a namespace. The object is a child of the namespace and the namespace is the parent of the object. The other objects in the namespace are siblings of the object.

2.4.4.2 Importing Component Commands

Components in Simics are grouped by machine architecture, or by type, into several modules. Before a component can be used in Simics, the corresponding component module has to be loaded. When the component module is loaded, CLI commands for creating components are added to the front end. The most common modules, that are not architecture specific, are memory-comp, pci-comp, std-comp, console-components. To import all modules that are used by the QSP-Simple machine, issue the following commands:

simics> load-module std-comp
simics> load-module memory-comp
simics> load-module console-components
simics> load-module x58-ich10-comp
simics> load-module x86-nehalem-comp

2.4.4.3 Creating Components

The create-<component> command is used to create non-instantiated components. There is one create command for each component class. The arguments to the create command represent attributes in the component. Standalone components can be created both non-instantiated and instantiated. To create instantiated components, there are new- commands, similar to the create- commands.

The following code creates a non-instantiated 'motherboard_x58_ich10' component , called 'motherboard0'

simics> load-module x58-ich10-comp
simics> create-motherboard-x58-ich10
Created non-instantiated 'motherboard_x58_ich10' component 'motherboard0'

2.4.4.4 Connectors

A connector provides a means for a component to connect to other components. Connectors have a defined direction: up, down, or any. The direction is up if it needs an existing hierarchy to connect to; for example, the PCI-bus connector in a PCI device must connect to a PCI slot. A connector has a down direction if it extends the hierarchy downwards; for example, a PCI slot is a connection downward from a board to a PCI device. There are also non-directed connectors, with direction any. You can only connect an up to a down connector or to an any connector, and similar for down connectors. Connectors with the any direction can not be connected together.

Many connectors have to be connected before the component is instantiated, while others can be empty. A standalone component, as described above, may have all connectors empty.

A hotplug connector supports connect and disconnect after instantiation. Other connectors can only be connected, or left unconnected, when the configuration is created and may not be modified after that point. A multi connector supports connections to several other connectors. Creating multi connectors should be avoided, it is often better to dynamically create non-multi connectors when new connectors are needed.

It is not possible to connect instantiated components with non-instantiated ones. The reason is that the instantiated component expects the other to have all objects already created, and need to access some of them to finish the connection.

The info command of a component lists all connectors and some information about them:

simics> motherboard0.info
Information about motherboard0 [class motherboard_x58_ich10]
============================================================

Slots:
      dimm[0] : motherboard0.dimm[0]
      dimm[1] : motherboard0.dimm[1]
      dimm[2] : motherboard0.dimm[2]
      dimm[3] : motherboard0.dimm[3]
           nb : motherboard0.nb
    reset_bus : motherboard0.reset_bus
           sb : motherboard0.sb
    socket[0] : motherboard0.socket[0]
    socket[1] : motherboard0.socket[1]
    socket[2] : motherboard0.socket[2]
    socket[3] : motherboard0.socket[3]
    socket[4] : motherboard0.socket[4]
    socket[5] : motherboard0.socket[5]
    socket[6] : motherboard0.socket[6]
    socket[7] : motherboard0.socket[7]

Connectors:
      dimm[0] : mem-bus              down
      dimm[1] : mem-bus              down
      dimm[2] : mem-bus              down
      dimm[3] : mem-bus              down
    reset_bus : x86-reset-bus        down
    socket[0] : x86-apic-processor   down
    socket[1] : x86-apic-processor   down
    socket[2] : x86-apic-processor   down
    socket[3] : x86-apic-processor   down
    socket[4] : x86-apic-processor   down
    socket[5] : x86-apic-processor   down
    socket[6] : x86-apic-processor   down
    socket[7] : x86-apic-processor   down

The board has four slots for memory modules, one north bridge, one reset bus, one south bridge, eight sockets. All slots are not listed as hotplug since they have to be inserted when the machine is configured initially.

Since the machine need a cpu, we also add a x86QSP1 processor to our example. A CLI variable is used to hold the name of the processor component.

simics> load-module x86-nehalem-comp
simics> $cpu = (create-processor-x86QSP1 freq_mhz = 2000)
simics> connect motherboard0.socket[0] $cpu.socket

To enable input and output for the simulated machine, the following commands create a serial text console and connect it to the serial[0] connector of the south bridge.

simics> load-module console-components
simics> connect motherboard0.sb.serial[0] (create-txt-console-comp).serial

Since the machine needs some memory to run, we also add a memory module to our example. A CLI variable is used to hold the name of the memory component.

simics> load-module memory-comp
simics> $dimm = (create-simple-memory-module memory_megs = 2048)
simics> connect motherboard0.dimm[0] $dimm.mem_bus

2.4.4.5 Instantiation

When a component hierarchy has been created, it can be instantiated using the instantiate-components command. This command will look for all non-instantiated top-level components and instantiate all components below them. The instantiate-components command can also be given a specific component as argument. Then only that component will be instantiated, including its hierarchy if it is a top-level component.

simics> instantiate-components

If there are unconnected connectors left that may not be empty, the command will return with an error.

When the instantiation is ready, all object and attributes have been created and initialized. In our example, a text console window should have opened. The hardware of the simulated motherboard is now properly configured, but since no software is loaded, it will not show any output on the console if the machine is started.

2.4.4.6 Inspecting Component Configurations

The list-components command prints a list of all components in the system. All connectors are included, and information about existing connections between them.

The info namespace command provides static information about a component, such as the slots and a list of connectors.

The status namespace command provides dynamic information about a component, such as attribute values and a list of all current connections. The output from status in the example:

simics> motherboard0.status
Status of motherboard0 [class motherboard_x58_ich10]
====================================================

Setup:
         Top component : none
          Instantiated : True

Attributes:
                  acpi : True
                  bios : 
       break_on_reboot : False
           mac_address : 20:20:20:20:20:20
              rtc_time : 2008-06-05 23:52:01
             spi_flash : 
          system_clock : False
    system_clock_class : clock

Connections:
               dimm[0] : dimm0:mem_bus
             socket[0] : processor0:socket

2.4.4.7 Accessing Objects from Components

When doing more advanced configuration of a machine, it may be necessary to access configuration objects and their attributes directly. Each object in a component has a slot name that can be used for accessing the object. A list of slot names, and their mappings to actual configuration object names, is available from the output of the component's info command. The next example prints the frequency attribute from the core object.

simics> processor0.core[0][0]->frequency
[20000000, 1]

Accessing objects of non-instantiated components is not possible, since they do not have any associated configuration objects. But it is possible to access the pre_conf_objects of a non-instantiated component from Python. The following example works both for instantiated and non-instantiated components:

simics> @print(conf.processor0.core[0][0].cpuid_stepping)
8

Remember that not all configuration object attributes are available on a pre_conf_object. Only attributes that have been assigned by the component during initialization exists.

2.4.4.8 Available Components

The Target Guide for each architecture lists and describes all components that are available.

2.4.5 Object Name

An object can be identified using more than one name. This section describes the different ways of identifying an object.

All objects have a name that is used when printing log messages, writing checkpoints, in CLI commands, etc. The SIM_object_name function returns this object name. This name will be referred to as the object name in this section, even though an object can have several names for identification.

The object name is the name the object is given when created, or the objects location in the hierarchy.

An object can be given a name when created. The SIM_create_object, SIM_add_configuration, or SIM_set_configuration functions takes the object name as argument. The given name can be a string without dots "foo", a string with dots "cmp0.foo", an empty string "", or None. This section will describe how the given name affects the object name.

An object's hierarchical location is defined by its component and component_slot attributes. The hierarchical location for an object is the name of the component that the object's component attribute points at, and the component's component_slot attribute string, concatenated with a dot. For example, an object that belongs to a component named "cmp0" with the slot name "bar" has the hierarchical location "cmp0.bar".

All objects that reside in a slot in a component have valid component and component_slot attributes. It is the component's responsibility that the attributes are valid. The attributes are set when an object is added to a slot. A name that contains dots is a hierarchical location.

All objects also have an ID. The SIM_object_id function returns the object ID as a string. The object ID is unique, never changes, and will be saved in checkpoints. The object ID will not change even if the object is moved around in the hierarchy or is given a new name.

The object name and object ID are always unique. Creating an object and giving it a name that already exist will generate an error.

If the given name is a hierarchical location, an object will be added to that hierarchical location even if the component and component_slot attributes are not set. Simics will extract the component name and slot name from the given name. This information is then used when looking up the component and adding the object to the slot via the component interface. An object given the name "cmp0.cmp1.foo" belongs to the component "cmp0.cmp1" and has the slot name "foo". Note that the component "cmp1" in this example belongs to the component "cmp0".

An object given a name without dots, e.g., "foo", will get a name that is the hierarchical location of the object if the component and component_slot attributes are valid. Otherwise it will get the name "foo", which means that the object does not reside in any slot in a component. The object will also get an automatically assigned unique object ID, unless legacy_object_id is set. In that case the object name is used as object ID.

An object given a name with dots "cmp0.foo" gets the name "cmp0.foo" or, if it is put in the slot "bar", the name "cmp0.bar", its hierarchical location, and an object ID of the form "obj_XYZ".

An object given an empty name "", or None, will get a hierarchical location as name, if the component and component_slot attributes are valid, otherwise a name of the form "obj_XYZ". The object ID will always be of the form "obj_XYZ", identical to the object name if component and component_slot attributes are invalid.

Given NameName (Slot = None)Name (Slot = bar)IDName After Move
foofoocmp0.barobj_XYZcmp1.smurf
cmp0.foocmp0.foocmp0.barobj_XYZcmp1.smurf
Noneobj_XYZcmp0.barobj_XYZcmp1.smurf

The given name is the name that the user has provided. Name is the name the object gets when created, depending on if component and component_slot are set: Slot = None when they are not set; Slot = bar when they are set and component_slot is bar. ID is the unique object ID. Name After Move is the name the object gets after being moved to cmp1 to a slot named smurf.

2.4.6 Ready-to-run Configurations

Simics includes many customizable ready-to-run configurations. Because checkpoint files are by definition static, these example configurations are not checkpoint-based, but rather built on components and scripts to generate a working simulated machine.

The example configurations are located in separate directories for each system architecture: [simics]\targets\architecture. Each of these directories contains a number of Simics scripts (i.e., files containing Simics commands):

These are the files you want to use to start the standard example machines in this directory.

\<machine\> in the script name is either a Linux machine name, or a some other name that defines the hardware/software combination.  

The example configurations are designed to work with the disk images distributed with Simics. The machines are described in the Target Guide corresponding to each architecture.

Several machines may be defined for a given architecture, and thus the corresponding architecture directory will contain several machine-common.simics scripts.

2.4.6.1 Customizing the Configurations

There are several ways to customize the examples provided with Simics. They are listed below ordered by how simple they are to use.

  1. Scripts
    A simulated machine is defined by several scripts, as described above. By replacing the -common.simics file with a user defined script, the system can be configured more in detail while keeping the machine definition provided by the -system.include file. Similarly the -setup.include can be replaced to configure different software on the same machine.
  2. Components
    Components represents real hardware items such as PCI cards, motherboards, and disks. Using components to configure a machine provides freedom to set up the simulated machine in any way that is supported by the architecture. The -system.include files use components to create their machine definitions. A complete description of components is provided earlier in this chapter.
  3. Objects and Attributes
    A component is implemented by one or more configuration objects in Simics, and each object has several attributes describing it. Configuring machines on the object and attribute level is not supported in Simics, and such configurations may not work in future versions.

Below is another example of a simple configuration based on QSP-x86, that uses parameters to configure two machines slightly differently that both run in the same Simics session.

simics> $freq_mhz = 2000
simics> $host_name = "target0"
simics> run-script "%script%/firststeps.simics"

simics> $freq_mhz = 3000
simics> $host_name = "target1"
simics> run-script "%script%/firststeps.simics"

2.4.6.2 Adding Devices to Existing Configurations

The parameters available for each predefined machine allows the user to do minor modifications. It is also possible to extend the ready-to-run configurations with additional components without creating new machine setups from scratch.

Since the machine setup scripts are located in the read-only master installation of Simics, they should not be modified. User files that add new components should instead be placed in the corresponding [project]\targets\architecture directory.

Here is a short example of how to extend the QSP-x86 to add a SATA disk:

# Add disk2
simics> $disk2 = (create-sata-disk-comp $system.disk2 size = $disk2_size)
simics> connect $system.mb.sb.sata_slot[2] $disk2.sata_slot

simics> instantiate-components

Notice, that script requires you to provide a disk image and a valid disk size. Essentially the script will run another script, firststeps.simics, which will create an instantiated QSP-x86 machine, then a SCSI disk is created and connected to that machine, and finally the new disk is instantiated.

It is possible to add many devices to an instantiated Simics machine in a similar manner. In the case a device must be added to the target machine before instantiation, write a script as described above.

2.3 Simics Scripting Environment 2.5 Moving Data in and out of the Simulation