23 Memory Spaces 25 Adding Flash to a System
Model Builder User's Guide  /  IV Creating Virtual Systems  / 

24 Components

This chapter describes how to write your own Simics components. It assumes you have already read the section about components in the Simics User's Guide, particularly for the definitions.

24.1 Introduction

A component represents a piece of hardware which connects to other parts of system through standardized interfaces. The primary purpose of component in Simics is to manage and encapsulate the complexity inherent in composing a system. Frequently, a component represents a piece of hardware which can be removed from the system and put back in without breaking it or the rest of the system. Some manufacturers refer to this sort of hardware as a Field Replaceable Unit or FRU. Whether modeling FRUs or not, the definition of components should closely follow the structure in the real system. Some examples of real hardware that generally modeled using components are motherboards, compact flash cards, disks, SOCs, and PCI cards.

Components are assembled to construct systems through two primary mechanisms: nested namespaces and connectors. A system will usually use both mechanisms. Namespaces are used to encapsulate parts of the system which from the outside can be considered one unit and connectors are used to connect the components to each other similar to how the real hardware is connected, for example memory slots, PCI slots, Ethernet sockets. When there is a tight coupling between a component and a subcomponent, for example between a board and an SOC, the component can set up the subcomponent and the connections between the two components without using connectors.

Consider a system consisting of a compact PCI chassis with a processor board and several I/O boards. The system runs a single operating system image and is considered a single machine; however, each board is clearly a separate entity from the others and from the chassis. Here, you would use simple connections between the components, with one component for each board and one for the chassis.

Now consider the processor board consisting of an integrated processor SOC, several discrete devices, RAM, and flash. Here, the SOC is clearly a reusable system with sufficient complexity that it should be encapsulated in a component, but the board is meaningless without the SOC. Thus a component should be used to represent the SOC, with the board being a component that contains the SOC. Since the board has such a strong dependency on the SOC it does not need to use a connector to connect to it, instead it can create the SOC component itself and set up the connections manually.

Namespaces are described in section 24.2, while component hierarchies are described in 24.3. To summarize, a namespace composition is used to describe components that consists of other components, while connectors are used to connect components to each other.

24.1.1 Component Class

A component is a generic Simics class, with the special ability to contain class instances. Each instance of a component represents a namespace described in section 24.2.1. In this chapter, we will refer to a component instance as a component, and the class instances within the component as objects.

More specifically, a component class is a Simics class written in Python that implements the component interface. Details of writing components are described in section 24.6. The component interface is documented in section 24.8.1.

A number of ready to use components are provided with Simics. Some of these are standard components that can be used in many kind of systems. Examples of standard components are disks, flashes, text consoles, etc. Other components are specific to a particular system, such as an evaluation board.

24.1.2 Component Module

A component module is a Simics module containing the implementation of one or more related components. The examples-comp module, for example, contains a set of example components. It will be used as an example in the following sections. It can be found at [simics]/src/components/.

Additional examples of flat components that do not support hierarchical composition can be found in the [simics]/src/extensions directory. These components are still supported, but they will not be discussed in this chapter.

24.1.3 Components in Project

When adding a component to a project, the component module source directory is placed in [project]/modules/component_module_name, and contains one component_module_name.py file, one Makefile and optionally one PNG image file for each top-level component.

The Makefile is essentially identical for all component modules; it simply points out the .py file that is part of the module. Section 24.6 describes how to create new components.

24.2 Namespace Hierarchies

All components also define its own namespace for other components and objects. Placing a component in another component's namespace is used to model systems composed of reusable subsystems that do not individually stand alone.

Figure 8. Example of a namespace hierarchy

Figure 8 shows an example system with one root component called cmp0 with two sub components, cmp1 and cmp2. The cmp1 component contains the obj object. Each component has a separate namespace. The namespaces form a tree of components. The cmp1 component is in the cmp0 component's namespace.

All objects have a name. For objects in the global namespace the name is simply a string given at its creation. Objects in other namespaces have names that depend on their location in the hierarchy. These names depend on which slot in the parent the object is placed in.

Slots are something all components have, and which mainly define the names of its children. Each slot has a name and a value. The value is often just an object reference. Other possible values are None or nested lists with objects references and Nones as elements. A slot with None value is called an unoccupied slot.

An object in a component has a local name and a full name. The local name is called slot name or simply slot. An object's slot name is the name of the slot it belongs to concatenated with its index in the slot. Assume that a component cmp has the slot sub with an object reference to the object A. The object A has the slot name sub and the full name cmp.sub. Now assume that the component cmp has the slot sub with a list of two object references to object B and object C. The object B has the slot name sub[0] and the full name cmp.sub[0].

In figure 8 the sub components names are actually the slot names. Both cmp1 and cmp2 are slots in cmp0. The cmp1 component in figure 8 has the full name cmp0.cmp1. All object names in the figures in this chapter are local names.

A component can be connected to components on the same hierarchical level or to a parent or child component; see section 24.4.

24.2.1 Namespaces

Component namespaces provide scoping for names in a manner similar to syntactic blocks in programming languages. As with programming structure, it is easier to understand when a minimal number of names are defined in the global scope or namespace. Another good analogy is a file system directory structure.

A component or object can be added to a component's namespace on definition, or after creation at run-time using the move-object command.

Ex. move component cmp1 to cmp0 as "cmp1":
simics> move-object src = cmp1 dst = cmp0.cmp1

Moving an object to a component puts the object in its namespace. This is done by putting the object in a slot in the component. The object can now be accessed relative to the parent component in CLI, Python, or in the Simics API.

Moving an object to a different hierarchical location changes its full name. The cmp1 is now accessed using the hierarchical name cmp0.cmp1, the slot name is cmp1.

Ex. execute info command for cmp1 in CLI:
simics> cmp0.cmp1.info
Ex. access queue attribute for cmp1 in CLI:
simics> cmp0.cmp1->queue
Ex. access queue attribute for cmp1 in Python:
simics> @conf.cmp0.cmp1.queue
Ex. get cpu object from cmp1 in Python:
simics> @conf.cmp0.cmp1.cpu

A normal object can only exist in one namespace at a time; only connector objects can exist in several namespaces. Connector objects are documented in section 24.4.2. Connector objects can be copied to a new namespace using the copy-connector command.

Ex. copy connector object cmp0.cmp1.port0 to cmp0 as "copy0":
simics> copy-connector cmp0.cmp1.port0 cmp0.copy0

The connector object can now be accessed as cmp0.copy0 or cmp0.cmp1.port0, and has two parents. The owner of the connector object is not changed and is still the cmp0.cmp1 component.

The component interface has functions to add and remove objects from a components namespace, which is used by the move-object and copy-connector commands.

The alias command can be used to avoid having to write the complete hierarchical name several times for often-used objects:

Ex. alias for cmp1 in CLI:
simics> alias short cmp0.cmp1.port0
simics> short.info

24.2.2 Precedence

Objects, attributes, and commands sometimes share the same namespace name, resulting in conflicts. Consider a root component named cmp0 with an object in slot cpu and also has a command called cpu that prints all processors in the components. There will be a conflict when typing cmp0.cpu on the command line.

