attr_value_t SIM_alloc_attr_dict(unsigned length);
attr_value_t
of type
dict with size len. The dictionary
elements are initialized to invalid values.
attr_value_t SIM_alloc_attr_list(unsigned length);
attr_value_t
of type
list with size length. The list
elements are initialized to invalid values.
attr_value_t SIM_attr_copy(attr_value_t val);
This function is not available from Python.
void SIM_attr_dict_resize(attr_value_t *NOTNULL attr, unsigned newsize);
void SIM_attr_dict_set_item(attr_value_t *NOTNULL attr, unsigned index, attr_value_t key, attr_value_t value);
void SIM_attr_free(attr_value_t *NOTNULL value);
void SIM_free_attribute(attr_value_t value);
SIM_attr_free is the preferred call because it changes the type of the argument variable to Invalid, preventing accidental use after freeing. SIM_free_attribute only differs in how the argument is passed, but cannot change the argument variable as it is passed by value.
attr_value_t
valuesFORCE_INLINE int64 SIM_attr_integer(attr_value_t attr);
FORCE_INLINE bool SIM_attr_boolean(attr_value_t attr);
FORCE_INLINE const char * SIM_attr_string(attr_value_t attr);
FORCE_INLINE char * SIM_attr_string_detach(attr_value_t *attr);
FORCE_INLINE double SIM_attr_floating(attr_value_t attr);
FORCE_INLINE conf_object_t * SIM_attr_object(attr_value_t attr);
FORCE_INLINE conf_object_t * SIM_attr_object_or_nil(attr_value_t attr);
FORCE_INLINE unsigned SIM_attr_data_size(attr_value_t attr);
FORCE_INLINE const uint8 * SIM_attr_data(attr_value_t attr);
FORCE_INLINE unsigned SIM_attr_list_size(attr_value_t attr);
FORCE_INLINE attr_value_t SIM_attr_list_item(attr_value_t attr, unsigned index);
FORCE_INLINE attr_value_t * SIM_attr_list(attr_value_t attr);
FORCE_INLINE unsigned SIM_attr_dict_size(attr_value_t attr);
FORCE_INLINE attr_value_t SIM_attr_dict_key(attr_value_t attr, unsigned index);
FORCE_INLINE attr_value_t SIM_attr_dict_value(attr_value_t attr, unsigned index);
SIM_attr_integer returns the integer attribute value
modulo-reduced to the interval
[−263,263−1].
(Converting the return value to uint64
gives the integer
attribute value modulo-reduced to [0,264−1].)
SIM_attr_string, SIM_attr_data and SIM_attr_list return values owned by attr. Ownership is not transferred to the caller.
SIM_attr_string_detach returns the string in attr and changes the value pointed to by attr into a nil attribute. Ownership of the string is transferred to the caller.
SIM_attr_object_or_nil accepts an attr parameter of either object or nil type. In case of a nil attribute, the function returns NULL.
SIM_attr_list_size and SIM_attr_dict_size return the number of items in the list and key-value pairs in the dict respectively. SIM_attr_data_size returns the number of bytes in the data value.
SIM_attr_list_item returns the item at index. The index must be less than the number of items in the list. The item returned is still owned by attr. Ownership is not transferred to the caller.
SIM_attr_list returns a pointer directly into the internal array of the attribute value; it is mainly present as an optimisation. Use SIM_attr_list_item and SIM_attr_list_set_item for type-safety instead.
SIM_attr_dict_key and SIM_attr_dict_value return the key and value at index. The index must be less than the number of items in the dict. The value returned is still owned by attr. Ownership is not transferred to the caller.
attr_value_t
type predicatesFORCE_INLINE bool SIM_attr_is_integer(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_boolean(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_string(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_floating(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_object(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_invalid(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_data(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_list(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_dict(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_nil(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_int64(attr_value_t attr);
FORCE_INLINE bool SIM_attr_is_uint64(attr_value_t attr);
void SIM_attr_list_resize(attr_value_t *NOTNULL attr, unsigned newsize);
void SIM_attr_list_set_item(attr_value_t *NOTNULL attr, unsigned index, attr_value_t elem);
bool SIM_attr_scanf(attr_value_t *NOTNULL list, const char *NOTNULL fmt, ...);
bool SIM_ascanf(attr_value_t *NOTNULL list, const char *NOTNULL fmt, ...) __attribute__((alias("SIM_attr_scanf")));
true
if all elements were
successfully converted, false
otherwise.
The characters in the format string mean:
format char
argument type
element must be
i
int64 *
integer
b
int *
boolean
f
double *
floating
s
const char **
string
S
const char **
string or nil
o
conf_object_t **
object
O
conf_object_t **
object or nil
l
attr_value_t **
list
d
attr_value_t **
data
a
attr_value_t **
any except invalid
The fmt string may also include a period (.
) at the end,
taken to mean that more elements may follow. If the period is not present,
the length of the list must equal the number of specified elements.
Converted values of type attr_value_t *
and
const char *
are still owned by list.
SIM_ascanf is an alias of SIM_attr_scanf and will be deprecated.
FORCE_INLINE attr_value_t SIM_make_attr_boolean(bool b);
attr_value_t
of boolean type.
attr_value_t SIM_make_attr_data(size_t size, const void *data);
FORCE_INLINE attr_value_t SIM_make_attr_data_adopt(size_t size, void *data);
attr_value_t
of type data using
size and data for the binary data.
SIM_make_attr_data will make a copy of the argument data.
SIM_make_attr_data_adopt is mainly provided for compatibility; it will assume ownership of the argument data, which must have been allocated using one of the MM_MALLOC functions.
FORCE_INLINE attr_value_t SIM_make_attr_floating(double d);
attr_value_t
of floating type with value
d.
FORCE_INLINE attr_value_t SIM_make_attr_int64(int64 i);
FORCE_INLINE attr_value_t SIM_make_attr_uint64(uint64 i);
attr_value_t
of integer type with value
i.
FORCE_INLINE attr_value_t SIM_make_attr_invalid(void);
attr_value_t
of invalid type.
attr_value_t SIM_make_attr_list(unsigned length, ...);
attr_value_t SIM_make_attr_list_vararg(unsigned length, va_list va);
attr_value_t
of type list
with size length. The list is filled with data from the
arguments following, which should be of type attr_value_t
.
This function must be called with exactly length+1 arguments. The attribute parameters should all be valid attributes; e.g., attributes of invalid type are not allowed. The length argument must be a constant expression.
The newly created list assumes ownership of the passed parameters, which therefore should not be freed.
FORCE_INLINE attr_value_t SIM_make_attr_nil(void);
attr_value_t
of type nil.
FORCE_INLINE attr_value_t SIM_make_attr_object(conf_object_t *obj);
attr_value_t
of object type
with value obj. Returns a nil value if
obj is NULL
.
attr_value_t SIM_make_attr_string(const char *str);
FORCE_INLINE attr_value_t SIM_make_attr_string_adopt(char *str);
attr_value_t
of type string with value
str. Returns Nil if str is NULL
.
SIM_make_attr_string will make a copy of the argument string.
SIM_make_attr_string_adopt is mainly provided for compatibility; it will assume ownership of the argument string, which must have been allocated using one the MM_MALLOC functions.
void SIM_attribute_error(const char *NOTNULL msg);
void SIM_c_attribute_error(const char *NOTNULL msg, ...);
The error message supplied will be attached to any frontend exception generated by the attribute access.
conf_class_t * SIM_class_port(const conf_class_t *NOTNULL cls, const char *NOTNULL name);
conf_class_t * SIM_copy_class(const char *NOTNULL name, const conf_class_t *NOTNULL src_cls, const char *desc);
Additional attributes and interfaces can be registered on the newly created class.
The new class is described by desc unless this parameter is NULL which means that the original class description should be used.
conf_class_t * SIM_create_class(const char *NOTNULL name, const class_info_t *NOTNULL class_info);
The name can contain upper and lower case ASCII letters, hyphens, underscores, and digits. It must not begin with a digit or a hyphen and must not end with a hyphen.
class_info may be freed when the function has returned.
typedef enum { Sim_Class_Kind_Vanilla = 0, /* object is saved at checkpoints */ Sim_Class_Kind_Session = 1, /* object is saved as part of a * session only */ Sim_Class_Kind_Pseudo = 2, /* object is never saved */ Sim_Class_Kind_Extension = 3, /* extension class (see SIM_extend_class) */ } class_kind_t;
typedef struct class_info { conf_object_t *(*alloc)(conf_class_t *cls); lang_void *(*init)(conf_object_t *obj); void (*finalize)(conf_object_t *obj); void (*objects_finalized)(conf_object_t *obj); void (*deinit)(conf_object_t *obj); void (*dealloc)(conf_object_t *obj); const char *description; const char *short_desc; class_kind_t kind; } class_info_t;
NULL
on error.
void SIM_ensure_partial_attr_order(conf_class_t *NOTNULL cls, const char *NOTNULL before, const char *NOTNULL after);
This function checks the registration order of the attributes before and after in the class cls. If before is not registered before after, or if at least one of the two are not registered at all, an ASSERT is triggered.
Use this function to ensure that e.g. code refactoring does not break a required attribute order.
void SIM_extend_class(conf_class_t *NOTNULL cls, conf_class_t *NOTNULL ext);
The extension class must be of the type
Sim_Class_Kind_Extension
and must not define any attributes
or interfaces which have already been defined by the class being
augmented.
Besides normal object initialization, the init_object method for the extension class, will be called when cls is instantiated. The pointer returned by init_object can be retrieved using SIM_extension_data. The init_object method may return NULL if no private data pointer is needed; this does not signify an error condition for extension classes.
The finalize_instance method defined by the extension class will be called before the finalize_instance method is called for the class being extended.
The SIM_extension_class function is intended to be used to extend a class with generic functionality, common to multiple classes.
void * SIM_extension_data(conf_object_t *obj, conf_class_t *ext_cls);
The object obj must be an instance of a class which has been extended with the extension class ext_cls using the SIM_extend_class function.
conf_class_t * SIM_get_class(const char *NOTNULL name);
If it finds no class called name, SIM_get_class will load a module implementing that class, if any can be found, and return the newly created class.
Note that loading a module can not be done during the simulation execution: in that case, SIM_get_class will trigger an error instead. If you encounter this problem, a simple work-around is to make sure that all necessary modules are loaded before starting the execution.
lang_void * SIM_get_class_data(conf_class_t *cls);
const char * SIM_get_class_name(const conf_class_t *NOTNULL class_data);
In Python, the name of an object's class is available via the classname attribute:
obj.classname
.
const void * SIM_get_interface(const conf_object_t *NOTNULL obj, const char *NOTNULL name);
const void * SIM_c_get_interface(const conf_object_t *NOTNULL obj, const char *NOTNULL name);
const void * SIM_get_class_interface(const conf_class_t *NOTNULL cls, const char *NOTNULL name);
const void * SIM_c_get_class_interface(const conf_class_t *NOTNULL cls, const char *NOTNULL name);
const void * SIM_get_port_interface(const conf_object_t *NOTNULL obj, const char *NOTNULL name, const char *portname);
const void * SIM_c_get_port_interface(const conf_object_t *NOTNULL obj, const char *NOTNULL name, const char *portname);
const void * SIM_get_class_port_interface(const conf_class_t *NOTNULL cls, const char *NOTNULL name, const char *portname);
const void * SIM_c_get_class_port_interface(const conf_class_t *NOTNULL cls, const char *NOTNULL name, const char *portname);
SIM_get_port_interface returns a port interface instance as registered with SIM_register_port_interface. The portname selects a particular implementation of the interface by obj's class. If no port name is supplied, the function behaves as SIM_get_interface.
SIM_get_class_interface and SIM_get_class_port_interface are similar but return the interface for a class instead of an object.
SIM_c_get_interface, SIM_c_get_port_interface, SIM_c_get_class_interface and SIM_c_get_class_port_interface are similar to their respective counterparts but never raise an exception, nor do they accept dashes inside name or portname instead of underscores.
The SIM_C_GET_INTERFACE macro is a useful type-safe replacement for SIM_c_get_interface. The macro takes an object and the name of the interface without quotes. Compare the three forms:
SIM_c_get_interface(obj, PCI_DEVICE_INTERFACE); SIM_c_get_interface(obj, "pci_device"); SIM_C_GET_INTERFACE(obj, pci_device);
The data the result points to is owned by Simics. The caller must not deallocate or modify it.
In Python, there is usually no need to use these functions
since Simics objects' interfaces are available via the iface
attribute. Here is sample Python code calling
the signal_raise method of the object's
signal
interface:
obj.iface.signal.signal_raise()
In a similar way one can call interfaces of an object's
port objects. Here is a respective example where
an object obj with a port object obj.port.reset
has a signal
interface:
obj.port.reset.iface.signal.signal_raise()
Port interfaces - interfaces that are registered with
SIM_register_port_interface and are considered legacy - are also
directly accessible in Python. Here is sample code calling
the signal_raise method of the signal
interface
from the object's RESET
port:
obj.ports.RESET.signal.signal_raise()
In order to check if a Simics object implements an interface
the following Python code can be used:
if hasattr(obj.iface, "signal"): # check whether obj has signal interface ...
SimExc_General Thrown if the interface name is illegal.
bool SIM_is_loading_micro_checkpoint(conf_object_t *obj);
true
if a persistent state or
a micro checkpoint is being loaded. The return value is thus equal to
the "SIM_object_is_configured(obj) && SIM_is_restoring_state(obj)"
expression.
bool SIM_is_restoring_state(conf_object_t *obj);
obj
when reading a checkpoint, applying a
persistent state or restoring a reverse execution bookmark.
SIM_is_restoring_state is typically used to prevent side effects in attribute set methods that only should run when the attribute is set manually, for example when hot plugging.
SIM_object_is_configured | SIM_is_restoring_state | |
Creating object | false | false |
Loading checkpoint | false | true |
Loading persistent state | true | true |
Loading micro-checkpoint (rev-exec) | true | true |
Manual attribute access (hot plug) | true | false |
LIMITATION: This function currently returns true for all objects in Simics while some state is being restored and not only for the affected objects.
bool SIM_marked_for_deletion(const conf_object_t *NOTNULL obj);
true
if the object is being
deleted.
conf_object_t * SIM_object_clock(const conf_object_t *NOTNULL obj);
lang_void * SIM_object_data(conf_object_t *NOTNULL obj);
It is initialised to the return value of the init
(from class_info_t
) method
that is called during object creation. For classes created using
the legacy SIM_register_class, the same functionality
is provided by the init_object method .
For classes implemented in Python, the data (which is then a Python
value) can also be accessed as obj.object_data
.
For classes written in C, the preferred way to store
instance-specific state is by co-allocation with the object's
conf_object_t
structure instead of using
SIM_object_data. Such classes should define the
alloc method in the class_info_t
passed to SIM_create_class for allocating its instance
data. For classes using the legacy SIM_register_class
class registration function, they should define the
alloc_object method in the class_data_t
data structure.
const char * SIM_object_id(const conf_object_t *NOTNULL obj);
The return value is a static string that should not be modified or freed by the caller.
bool SIM_object_is_configured(const conf_object_t *NOTNULL obj);
void SIM_set_object_configured(conf_object_t *NOTNULL obj);
An object is configured once its finalize_instance method (post_init in DML) has completed, or SIM_set_object_configured has been called for it. Being configured indicates that the object is in a consistent state and is ready to be used by other objects.
SIM_set_object_configured is used to avoid circular dependencies between objects. It may only be called from the object's own finalize_instance method, when the object is known to be in a consistent state.
const char * SIM_object_name(const conf_object_t *NOTNULL obj);
The return value is a string, owned by obj
, that should not be
modified or freed by the caller.
In Python, an object's name is available via the name attribute:
obj.name
.
conf_object_t * SIM_picosecond_clock(conf_object_t *NOTNULL obj);
The returned clock uses a cycle period of exactly 1 ps. It has full picosecond resolution even if the processor (or clock) driving the simulation uses a lower resolution. An event posted at a particular picosecond triggers always at that precise time, without any rounding issues.
The returned object is the vtime.ps port object of
the default clock for the object, and it implements the
cycle_event
interface.
The API functions SIM_event_post_cycle, SIM_event_post_time, SIM_event_find_next_cycle, SIM_event_cancel_time, and SIM_cycle_count can be used directly on the picosecond clock.
void SIM_register_attribute( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(conf_object_t *), set_error_t (*set_attr)(conf_object_t *, attr_value_t *), attr_attr_t attr, const char *type, const char *desc);
void SIM_register_class_attribute( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(conf_class_t *), set_error_t (*set_attr)(conf_class_t *, attr_value_t *), attr_attr_t attr, const char *type, const char *desc);
void SIM_register_attribute_with_user_data( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(conf_object_t *, lang_void *), lang_void *user_data_get, set_error_t (*set_attr)(conf_object_t *, attr_value_t *, lang_void *), lang_void *user_data_set, attr_attr_t attr, const char *type, const char *desc);
void SIM_register_class_attribute_with_user_data( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(conf_class_t *, lang_void *), lang_void *user_data_get, set_error_t (*set_attr)(conf_class_t *, attr_value_t *, lang_void *), lang_void *user_data_set, attr_attr_t attr, const char *type, const char *desc);
For SIM_register_attribute and SIM_register_class_attribute, the function get_attr is called with the object as argument, and returns the current value of the attribute. For SIM_register_attribute_with_user_data and SIM_register_class_attribute_with_user_data, the function get_attr takes an additional user data argument, which takes the value passed as user_data_get.
On error, get_attr should call SIM_attribute_error. The return value is then ignored; typically, SIM_make_attr_invalid is used to generate an explicitly invalid value.
If get_attr is a null pointer, the attribute will be write-only.
For SIM_register_attribute and SIM_register_class_attribute, the function set_attr is called with the object as argument. For SIM_register_attribute_with_user_data and SIM_register_class_attribute_with_user_data, the function set_attr takes an additional user data argument, which takes the value passed as user_data_set. The set_attr function is called when the attribute is initialised or changed. The argument value is owned by the caller, so any data from it must be copied.
The set_attr method should return Sim_Set_Ok
if the new value could be set. On error, it should return an appropriate
error code (usually Sim_Set_Illegal_Value
), and optionally
call SIM_attribute_error with an explanatory message.
If set_attr is a null pointer, the attribute will be read-only.
The attr parameter is one of
Sim_Attr_Required
, Sim_Attr_Optional
or
Sim_Attr_Pseudo
.
Attributes marked Sim_Attr_Required
or
Sim_Attr_Optional
are saved in checkpoints. Both
set_attr and get_attr must be non-null
for such attributes.
All attributes that are marked Sim_Attr_Required
must be present in all configurations.
The set of permitted values is encoded in the string type.
The type strings are composed as follows:
i |
integer |
f |
floating-point |
s |
string |
b |
boolean |
o |
object |
d |
data |
n |
nil |
a |
any type (not valid when the attribute is marked with
Sim_Attr_Required ) |
|
(vertical bar) operator specifies the union of
two types; eg, s|o
is the type of a string or an object.
[]
. There are two
kinds of list declarations:
[ios]
specifies a 3-element list
consisting of an integer, an object and a string, in that order.
{ N: M} |
between N and M elements, inclusive |
{ N} |
exactly N elements |
* |
zero or more elements |
+ |
one or more elements |
For example, [i{3,5}]
specifies a list of
3, 4 or 5 integers.
Inside heterogeneous lists, |
(union) has higher precedence
than juxtaposition; ie, [i|so|n]
defines a list of two
elements, the first being an integer or a string and the second
an object or NIL.
SIM_register_class_attribute and SIM_register_class_attribute_with_user_data will register a class attribute. Class attributes are the same for all instances of the class.
conf_class_t * SIM_register_class(const char *NOTNULL name, const class_data_t *NOTNULL class_data);
The function registers a new class that can be instantiated by calling the SIM_create_object function.
The name can contain upper and lower case ASCII letters, hyphens, underscores, and digits. It must not begin with a digit or a hyphen and must not end with a hyphen.
class_data may be freed when the function has returned.
typedef struct class_data { conf_object_t *(*alloc_object)(lang_void *data); lang_void *(*init_object)(conf_object_t *obj, lang_void *data); void (*finalize_instance)(conf_object_t *obj); void (*pre_delete_instance)(conf_object_t *obj); int (*delete_instance)(conf_object_t *obj); const char *description; const char *class_desc; class_kind_t kind; } class_data_t;
NULL
on error.
void SIM_register_class_alias(const char *NOTNULL alias, const char *NOTNULL name);
Aliases are used to support compatibility with old class names if a class is renamed. They can also be used to allow different modules, which define different specific implementations of the same generic base class, to read the same configuration files.
int SIM_register_clock(conf_class_t *NOTNULL cls, const cycle_interface_t *NOTNULL iface);
cycle
interface
(iface), in addition to which SIM_register_clock
registers the cell attribute required for scheduling the clock
and some other Simics specific attributes. Simics will be able to schedule
objects instantiated from the class cls.
The return value is 0 if everything works, and non-zero if something fails. Depending on the stage that failed, SIM_register_clock() will return the error value provided by SIM_register_interface().
cycle
interface has
already been registered for this class.
void SIM_register_compatible_interfaces(conf_class_t *NOTNULL cls, const char *NOTNULL name);
When supported, this function lets a module implement a single version of an interface while still exporting earlier versions.
The following interfaces are currently accepted by this function, with the additional interfaces that are exported given in parenthesis: BREAKPOINT_QUERY_V2 (BREAKPOINT_QUERY), PROCESSOR_INFO_V2 (PROCESSOR_INFO).
int SIM_register_interface(conf_class_t *NOTNULL cls, const char *NOTNULL name, const void *NOTNULL iface);
int SIM_register_port_interface(conf_class_t *NOTNULL cls, const char *NOTNULL name, const void *NOTNULL iface, const char *NOTNULL portname, const char *desc);
SIM_register_port_interface registers a port instance of an interface that must be looked up using SIM_get_port_interface. The portname parameter is the name of the port. The port name may not be the same as any attribute name used by the class. A short description of the port is provided with the desc parameter and should be identical for all interfaces for a port.
The data iface points to must not be deallocated or overwritten by the caller. Simics will use that data to store the interface structure. It will never be freed or written to by Simics.
void SIM_register_port(conf_class_t *NOTNULL cls, const char *NOTNULL name, conf_class_t *NOTNULL port_cls, const char *desc);
The result of this is that whenever an object of class cls is
created, Simics will automatically create a port object of class
port_cls
. The name of the port object is created by appending
.
followed by the name string to the parent object's
name.
If the port name contains dots or brackets, then intermediate port objects
are registered as well. For instance, the port name x.array[2]
will
implicitly register ports x
of class namespace
, and
x.array
of class index-map
.
Each port name may be registered at most once in a class. One exception is
namespace classes: If the port is registered once as class
namespace
and once as some other class, then the namespace
registration is dropped. Also, if a port is registered twice as class
index-map
, or twice as class namespace
, then the second
registration is dropped.
conf_class_t * SIM_register_simple_port(conf_class_t *NOTNULL cls, const char *NOTNULL name, const char *desc);
int SIM_register_typed_attribute( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(lang_void *user_data, conf_object_t *obj, attr_value_t *idx), lang_void *user_data_get, set_error_t (*set_attr)(lang_void *user_data, conf_object_t *obj, attr_value_t *val, attr_value_t *idx), lang_void *user_data_set, attr_attr_t attr, const char *type, const char *idx_type, const char *desc);
int SIM_register_typed_class_attribute( conf_class_t *NOTNULL cls, const char *NOTNULL name, attr_value_t (*get_attr)(lang_void *ptr, conf_class_t *c, attr_value_t *idx), lang_void *user_data_get, set_error_t (*set_attr)(lang_void *ptr, conf_class_t *c, attr_value_t *val, attr_value_t *idx), lang_void *user_data_set, attr_attr_t attr, const char *type, const char *idx_type, const char *desc);
The function get_attr is called with the object and the value from user_data_get as arguments, and returns the current value of the attribute.
On error, get_attr should call SIM_attribute_error. The return value is then ignored; typically, SIM_make_attr_invalid is used to generate an explicitly invalid value.
If get_attr is a null pointer, the attribute will be write-only.
The function set_attr is called with the object and the value from user_data_set as arguments when the attribute is initialised or changed. The argument value is owned by the caller, so any data from it must be copied.
The set_attr method should return Sim_Set_Ok
if the new value could be set. On error, it should return an appropriate
error code (usually Sim_Set_Illegal_Value
), and optionally
call SIM_attribute_error with an explanatory message.
If set_attr is a null pointer, the attribute will be read-only.
The attr parameter is one of
Sim_Attr_Required
, Sim_Attr_Optional
,
Sim_Attr_Session
or Sim_Attr_Pseudo
.
Attributes marked Sim_Attr_Required
or
Sim_Attr_Optional
are saved in checkpoints. Both
set_attr and get_attr must be non-null
for such attributes.
All attributes that are marked Sim_Attr_Required
must be present in all configurations.
The set of permitted values is encoded in the string type,
and in idx_type for values during indexed access.
A NULL
value for either type string means that values of
any type are permitted.
The type strings are composed as follows:
i |
integer |
f |
floating-point |
s |
string |
b |
boolean |
o |
object |
d |
data |
n |
nil |
D |
dictionary |
a |
any type |
|
(vertical bar) operator specifies the union of
two types; eg, s|o
is the type of a string or an object.
[]
. There are two
kinds of list declarations:
[ios]
specifies a 3-element list
consisting of an integer, an object and a string, in that order.
{ N: M} |
between N and M elements, inclusive |
{ N} |
exactly N elements |
* |
zero or more elements |
+ |
one or more elements |
For example, [i{3,5}]
specifies a list of
3, 4 or 5 integers.
Inside heterogeneous lists, |
(union) has higher precedence
than juxtaposition; ie, [i|so|n]
defines a list of two
elements, the first being an integer or a string and the second
an object or NIL.
SIM_register_typed_class_attribute will register a class attribute. Class attributes are the same for all instances of the class.
SimExc_AttrNotReadable Thrown if a checkpointed attribute is not
readable.
SimExc_AttrNotWritable Thrown if a checkpointed attribute is not
writable.
void SIM_require_object(conf_object_t *NOTNULL obj);
Each object will have its finalize method called automatically, usually in hierarchical order, during object creation. Since it is only permitted to call methods on objects that have been configured, SIM_require_object is a way to allow such calls during finalisation by ensuring that those objects are correctly set up. A better way to call methods on other objects during finalization is to defer such calls to the objects_finalized method.
SIM_require_object may only be called from the finalize method of another object.
Finalisation cycles can occur if two or more objects call SIM_require_object on each other. Such cycles are treated as errors. To avoid them, call SIM_set_object_configured as soon as the object has reached a consistent state.
set_error_t SIM_set_attribute_default(conf_object_t *NOTNULL obj, const char *NOTNULL name, attr_value_t val);
After the call val is still owned by the caller.
The function may only called while obj is being under construction and before its attributes have been set. More precisely, it is only legal to use this function from init_object callbacks or from an attribute setters belonging to a hierarchical ancestor of the object.
The main purpose of this function is setting suitable default attribute values for port objects.
Sim_Set_Ok
if successful.
void SIM_set_class_data(conf_class_t *cls, lang_void *data);
The class data can be fetched at any time during the object initialisation, using SIM_get_class_data.
FORCE_INLINE void * SIM_cbdata_data(const cbdata_t *cbd);
cbdata_t
FORCE_INLINE const cbdata_type_t * SIM_cbdata_type(const cbdata_t *cbd);
cbdata_t
FORCE_INLINE void SIM_free_cbdata(cbdata_t *cbd);
cbdata_t
FORCE_INLINE cbdata_t SIM_make_cbdata(const cbdata_type_t *type, void *data);
cbdata_t
FORCE_INLINE cbdata_t SIM_make_simple_cbdata(void *obj);
cbdata_t
sim_exception_t SIM_clear_exception(void);
SimExc_No_Exception
.
const char * SIM_describe_pseudo_exception(exception_type_t ex);
exception_type_t
.
sim_exception_t SIM_get_pending_exception(void);
SimExc_No_Exception
if none available.
const char * SIM_last_error(void);
The returned string is only valid until the next use of the Simics API in the same thread.
notifier_handle_t * SIM_add_notifier( conf_object_t *NOTNULL obj, notifier_type_t type, conf_object_t *subscriber, void (*callback)(conf_object_t *subscriber, conf_object_t *NOTNULL notifier, lang_void *data), lang_void *data);
The subscriber argument should be the object listening for
the notification; this object is passed as the first
argument to the callback function. The installed callback is
automatically removed if the subscriber object is deleted.
It is legal to pass NULL
in the subscriber
argument if there is no object associated with the callback.
The data argument is passed as the last argument to the callback function.
The function returns a handle to the installed notifier
or NULL
if the notifier type is not supported by the object.
Adding a notifier callback from another notifier callback of the same notifier type and object does not trigger an immediate callback invocation (from the same call to SIM_notify).
bool SIM_class_has_notifier(conf_class_t *NOTNULL cls, notifier_type_t type);
true
if the class
cls supports the notifier specified by type.
void SIM_delete_notifier(conf_object_t *NOTNULL obj, notifier_handle_t *handle);
Notifiers callbacks are deleted automatically when the subscribing object is deleted.
Deleting a notifier callback from another notifier callback of the same notifier type and object may or may not inhibit the last invocation of the deleted callback; this is undefined.
void SIM_describe_notifier(notifier_type_t type, const char *NOTNULL generic_desc);
const char * SIM_notifier_description(notifier_type_t type);
SIM_notifier_description returns a generic description that was set with the SIM_describe_notifier for the notification specified by the type argument, or the empty string if no description has been set.
bool SIM_has_notifier(conf_object_t *NOTNULL obj, notifier_type_t type);
true
if the object
obj supports the notifier specified by type.
notifier_type_t SIM_notifier_type(const char *NOTNULL type);
notifier_type_t
identifier which uniquely
corresponds to this type.
This function always returns a valid notifier type; if a particular type string has not been seen before, then a new notifier type is created and associated with this string.
The string must consist of printable 7-bit ASCII characters, and by convention it should be expressed as a noun with words separated by dashes.
void SIM_notify(conf_object_t *NOTNULL obj, notifier_type_t type);
The order in which notifier callbacks are invoked is undefined, but the same order every time.
void SIM_register_notifier(conf_class_t *NOTNULL cls, notifier_type_t type, const char *desc);
void SIM_register_tracked_notifier(conf_class_t *NOTNULL cls, notifier_type_t type, const char *desc, void (*subscribed_changed)(conf_object_t *obj, notifier_type_t type, bool has_subscribers));
SIM_register_notifier is the base registration function.
SIM_register_tracked_notifier accepts an additional argument
subscribed_changed. If not NULL
, this callback
will be invoked whenever a notifier of the type type
is installed (see SIM_add_notifier) or deleted (see
SIM_delete_notifier) on an object of class cls.
The obj argument passed to subscribed_changed
is the object of the class cls.
It is legal to call SIM_register_notifier multiple times for the same class and the same notifier type. All subsequent invocations done after the notifier was registered are ignored. Calling SIM_register_tracked_notifier multiple times registers multiple subscribed_changed callbacks. All of them will be invoked as it is specified above.
global_notifier_callback_t * SIM_add_global_notifier( global_notifier_type_t type, conf_object_t *subscriber, void (*callback)(conf_object_t *subscriber, lang_void *data), lang_void *data);
The subscriber argument should be the object listening on
the notifier; this object is passed as the first
argument to the callback. The installed callback is automatically
removed if the subscriber object is deleted.
It is legal to pass NULL
in the subscriber
argument if there is no object associated with the callback.
The function returns a handle to the installed callback
or NULL
if the notifier type is unknown.
Adding a notifier callback from another notifier callback of the same notifier type does not trigger an immediate callback invocation.
global_notifier_callback_t * SIM_add_global_notifier_once( global_notifier_type_t type, conf_object_t *subscriber, void (*callback)(conf_object_t *subscriber, lang_void *data), lang_void *data);
void SIM_delete_global_notifier(global_notifier_callback_t *handle);
Global notifier callbacks are deleted automatically when the subscribing object is deleted.
Deleting a notifier callback from another notifier callback of the same notifier type may or may not inhibit the last invocation of the deleted callback; this is undefined.
hap_type_t SIM_hap_add_type(const char *NOTNULL hap, const char *NOTNULL params, const char *param_desc, const char *index, const char *desc, int unused);
The params parameter specifies the argument that callbacks
for this hap is called with; e.g., "s"
or
"II"
. The first two arguments are always lang_void *
and conf_object_t *
respectively, and should not be included
in that string. The table below shows which characters may be used, and
what their meaning is:
i | an int |
I | an int64 (64 bit integer) |
e | an exception_type_t |
o | a script specific object; i.e.,
void * in C and any Python object in Python |
s | a string |
m | a memory transaction
(generic_transaction_t * in C) |
c | a configuration object
(conf_object_t * in C) |
param_desc should be a string of space-separated parameter names, or NULL if params is the empty string. There should be one word in param_desc for each character in params.
index is a string describing the index value for this hap, or NULL if there is no index value.
desc is a description string for the hap.
const char * SIM_hap_get_name(hap_type_t hap);
NULL
for no
such hap. The returned value is a static string that should not
be modified or freed by the caller.
hap_type_t SIM_hap_get_number(const char *NOTNULL hap);
bool SIM_hap_is_active(hap_type_t hap);
bool SIM_hap_is_active_obj(hap_type_t hap, conf_object_t *NOTNULL obj);
bool SIM_hap_is_active_obj_idx(hap_type_t hap, conf_object_t *NOTNULL obj, int64 index);
The SIM_hap_is_active function should be avoided; it may be slower and less precise than the other variants.
int SIM_hap_occurred_always(hap_type_t hap, conf_object_t *obj, int64 value, attr_value_t *NOTNULL list);
int SIM_c_hap_occurred_always_vararg(hap_type_t hap, conf_object_t *obj, int64 value, va_list ap);
int SIM_c_hap_occurred_always(hap_type_t hap, conf_object_t *obj, int64 value, ...);
int SIM_hap_occurred(hap_type_t hap, conf_object_t *obj, int64 value, attr_value_t *NOTNULL list);
int SIM_c_hap_occurred_vararg(hap_type_t hap, conf_object_t *obj, int64 value, va_list ap);
int SIM_c_hap_occurred(hap_type_t hap, conf_object_t *obj, int64 value, ...);
hap
. When a hap
triggers, all callback functions installed on the hap are called.
The obj
argument is the object that the hap triggering is
associated with. It may be NULL for haps that are not associated with an
object.
The value
argument is used for filtering out callback functions to
call based on the index or range that they are installed for. What the
index or range corresponds to is hap specific, but could for example be the
exception number for the Core_Exception
hap.
SIM_hap_occurred() will only call the callbacks once every simulated cycle; if this hap is triggered several times during one cycle, the callbacks will still only be called once. The SIM_hap_occurred_always() function will always call the hap callback functions every time. It is recommended that SIM_hap_occurred_always() is used.
These hap triggering functions return whether or not there were any matching callback function registered on the hap. This can be useful information when one wants a default behavior to be triggered (for example, stopping the simulation) if nobody is listening to the hap.
The hap-specific parameters to the callback function can be passed in
various ways: The SIM_c_hap_occurred... functions are only
available in C/C++ and are variadic or take a va_list
as argument.
SIM_hap_occurred and SIM_hap_occurred_always are
mainly intended to be used from Python, taking the parameters from an
attribute value of list type, or an empty list ([]
) if no
parameters are passed. In all cases, the number and types of passed
parameters must agree with the hap type definition.
hap
is not a valid hap type or if
the values in the list
argument are of the wrong type.
void SIM_hap_remove_type(const char *NOTNULL hap);
void SIM_log_info(int level, conf_object_t *NOTNULL dev, int grp, const char *NOTNULL fmt, ...);
void SIM_log_spec_violation(int level, conf_object_t *NOTNULL dev, int grp, const char *NOTNULL fmt, ...);
void SIM_log_unimplemented(int level, conf_object_t *NOTNULL dev, int grp, const char *NOTNULL fmt, ...);
void SIM_log_error(conf_object_t *NOTNULL dev, int grp, const char *NOTNULL fmt, ...);
void SIM_log_critical(conf_object_t *NOTNULL dev, int grp, const char *NOTNULL fmt, ...);
void SIM_log_message(conf_object_t *obj, int level, uint64 group_ids, log_type_t log_type, const char *message);
The level parameter indicates the importance; a lower value means a more important message. SIM_log_error has no level parameter and will always emit messages.
level should be between 1 and 4:
The grp parameter should have a bit set for each log group that the message corresponds to, as defined by the SIM_log_register_groups function, while a value of 0 equals any group.
The level and grp parameters allow the user to selectively display more or fewer messages using the log-level, log-group and log-type commands.
The fmt argument and those following it are used for string formatting in the same way as in the standard sprintf function.
The logging functions are among the few Simics API functions that may be called while a frontend exception is pending.
Note that the macro versions of these functions, listed below, are usually more efficient.
The use of the different functions is discussed in Simics Model Builder User's Guide, section "Logging":
--stop-on-error
command
line flag, then Simics will exit with an error code when a log message of the
error type is generated.
#define SIM_LOG_INFO(level, dev, grp, ...) #define SIM_LOG_SPEC_VIOLATION(level, dev, grp, ...) #define SIM_LOG_UNIMPLEMENTED(level, dev, grp, ...) #define SIM_LOG_ERROR(dev, grp, ...) #define SIM_LOG_CRITICAL(dev, grp, ...) #define SIM_LOG_INFO_ONCE(level1, level2, dev, grp, ...) #define SIM_LOG_SPEC_VIOLATION_ONCE(level1, level2, dev, grp, ...) #define SIM_LOG_UNIMPLEMENTED_ONCE(level1, level2, dev, grp, ...)
unsigned SIM_log_level(const conf_object_t *NOTNULL obj);
void SIM_set_log_level(conf_object_t *NOTNULL obj, unsigned level);
void SIM_log_register_groups(conf_class_t *NOTNULL cls, const char *const *NOTNULL names);
NULL
-terminated array.
A class may have up to 63 user-defined log groups.
The Default_Log_Group
group is present on all classes. It is used
for log entries where no group is specified.
atom_id_t
. Most atoms types are
pre-defined by Simics Core and have static ids, but there are
also dynamically assigned ids which are used for custom atom types.
Atom ids are internal to Simics Core and should never be used explicitly by a Simics models. Instead, there are API functions like e.g. ATOM_size or ATOM_initiator which should be used instead.
atom_t
type is a container
type for tagged data associated with a transaction. The kind of data
stored in the atom is determined by the id field, and a pointer
to the data or the data itself is stored in the ptr field.
Atoms should always be initialized using provided constructor functions like ATOM_flags or ATOM_size. Usage of the constructors ensures that the data payload is of the correct type and that the id is set to the correct value.
Atom lists must be terminated with the special ATOM_LIST_END
marker.
typedef exception_type_t (*transaction_completion_t)( conf_object_t *obj, transaction_t *t, exception_type_t ex);
completion
atom belonging
to the transaction t. Similarly, obj is an
object stored in either an owner
atom or an
initiator
atom. The former takes precedence if both are
present.
Completion callbacks are only invoked for transactions monitored with either SIM_monitor_transaction or SIM_monitor_chained_transaction, or for transactions deferred with SIM_defer_owned_transaction.
The completion status for the operation is given in the
ex argument, and is usually equal to
Sim_PE_No_Exception
.
The return value of the callback is the completion status for the transaction t. This status is used to complete the parent transaction if the transaction is being monitored with SIM_monitor_chained_transaction. The return value is also returned by SIM_monitor_transaction or SIM_monitor_chained_transaction when a transaction is completed synchronously.
If the callback returns Sim_PE_Deferred
, then
the transaction t is left uncompleted. It must then
be completed later on by an explicit call to
SIM_complete_transaction.
typedef enum { Sim_Transaction_Fetch = 1 << 0, Sim_Transaction_Write = 1 << 1, Sim_Transaction_Control = 1 << 2, Sim_Transaction_Inquiry = 1 << 8, Sim_Transaction_Incoherent = 1 << 9, Sim_Transaction_Atomic = 1 << 10, } transaction_flags_t;
transaction_flags_t
type is bitmask
used to specify the transaction type. It is a combination
of the following bits:
Sim_Transaction_Fetch
indicates that the transaction is
an instruction fetch.
Sim_Transaction_Write
is set if the transaction is a write.
Sim_Transaction_Control
is set if the transaction does not
actually transfer any data. One example of such transactions is
cache control operations.
The Sim_Transaction_Inquiry
bit signifies that side
effects normally triggered by the transaction should be suppressed.
Examples of side effects include triggering breakpoints and
clearing "read-to-clear" device registers.
When neither Sim_Transaction_Fetch
nor Sim_Transaction_Write
is set the transaction is
a read transaction.
transaction_t
represents a memory transaction. The properties of the
transaction is stored in the form of an atom list, where each
atom describes a particular aspect of the transaction, like the
size of the transaction.
The field atoms points to the atoms list,
which must be terminated with the constant ATOM_LIST_END
.
The prev field points to an optional parent transaction. If a particular atom is not found in the atoms list, then the parent's list of atoms is consulted instead. The prev pointer is also used when a chained transaction is monitored with SIM_monitor_chained_transaction.
Besides the fields above, the transaction contains some internal fields that should be initialized to 0. The internal fields should not be referenced explicitly since they are likely to change in future Simics releases.
For details, please refer to "Transactions" chapter in the Model Builder's User Guide.
static inline atom_t ATOM_flags(transaction_flags_t val);
static inline atom_t ATOM_data(uint8 *val);
static inline atom_t ATOM_size(uint32 val);
static inline atom_t ATOM_initiator(conf_object_t *val);
static inline atom_t ATOM_completion(transaction_completion_t val);
static inline atom_t ATOM_list_end(int val);
ATOM_flags returns a transaction atom specifying transaction
flags (see description of transaction_flags_t
for information
about available transaction flags).
ATOM_data returns a transaction atom that holds the pointer to a buffer that is used to get the data from (for write transactions) to store the data to (for read and instruction fetch transactions).
ATOM_size returns a transaction atom that holds the size of a transaction.
ATOM_initiator returns a transaction atom that holds the initiator of a transaction.
ATOM_completion creates a completion atom - a special atom that holds a callback that is invoked when a transaction is completed asynchronously.
ATOM_list_end returns a special atom that should end the list
of transaction atoms. One can use the ATOM_LIST_END
macro instead.
uint8 val; atom_t atoms[] = { // the flags atom value specifies the transaction type: // - 0 defines a read transaction // - Sim_Transaction_Write - a write transaction // - Sim_Transaction_Fetch - an instruction fetch transaction ATOM_flags(0), ATOM_data(&val), ATOM_size(sizeof val), ATOM_initiator(obj), ATOM_LIST_END }; transaction_t t = { atoms };
transaction_t
, transaction_flags_t
,
transaction_completion_t
void SIM_complete_transaction(transaction_t *t, exception_type_t status);
If the transaction t has not been monitored, then the completion code is stored in an internal transaction field until the transaction is monitored with SIM_monitor_transaction or SIM_monitor_chained_transaction, at which time the completion callback is invoked using the stored completion code.
Note that SIM_complete_transaction is normally only used to complete asynchronous transactions. Synchronous transactions are completed by returning the appropriate return code directly from the issue method.
transaction_t * SIM_defer_owned_transaction(transaction_t *t);
transaction
interface, then the completion
can be deferred to a later time by calling SIM_defer_transaction
which allocates a new transaction. One alternative is calling
SIM_defer_owned_transaction which allows the caller to
allocate the deferred transaction explicitly, in which case
provides a heap-allocated transaction t which is linked
to the transaction which should be deferred through its prev
field. The provided transaction should have an owner
and a
completion
atoms, which are used when the deferred
transaction is completed.
When a transaction is deferred, the status code
Sim_PE_Deferred
must be returned rom
the issue method of the transaction
interface.
NULL
if the transaction was issued synchronously and cannot be deferred.
When a transaction cannot be deferred, the issue method
may choose to return the error status
Sim_PE_Async_Required
.
transaction_t * SIM_defer_transaction(conf_object_t *obj, transaction_t *t);
transaction
interface, then the completion
can be deferred to a later time by calling SIM_defer_transaction
with the transaction as a parameter.
The SIM_defer_transaction function returns a new transaction
pointer which is guaranteed to be available until it is completed
with a call to SIM_complete_transaction.
When a transaction is deferred, the status code
Sim_PE_Deferred
must be returned from
the issue method of the transaction
interface.
NULL
if the transaction was issued synchronously and cannot be deferred.
When a transaction cannot be deferred, the issue method
may choose to return the error status
Sim_PE_Async_Required
.
void SIM_get_transaction_bytes(const transaction_t *t, buffer_t buf);
void SIM_get_transaction_bytes_offs(const transaction_t *t, unsigned offs, buffer_t buf, bool zerofill_holes);
uint64 SIM_get_transaction_value_le(const transaction_t *t);
uint64 SIM_get_transaction_value_be(const transaction_t *t);
SIM_get_transaction_bytes_offs retrieves the bytes of the transaction which starts at offset offs. The sum of the offset and the buffer size must not exceed the transaction size.
SIM_get_transaction_value_le returns the value obtained when the transaction buffer is interpreted as an encoded little endian integer. The size of the transaction must not exceed 8 for this function to be used.
SIM_get_transaction_value_be returns the value obtained when the transaction buffer is interpreted as an encoded big endian integer.
int64 SIM_get_transaction_id(transaction_t *t);
This function must only be called on a deferred transaction.
bool SIM_inspect_address_routing(const map_target_t *NOTNULL mt, transaction_t *NOTNULL t, uint64 addr, bool (*NOTNULL callback)( const map_target_t *mt, const transaction_t *t, uint64 addr, uint64 base, uint64 start, uint64 size, access_t access, translation_flags_t flags, lang_void *data), lang_void *data);
The transaction t can be of any type: a read, a write, or an instruction fetch. Please note that the type of the transaction may affect its path through memory hierarchy. The size of the transaction t is ignored: when accesses are done larger transactions may be split depending on how the memory mapping are set up; no such splitting is occurred while the transaction route is traced with SIM_inspect_address_routing. It is allowed to use transactions with zero size.
The callback callback will be called for every device (memory
spaces and translator objects) encountered on the transaction’s route to the
destination as well as for the destination device (this is usually a device
implementing the transaction
interface or the
io_memory
interface). The first invocation of
callback is done with the mt argument itself.
The arguments passed to the callback are:
- mt is a map target representing an intermediate device or
the endpoint. If nothing is mapped then NULL
(or None
in Python) value is passed. The map target value should not be cached
but it can be inspected
- t is usually the original transaction, but it can be also a
new transaction in the case when additional atoms were appended to the
original transaction via the transaction chaining (see, e.g., the
documentation for the transaction_translator
interface)
- addr is address inside the intermediate device or the
endpoint where the transaction is sent
- base, start, size,
access, and flags arguments describe
the mapping which led to the mt map target. These
arguments (except for access) are the same as
the fields of the translation_t
structure. Please refer to
the translation_t
's documentation for their description.
The access argument is a bitmask specifying access types.
The bit corresponding to the type of the t transaction
is always set. Other access bits may also be set optionally. But
the latter is not guaranteed even if read/write/execute accesses
are routed similarly
- data is the data argument passed to
SIM_inspect_address_routing
The callback callback may return false to stop inspection. I.e. if the false value is returned callback will not be invoked any more.
# The following example shows how one can determine # the destination of a read transaction sent from a CPU object: import conf, simics def get_destination(memory_space, address): '''Example function returning destination object for the access sent to address in the memory space memory_space.''' mt = simics.SIM_new_map_target(memory_space, None, None) t = simics.transaction_t() # read transaction of zero size l = [None] # data for the callback def callback(mt, t, addr, base, start, size, access, flags, l): l[0] = (simics.SIM_map_target_object(mt) if mt is not None else None) return True simics.SIM_inspect_address_routing(mt, t, address, callback, l) # We free mt here but it can be saved and reused if needed. # Transaction t can also be reused. simics.SIM_free_map_target(mt) return l[0] # Please specify here CPU object and address you are interested in: cpu = simics.SIM_get_processor(0) address_of_access = 0x1000 dest = get_destination( cpu.iface.processor_info_v2.get_physical_memory(), address_of_access) print("Destination: ", dest.name if dest is not None else "'nothing is mapped'")
bool SIM_inspect_breakpoints( const map_target_t *NOTNULL mt, transaction_t *NOTNULL t, uint64 start, uint64 end, bool (*NOTNULL callback)( conf_object_t *trigger_object, breakpoint_set_t bp_set, const transaction_t *t, uint64 start, uint64 end, lang_void *data), lang_void *data);
For all matching breakpoints Simics invokes callback.
The callback function may be called multiple times: with
different trigger_object objects. The callback
function gets the following two arguments that describe the breakpoints:
- trigger_object is the object implementing
the breakpoint_trigger
interface. The interface is used
during simulation to signal that an access triggers a breakpoint
- bp_set is a set of breakpoints. The breakpoints
match the transaction t's type and intersect
the [start, end] range within
the mt map target. Bp_set should
only be used inside callback. Data ownership is preserved
by the caller (i.e., Simics)
Auxiliary information is provided to callback via
the following arguments:
- t is usually the original transaction, but it can be also
a new transaction in the case when additional atoms were appended to the
original transaction via the transaction chaining (see, e.g., the
documentation for the transaction_translator
interface)
- callback's start and end
arguments specify an address range inside the trigger_object
Simics object where the accesses to the requested address range inside
the mt map target would go. The size of the range
reported to callback may be smaller than the size of
the requested range. This may occur, e.g., when different parts of
the original address range are translated to different destinations
based the memory mappings of the simulated machine
- callback's data is
the data argument passed to
SIM_inspect_breakpoints
The callback function may return false
to stop searching for breakpoints. I.e. if the false
value
is returned callback will not be invoked any more
even if more breakpoints are present.
The SIM_inspect_breakpoints function is an inspection function: the endpoint of the transaction is never accessed when the function is used.
false
if
callback returned false in order to stop the inspection.
Otherwise, true
is returned.
exception_type_t SIM_issue_transaction(const map_target_t *NOTNULL mt, transaction_t *NOTNULL t, uint64 addr);
conf_object_t
.Sim_PE_Deferred
if the transaction was
deferred for completion at a some later time. The corresponding pseudo
exception is returned if the transaction completed directly (normally
Sim_PE_No_Exception
or Sim_PE_IO_Not_Taken
).
exception_type_t SIM_monitor_transaction(transaction_t *t, exception_type_t ex);
exception_type_t SIM_monitor_chained_transaction(transaction_t *t, exception_type_t ex);
completion
atom with a callback which is
not NULL
. The function should be
called after the transaction has been issued, and the
ex status code should be the return value from
function used to issue the transaction, for example
the issue method of the transaction
interface.
If ex equals Sim_PE_Deferred
, then
SIM_monitor_transaction will check if the transaction has in
fact been completed already, and in that case, immediately invoke the
completion callback. If the transaction is still uncompleted, then it
will be marked as monitored and a subsequent call to
SIM_complete_transaction will immediately complete the
transaction by invoking the completion callback.
If ex is not equal to Sim_PE_Deferred
, then the
transaction has been completed synchronously, and the completion
callback is called using ex as the completion status.
Note that it is illegal to call SIM_monitor_transaction
for transactions that do not have a valid
completion
callback.
SIM_monitor_chained_transaction is similar to SIM_monitor_transaction except that when a deferred transaction is completed, its parent transaction will be completed too.
Sim_PE_Deferred
if the
transaction is monitored for completion at a later time.
exception_type_t SIM_poll_transaction(transaction_t *t);
It is illegal to call SIM_poll_transaction on a transaction which is monitored for completion using e.g. the SIM_monitor_transaction function.
void SIM_reconnect_transaction(transaction_t *t, int64 id);
void SIM_register_python_atom_type(const char *NOTNULL name);
void SIM_replace_transaction(transaction_t *t_old, transaction_t *t_new);
A transaction which has been issued and is deferred by some downstream device can be replaced with a different transaction by initiator. This is mostly useful when a transaction is allocated on the stack and it turns out that the transaction is not completed synchronously, in which case SIM_replace_transaction can be used to move the transaction to e.g. the heap.
This function ensures that the deferred transaction is properly linked with the t_new transaction. It is the caller's responsibility to fill in the contents of the new transaction, including the prev pointer.
It is illegal to replace a transaction which has been monitored.
void SIM_set_transaction_bytes(const transaction_t *t, bytes_t bytes);
void SIM_set_transaction_bytes_offs(const transaction_t *t, unsigned offs, bytes_t bytes);
void SIM_set_transaction_value_le(const transaction_t *t, uint64 value);
void SIM_set_transaction_value_be(const transaction_t *t, uint64 value);
void SIM_set_transaction_bytes_constant(const transaction_t *t, uint8 value);
The SIM_set_transaction_bytes_offs function sets some bytes of the transaction starting at offset offs. The sum of the offset and the number of provided bytes must not exceed the transaction size.
The SIM_set_transaction_value_le function sets the transaction bytes to the little endian representation of value. The size of the transaction must not exceed 8 for this function to be used. Similarly, SIM_set_transaction_value_be sets the transaction bytes to the big endian representation of the provided value. If the transaction is smaller than 8 bytes the functions will truncate the representation of the value.
The SIM_set_transaction_bytes_constant function can be used when an endpoint wants to set all transaction bytes to value.
transaction_flags_t SIM_transaction_flags(const transaction_t *NOTNULL t);
bool SIM_transaction_is_fetch(const transaction_t *NOTNULL t);
bool SIM_transaction_is_write(const transaction_t *NOTNULL t);
bool SIM_transaction_is_read(const transaction_t *NOTNULL t);
bool SIM_transaction_is_inquiry(const transaction_t *NOTNULL t);
transaction_flags_t
bitmap describing transaction
type. Most of the flags can be queried using
specific accessors.
The SIM_transaction_is_fetch function returns
true
if the transaction is an instruction fetch.
The SIM_transaction_is_read function returns
true
if the transaction is a read operation.
The SIM_transaction_is_write function returns
true
if the transaction is a write operation.
The SIM_transaction_is_inquiry function returns
true
if the transaction is an inquiry operation.
conf_object_t * SIM_transaction_initiator(const transaction_t *t);
bool SIM_transaction_is_deferrable(const transaction_t *NOTNULL t);
Usually, this function is not needed since SIM_defer_transaction
and SIM_defer_owned_transaction return NULL
for transactions that cannot be deferred.
false
if the transaction was issued
synchronously and cannot be deferred. Otherwise, true
.
When a transaction cannot be deferred, endpoint's issue method
of the transaction
interface may choose
to return the error status Sim_PE_Async_Required
.
unsigned SIM_transaction_size(const transaction_t *NOTNULL t);
exception_type_t SIM_transaction_wait(transaction_t *t, exception_type_t ex);
The function may only be invoked for transactions which
have a completion
atom containing a NULL
pointer. The NULL
pointer means that the
initiator will wait for transaction completion using this function.
Moreover, SIM_transaction_wait must always be called
after a transaction has been issued with a NULL
completion callback.
The ex argument should be the return value of the function or method used to issue the transaction.
SIM_transaction_wait will not return until the transaction has completed. While waiting, a different user-level thread will be activated, which allows simulated time to advance.
Note that checkpointing is not possible while waiting for a transaction to complete using this function. Moreover, transaction wait is not supported from all contexts. When unsupported, the transaction will appear to be issued synchronously.
struct arm_memory_transaction * SIM_arm_mem_trans_from_generic(generic_transaction_t *NOTNULL mop);
struct mips_memory_transaction * SIM_mips_mem_trans_from_generic(generic_transaction_t *NOTNULL mop);
struct ppc_memory_transaction * SIM_ppc_mem_trans_from_generic(generic_transaction_t *NOTNULL mop);
struct x86_memory_transaction * SIM_x86_mem_trans_from_generic(generic_transaction_t *NOTNULL mop);
struct pci_memory_transaction * SIM_pci_mem_trans_from_generic(generic_transaction_t *NOTNULL mop);
void SIM_c_get_mem_op_value_buf(const generic_transaction_t *NOTNULL mop, uint8 *NOTNULL dst);
attr_value_t SIM_get_mem_op_value_buf(const generic_transaction_t *NOTNULL mop);
uint64 SIM_get_mem_op_value_cpu(const generic_transaction_t *NOTNULL mop);
uint64 SIM_get_mem_op_value_le(const generic_transaction_t *NOTNULL mop);
uint64 SIM_get_mem_op_value_be(const generic_transaction_t *NOTNULL mop);
If your model is compiled with one of the
DEVICE_IS_LITTLE_ENDIAN
, DEVICE_IS_BIG_ENDIAN
pre-processor defines, then the
SIM_get_mem_op_value function can be used
as an alias to the SIM_get_mem_op_value_le and
SIM_get_mem_op_value_be versions.
The SIM_c_get_mem_op_value_buf function is only available
from C/C++. It places the data into the buffer pointed to by dst.
No endian conversion is performed, i.e. data is returned in target
endianness. There is no alignment requirement on the dst
parameter.
attr_value_t
(type data) containing the data buffer
of the memory transaction.
SIM_get_mem_op_value_be returns the zero-extended value
in host endian order (interpreted as big endian) for the memory transaction.
SIM_get_mem_op_value_le returns the zero-extended value
in host endian order (interpreted as little endian).
SIM_get_mem_op_value_cpu interprets the data in the default
endian order for the initiating processor. This function can only be used
for processor initiated memory operations. It is recommended that one of the
other functions are used instead.
void SIM_c_set_mem_op_value_buf(generic_transaction_t *NOTNULL mop, const uint8 *NOTNULL src);
void SIM_set_mem_op_value_buf(generic_transaction_t *NOTNULL mop, attr_value_t value);
void SIM_set_mem_op_value_cpu(generic_transaction_t *NOTNULL mop, uint64 value);
void SIM_set_mem_op_value_le(generic_transaction_t *NOTNULL mop, uint64 value);
void SIM_set_mem_op_value_be(generic_transaction_t *NOTNULL mop, uint64 value);
attr_value_t
of data type.
If your model is compiled with one of the
DEVICE_IS_LITTLE_ENDIAN
, DEVICE_IS_BIG_ENDIAN
pre-processor defines, then the
SIM_set_mem_op_value function can be used
as an alias to the SIM_set_mem_op_value_le and
SIM_set_mem_op_value_be versions.
SIM_c_set_mem_op_value_buf is only available from C/C++,
it operates on data in target endian order. There is no alignment
requirement on the buf
parameter.
SIM_set_mem_op_value_be takes data in host endian order and sets it in big-endian.
SIM_set_mem_op_value_le takes data in host endian order and sets it in little-endian.
SIM_set_mem_op_value_cpu takes data in host endian order and sets it in the default endian order for the initiating processor. This function can only be used for processor initiated memory operations. It is recommended that one of the other functions are used instead.
The functions that set the memory operation based on a value will truncate the representation of that value if the memory operation is smaller than 8 bytes.
FORCE_INLINE unsigned SIM_get_mem_op_page_cross(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE unsigned SIM_get_mem_op_size(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE mem_op_type_t SIM_get_mem_op_type(const generic_transaction_t *NOTNULL mop);
generic_transaction_t
,
SIM_set_mem_op_type
const char * SIM_get_mem_op_type_name(mem_op_type_t type);
NULL
if unknown.
generic_transaction_t SIM_make_mem_op_write(physical_address_t addr, bytes_t data, bool inquiry, conf_object_t *initiator);
generic_transaction_t SIM_make_mem_op_read(physical_address_t addr, buffer_t buffer, bool inquiry, conf_object_t *initiator);
These functions do not actually perform any memory operation; they just
construct the generic_transaction_t
that can be used in
other calls.
The buffer argument must refer to an allocated buffer, and data must contain valid data. They must remain valid and allocated during the life-time of the returned value.
FORCE_INLINE void SIM_mem_op_ensure_future_visibility(generic_transaction_t *NOTNULL mop);
FORCE_INLINE bool SIM_mem_op_is_atomic(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE bool SIM_mem_op_is_control(const generic_transaction_t *NOTNULL mop);
mem_op
is a control transaction (one that does not
actually transfer any data, such as cache control operations).
FORCE_INLINE bool SIM_mem_op_is_data(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE bool SIM_mem_op_is_instruction(const generic_transaction_t *NOTNULL mop);
mem_op
is a data or an instruction
transaction. Currently, the only transactions that are instruction
transactions are instruction fetches.
FORCE_INLINE bool SIM_mem_op_is_from_cache(const generic_transaction_t *NOTNULL mop);
mem_op
is sent from a cache timing model.
FORCE_INLINE bool SIM_mem_op_is_from_cpu(const generic_transaction_t *NOTNULL mop);
mem_op
is sent from a processor.
FORCE_INLINE bool SIM_mem_op_is_from_cpu_arch(const generic_transaction_t *NOTNULL mop, ini_type_t arch);
mem_op
is sent from a processor
of a specific architecture.
FORCE_INLINE bool SIM_mem_op_is_from_device(const generic_transaction_t *NOTNULL mop);
mem_op
is sent from a device.
FORCE_INLINE bool SIM_mem_op_is_prefetch(const generic_transaction_t *NOTNULL mop);
mem_op
is prefetch transaction.
FORCE_INLINE bool SIM_mem_op_is_read(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE bool SIM_mem_op_is_write(const generic_transaction_t *NOTNULL mop);
mem_op
is a read or a write
transaction.
FORCE_INLINE bool SIM_mem_op_may_stall(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_exception(generic_transaction_t *NOTNULL mop, exception_type_t exc);
FORCE_INLINE exception_type_t SIM_get_mem_op_exception(const generic_transaction_t *NOTNULL mop);
Sim_PE_No_Exception
, the transaction will be interrupted
and an exception will be taken.
FORCE_INLINE void SIM_set_mem_op_initiator(generic_transaction_t *NOTNULL mop, ini_type_t type, conf_object_t *obj);
FORCE_INLINE conf_object_t * SIM_get_mem_op_initiator(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE ini_type_t SIM_get_mem_op_ini_type(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_inquiry(generic_transaction_t *NOTNULL mop, bool inquiry);
FORCE_INLINE bool SIM_get_mem_op_inquiry(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_physical_address(generic_transaction_t *NOTNULL mop, physical_address_t pa);
FORCE_INLINE physical_address_t SIM_get_mem_op_physical_address(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_virtual_address(generic_transaction_t *NOTNULL mop, logical_address_t va);
FORCE_INLINE logical_address_t SIM_get_mem_op_virtual_address(const generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_reissue(generic_transaction_t *NOTNULL mop);
FORCE_INLINE void SIM_set_mem_op_type(generic_transaction_t *NOTNULL mop, mem_op_type_t type);
generic_transaction_t
,
SIM_get_mem_op_type
FORCE_INLINE void SIM_set_mem_op_user_data(generic_transaction_t *NOTNULL mop, void *data);
FORCE_INLINE void * SIM_get_mem_op_user_data(const generic_transaction_t *NOTNULL mop);
void SIM_free_map_target(map_target_t *mt);
bool SIM_map_target_flush(const map_target_t *NOTNULL mt, uint64 base, uint64 size, access_t access);
translation_flush
interface. The documentation for
the translation_flush
interface describes
how to use this function for the interface implementation.
Additionally, this function can be used as a replacement for the SIM_translation_changed function to do a more fine-grain invalidation in the case when a previously returned translation becomes invalid.
When a previously returned translation becomes invalid the translator object should notify Simics which can have translations cached. To notify Simics the translator object can either do the call to the SIM_translation_changed function or, as a potential performance optimization, do a more fine-grain invalidation by using the SIM_map_target_flush function.
The translator object is expected to call
the SIM_map_target_flush function for
all targets of all previously returned translations which became invalid.
If there are too many translations which are to be invalidated, then,
performance-wise, it may be better just to do a single call to the
SIM_translation_changed function. Also, if,
during invalidation, any of the calls to the SIM_map_target_flush
fails (i.e. the false
value is returned by the function) then
the translator is expected to call
the SIM_translation_changed function which always succeeds.
Please note that there is no need to call
the SIM_map_target_flush function for the translations which
were tagged with the Sim_Translation_Dynamic
flag. Either, no
invalidation is needed for the destinations where nothing is mapped.
conf_object_t * SIM_map_target_object(const map_target_t *NOTNULL mt);
const char * SIM_map_target_port(const map_target_t *NOTNULL mt);
const map_target_t * SIM_map_target_target(const map_target_t *NOTNULL mt);
map_target_t * SIM_new_map_target(conf_object_t *NOTNULL obj, const char *port, const map_target_t *chained_target);
Map targets are usually used in conjunction with the
translator
interface and can represent anything
which is mappable in a memory space, e.g., IO banks, RAM, ROM,
memory spaces, port spaces, bridges, or translators. In order to
get better performance, we recommend to allocate a map target once
and reuse it rather than to allocate and delete it every time.
If the chained_target parameter is null,
obj is searched for one of the following interfaces:
ram
, rom
, io_memory
,
port_space
, translator
,
transaction_translator
, transaction
or
memory_space
. The interfaces are tried in the
listed order, and the first interface found determines the "type" of the
map target. For example, if obj implements both the
io_memory
and the translator
interface, then the created map target will direct memory transactions
to the io_memory
interface.
If a map target argument is passed in the
chained_target parameter, then obj must
implement one of the following interfaces:
translator
, bridge
,
or translate
. The chained target contains information
about a secondary map target used either directly or indirectly
by the interface. For objects implementing the
translator
interface, the chained
target is passed as an argument to the translate
method. For bridges, the chained target is the target which
is accessed through the bridge. For objects implementing
translate
, the chained target is used as the target
of the translation if the translate method returns null.
Note: Information about the chained target is encoded in the created map target, but no direct references are kept to the argument. In other words, ownership is not transferred by this call and the caller is responsible for releasing chained_target as appropriate.
If a string is passed in the port parameter, then Simics looks for port interfaces instead of regular interfaces.
map_target_t
,
SIM_map_target_object, translator_interface_t
void SIM_translation_changed(conf_object_t *NOTNULL obj);
translator
interface
need to call this function whenever a previously returned
translation becomes invalid; the only exception is
if all invalid translations were tagged with
Sim_Translation_Dynamic
, in which case this is
not necessary.
Failure to call this function will likely result in Simics continuing to use old translations, since those may have been cached internally.
The object implementing the translator should be passed in the obj parameter.
translator_interface_t
cycles_t SIM_cycle_count(conf_object_t *NOTNULL obj);
If obj is a cycle counter implementing either the
cycle_event
interface or the cycle
interface, then the returned count is the number of elapsed
cycles according to that object. If obj is not a cycle
counter, then the default clock associated with the object is
queried for its cycle count.
void SIM_event_cancel_time(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data);
void SIM_event_cancel_step(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data);
There are separate calls for events posted at a point in time (cycle or seconds) and on a specific step.
step
interface.
cycles_t SIM_event_find_next_cycle(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data);
double SIM_event_find_next_time(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data);
pc_step_t SIM_event_find_next_step(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, int (*pred)(lang_void *data, lang_void *match_data), lang_void *match_data);
There are separate calls of events posted at a point in time (cycle
or seconds) and on a specific step. Note that the return value of
SIM_event_find_next_cycle is only a preliminary
estimate; the number of remaining cycles will change if the
clock's frequency changes dynamically. To handle dynamically
changing clock frequencies correctly, subscribe to the frequency
changes via the clock's simple_dispatcher
interface.
step
interface: Minus one is returned
in such a case.
void SIM_event_post_time(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, double seconds, lang_void *user_data);
void SIM_event_post_cycle(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, cycles_t cycles, lang_void *user_data);
void SIM_event_post_step(conf_object_t *NOTNULL clock, event_class_t *NOTNULL evclass, conf_object_t *NOTNULL obj, pc_step_t steps, lang_void *user_data);
The clock is the object that should be used for keeping track of time for the event. It can be a processor or an instance of the clock class.
If a configuration class was specified when evclass was registered, then obj must be an instance of that class.
The expiration point can be specified in seconds, cycles or steps by using
the appropriate call, and these values are relative to the current
state. Events that need to run synchronized
(Sim_EC_Machine_Sync
) can only be posted in seconds or
cycles, not steps, since synchronization can only be perform in virtual
time.
step
interface.Sim_EC_Machine_Sync
events, if the event is posted less than a time quantum in the future.
event_class_t * SIM_register_event( const char *NOTNULL name, conf_class_t *cl, event_class_flag_t flags, void (*NOTNULL callback)(conf_object_t *obj, lang_void *data), void (*destroy)(conf_object_t *obj, lang_void *data), attr_value_t (*get_value)(conf_object_t *obj, lang_void *data), lang_void *(*set_value)(conf_object_t *obj, attr_value_t value), char *(*describe)(conf_object_t *obj, lang_void *data));
MM_MALLOC
or
MM_STRDUP
).
May be null, in which case the name is used.Null function pointers correspond to the value None when invoked from Python.
The flags is typically either zero or Sim_EC_Notsaved, where Sim_EC_Notsaved indicates that the event should not be saved as part of the configuration. In that case, get_value and set_value must both be null, and cl may then also be null. The other flag bits defined in the event_class_flag_t are reserved for internal use in the Simics platform and some tightly coupled modules. See the event queue sample code for details of their use.
void SIM_run_unrestricted(conf_object_t *NOTNULL obj, void (*NOTNULL func)(conf_object_t *obj, lang_void *param), lang_void *user_data);
Note that with the introduction of Multicore Accelerator, if an instruction
is running when calling SIM_run_unrestricted, other simulation
threads may continue for a while until they stop and the callback is
serviced. This means that an object using SIM_run_unrestricted
may receive calls through for example the io_memory
interface
after returning from the scope where SIM_run_unrestricted is
called but before the func callback function is called. For more
information on considerations for Multicore Accelerator, see the
Simics Model Builder User's Guide.
If several functions are registered this way before any of them has had a chance to run, the functions will be run in their order of registration.
This call is mainly useful for actions that for various reasons can not be done while an instruction is being emulated.
The obj is an object that has a clock (as defined by SIM_object_clock). This object and user_data are passed to the callback function.
Since the callback is run in Cell Context, simulation threads for other cells may be running when the callback is executed. Consequently, only objects in the same cell as obj may be accessed from the callback.
pc_step_t SIM_step_count(conf_object_t *NOTNULL obj);
step
interface. Minus one is returned
in such a case.
double SIM_time(conf_object_t *NOTNULL obj);
The returned time relates to how long the simulation has been running, and is usually not very useful in itself, but it can be used to compare with other times. The time on a specific processor is guaranteed to increase when simulation progresses, even if the clock frequency is changed. When adding a processor, it is assigned a current time to be synchronized with other processors in the simulation, or the time 0.0 if it is the first processor.
void SIM_register_copyright(const char *NOTNULL str);
The string should contain only standard ASCII characters, be pre-formatted for at most 80-character width terminal, be non-indented, and have no spurious new-line characters before or after the last line (except for the new-line that marks the end of the last line).
The string will not be copied so needs to be either static or a copy generated by the callee (preferably static).
const char * SIM_version(void);
const char * SIM_version_base(void);
const char * SIM_version_major(void);
void SIM_license(void);
char * SIM_license_file(const char *format);
char * SIM_copyright(void);
char * SIM_vmxmon_version(void);
NULL
is returned
if the module is not loaded.SIM_version, SIM_version_base, SIM_version_major return a string owned by Simics. These strings must not be deallocated or modified by the caller.
SIM_vmxmon_version, SIM_license_file, and SIM_copyright return a newly allocated string which is owned by the caller. This means that when the function is called from C its return value should be later freed with the use of the MM_FREE macro.
The frags_t
data type is part of the Simics API. It is used to
manipulate and modify network packets inside models efficiently. It is
meant to replace DBuffer in network device models wherever appropriate.
See the Model Builder User's Guide for an introduction to
programming with frags_t
.
frags_t
FORCE_INLINE void frags_add(frags_t *buf, const void *data, size_t len);
frags_t
can hold up to 8 data fragments.uint8 new_data[2] = { 7, 8 }; frags_add(&foo, new_data, sizeof(new_data));
frags_t
to anotherFORCE_INLINE void frags_add_from_frags(frags_t *dst, const frags_t *src, size_t offset, size_t len);
frags_add_from_frags(&foo, &bar, 4, frags_len(&bar) - 4);
frags_t
void frags_extract(const frags_t *buf, void *vdst);
This function is completely equivalent to frags_extract_slice()
with an offset and a length covering the
whole contents of the frags_t
, and is provided for
convenience.
uint8 all_data[frags_len(&foo)]; frags_extract(&foo, all_data);
uint8 frags_extract_8(const frags_t *buf, size_t offset);
uint16 frags_extract_be16(const frags_t *buf, size_t offset);
uint16 frags_extract_le16(const frags_t *buf, size_t offset);
uint32 frags_extract_be32(const frags_t *buf, size_t offset);
uint32 frags_extract_le32(const frags_t *buf, size_t offset);
uint64 frags_extract_be64(const frags_t *buf, size_t offset);
uint64 frags_extract_le64(const frags_t *buf, size_t offset);
frags_t
buf at offset offset.uint8 val8 = frags_extract_8(&frame, 1); uint16 val16 = frags_extract_be16(&frame, 2); uint32 val32 = frags_extract_le32(&frame, 4); uint32 val64 = frags_extract_be64(&frame, 8);
frags_t
void *frags_extract_alloc(const frags_t *buf);
This function is equivalent to allocating a buffer of the correct size with MM_MALLOC() followed by a call to frags_extract(), and is provided for convenience.
uint8 *all = frags_extract_alloc(&foo); /* ... */ MM_FREE(all);
frags_t
void frags_extract_slice(const frags_t *buf, void *vdst, size_t offset, size_t len);
uint8 some_data[16]; frags_extract_slice(&foo, some_data, 4, 16);
frags_t
void *frags_extract_slice_alloc(const frags_t *buf, size_t offset, size_t len);
This function is equivalent to allocating a buffer of the correct size with MM_MALLOC() followed by a call to frags_extract_slice(), and is provided for convenience.
uint8 *slice = frags_extract_slice_alloc(&foo, 4, 16); /* ... */ MM_FREE(slice);
frags_t
FORCE_INLINE void frags_init(frags_t *buf);
frags_t
buf. An alternative is
to use the FRAGS_INIT
constant value./* Initialization with frags_init() */ frags_t bar; frags_init(&bar);
/* Initialization with FRAGS_INIT */ frags_t foo = FRAGS_INIT;
frags_t
with an initial valueFORCE_INLINE void frags_init_add(frags_t *buf, const void *data, size_t len);
frags_t
buf and set it to
represent the initial data data of size
len.
This function is exactly equivalent to using frags_init(), followed by frags_add(), and is provided for convenience.
frags_t baz; uint8 data[5] = { 0, 1, 2, 3, 4 }; frags_init_add(&baz, data, sizeof(data));
frags_t
from anotherFORCE_INLINE void frags_init_add_from_frags(frags_t *dst, const frags_t *src, size_t offset, size_t len);
This function is exactly equivalent to using frags_init(), followed by frags_add_from_frags(), and is provided for convenience.
frags_t bat; ASSERT(frags_len(&foo) > 16); frags_init_add_from_frags(&bat, &foo, 16, frags_len(&foo) - 16);
FORCE_INLINE frags_it_t frags_it(const frags_t *buf, size_t offset, size_t len);
unsigned sum = 0; for (frags_it_t it = frags_it(&foo, 0, frags_len(&foo)); !frags_it_end(it); it = frags_it_next(it)) { unsigned f_len = frags_it_len(it); const uint8 *f_data = frags_it_data(it); for (int i=0; i<f_len; i++) sum += f_data[i]; }
FORCE_INLINE const uint8 * frags_it_data(frags_it_t it);
unsigned sum = 0; for (frags_it_t it = frags_it(&foo, 0, frags_len(&foo)); !frags_it_end(it); it = frags_it_next(it)) { unsigned f_len = frags_it_len(it); const uint8 *f_data = frags_it_data(it); for (int i=0; i<f_len; i++) sum += f_data[i]; }
FORCE_INLINE bool frags_it_end(frags_it_t it);
true
when the iterator it does not
have any next fragment to return at the next call of
frags_it_next(), and false
otherwise.true
if the iterator is finished,
false
otherwise.unsigned sum = 0; for (frags_it_t it = frags_it(&foo, 0, frags_len(&foo)); !frags_it_end(it); it = frags_it_next(it)) { unsigned f_len = frags_it_len(it); const uint8 *f_data = frags_it_data(it); for (int i=0; i<f_len; i++) sum += f_data[i]; }
FORCE_INLINE size_t frags_it_len(frags_it_t it);
unsigned sum = 0; for (frags_it_t it = frags_it(&foo, 0, frags_len(&foo)); !frags_it_end(it); it = frags_it_next(it)) { unsigned f_len = frags_it_len(it); const uint8 *f_data = frags_it_data(it); for (int i=0; i<f_len; i++) sum += f_data[i]; }
FORCE_INLINE frags_it_t frags_it_next(frags_it_t it);
false
.unsigned sum = 0; for (frags_it_t it = frags_it(&foo, 0, frags_len(&foo)); !frags_it_end(it); it = frags_it_next(it)) { unsigned f_len = frags_it_len(it); const uint8 *f_data = frags_it_data(it); for (int i=0; i<f_len; i++) sum += f_data[i]; }
FORCE_INLINE size_t frags_len(const frags_t *buf);
unsigned len = frags_len(&foo);
frags_t
with a headerFORCE_INLINE frags_t frags_prefix(const void *header, size_t header_len, const frags_t *body);
frags_t
composed of the header header
of size header_len, followed by the contents of the
frags_t
body.
This function is equivalent to a sequence of frags_init(), frags_add(), frags_add_from_frags() to build a new fragment containing the prefix. It is provided for convenience.
frags_t
including
header and bodyuint8 eth_header[14] = { 0 }; frags_t packet = frags_prefix(eth_header, sizeof(eth_header), &foo);
frags_t
FORCE_INLINE frags_t frags_suffix(const frags_t *body, void *header, size_t header_len);
frags_t
composed of the contents of the
frags_t
body, followed by the data
header of size header_len.
This function is equivalent to a sequence of frags_init(), frags_add_from_frags(), frags_add() to build a new fragment containing the suffix. It is provided for convenience.
frags_t
including
body and headeruint8 eth_checksum[4] = { 0 }; frags_t frame = frags_suffix(&foo, eth_checksum, sizeof(eth_checksum));
typedef struct frags frags_t;
typedef struct frags_it frags_it_t;
The structures should never be used directly. Only use the accessor functions.
These are routines for manual dynamic memory allocation providing some memory leak detection. They replace the standard C malloc facility in Simics APIs.
Example: to allocate an array of 13 elements of type device_t
,
use
device_t *d = MM_MALLOC(13, device_t);
It must be possible to get a pointer to the type by appending an asterisk to
the type name; so struct foo *
is acceptable, but
int (*)(void)
is not. Use a typedef in these cases.
It is not possible to mix these calls with the malloc facility for the same allocations.
MM_FREE(p);
A null pointer argument is legal, in which case nothing happens.
MM_MALLOC(nelems, type);
MM_MALLOC_SZ(size, type);
MM_ZALLOC(nelems, type);
MM_ZALLOC_SZ(size, type);
MM_ZALLOC and MM_ZALLOC_SZ do the same thing as MM_MALLOC and MM_ZALLOC respectively but in addition fill the allocated memory with null bytes.
If nelems or size are zero, either a null pointer or a pointer to a zero-sized allocation is returned.
MM_REALLOC(p, nelems, type);
MM_REALLOC_SZ(p, size, type);
The allocation must originally have been made by a call to MM_MALLOC, MM_MALLOC_SZ, MM_ZALLOC, MM_ZALLOC_SZ, MM_REALLOC, MM_REALLOC_SZ or MM_STRDUP.
If the passed pointer is null, then these macros are equivalent to an allocation of the desired amount. If nelems or size is zero, either a null pointer or a pointer to a zero-sized allocation is returned, and the original allocation is freed.
The allocation must be freed using MM_FREE.
MM_STRDUP(str);