Previous - Up - Next

6.4   Attributes

Attributes are linked to the class definition, usually just after the class has been declared, with the SIM_register_typed_attribute() function. It has the following declaration in C, and Python:

// in C
int SIM_register_typed_attribute(
           conf_class_t *cls, const char *name,
           get_attr_t get_attr, lang_void *user_data_get,
           set_attr_t set_attr, lang_void *user_data_set,
           attr_attr_t attr,
           const char *type, const char *idx_type,
           const char *doc);

# in Python
SIM_register_typed_attribute(cls, 
                             name,
                             get_attr, user_data_get,
                             set_attr, user_data_set,
                             attr,
                             type, idx_type,
                             doc)

The parameters of SIM_register_typed_attribute() are:

cls
The name of the class (previously registered with SIM_register_class().)
name
The name of the attribute to register.
get_attr, set_attr
The get() and set() functions for the attribute. If one of these operations is not supported, NULL (or None in Python) can be used.
user_data_get, user_data_set
User defined parameters that are passed to the get and set functions as the arg parameters. This allows passing one extra argument, for example when the same get() or set() function is shared between a number of attributes.
attr
The properties of the attribute, a combination of the configuration type, an optional index type and initialization order. It tells Simics how the attribute will be saved and addressed, and is specified using the constants described below.

The configuration type of an attribute must be selected from one of the following values:

Sim_Attr_Required
The attribute has to be set when creating the object. It will also be saved during checkpointing.
Sim_Attr_Optional
If a value isn't specified, the attribute will keep its default value when creating an object. It will be saved during checkpointing.
Sim_Attr_Session
The attribute is only used in the current session and won't be saved during checkpointing.
Sim_Attr_Pseudo
The attribute does not really represent any internal state. It may work instead as a command in disguise, or as a redundant way of accessing internal state. It won't be saved during checkpointing.

Attributes may also have the following additional kinds added (using a bitwise or operation).

Sim_Attr_Persistent
Attribute represents a persistent value and is included in persistent files, created with the save-persistent-state command. Persistent attributes are used for data that survives power-cycling.
Sim_Attr_Internal
Indicates that the attribute is internal to the object and should not be accessed directly by other users.

The index type, if any, is added (using a bitwise or operation) with one or several of the following values:

Sim_Attr_Integer_Indexed
Attribute can be indexed with an integer value.
Sim_Attr_String_Indexed
Attribute can be indexed with a string.
Sim_Attr_List_Indexed
Attribute can be indexed with a list.

In addition the order in which the attribute will be initialized can be defined by adding (also using a bitwise or operation) with one of the following values:

Sim_Init_Phase_0
Early initialization (default)
Sim_Init_Phase_1
Late initialization

Attributes with Sim_Init_Phase_1 will be initialized after attributes with Sim_Init_Phase_0, but no other order is guaranteed.

type, idx_type
Strings describing the data type of the attribute. type describes the type when not indexed, and type_idx when indexed.
desc
A documentation string describing the attribute.

6.4.1   A Simple Example

Let us use a simple counter attribute as an example.

In C, we'll have an object declared as:

typedef struct my_object {
        conf_object_t obj;
        int foo;
} my_object_t;

In Python, we'll use a data member of the class defining the object (sample-python-class) that we will call foo.

We want to implement an attribute called counter, thus we need a pair of set/get functions. counter will internally use foo to keep its value. The pair of get/set functions could be defined as:

// In C
static attr_value_t
get_counter(void *arg, conf_object_t *obj, attr_value_t *idx)
{
        my_object_t *mo = (my_object_t *)obj;

        return SIM_make_attr_integer(mo->foo);
}

static set_error_t
set_counter(void *arg, conf_object_t *obj, attr_value_t *val,
            attr_value_t *idx)
{
        my_object_t *mo = (my_object_t *)obj;

        mo->foo = val->u.integer;
        return Sim_Set_Ok;
}