You should try to avoid such name conflicts, and Simics will print a warning message if any are detected. If there are conflicting names, the following precedence describes how the name is interpreted.

The order in decreasing precedence is:

  1. slots,
  2. attributes, and
  3. commands.

Thus continuing the example above, cmp0.cpu is an object reference, not a command.

24.3 Component Hierarchies

To model how the parts of a system are connected to each other components are connected to each other using connectors. The connectors allow you to build a system that consist of reusable components. The connections form a graph separate from the namespace hierarchy.

Components in the global namespace which represent systems of interest to the user should be top-level components. A top-level component's main responsibility is to provide informational metadata in some standardized attributes which are mainly used by Simics's graphical tools; see the details in 24.8.3. The user can set which components he wants as top-level components when he configures the system. For example, a compact PCI form factor single board computer can either be the top-level component in a standalone system or part of a chassis based system, where the chassis would be the top-level component.

24.4 Connecting Components

Components can connect to other components via connector objects (described in more detail in section 24.4.2). The connector objects used for connecting components must implement the connector interface. The connector interface description can be found in section 24.8.1.

Figure 9. Example component connection

In figure 9, the southbridge component is connected to the usb_device component. The southbridge component contains the usb_host object, and the usb_device component contains the usb_disk object. It is the components that set up the connection through their connectors, but it is the usb_host and usb_disk objects that are connected via interfaces. The usb_host object has a usb_devices attribute that connects to the usb_device interface on all connected usb device objects. The usb_disk object has a usb_host attribute that connects to the usb interface of the host object. Data exchanged during the connection process sets the attributes to appropriate values. A component connection between two components can exchange data to set up multiple attributes for several objects in the components.

24.4.1 Connector Properties

A component connector has several properties that define the type of connection. The properties are defined in the connector interface, which is implemented by all component connectors.

24.4.2 Connector Objects

Component connector objects are normal Simics objects that implement the connector interface. Connector objects contain only the functionality needed to set up the connections between objects within the components they connect.

Connector objects are explicitly defined in the component class. The connector objects will be created when an instance of the component class is created. Defining a connector will automatically put the connector object in its component's namespace, i.e., all connectors will exist in a slot in a component.

A component can inherit another component's connectors. This can either be done using the copy-connector command at run-time, as described in section 24.2.1, or when defining a component, as described in section 24.6.11.

24.4.3 Connecting Connectors

Component connectors are connected using the connect command, which takes the two component connector objects as parameters.

Ex. connecting cmp0 and cmp1 in figure :
simics> connect cmp0.cnt0 cmp1.cnt0

Figure 10. Component connection between components on the same level

Figure 11. Component connection between component parent and child

Two components can only be connected if they are siblings in the namespace hierarchy or if they are parent and child. The former is illustrated in figure 10, the latter in figure 11.

24.4.4 Adding Connector References

When making a component into a sub component of another component, it is often desirable to expose the sub component connectors as connectors of the parent component. This is very simple, as it is possible to add a reference to the sub component's connector to the parent component.

Figure 12. Connector reference

Assume we have two components, cmp0 and cmp1. The cmp1 has a connector object named cnt0. First add cmp1 to cmp0 and then add a reference:

simics> move-object src = cmp1 dst = cmp0.cmp1
simics> copy-connector cmp0.cmp1.cnt0 cmp0.cpy0

The legacy Ethernet link component using the std-ethernet-link class is not compatible with the new hierarchical components. The new Ethernet link components ethernet_cable, ethernet_hub, and ethernet_switch should be used with hierarchical components.

24.5 Life of a Component

A component goes through several phases, from creation to fully instantiated component connected to other components with a lot of sub components and objects.

These are the phases:

24.5.1 Creation Phase

The components are created using the create-name-of-component command, which will create a non-instantiated component.

The first argument to the create- command is the name of the component. The components default name will be used if the name is not provided when creating the component. The default name is defined by the basename class attribute. The basename attribute is set to component for all components that are based on the StandardComponent class, but it can be overridden in the inheriting class.

The remaining arguments to the create- command are the config attributes, see section 24.6.6. The config attributes are either required or optional. All required attributes must be set when the component is created.

24.5.2 Setup Phase

All attributes that were not set during the creation phase, but should be set are set in the setup phase. Some attributes might depend on other components or one script creates the component and another script setup the components.

24.5.3 Connect Phase

All required connectors must be connected before the component is instantiated. Required connectors might for instance be connectors for connecting a processor to board. The objects on the board component require the processor object in the processor component to function.

24.5.4 Instantiate Phase

The component can be instantiated when all connectors have been connected. The instantiation process will collect all pre objects in the component and create new real objects to replace the pre objects.

The component interface has two functions that will be called in the instantiate process. The pre_instantiate function will be called right before the component is instantiated. The function returns True if the component is allowed to be instantiated. The post_instantiate function will be called right after the component is instantiated. This function is a good place to add code that requires real objects from the component.

24.6 Implementing Components

This section describes how to implement components. All examples in this section are fully functional and are included in the examples-comp module. To test the examples, start the vacuum target machine and load the examples-comp module.

The source code for the component examples can be found in [simics]/src/components/examples-comp/examples_comp.py.

24.6.1 Creating a New Component

A new component module can be created using the project-setup utility:

This will create skeleton code for a new component in the [project]/modules/my-own-component/ directory, with all files needed to build it as a Simics module.

24.6.2 The comp Python Module

Simics includes the comp Python module that greatly simplifies writing components. The module contains all functionality needed for creating a component.

The comp module and its classes and methods are documented in section 24.11. This help for writing components is only available in Python, as Python is the only language supported for writing components.

To use the comp module, make sure your Python file contains:

from comp import *

The comp module contains the StandardComponent class that should be used as base class when creating new components. The class contains a lot of useful methods and parameters to define a new component. The comp module also contains other classes that are based on the StandardComponent class. These classes will be described later.

Here is an example how to create a simple component:

import simics
from comp import *

class emmett(StandardComponent):
    """The long description for the Emmett component."""
    _class_desc = 'short Emmett description'

In the example we create the component class emmett. The first string in the class is a long description of the component that can be several sentences. The _class_desc is a short class description beginning with lower case, without trailing dot, and at most 50 characters long. The longer description is used in the help commands and reference manuals, while the shorter description is used for example in the GUI to describe the component.

Upon registration, the newly defined component registers itself as a common Simics class which allows instances of it to be created like for any other Simics object. By inheriting the StandardComponent, the component will also get a set of predefined attributes that all components should have; see section 24.8.3. The default value and functionality for the attributes can be overridden if needed; see section 24.6.7.

All components will also automatically define a few default commands. new-name-of-component will create an instantiated component of the type name_of_component. create-name-of-component will create a non-instantiated component. Note that underscores are converted to dashes for class name for the new- and create- commands. The component will also automatically get info and status commands.

simics> load-module examples-comp
simics> new-emmett name = my_emmett
Created instantiated 'emmett' component 'my_emmett'

