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:
The configuration type of an attribute must be selected from one of the following values:
Attributes may also have the following additional kinds added (using a bitwise or operation).
The index type, if any, is added (using a bitwise or operation) with one or several of the following values:
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:
Attributes with Sim_Init_Phase_1 will be initialized after attributes with Sim_Init_Phase_0, but no other order is guaranteed.
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");
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.")
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.
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]