In the get_counter() function, obj is the object that owns the attribute and arg is the user information that was registered along with the attribute. Note that obj can be safely cast to my_object_t (conf_object_t is used as a "base type" here). The function creates an attr_value_t variable that will be of type Sim_Val_Integer and contain the value foo. It then returns this attribute value.

The set_counter() function on the other hand takes a val argument which contains the value to be written. The return value is of type set_error_t, which is defined as below. Descriptions of the values can be found in the Simics Reference Manual.

typedef enum {
        Sim_Set_Ok,
        Sim_Set_Need_Integer,
        Sim_Set_Need_Floating,
        Sim_Set_Need_String,
        Sim_Set_Need_List,
        Sim_Set_Need_Dict,
        Sim_Set_Need_Boolean,
        Sim_Set_Need_Data,
        Sim_Set_Need_Object,
        Sim_Set_Object_Not_Found,
        Sim_Set_Interface_Not_Found,
        Sim_Set_Illegal_Value,
        Sim_Set_Illegal_Type,
        Sim_Set_Attribute_Not_Found,
        Sim_Set_Not_Writable,
        Sim_Set_Ignored
} set_error_t;

In Python, the value used in the get()/set() functions is represented in Python's native types:

# In Python
def get_counter(arg, obj, idx):
    return obj.object_data.counter

def set_counter(arg, obj, val, idx):
    obj.object_data.counter = val
    return Sim_Set_Ok

Note, however, that the object is referred to by the log object, and not the object created from the new_instance() function that creates object instances. The data of the object is thus referred to via the object_data member of the obj parameter.

Registering the counter attribute is just a matter of calling SIM_register_typed_attribute():

// in C
SIM_register_typed_attribute(my_class, 
                             "counter",
                             get_counter, NULL,
                             set_counter, NULL,
                             Sim_Attr_Required,
                             "i", NULL,
                             "A counter");

# and in Python
SIM_register_typed_attribute(my_class, 
                             "counter",
                             get_counter, None,
                             set_counter, None,
                             Sim_Attr_Required,
                             "i", None,
                             "A counter");

6.4.2   A Pseudo Attribute

In the previous example, the attribute counter was a direct representation of the value foo inside the object. Now let us add an attribute called add_counter that will increase foo by a given value when the attribute is set, and do nothing when the attribute is read. This would give us the following code:

// In C
static set_error_t
set_add_counter(void *arg, conf_object_t *obj, attr_value_t *val,
                attr_value_t *idx)
{
        my_object_t *mo = (my_object_t *)obj;

        mo->foo += val->u.integer;
        return Sim_Set_Ok;
}

# and in Python
def add_counter(arg, obj, val, idx):
    obj.object_data.counter += val
    return Sim_Set_Ok

There is no need for a get function since this attribute only can be written. The semantics of set_add_counter() are also slightly different, since the function actually adds a value to foo.

It is thus possible to create real attributes whose value corresponds to a real variable in an object, and pseudo attributes which are only used as object "methods".

Registering the add_counter attribute is straightforward:

// In C
SIM_register_typed_attribute(class_name, "add_counter",
                             NULL, NULL,
                             set_add_counter, NULL,
                             Sim_Attr_Pseudo,
                             "i", NULL,
                             "A sample pseudo attribute.")

# and in Python
SIM_register_typed_attribute(class_name, "add_counter",
                             None, None,
                             add_counter, None,
                             Sim_Attr_Pseudo,
                             "i", None,
                             "A sample pseudo attribute.")

6.4.3   An indexed attribute

If an attribute advertises the possibility of being indexed, Simics will allow access using an index of a type specified when registering the attribute: an integer, a string and/or a list. In Python, a slice is also accepted and will be converted to a list as described later in this section. Using indexed attributes in Python would correspond to the following code:

object.attr[i] = a
b = object.attr['a string']
c = object.attr[i:j]
d = object.attr[[i,j]]

When an indexed access is performed, the idx parameter of the get()/set() functions contains the index value. In C this value will be coded as an attr_value_t representing an integer, a string or a list. In Python, the index will be the corresponding native python type.