To define a top-level component, override the top_level class definition in the StandardComponent class like this:

class mcfly(StandardComponent):
    """The McFly component."""
    _class_desc = 'a McFly component'

    class top_level(StandardComponent.top_level):
        def _initialize(self):
            self.val = True

In this example, we override the default value for top_level. The top_level attribute is by default set to False. Section 24.6.7 says more about how to override the default attribute functionality. It is possible to change a non top-level component into a top-level component by setting the attribute when creating it or at run time.

24.6.3 StandardComponent Class Basic Methods

The StandardComponent class is defined in the comp Python module. The class is based on pyobj.ConfObject, from which it inherits some rarely used methods (see section 15.3).

The StandardComponent class adds a method setup, which is called after all attributes of a component object's have been set. This method is often used for adding new objects to the component. Objects should only be added if the component has not been yet instantiated. This makes the setup methods very similar in all components. The instantiated attribute is checked to determine if we should add new objects or not. More information about attributes can be found in section 24.6.5.

class tyrell(StandardComponent):
    """The Tyrell component."""
    _class_desc = 'a Tyrell component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.olws = 1
            self.add_tyrell_objects()

    def add_tyrell_objects(self):
        self.add_pre_obj('mem', 'memory-space')

class sebastian(tyrell):
    """The Sebastian component."""
    _class_desc = 'a Sebastian component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_sebastian_objects()

    def add_sebastian_objects(self):
        self.add_pre_obj('mem', 'memory-space')

The setup method corresponds to the finalize field in the class_info_t struct that is passed to the SIM_create_class function, see the Simics Reference Manual.

To prevent name conflicts when using class inheritance, use unique names for the methods that add objects. For example, tint the method name with the name of its class, such as `add_banana_objects()` in class "banana". A component that inherits another component class and calls its `setup` method with *self* can cause problems when the method that adds objects has the same name in both classes.

The StandardComponent class defines the _finalize method which also the pyobj.ConfObject class defines. Old components often implement this method. It is not recommended to implement the _finalize method, the setup method should instead be implemented to get better component error messages on component exceptions.

class roy_batty(StandardComponent):
    """The Roy Batty component."""
    _class_desc = 'a Roy Batty component'

    def _initialize(self):
        super()._initialize()
        self.replicants = 4

    def _finalize(self):
        super()._finalize()
        if not self.instantiated.val:
            self.add_roy_batty_objects()

    def add_roy_batty_objects(self):
        self.add_pre_obj('mem', 'memory-space')

24.6.4 StandardComponent Class Parameters

The StandardComponent class in the comp module includes parameters that can be set to control the component behavior. Since the StandardComponent class is based on pyobj.ConfObject, also see the parameters defined in 15.4.

class henry_hill(StandardComponent):
    """The wiseguy, Henry Hill component."""
    _class_desc = 'a Henry Hill component'
    _do_not_init = object()

class frankie_carbone(henry_hill):
    """The wiseguy, Frankie Carbone component."""
    _class_desc = 'a Frankie Carbone component'
    _help_categories = ('Goodfellas',)
    def _initialize(self):
        super()._initialize()

24.6.5 Adding Attributes

A component that inherits the StandardComponent class creates a new attribute by defining a new class in the component class that inherits the Attribute class, which is defined in the pyobj module. See the section 15.5.

24.6.6 Adding Config Attributes

The comp Python module provides the ConfigAttribute class and the SimpleConfigAttribute function for creating parameterized config attributes. Attributes that are used to parameterize the component will automatically become arguments to the new- and create- commands. This allows for an easy way to create a component with the desired parameters.

Because config attributes are used as arguments to new- and create- commands, they must be documented. Hence the default value of the attrattr class member is Sim_Attr_Optional.

Config attributes are created like this:

class ripley(StandardComponent):
    """The Ripley component."""
    _class_desc = 'a Ripley component'

    def setup(self):
        super().setup()
        print("sequels is", self.sequels.val)
        print("eggs is", self.eggs.val)
        print("marine is", self.marine.val)

    class sequels(SimpleConfigAttribute(
            None, 'i', simics.Sim_Attr_Required, [4])):
        """Number of sequels."""

    class eggs(ConfigAttribute):
        """The number of hatched eggs."""
        attrtype = "i"
        valid = [821, 1023]
        def _initialize(self):
            self.val = 50
        def getter(self):
            return self.val
        def setter(self, val):
            if val == 0:
                return simics.Sim_Set_Illegal_Value
            self.val = val

    class marine(SimpleConfigAttribute(
            'hudson', 's', val = ['hudson', 'gorman', 'vasquez'])):
        """The name of the marine."""

An optional config attribute such as eggs becomes an optional argument to the new- and create- commands for the component. A required attribute such as the sequels attribute becomes a required argument when creating the component.

simics> load-module examples-comp
simics> new-ripley name = my_ripley sequels = 3
sequels is 3
eggs is 50
marine is hudson
Created instantiated 'ripley' component 'my_ripley'

Use the SimpleConfigAttribute function when a simple attribute without any special functionality is required, just like the SimpleAttribute function is used.

The ConfigAttribute class contains a valid attribute which is a list of valid values. The list gives the user a hint about valid values when creating a component. There is no check that the value written to the attribute is a value in the list of valid values. The list of valid value(s) does not need to contain the default initial value for the config attribute, but it usually does. The valid list should at least contain one valid value even if several values are valid.

24.6.7 Overriding Attributes

The StandardComponent class defines a set of attributes that all components should implement. The attributes are described in detail in the section 24.8.3. All of the attributes can be overridden if needed.

Here is an example how to override the component_icon attribute:

class nemo(StandardComponent):
    """The Nemo component."""
    _class_desc = 'a Nemo component'

    class component_icon(StandardComponent.component_icon):
        def _initialize(self):
            self.val = "stanton.png"

The new component icon attribute example code only overrides the initial value, but it is also possible to override anything in the class definition, such as the getter or setter methods, if required.

24.6.8 The _up Member

To access a class's containing class the _up member is used. See the section 15.7.

24.6.9 Adding Objects to Slots

A component can define slots. A slot has a name and a value, often a single object. Slots can be defined in the component; however, new slots can also be added after a component has been created, but that will not be discussed in this section. Slots defined in the component are called static slots. Static slots cannot be removed after the component has been created. The value in the slot can be changed at any time. The name of the slot is used to access an object in the slot.

24.6.9.1 Adding Single Object

Common Simics objects are added to a component using the add_pre_obj method. The method will create pre objects that will be converted to real objects when the component is instantiated.

class wall_e(StandardComponent):
    """The WALL-E component."""
    _class_desc = 'a WALL-E component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_wall_e_objects()

    def add_wall_e_objects(self):
        p = self.add_pre_obj('p_mem', 'memory-space')
        v = self.add_pre_obj('v_mem', 'memory-space')
        self.add_pre_obj('clock', 'clock', freq_mhz = 10)
        p.map = [[0x100, v, 0, 0, 0x10]]

    class cpu_list(StandardComponent.cpu_list):
        def getter(self):
            return [self._up.get_slot('clock')]

The component in the example defines three objects and three slots to hold references to them. The add_pre_obj function has two required and two optional arguments. The two required arguments are the slot name and the class name. The third argument is optional and specifies the name of the object. The name defaults to an empty string and will be the object's hierarchical name, it is not shown in the example and it should only be used in special cases and then it is given as name = "pineapple". The fourth argument is also optional and is attribute values for the object. The add_pre_obj function returns a pre_conf_object or an array of pre_conf_objects.

The p_mem and v_mem slots contain memory-space objects and the clock slot contains a clock object. In this example, we save references to the added objects in p and v to make it easier when setting attributes for the objects.

A slot value can be extracted with the get_slot method in the StandardComponent class. The method takes the slot name as argument. Note that to access the component class from the cpu_list attribute class the _up member is required.

24.6.9.2 Adding Array of Objects

The add_pre_obj function can create nested arrays of identical objects. This is done by adding an index suffix to the slot name. All created objects are returned as nested array corresponding to the suffix. Here is an example that better explains how it works:

class hal(StandardComponent):
    """The HAL component."""
    _class_desc = 'a HAL component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_hal_objects()
            self.do_hal_stuff()

    def add_hal_objects(self):
        self.add_pre_obj('clock', 'clock', freq_mhz = 2001)

        self.add_pre_obj('p_mem[4]', 'memory-space')
        self.add_pre_obj('v_mem[6][10]', 'memory-space')

    def do_hal_stuff(self):
        c = self.get_slot('clock')

        self.get_slot('p_mem[1]').queue = c
        self.get_slot('p_mem')[1].queue = c

        self.get_slot('v_mem[2][3]').queue = c
        self.get_slot('v_mem[2]')[3].queue = c
        self.get_slot('v_mem')[2][3].queue = c

The p_mem and v_mem slots both contain arrays of objects. The p_mem slot contains an array of 4 elements where each element is a memory-space object. The v_mem slot contains an array of 6 elements where each element is an array of 10 memory-space objects, i.e. totally 60 objects.

The do_hal_stuff method fetches the slots using the get_slot method. The slot argument can either be indexed or the indexing can be done after getting the slot. The two lines that work on the p_mem slot do the same and the three lines that work on the v_mem slot do the same thing.

Slot arrays are supported and can sometimes help when having many objects. Here is the output from using the arrays in the hal component class.

simics> load-module examples-comp
simics> new-hal name = my_hal
Created instantiated 'hal' component 'my_hal'
simics> my_hal.p_mem
["my_hal.p_mem[0]", "my_hal.p_mem[1]", "my_hal.p_mem[2]", "my_hal.p_mem[3]"]
simics> my_hal.p_mem[0]
"my_hal.p_mem[0]"
simics> my_hal.v_mem[0]
[my_hal.v_mem[0][0], my_hal.v_mem[0][1], my_hal.v_mem[0][2], ↩
my_hal.v_mem[0][3], my_hal.v_mem[0][4], my_hal.v_mem[0][5], ↩
my_hal.v_mem[0][6], my_hal.v_mem[0][7], my_hal.v_mem[0][8], my_hal.v_mem[0][9]]
simics> my_hal.v_mem[0][0]
my_hal.v_mem[0][0]

24.6.9.3 Lazy Slot Object Assignment

The add_pre_obj method supports None as slot argument. This means that the pre objects will be created and returned by the method, but they will not be added to any slot. The pre objects can later be added to a slot using the add_slot method.

class marvin(StandardComponent):
    """The Marvin component."""
    _class_desc = 'a Marvin component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_marvin_objects()

    def add_marvin_objects(self):
        self.add_pre_obj('clock', 'clock', freq_mhz = 2001)
        p_mem = [None,
                 self.add_pre_obj(None, 'memory-space'),
                 self.add_pre_obj(None, 'memory-space'),
                 None]
        self.add_slot('p_mem', p_mem)

This example shows how to create a slot with an mixed array of None and pre objects. The first and the last elements in the slot are unoccupied. The two middle elements contain pre objects. Here is the output when getting the slot value:

simics> new-marvin name = my_marvin
Created instantiated 'marvin' component 'my_marvin'
simics> my_marvin.p_mem
[NIL, "my_marvin.p_mem[1]", "my_marvin.p_mem[2]"]

24.6.10 Adding Connectors

Connectors are added to components similarly to how objects are added to slots, see 24.6.9. A component that has connectors must implement the component_connector interface. Below we describe how to add connectors either by explicitly implementing the component_connector interface in section 24.6.10.1, or using connector classes in section 24.6.10.2.

24.6.10.1 Defining Connector Explicitly

One way of implementing connectors is to define the component_connector interface and implement all the functions that are needed for the connectors.

class elliot(StandardComponent):
    """The Elliot component."""
    _class_desc = 'an Elliot component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_elliot_objects()

    def add_elliot_objects(self):
        self.add_connector(
             'eth0', 'ethernet-link', True, False, False,
             simics.Sim_Connector_Direction_Down)
        self.add_connector(
             'uart[2]', 'serial', True, False, False,
             simics.Sim_Connector_Direction_Down)
        dbg = self.add_connector(
             None, 'serial', True, False, False,
             simics.Sim_Connector_Direction_Down)
        self.add_slot('debug', dbg)

    class component_connector(Interface):
        def get_check_data(self, cnt):
            # same as connect_data
            return self._up.get_connect_data(cnt)
        def get_connect_data(self, cnt):
            return self._up.get_connect_data(cnt)
        def check(self, cnt, attr):
            return True
        def connect(self, cnt, attr):
            self._up.connect(cnt, attr)
        def disconnect(self, cnt):
            self._up.disconnect(cnt)

    def get_connect_data(self, cnt):
        if cnt in self.get_slot('uart'):
            num = self.get_slot('uart').index(cnt)
            return [None, self.get_slot('uart_dev%d' % num), 'uart%d' % num]
        elif cnt == self.get_slot('debug'):
            return [None, self.get_slot('dbg_dev'), 'debug']
        elif cnt.type == 'ethernet-link':
            return []

    def connect(self, cnt, attr):
        if cnt in self.get_slot('uart'):
            (link, console) = attr
            num = self.get_slot('uart').index(cnt)
            self.get_slot('uart_dev%d' % num).console = console
        elif cnt == self.get_slot('debug'):
            (link, console) = attr
            self.get_slot('dbg_dev').console = console
        elif cnt == self.get_slot('eth0'):
            self.get_slot('emac0').link = attr[0]

    def disconnect(self, cnt):
        if cnt in self.get_slot('uart'):
            num = self.get_slot('uart').index(cnt)
            self.get_slot('uart_dev%d' % num).console = None
        elif cnt == self.get_slot('debug'):
            self.get_slot('dbg_dev').console = None
        elif cnt == self.get_slot('eth0'):
            self.get_slot('emac0').link = None

The example component creates one connector in the slot eth0, one array of two connectors in the slot uart, and one connector in the slot debug.