Let us add an array of counters to our previous example:

// In C
typedef struct my_object {
        conf_object_t obj;
        int foo[10];
} my_object_t;

The get() and set() function will be:

// In C
static attr_value_t
get_counter_array(void *arg, conf_object_t *obj, attr_value_t *idx)
{
        my_object_t *mo = (my_object_t *)obj;

        if (idx->kind != Sim_Val_Nil) {
                if (idx->u.integer < 0 || idx->u.integer >= 10)
                        return SIM_make_attr_invalid();

                return SIM_make_attr_integer(mo->foo[idx->u.integer]);
        } else {
                attr_value_t ret = SIM_alloc_attr_list(10);
                int i;
                for (i = 0; i < 10; i++) {
                        ret.u.list.vector[i] =
                                SIM_make_attr_integer(mo->foo[i]);
                }
                return ret;
        }
}

static set_error_t
set_counter_array(void *arg, conf_object_t *obj, attr_value_t *val,
                  attr_value_t *idx)
{
        my_object_t *mo = (my_object_t *)obj;

        if (idx->kind != Sim_Val_Nil) {
                if (idx->u.integer < 0 || idx->u.integer >= 10)
                        return Sim_Set_Illegal_Value;

                mo->foo[idx->u.integer] = val->u.integer;
        } else {
                int i;
                for (i = 0; i < 10; i++)
                        mo->foo[i] = ret.u.list.vector[i].u.integer;
        }

        return Sim_Set_Ok;
}

# And in Python:
def get_counter_array(arg, obj, idx):
    if idx != None:
        if idx < 0 or idx >= 10:
            return None
        return obj.object_data.vcounter[idx]
    else:
        return obj.object_data.vcounter

def set_counter_array(arg, obj, val, idx):
    if idx != None:
        if idx < 0 or idx >= 10:
            return Sim_Set_Illegal_Value
        obj.object_data.vcounter[idx] = val
    else:
        obj.object_data.vcounter = val
    return Sim_Set_Ok

SIM_register_typed_attribute(class_name, "counter_array",
                             get_counter_array, None,
                             set_counter_array, None,
                             Sim_Attr_Optional | Sim_Attr_Integer_Indexed,
                             "[i{10}]", "i",
                             "Sample array of counters")

These functions handle both indexed and non-indexed access to the attribute. This is important for checkpointing, as all attributes are saved and restored using non-indexed access. Thus, if you wish your attribute to be checkpointed, you will always have to handle non-indexed accesses.

As shown in the example usages above, it is also possible to use strings, lists and Python slices as index. An index corresponding to a slice is provided as a list of two elements, the first and last element that should be included. Note that this means a slight change, since the Python slice refers to the first element included, and the one after the last included. This means that: object.attr[i:j] will be translated to an index parameter with the value [i, j - 1], or in C, an attribute of type Sim_Val_List with two Sim_Val_Integer elements i and j − 1.


Note: When accessing attributes from Python, you may not be able to access an indexed attribute without specifying an index even if your get function supports a non-indexed access. The reason is that the short access syntax is ambiguous: it can not differentiate between an indexed access to a Simics attribute and a non-indexed access later indexed by Python itself. You will have to explicitly use SIM_get_attribute() to access the attribute's non-indexed value.

As an example, let's consider two attributes, foo and bar. foo is an array of 5 elements that do not support indexing, while bar is a table of 10 elements supporting both indexed and non-indexed accesses:

# non-indexed access to 'foo' attribute
simics> @conf.object.foo
[0,1,2,3,4]

# non-indexed access to 'foo' attribute, later indexed by python
simics> @conf.object.foo[2]
2

# indexed access to 'bar' attribute
simics> @conf.object.bar[12]
12

# no access performed to 'bar' attribute
simics> @conf.object.bar
<object bar>

# non-indexed access to 'bar' attribute
simics> @SIM_get_attribute(conf.object, "bar")
[0,1,2,3,4,5,6,7,8,9]

Previous - Up - Next