The connector objects are created at once when adding a connector slot with the add_connector method. The function returns the objects or nested arrays of objects if the slot was specified, otherwise the function returns pre objects or nested arrays of pre objects, like the dbg connector in the example. In the latter case the objects are created when they are assigned to a slot using the add_slot function. This is to avoid creating connectors that are never assigned to any slot and therefore are useless.

The component implements the component_connector interface to handle the connector functionality. The interface is documented in the API Reference Manual. The different connector types are documented in section 24.10.

24.6.10.2 Connector Classes

Most connectors are simple connectors with standard behavior. This makes it possible to use the same code for several components. The StandardConnectorComponent class in the comp Python module helps with this. The StandardConnectorComponent class inherits the StandardComponent class and supports the same features as that class. In addition the StandardConnectorComponent class implements the component_connector interface and a new definition of the add_connector method. The function takes a connector class as argument. The connector class provides all functionality for handling the connection.

The most common standard type of connectors has predefined connector classes. They are included in the connector Python module. This module is imported by the comp module, so there is no need to import it explicitly. The source code for these classes can be found in [simics]/src/core/common/connectors.py.

class gertie(StandardConnectorComponent):
    """The Gertie PCI component."""
    _class_desc = "a Gertie PCI component"
    _help_categories = ('PCI',)

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_gertie_objects()
        self.add_gertie_connectors()

    def add_gertie_connectors(self):
        self.add_connector('pci', PciBusUpConnector(0, 'sample_dev'))

    def add_gertie_objects(self):
        self.add_pre_obj('sample_dev', 'sample_pci_device',
                         int_attr = 10)

Note that connectors instantiating a helper connector class that handles the connection must instantiate the connector class even if the component has been instantiated. The call to add_gertie_connectors in the example is independent of the instantiated attribute. This would otherwise result in an error when loading a checkpoint as the checkpoint will not contain the information about the helper class.

This is important to understand when using dynamic connectors (i.e. connectors created on demand); the component must be able to recreate the helper connector classes when a checkpoint is loaded. If the component did not do this, the checkpoint would load (and the system would run), but you would not be able to connect or disconnect any connectors. One way to determine which helper connectors to recreate is to look at attributes (or attributes of objects in the component).

One standard connector class is the PciBusUpConnector. The class takes fun_num and device as arguments. The fun_num is the function number and the device is the slot name of the PCI device that should be added to the PCI bus. The slot name must be given as a string.

It is possible to create own connector classes by inheriting from the StandardConnector class.

class HarpoonUpConnector(StandardConnector):
    def __init__(self, device, required = False):
        if not isinstance(device, str):
            raise CompException('device must be a string')
        self.device = device
        self.type = 'harpoon-bus'
        self.hotpluggable = False
        self.required = required
        self.multi = False
        self.direction = simics.Sim_Connector_Direction_Up

    def get_check_data(self, cmp, cnt):
        return []
    def get_connect_data(self, cmp, cnt):
        return [cmp.get_slot(self.device)]
    def check(self, cmp, cnt, attr):
        return True
    def connect(self, cmp, cnt, attr):
        (num,) = attr
        cmp.get_slot(self.device).int_attr = num
    def disconnect(self, cmp, cnt):
        cmp.get_slot(self.device).int_attr = 0

class brody(StandardConnectorComponent):
    """The Brody component."""
    _class_desc = 'a Brody component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_brody_objects()
        self.add_brody_connectors()

    def add_brody_connectors(self):
        self.add_connector('jaws', HarpoonUpConnector('sample'))

    def add_brody_objects(self):
        self.add_pre_obj('sample', 'sample_device_dml')

24.6.11 Adding Components

A component can contain sub components. The sub components are added when defining a component. A sub component will be assigned to a slot.

24.6.11.1 Adding Sub Component

The first example just shows how to add a sub component.

class hunt(StandardConnectorComponent):
    """The Hunt component."""
    _class_desc = 'a Hunt component'

    class impossible(SimpleAttribute(False, 'b')):
        """True if impossible, default is False."""

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_hunt_objects()
        self.add_hunt_connectors()

    def add_hunt_connectors(self):
        self.add_connector('mission1', HarpoonUpConnector('sample'))
        self.add_connector('mission2', HarpoonUpConnector('sample'))

    def add_hunt_objects(self):
        self.add_pre_obj('sample', 'sample_device_dml')
        self.add_pre_obj('clock', 'clock', freq_mhz = 4711)

class ethan(StandardConnectorComponent):
    """The Ethan component."""
    _class_desc = 'an Ethan component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_ethan_objects()

    def add_ethan_objects(self):
        self.add_component('last', 'hunt', [['impossible', True]])
        self.copy_connector('copy', 'last.mission1')
        mem = self.add_pre_obj('mem', 'memory-space')
        mem.queue = self.get_slot('last.clock')

The ethan component in the example creates a sub component with the slot name last of the hunt class type.

The ethan component also copies the mission connector from the last component and puts it in the slot copy. This kind of copy can only be done for connector objects. Note that this is a superior solution to the runtime command copy-connector, which has the same effect at run time but does not update the documentation and requires care when checkpointing.

Note that the get_slot and copy_connector methods can get slots not only in the components own namespace but in the sub components namespace. In the example this is done by the self.get_slot('last.clock') call. It is also possible to access slots in a sub component to the sub component. There is no limit to the look-up depth.

24.6.11.2 Adding and Connecting Sub Components

Two sub components can be both instantiated and connected when defined.

class BessonUpConnector(StandardConnector):
    def __init__(self):
        self.type = 'besson'
        self.hotpluggable = False
        self.required = False
        self.multi = False
        self.direction = simics.Sim_Connector_Direction_Up
    def get_check_data(self, cmp, cnt):
        return []
    def get_connect_data(self, cmp, cnt):
        return []
    def check(self, cmp, cnt, attr):
        return True
    def connect(self, cmp, cnt, attr):
        pass
    def disconnect(self, cmp, cnt):
        pass

class BessonDownConnector(StandardConnector):
    def __init__(self):
        self.type = 'besson'
        self.hotpluggable = False
        self.required = False
        self.multi = False
        self.direction = simics.Sim_Connector_Direction_Down
    def get_check_data(self, cmp, cnt):
        return []
    def get_connect_data(self, cmp, cnt):
        return []
    def check(self, cmp, cnt, attr):
        return True
    def connect(self, cmp, cnt, attr):
        pass
    def disconnect(self, cmp, cnt):
        pass

class korben(StandardConnectorComponent):
    """The Korben component."""
    _class_desc = 'a Korben component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_korben_objects()
        self.add_korben_connectors()

    def add_korben_connectors(self):
        self.add_connector('earth', BessonUpConnector())

    def add_korben_objects(self):
        pass

class zorg(StandardConnectorComponent):
    """The Zorg component."""
    _class_desc = 'a Zorg component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_zorg_objects()
        self.add_zorg_connectors()

    def add_zorg_connectors(self):
        self.add_connector('water', BessonDownConnector())

    def add_zorg_objects(self):
        pass

class leeloo(StandardConnectorComponent):
    """The Leeloo component."""
    _class_desc = 'a Leeloo component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_leeloo_objects()

    def add_leeloo_objects(self):
        self.add_pre_obj('clock', 'clock', freq_mhz = 10)
        self.add_component('korb_slot', 'korben', [])
        self.add_component('zorg_slot', 'zorg', [])
        self.connect(self.get_slot('korb_slot.earth'),
                     self.get_slot('zorg_slot.water'))

The leeloo component creates a korben sub component and zorg sub component. The two sub components are connected using the connect method. Arguments to the method are the connectors in the sub components.

24.6.12 Overriding Interfaces

The StandardComponent class implements the component interface. The interface implementation can be overridden if needed.

class godzilla(StandardComponent):
    """The Godzilla component."""
    _class_desc = 'a Godzilla component'

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_godzilla_objects()

    def add_godzilla_objects(self):
        self.add_pre_obj('mem', 'memory-space')
        self.add_pre_obj('p_mem', 'memory-space')

    class component(StandardComponent.component):
        def post_instantiate(self):
            self._up.get_slot('mem').default_target = [
                self._up.get_slot('p_mem'), 0, 0, None]

In the example we override the post_instantiate method. A component usually does not have to override the component interface.

24.6.13 Adding Dynamic Connectors

A connector can support connecting to multiple connectors or just one other connector. This is defined by the multi attribute for the connector. Connectors that connect to multiple connectors are not recommended, it is often better to support dynamic connectors, i.e. new connectors that are created when required.

Here is an example how to create connectors when needed, the example can be found in [simics]/src/components/sample-dynamic-connectors:

import simics
from comp import *

class sample_dynamic_connectors(StandardComponent):
    """A sample component dynamically creating connectors."""
    _class_desc = "sample comp with dynamic connectors"

    def setup(self):
        super().setup()
        if not self.instantiated.val:
            self.add_objects()

    class top_level(StandardComponent.top_level):
        def _initialize(self):
            self.val = True

    class num_serials(SimpleAttribute(0, 'i')):
        """Number of serial connectors"""

    def create_uart_and_connector(self):
        num = self.num_serials.val
        self.add_connector(
            'uart%d' % num, 'serial', True, False, False,
            simics.Sim_Connector_Direction_Down)
        if self.instantiated.val:
            o = simics.SIM_create_object('NS16550', '')
        else:
            o = pre_obj('', 'NS16550')
        self.add_slot('uart_dev%d' % num, o)
        self.num_serials.val += 1

    def add_objects(self):
        self.add_pre_obj('clock', 'clock', freq_mhz = 10)
        self.create_uart_and_connector()

    class component_connector(Interface):
        def get_check_data(self, cnt):
            return []
        def get_connect_data(self, cnt):
            self._up.create_uart_and_connector()
            num = int(cnt.name.split('uart')[1])
            return [None, self._up.get_slot('uart_dev%d' % num), cnt.name]
        def check(self, cnt, attr):
            return True
        def connect(self, cnt, attr):
            num = int(cnt.name.split('uart')[1])
            udev = self._up.get_slot('uart_dev%d' % num)
            (link, console) = attr
            if link:
                udev.link = link
            else:
                udev.console = console
        def disconnect(self, cnt):
            num = int(cnt.name.split('uart')[1])
            udev = self._up.get_slot('uart_dev%d' % num)
            udev.link = None
            udev.console = None

The create_uart_and_connector method in the sample_dynamic_connectors component creates a new uart connector object and a uart device. The function is called each time someone connects to one of the component's connectors and when the component is created. This means that the component will have one empty connector when the component is created and there will always exist at least one empty connector in the component.

The example code does not handle disconnecting and removal of unused connectors. This means that there might exist more than one empty connector. But it is just an example that can be used as reference.

24.7 Example Component

24.7.1 Sample Component

The sample component is a very simple component that can be used as reference when writing a component. The source code can be found in the src/components/sample-components directory. The sample-pci-card can for instance be added to the Firststeps machine. This Firststeps machine is in QSP-x86 Package.

Here is an example how to add a new PCI card from the command line:

simics> load-module sample-components
simics> new-sample-pci-card foo integer_attribute = 99
Created instantiated 'sample_pci_card' component 'foo'
simics> connect foo.pci_bus "board.mb.nb.pci_slot[0]"

24.7.2 Hierarchical Component Example

This section describes a hierarchical system with components. We use the simple PC system in figure 13 as an example.

Figure 13. Example hierarchical system

The system in figure 13 consists of the pc_system, motherboard, northbridge, southbridge, pci_eth, usb_device, and two ddr_memory components.

The components contains both regular objects (drawn as ellipses in the figure), and sub components (drawn as rectangles with drop shadows). An object can be a processor, device, or an extension. Remember that an extension is something that add simulation functionality, such as a trace object.

The blue and cyan circular objects on the edge of the components are connectors. A blue connector is a connector that is owned by the component it is in. A cyan connector is an inherited connector from another component. An inherited connector can also be seen as a reference connector. The dashed line shows how the connector has been inherited. The connector called eth in northbridge has been inherited to motherboard as geth, and geth has been inherited by pc_system as eth0. The pc_system component could inherit eth directly from northbridge, but that is not the case in this example. We will not distinguish reference connectors from real connectors in this section, as they look identical to an outside observer. Only the component designer who sets up the system needs to be aware of the distinction.

Connectors can be connected to other connectors, forming connections. The solid blue lines in the figure are connections. Two connectors can only be connected if they belong to components that are on the same hierarchical level, or if one of the components is a sub component of the other. The northbridge and southbridge components are on the same level, and they can connect to each other through their connectors. The ddr_memory and motherboard components can be connected because the ddr_memory components are sub components of motherboard.

The pc_system component contains all components in its component tree except usb_device. Both pc_system and usb_device are on the same level—in this case, the so-called root level.

The pci_eth and ddr_memory0_1 components have a different color to indicate that they were not defined in the pc_system component, but added at runtime. The pc_system.ddr2_3 connectors have not been connected to any component.

Figure 14. Object connections in a hierarchical system

It is actually objects that must be connected (see section 24.7.2); the component connectors merely provide a way of sending the data between components needed for setting up the object attributes. Figure 14 shows the components and the objects from figure 13, but now the actual connections between the objects are in focus. The dashed lines between the objects show how the objects are connected. A lot of the objects are connected to the pci_bus object. This is very common on a generic PC system. Even objects that are not in the same component tree are connected, e.g., the usb object and the usb_disk object.

24.7.3 Flat Component Example

This section describes how to create a system consisting of only root components in component hierarchies. Although it is better to create hierarchical system models as shown in the example in section 24.7.2, the creation of flat systems is supported. The flat system modeling concept is primarily useful when modeling a system that is best described as a "collection of peers", such as a rack of computers. Additionally, several standard system models provided with Simics are implemented as flat systems, since hierarchical components were not supported when these systems were built. It is expected that all such systems will be upgraded to hierarchical models over time.

Figure 15. Example of a flat system

Figure 15 shows a flat system representation of the system shown in figure 13. The difference is that there are no hierarchical components in figure 15.

The biggest advantage of hierarchical systems over flat systems is clarity. In a hierarchical configuration, objects, commands, and attributes are only accessible via their hierarchical name in the hierarchy, and do not clutter the top-level namespace. This is particularly important for large systems with many objects. Consider a big rack with several boards, each with several devices and processors. Simics requires that all objects at the same level of the namespace have unique names. Thus, objects of the same type in different parts of the system either need long, essentially hierarchical, names; or else the user must remember which randomly named objects make up which part of the system. Using hierarchy reduces complexity by providing a system for naming.

24.8 Component Reference

This section describes the interfaces, commands, attributes, and classes that are provided by Simics to help implement components.

24.8.1 Component Interfaces

This section documents the set of interfaces that every component is required to implement. These interfaces ensure that the component works with Simics commands and the API that operates on components. See 24.11 for the default implementations provided by the comp Python module.

24.8.1.1 component_interface_t

All component classes must implement the component interface. All functions in the interface must be implemented.

The pre_instantiate function is called before the component is instantiated. The function returns true if the component can be instantiated, or false if not.

The component might need to do some extra work after the component has been instantiated. This should be done when called via the post_instantiate function.

The create_cell function returns true if the configuration system can create a default cell object for the component, or false if not. Both pre_instantiate and create_cell typically return true.

Component has slots. A slot has key and value. The key is the slot name as a string. The value is a conf object, a pre conf object, or None, or nested lists of such types.

Slots are either defined in the component or added after the component has been created. Slots defined in the component are static slots which can not be deleted, but the slot value can be changed. Slots added to the component after creation are dynamic slots and they can be removed when wanted.

The get_slots function returns a dictionary with slot names as dictionary keys and slot values as dictionary values.

The get_slot_objects function returns a list of all conf objects and pre conf objects extracted from all slot values.

The get_slot_value returns the slot value. The slot name is passed as slot argument. A slot value is set using the set_slot_value function. The value argument should be a conf object, pre conf object, or None, or nested lists of such types. The get function returns NULL on failure. The set function does not return anything to indicate failure.

The has_slot function returns true if the slot exists, otherwise false. The slot can either be a static slot or a dynamic slot. The add_slot function adds the slot named slot. Adding a slot can fail if the slot already exist. The added slot will be a dynamic slot. A dynamic slot can be deleted. The del_slot function deletes a dynamic slot. Deleting a slot will fail if the slot does not exist or if the slot is static. Both add_slot and del_slot returns true on success or false on failure.

SIM_INTERFACE(component) {
        bool (*pre_instantiate)(conf_object_t *obj);
        void (*post_instantiate)(conf_object_t *obj);
        bool (*create_cell)(conf_object_t *obj);

        attr_value_t (*get_slots)(conf_object_t *obj);
        attr_value_t (*get_slot_objects)(conf_object_t *obj);

        attr_value_t (*get_slot_value)(conf_object_t *obj,
                                 const char *NOTNULL slot);
        void (*set_slot_value)(conf_object_t *obj,
                         const char *NOTNULL slot,
                         attr_value_t value);

        bool (*has_slot)(conf_object_t *obj,
                         const char *NOTNULL slot);
        bool (*add_slot)(conf_object_t *obj,
                         const char *NOTNULL slot);
        bool (*del_slot)(conf_object_t *obj,
                         const char *NOTNULL slot);
};
#define COMPONENT_INTERFACE "component"

24.8.1.2 component_connector__interface_t

The component_connector is implemented by components that use connector objects for handling connections between components.

The connection setup is made in two stages, the check stage and the connect stage. The check stage is often not needed, but it can be used to make sure that the later connect step will not fail. Each connection is handled by a connector object. The connector object will both handle the connection in both direction, i.e. sending connect information and receiving connector information. Two components that should be connected must implement one connector object each.

The get_check_data and get_connect_data will be called from the connector object to get connection data to send to the other part of the connection, i.e. to the destination. The data sent must be an attr_value_t type.

The check, connect, and disconnect functions are called from the connector object when another connector wants to connect to this connection. The connection data is passed as the attr argument.

SIM_INTERFACE(component_connector) {
        attr_value_t (*get_check_data)(conf_object_t *obj,
                                       conf_object_t *NOTNULL connector);
        attr_value_t (*get_connect_data)(conf_object_t *obj,
                                         conf_object_t *NOTNULL connector);
        bool (*check)(conf_object_t *obj, conf_object_t *NOTNULL connector,
                      attr_value_t attr);
        void (*connect)(conf_object_t *obj, conf_object_t *NOTNULL connector,
                        attr_value_t attr);
        void (*disconnect)(conf_object_t *obj,
                           conf_object_t *NOTNULL connector);
};

#define COMPONENT_CONNECTOR_INTERFACE "component_connector"

24.8.2 Component Commands

All required component commands are either provided by the comp Python module or generated by project-setup as described in 24.6.1. The standard info and status commands will need to be extended to be relevant to the actual component.

24.8.3 Component Attributes

This section documents the set of attributes that every component is required to implement. These attributes ensure that the component works with Simics commands and the API that operates on components.

Note that some attributes are marked as optional. This means that the value of the attribute does not need to be specified, not that the attribute does not need to be implemented.

We draw a distinction between attributes that define the state of a given instance of a component, and class attributes that are the same for all instances of a given component class.

24.8.3.1 Attributes

24.8.3.2 Class Attributes

24.8.4 Standard Attributes

It is recommended that some standard attribute names are used for common component characteristics. Such attributes are used by the Simics Control window in the GUI for example to collect information. The attributes only need to be readable.

24.8.5 Standard Interfaces

Storage devices should implement the disk_component interface that is used by the GUI to present information about the total amount of attached disk storage in a system.

24.9 Various Component Features

The following information is not meant to provide coding guidelines but to help the programmer gain a better understanding of components.

24.9.1 Checkpointing

The majority of a component's state is checkpointed via the attributes that are used to configure it. There are also separate attributes that are only used for checkpointing, discussed in sections 24.6.5 and 24.8.3.

Other data that should be checkpointed includes state calculated or received during the connection phase, since it may be needed to support later reconfiguration for hotplugging components.

All information about connectors and connections is checkpointed automatically by the connector objects in the components.

24.9.2 Automatic Queue Assignment

All Simics configuration objects that handle time in any way must have their queue attribute set. A queue makes time advance, and makes it possible to post events. Any object that implements the cycle interface can be used as a queue; the only objects that currently do this are processors and objects of the class clock. All objects that have the same queue are said to be part of the same time domain.

The component system automatically sets the queue attribute for all objects at instantiation time, based on the component hierarchy. To override the automatic queue assignment, for example on multiprocessor boards where each processor should be its own queue, simply assign the queue attribute when adding the pre-configuration objects.

When building a model of an asymmetric multiprocessor board which logically consists of multiple systems, create multiple sub components rather than spending time manually setting queue attributes.

24.9.3 Automatic Recorder Assignment

Devices that handle input, such as serial and network devices, keyboards, and mice, usually implement a connection to a recorder object. All their input passes through the recorder so that it may record the input to the file and later replay the same input from the file.

The component system automatically creates a recorder and connects it to all input devices that have a recorder attribute. A component can override this automatic assignment by setting the recorder attribute itself for its objects.

24.9.4 Inheritance

Since components are implemented as Python classes, it is easy to create new components that are similar to existing ones by using inheritance. Instead of basing the component on the StandardComponent base class, another component class can be used. The new component class can, for example, remove unnecessary connectors, add new connectors, add new objects, and override methods.

24.9.5 Hotplugging

Components may be added and removed from the configuration during simulation. This can be used to simulate the effects of changes in the simulated hardware, e.g., plugging in a new board into a rack or unplugging a network cable.

The connectors to the component must be set to support hotplugging to allow connection or disconnection during simulation. You can even instantiate an extension to an existing configuration using hotplugging.

When a connector representing a link is disconnected, all in-flight messages on the link are discarded.

If a component is disconnected, i.e., all of its connectors are disconnected, communication to and from the component is stopped. Both the objects within component and the rest of the configuration continues to be simulated. Additionally, any communication within the component continues. To stop the simulation in the component while continuing simulation in the rest of the configuration (e.g., simulating power-off of a component), you have to explicitly add this functionality to the models. The models should remove the events that they have posted on any event queues. The queue attributes will be set to Nil by Simics after the disconnection.

If a component is connected again to some other part of the configuration, the queue attributes of the object making up the component are automatically set to a queue inside the new top-level component. If a specific queue is needed, it should be passed along in the connect data and be assigned in the connect method. Simics will not touch queue attributes that are set by the connect method. The component should notify its models about being hotplugged, so that they can repost their events on the new queue.

24.10 Standard Connector Types

The following is a list of common connector types found in many of the architecture models implemented by Simics. Machine-specific connector types are not described in this section. The tables list all connector directions and the data that should be passed for connectors of each direction, including check data if it is different from the connect data.

The data listed for each connector type should be returned by the get_connect_data function in the component_connector interface, which is implemented by all components. The check data should be returned by the get_check_data function in the component interface. See the section 24.6.10 for more information how to implement connectors.

one direction[<first argument>, <second argument>, ]
other direction(s)[<first argument>, ]
down[<AGP slot>, <agp-bus object>]
up[[[<AGP function>, <agp-device object>]*]]
down[<PCI device number>, <PCI bus object>]
up[[[<PCI function number>, <PCI device object>, <is_bridge>]*]]
any[<datagram link object>]
down[<device object>] or [[<device object>, <port>]]
up[<device object>] or [[<device object>, <port>]]
any[<ethernet link object>]
down[<device object>] or [[<device object>, <port>]]
up[<device object>] or [[<device object>, <port>]]
down[<fc-controller object>]
up[<fc-device object>, <loop ID>]
down[<graphics-device object>]
up[<graphics-console object>]
down[<keyboard object>]
up[<console object>]
down[<i2c-link-v2 device object>, <port>]
up[<i2c-link-v2 link object>]
down-
up[<IDE device object>]
down[<port-space object>, <memory-space object>, <interrupt object>, <dma object>]
up-
up check[[<port-number>*]]
down[<i2c-bus object>, <i2c-bus address>]
up[<memory-megs>, <memory-ranks>]
up check[<type>, <memory_megs>, <memory-ranks>, <bit width>, <ECC width>]
down[<mouse object>]
up[<console object>]
down/input or up/output[<target object>, <port>]
down/output or up/input-
down[<PCI device number>, <PCI bus object>]
up[[[<PCI function number>, <PCI device object>]*]]
down[<PCMCIA device object>, <slot ID>]
up[<attr-space object>, <common-space object>, <io-space object>]
down[<device object>] or [[<device object>, <port>]]
up[<device object>] or [[<device object>, <port>]]
down[<interrupt object>, <io-apic object>]
up[<interrupt object>]
down[<link>, <serial-device object>, <console title>]
up[<serial-link>, <text-console object>]
down-
up[<MMC/SD card object>]

24.11 The comp Component

The API Reference Manual documents the classes and methods implemented in the comp Python module. This section documents the default implementation of the methods in the component interface in the StandardComponent class. This section does not duplicate the documentation of StandardComponent methods not part of the component interface, for example get_slot, which are documented in the reference manual in the Python-specific API section of the API chapter.

comp.StandardComponent.component.add_slot()

NAME

  • add_slot — add slot

SYNOPSIS

  • add_slot(self, slot)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function adds a dynamic slot named slot if it does not already exist. It returns True if it could add the slot.

RETURN VALUE

  • True or False

comp.StandardComponent.component.create_cell()

NAME

  • create_cell — create cell for component

SYNOPSIS

  • create_cell(self)
    

DESCRIPTION

  • Returns cell creation status for component. The default behavior depends on the automatic_cell_partition attribute in the sim object and if the component is a top-level component.

RETURN VALUE

  • Returns True if automatic cell partitioning is enabled and the component is a top-level component, otherwise it returns False.

comp.StandardComponent.component.del_slot()

NAME

  • del_slot — delete slot

SYNOPSIS

  • del_slot(self, slot)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function deletes the dynamic slot named slot. The function returns True if it could remove the slot, otherwise it returns False.

RETURN VALUE

  • True or False

comp.StandardComponent.component.get_slot_objects()

NAME

  • get_slot_objects — get slot objects

SYNOPSIS

  • get_slot_objects(self)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function will return all objects in the static and dynamic slots.

RETURN VALUE

  • list of objects

comp.StandardComponent.component.get_slot_value()

NAME

  • get_slot_value — get slot

SYNOPSIS

  • get_slot_value(self, slot)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function will return the slot value for the slot named slot.

RETURN VALUE

  • value in slot

comp.StandardComponent.component.get_slots()

NAME

  • get_slots — get slot dictionary

SYNOPSIS

  • get_slots(self)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function will return all static and dynamic slots as a dictionary.

RETURN VALUE

  • dictionary with all slots

comp.StandardComponent.component.has_slot()

NAME

  • has_slot — check if valid slot

SYNOPSIS

  • has_slot(self, slot)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function returns True if there exists a static or dynamic slot named slot in the component, otherwise it returns False.

RETURN VALUE

  • True or False

comp.StandardComponent.component.post_instantiate()

NAME

  • post_instantiate — post instantiation functionality

SYNOPSIS

  • post_instantiate(self)
    

DESCRIPTION

  • The function will be called when the component has been instantiated.

    The default behavior is to do nothing.

comp.StandardComponent.component.pre_instantiate()

NAME

  • pre_instantiate — instantiate component status

SYNOPSIS

  • pre_instantiate(self)
    

DESCRIPTION

  • Should return True if component is allowed to be instantiated, False otherwise. The default behavior is to return TRUE.

RETURN VALUE

  • True

comp.StandardComponent.component.set_slot_value()

NAME

  • set_slot_value — set slot

SYNOPSIS

  • set_slot_value(self, slot, val)
    

DESCRIPTION

  • Standard implementation, see the component interface. The function sets the slot named slot to val.
23 Memory Spaces 25 Adding Flash to a System