Skip to content

4. Libraries and Built ins

simicsmp edited this page Mar 7, 2024 · 15 revisions

Most standard functionality in Device Modeling Language (DML) is implemented in templates. Built-in templates can be categorized as follows:

  • Each object type has a corresponding template which is instantiated for all objects of that type. For instance, the template register is automatically instantiated in all registers. All such templates inherit the object template, and define the objtype parameter to the name of the object type, e.g. "register" for registers.

  • Some templates primarily provide a standard implementation of some behaviour. For instance, the uint64_attr template can be applied on attribute objects to make it a simple integer attribute.

  • Some templates primarily specify a programming interface, typically by providing an abstract or overrideable method or parameter. Such templates are often named like the provided member. For instance, objects that implement the init template provide the abstract method init. Interface templates have a number of uses:

    • In some cases, an interface template extends an existing template, altering its default behaviour to make sure its interface method is called. Often, this means that when you provide an implementation of an interface method, you must instantiate the corresponding template; otherwise, the method will not be called. For instance, if you implement the write method in a register, it is not called by default upon a write access; however, if you instantiate the write template in the register, then the register's behaviour is altered to call the write method. Thus, in order to provide custom side-effects on a write, you must write something like:

      register r @ 0 is write { method write(uint64 value) { default();
      log info: "wrote r"; } }
      
    • When writing a method or parameter override inside a template, you must explicitly instantiate the template you are overriding in order to specify that your declaration takes precedence over the default implementation. If your template is intended for a specific object type, then it is sufficient to override that template, but it is often better to override a more specific template if possible. For instance, the init_val parameter belongs to the init_val template, which is inherited by all registers. So a template that overrides this parameter may be implemented as follows:

      template init_to_ten is register {
          param init_val = 10;
      }
      

      However, it is even better to only inherit the init_val template:

      template init_to_ten is init_val {
          param init_val = 10;
      }
      

      The latter improves efficiency and permits init_to_ten to also be used on fields.

    • Similarly, if you write a template that needs to access some member of the object, then it must inherit a template that provides that member. For instance:

      template log_on_change is (write, get, name) {
          method write(uint64 value) {
              if (this.get() != value) {
                  log info: "%s changed!", this.name;
              }
              default();
          }
      }
      

      Again, it would also work to inherit register instead of get and name, but at the cost of reduced flexibility and efficiency.

Universal templates

The following templates are applicable to all object kinds:

name

Provides a string parameter name, containing the name of the object, as exposed to the end-user. This parameter is typically used in log messages and names of configuration attributes. The name can be overridden in order to hide confidential information from the end-user.

desc

Provides a string parameter desc, with a short description in plain text. By convention, this should preferably be a few descriptive words, but may also be a long name. The description can appear during simulation, when inspecting the device, and also serves as a docstring that enriches the DML source code. The parameter's default value is undefined. The desc parameter has a short-hand syntax described in section Object Declarations.

Also provides a string parameter shown_desc, which is the string actually exposed to the end-user during simulation. This parameter defaults to desc, and can be overridden in order to hide confidential information from the end-user.

See also template documentation.

shown_desc

A subtemplate of desc that makes the shown_desc parameter a typed parameter. This is inherited by objects that need to access shown_desc from the context of a shared method.

documentation

Provides a string parameter documentation, with a longer description. The documentation may appear when extracting documentation from the device.

If you have the Documentation and Packaging package and intend to generate Simics reference documentation for the device then the documentation string must follow the Simics documentation XML format, otherwise you will get a syntax error during the documentation build. See the Writing Documentation application note.

Also provides a string parameter shown_documentation, defaulting to documentation. This parameter is similar to shown_desc in the desc template, and is mainly a convenient way to suppress documentation.

limitations

Provides a string parameter limitations, describing limitations in the implementation of this object. The documentation may appear when extracting documentation from the device.

If you have the Documentation and Packaging package and intend to generate Simics reference documentation for the device then the limitations string must follow the Simics documentation XML format, otherwise you will get a syntax error during the documentation build. See the Writing Documentation application note.

Also provides a string parameter shown_limitations, defaulting to limitations. This parameter is similar to shown_desc, and is mainly a convenient way to suppress documentation.

init

Provides an abstract method init, which is called when the device is created, before any attributes have been initialized. Typically used to initialize a default value, or to set up data structures.

The method init is automatically called on all objects that implement the init template.

post_init

Provides an abstract method post_init, which is called when the device is created, after any attributes have been initialized. Typically used to establish connections to other devices, or to set up data structures that depend on configured attribute values.

The method post_init is automatically called on all objects that implement the post_init template.

object

Base template, implemented by all objects. Inherits the templates name, desc, documentation and limitations. Provides the following additional parameters, which cannot be overridden:

  • this (reference): Always refers to the current object, i.e., the nearest enclosing object definition.

  • objtype: string constant describing the object type, e.g. "register"

  • parent (reference or undefined): Always refers to the parent (containing) object. Has the value undefined in the device object.

  • qname: The fully qualified name, including indices, such as some_bank.r0. Constructed from the name parameter. In the device object, this is equal to the name parameter.

  • dev: The top-level device object

  • indices: List of local indices for this object. For a non-array object, this is the empty list. In a register array of size N, it is a list with one element, a non-negative integer smaller than N. The parameter is not cumulative across the object hierarchy, so for a single field inside a register array, the value is the empty list.

  • Each array has an individual index parameter, to make it possible to refer to both inner and outer indexes when arrays are nested (cf. the indices parameter, above). The parameter name is specified in the array declaration; for instance, the declaration register regs[i < 4][j < 11]; defines two index parameters, i and j. In this case, the indices parameter is [i, j].

The object template provides the non-overridable method cancel_after(), which cancels all pending events posted using after which are associated with the object (any events associated with subobjects are unaffected).

There are no other methods common for all objects, but the methods init and post_init are automatically called on all objects that implement the init and post_init template, respectively.

Device objects

The top-level scope of a DML file defines the device object, defined by the template device. This template inherits the init and post_init templates.

The device template contains the following methods:

  • init(): Called when the device object is loaded, but before its configuration-object attributes have been initialized.

  • post_init(): Called when the device object is loaded, after its configuration-object attributes have been initialized.

  • destroy(): Called when the device object is being deleted.

The device template contains the following parameters:

  • classname [string]: The name of the Simics configuration object class defined by the device model. Defaults to the name of the device object.

  • register_size [integer | undefined]: The default size (width) in bytes used for register objects; inherited by bank objects. The default value is undefined.

  • byte_order [string]: The default byte order used when accessing registers wider than a single byte; inherited by bank objects. Allowed values are "little-endian" and "big-endian". The default value is "little-endian".

  • be_bitorder [bool]: The default value of the be_bitorder in banks. The default value is true if the DML file containing the device statement declares bitorder be;, and false otherwise.

  • use_io_memory [bool]: The default value of the use_io_memory parameter in banks. The current default value is true, but in future Simics versions it will be false.

  • obj *[conf_object_t ]: A pointer to the conf_object_t C struct that Simics associates with this device instance

  • simics_api_version [string]: The Simics API version used when building this device, as specified by the --simics-api command-line argument; e.g. "6" for the Simics 6 API.

Group objects

Group objects are generic container objects, used to group other objects. They can appear anywhere in the object hierarchy, but some object types (currently implement and interface) may not have a group as parent.

The group template contains no particular methods or parameters other than what is inherited from the object template.

In a group hierarchy directly below a port or device object, none of the group object may be named bank or port; this is to avoid name clashes.

Attribute objects

The attribute template contains the following methods:

get() -> (attr_value_t)

Abstract method. Returns the value of the attribute.

set(attr_value_t value) throws

Abstract method. Sets the value of the attribute. If the provided value is not allowed, use a throw statement to signal the error.

get_attribute -> (attr_value_t), set_attribute(attr_value_t value) -> (set_error_t)

Not intended to be used directly. Called by Simics for reading and writing the attribute value. Calls the get and set methods.

The attribute template contains the following parameters:

type [string | undefined]

A Simics configuration-object attribute type description string, such as "i" or "[s*]", specifying the type of the attribute. (See the documentation of SIM_register_typed_attribute in the Model Builder Reference Manual for details.) For simple types this can easily be set by standard attribute templates.

configuration ["required" | "optional" | "pseudo" | "none"]

Specifies how Simics treats the attribute. The default value is "optional". A required attribute must be initialized to a value when the object is created, while an optional attribute can be left undefined. In both cases, the value is saved when a checkpoint is created. For a pseudo-attribute, the value is not saved when a checkpoint is created (and it is not required to be initialized upon object creation). Setting the value to "none" suppresses creation of the attribute. This is seldom useful in attribute objects, but can be used in related object types like register to suppress checkpointing.

persistent [bool]

If this parameter is true, the attribute will be treated as persistent, which means that its value will be saved when using the save-persistent-state command. The default value is false.

readable [bool]

If false, the attribute cannot be read. This can only be used if the configuration parameter is set to "pseudo". Normally set by the write_only_attr template.

writable [bool]

If false, the attribute cannot be written. This should normally be set by instantiating the read_only_attr template, and requires that the configuration parameter is "pseudo". Normally set by the read_only_attr template.

internal [bool]

If this parameter is true, the attribute will be treated as internal, meaning that it will be excluded from documentation. The default value is true if the documentation and desc parameters are undefined, and false otherwise.

Attribute templates

Four templates are used to create a simple checkpointable attribute with standard types. Each store the attribute value in a member val, provide default implementations of methods get and set according to the type, and provide a default implementation of init that initializes val using the init_val parameter also provided by the template (whose default definition simply zero-initializes val.) These four templates are:

bool_attr

boolean-valued attribute, val has type bool

int64_attr

integer-valued attribute, val has type int64

uint64_attr

integer-valued attribute, val has type uint64

double_attr

floating-point-valued attribute, val has type double

In addition, three templates can be used to define a pseudo attribute, and cannot be used with the above templates:

pseudo_attr

Pseudo attribute. Will not be checkpointed. Methods get and set are abstract.

read_only_attr

Pseudo attribute that cannot be written. Method get is abstract.

write_only_attr

Pseudo attribute that cannot be read. Method set is abstract

These templates are not compatible with the bool_attr, uint64_attr, int64_attr, or float_attr templates.

Connect objects

The connect template contains the following methods:

validate(conf_object_t *obj) -> (bool)

Called when Simics attempts to assign a new target object. If the return value is false, the attempted connection will fail, and any existing connection will be kept. The default is to return true.

If connecting to a port interface (rather than a port object), then a session variable port, of type char *, is set to the new port name during the validate call. This will be removed in future versions of Simics.

set(conf_object_t *obj)

Called after validation, to assign a new target object. Can be overridden to add side-effects before or after the assignment

get_attribute -> (attr_value_t), set_attribute(attr_value_t value) -> (set_error_t)

Internal, not intended to be used directly. Called by Simics for accessing the attribute value.

The connect template contains the following parameters:

configuration ["required" | "optional" | "pseudo" | "none"]

Specifies how Simics treats the automatically created attribute corresponding to the connect object. The default value is "optional".

The attribute can be set to a nil value only if this parameter is "optional" or "pseudo". In an array of connects, this applies element-wise.

internal [bool]

Specifies whether the attribute should be internal.

Connect templates

A connect object can instantiate the template init_as_subobj. This causes the connect to automatically set itself to an automatically created object. This can be used to create a private helper object.

The init_as_subobj template accepts one parameter, classname. This defines the class of the automatically created object.

The template also overrides the configuration parameter to "none" by default, which makes the connect invisible to the end-user.

The init_as_subobj inherits the init and connect templates.

Interface objects

The interface template contains one parameter, required. Defaults to true. If overridden to false, the interface is optional and the parent connect object can connect to an object that does not implement the interface.

The template provides a session variable val of type const void *. It points to the Simics interface struct of the currently connected object. If the interface is optional, then the variable can be compared to NULL to check whether the currently connected object implements the interface.

Port objects

The port template exposes one parameter, obj. When compiling with Simics API 5 or earlier, evaluates to dev.obj. When compiling with Simics API 6 or newer, evaluates to the conf_object_t * of the port's port object.

Subdevice objects

The subdevice template exposes one parameter, obj, which evaluates to the conf_object_t * of the Simics object that represents the subdevice.

Implement objects

The implement template provides no particular parameters or methods.

Implement templates

There is a single template for implement objects, namely bank_io_memory. The template can be instantiated when implementing the io_memory interface, and redirects the access to a bank, specified by the bank parameter.

Bank objects contain an implementation of io_memory that inherits this template.

Bank objects

In addition to the object template, the bank template also inherits the shown_desc template.

The bank template contains the following methods:

io_memory_access(generic_transaction_t *memop, uint64 offset, void *aux) -> (bool)

Entry point for an access based on generic_transaction_t. Extracts all needed info from memop, calls appropriate memop-free methods, updates the memop parameter accordingly, and returns true if the access succeeded. The offset parameter is the offset of the access relative to the bank. The aux parameter is NULL by default, and is passed on to bank methods. In order to pass additional information on the access down to register and field methods, one can override io_memory_access, decode needed information from the incoming memop, and call default with the extracted information in the aux argument.

transaction_access(transaction_t *t, uint64 offset, void *aux) -> (exception_type_t)

Entry point for an access based on the transaction interface. Extracts all needed info from t, calls appropriate access methods (read, write, get, set), and updates the t parameter accordingly. Returns Sim_PE_No_Exception if the access succeeded, and SIM_PE_IO_Not_Taken otherwise. The offset parameter is the offset of the access relative to the bank. The aux parameter is NULL by default, and is passed on to bank methods. In order to pass additional information on the access down to register and field methods, one can override transaction_access, decode needed information from the incoming transaction, and call default with the extracted information in the aux argument. Accesses that bigger than 8 bytes are split into smaller sized chunks before being completed, the exact details of which are undefined.

write(uint64 offset, uint64 value, uint64 enabled_bytes, void *aux) throws

A write operation at the given offset. Throwing an exception makes the access fail, and is typically signaled for writes outside registers. The default behavior is to forward the access to registers, as follows:

  1. Deduce which registers are hit by the access. The offset and size parameters of each register object is used to deduce whether the register is covered by the access. A register which is only partially covered will be considered hit if the bank parameter partial is true, and a register which does not fully cover the access is considered hit if the bank parameter overlapping is set.

  2. If any portion of the access is not covered by a hit register, then the unmapped_write method is called with a bit pattern showing what parts of the access are unmapped. Any exception thrown by unmapped_write is propagated, causing the access to fail.

  3. The write_register method is called in all hit registers, starting with the register at the lowest offset.

unmapped_write(uint64 offset, uint64 value, uint64 bits, void *aux) throws

If an access is not fully covered by registers, then this method is called before the access is performed. Throwing an exception aborts the entire access. bits is a bit pattern showing which bits are affected; in the lowest 'size' bits, each 0xff byte represents the position of one unmapped byte in the access. The value parameter contains the originally written value, including parts that are mapped to registers. Both bits and value are expressed in the host's endianness. The default behavior is to log a spec-viol message on level 1, and throw an exception.

read(uint64 offset, uint64 enabled_bytes, void *aux) -> (uint64 value) throws

A read operation at the given offset. The access is decomposed in the same way as in write. If there are unmapped portions of the access, unmapped_read is invoked, possibly aborting the operation. The return value is composed by the results from calling read_register in hit registers, combined with the result of unmapped_read if the access is not fully mapped.

unmapped_read(uint64 offset, uint64 bits, void *aux) throws

Like unmapped_write but for reads. The default implementation unconditionally throws an exception.

The bank template contains the following parameters:

mappable [boolean]

Controls whether a bank is visible as an interface port for the io_memory interface, which makes it mappable in a memory space. This defaults to true.

overlapping [bool]

Specifies whether this bank allows accesses that cover more than one register. (This translates to one or more, possibly partial, accesses to adjacent registers.) Defaults to true. This parameter must have the same value among all elements in a bank array object, i.e., it must not depend on the index of the bank.

partial [bool]

Specifies whether this bank allows accesses that cover only parts of a register. A partial read will read the touched register fields (or the whole register if there are no fields) and extract the bits covered by the read. A partial write will call the get method on the touched register fields (or the whole register when there are no fields) and replace the written bits with the written value and then call the write method on the fields (or the register) with the merged value. Defaults to true. This parameter must have the same value among all elements in a bank array object, i.e., it must not depend on the index of the bank.

register_size [integer | undefined]

Inherited from the device object; provides the default value for the size parameter of register objects.

byte_order [string]

Specifies the byte order used when accessing registers wider than a single byte; inherited from device objects. Allowed values are "little-endian" and "big-endian". This parameter must have the same value among all elements in a bank array object, i.e., it must not depend on the index of the bank.

be_bitorder [bool]

Controls the preferred bit ordering of registers within this bank. Whenever the register is presented to the user as a bitfield, bit 0 refers to the least significant bit if the parameter is false (the default), and to the most significant bit if the parameter is true. The parameter is only a presentation hint and does not affect the model's behaviour. The parameter is technically unrelated to the top-level bitorder declaration, though in most cases the two should match.

use_io_memory [bool]

If true, this bank is exposed using the legacy io_memory interface. In this case, the io_memory_access method can be called and overridden, but the transaction_access method can not.

If false, this bank is exposed using the transaction interface. In this case, the transaction_access method can be called and overridden, but the io_memory_access method can not.

The default is inherited from dev.use_io_memory.

obj [conf_object_t *]

When compiling with Simics API 5 or earlier, evaluates to dev.obj. When compiling with Simics API 6 or newer, evaluates to the bank's port object.

Register objects

In addition to object, the register template inherits the templates get, set, shown_desc, read_register, write_register, and init_val.

The register template contains the following parameters:

val [integer]

The contents of the register. Manipulating val is a simpler, but less safe alternative to using get_val() and set_val() — unlike set_val(), it is undefined behavior to write a value to val larger than what the register can hold.

size [integer]

The size (width) of the register, in bytes. This parameter can also be specified using the "size n" short-hand syntax for register objects. The default value is provided by the register_size parameter of the enclosing bank object.

bitsize [integer]

The size (width) of the register, in bits. This is equivalent to the value of the size parameter multiplied by 8, and cannot be overridden.

offset [integer]

The address offset of the register, in bytes relative to the start address of the bank that contains it. This parameter can also be specified using the "@ n" short-hand syntax for register objects. There is no default value. If the register inherits the unmapped template, the register is not mapped to an address. This parameter must have the same value among all elements in a bank array object, i.e., it must not depend on the index of the bank.

fields [list of references]

A list of references to all the field objects of a register object.

init_val [integer]

The value used by the default implementation of the init method, when the device is instantiated. The value is also used by the default implementations of hard reset, soft reset and power-on reset. Defaults to 0.

configuration ["required" | "optional" | "pseudo" | "none"]

Specifies how Simics treats the automatically created [attribute] (#attribute-objects) corresponding to the register. The default value is "optional".

persistent [bool]

Specifies whether the register attribute should be persistent.

internal [bool]

Specifies whether the register attribute should be internal, default is true.

The register template provides the following overridable methods:

read_unmapped_bits(uint64 unmapped_enabled_bits, void *aux) -> (uint64)

read_unmapped_bits is used by the default implementation of read_register to read the bits not covered by fields from a register, possibly with side-effects.

The default implementation of read_unmapped_bits acts similarly to if the unmapped regions of the register were covered by fields, the value of the register is masked by unmapped_enabled_bits and returned.

write_unmapped_bits(uint64 val, uint64 enabled_bits, void *aux).

The write_unmapped_bits method is called from the default implementation of the write_register method when enabled_bytes specifies bytes not completely covered by field in the register (and the register has at least one field). unmapped_enabled_bits is defined as in the read_unmapped_bits method. val is the val argument passed to write_register, masked with the bits not covered by fields.

Default behaviour of write_unmapped_bits is to compare the unmapped_enabled_bits in value to those in the register val; if they do not match, a message of type spec-viol is logged for each bitrange that does not match, but val is not modified.

Field objects

In addition to object, the field template inherits the templates init_val and shown_desc.

The template inherits methods get, set and init, and the parameter init_val

The field template contains the following parameters:

val [integer]

The bitslice of the parent register corresponding to the field. Manipulating val is a simpler alternative to using get_val() and set_val(), while being just as safe (unlike with register objects). Unlike get_val() and set_val(), val is not a member of the field template type, and thus can't be used in certain contexts.

reg [reference]

Always refers to the containing register object.

lsb [integer]

Required parameter. The bit number in the containing register of the field's least significant bit. Represented in little-endian bit order, regardless of bitorder declarations. The preferred way of defining this parameter is to use the "[highbit:lowbit]" short-hand syntax for field ranges, whose interpretation is dependent on the bitorder declaration of the file. Care must be taken when referring to this parameter in a big-endian bit numbering system - if possible, put such code in a separate file that uses little-endian bit order interpretation.

msb [integer]

Required parameter. The bit number in the containing register of the field's most significant bit. Represented in little-endian bit order. See lsb for details.

bitsize [integer]

The size (width) of the field, in bits. This is automatically set from the lsb and msb parameters and cannot be overridden.

init_val [integer]

The value used by the default implementation of the init method, when the device is instantiated. The value is also used by the default implementations of hard reset, soft reset and power-on reset. Defaults to 0.

Templates for registers and fields

This section lists templates that are specific for register and field objects.

All templates (except read_register and write_register) are applicable to both registers and fields. When writing a template that is applicable to both registers and fields, one should normally inherit one or more of the read, write, get and set methods.

Some methods have an argument void *aux. By default, that argument is NULL. The value can be overridden to carry arbitrary extra information about the access; this is done by overriding the io_memory_access method in the parent bank.

get_val

Provides a single non-overrideable method get_val() -> (uint64). In a register, it returns the value of the .val member; in a field, it returns the bits of the parent register's val member that are covered by the field.

get_val is very similar to get; the difference is that get_val is unaffected if get is overridden. Thus, get_val is slightly more efficient, at the cost of flexibility. It is generally advisable to use get.

set_val

Provides a single non-overrideable method set_val(uint64). In a register, it sets the value of the .val member; in a field, it sets the bits in the parent register's val member that are covered by the field.

set_val is very similar to (set)[#set); the difference is that set_val is unaffected if set is overridden. Thus, set_val is slightly more efficient, at the cost of flexibility. It is generally advisable to use set.

get

Extends the get_val template. Provides a single overrideable method get() -> (uint64), which retrieves the register's value, without side-effects, used for checkpointing and inspection. The default is to retrieve the value using the get_val` method.

In a field, this template must be explicitly instantiated in order for an override to take effect. Note however that field objects do provide a callable default implementation of the method.

set

Extends the set_val template. Provides a single overrideable method set(uint64), which modifies the register's value, without triggering other side-effects. Used for checkpointing and inspection. The default is to set the value using the set_val method.

In a field, this template must be explicitly instantiated in order for an override to take effect. Note however that field objects do provide a callable default implementation of the method.

read_register

Implemented only by registers, not applicable to fields.

Provides a single abstract method read_register(uint64 enabled_bytes, void *aux) -> (uint64).

The method reads from a register, possibly with side-effects. The returned value is represented in the host's native endianness. The enabled_bytes argument defines which bytes of the register is accessed, as a bitmask; each byte of the returned value has significance only if the corresponding byte in enabled_bytes is 0xff. If the access covers more than one register, then the parts of enabled_bytes that correspond to other registers are still zero.

Register objects provide a default implementation of read_register. The implementation invokes the read_field method of all sub-fields at least partially covered by enabled_bytes, in order from least to most significant bit. Bits not covered by fields are retrieved by calling the read_unmapped_bits, with unmapped_enabled_bits set as the enabled_bytes not covered by fields. If a register implements no fields, then the read_unmapped_bits is not called by default.

If a register inherits the read_field or read templates, then that template takes precedence over read_register, and the register's read behaviour is specified by the read_field or read method.

write_register

Implemented only by registers, not applicable to fields.

Provides a single abstract method: write_register(uint64 value, uint64 enabled_bytes, void *aux).

The method writes to the register, possibly with side-effects. The enabled_bytes parameter is defined as in the read_register method.

Register objects provide a default implementation of write_register. The default behaviour depends on whether the register has fields:

  • If the register has no fields, then the default behaviour is to set the register's val member to the new value, using the set method.

  • If the register has fields, then the default behavior is to invoke the write_fields method of all sub-fields covered at least partially by enabled_bytes, in order from least to most significant bit. Then write_unmapped_bits is called with the enabled bits that were not covered by fields.

    If a register inherits the write or write_field template, then that template takes precedence over write_register, and the register's write behaviour is specified by the write (or write_field) method.

read_field

Provides a single abstract method read_field(uint64 enabled_bits, void *aux) -> (uint64).

The method reads from a field or register, possibly with side-effects. The returned value is represented in the host's native endianness. The enabled_bits argument defines which bits of the register is accessed, as a bitmask; each bit of the returned value has significance only if the corresponding bit in enabled_bits is 1. If the access covers more than one field, then the parts of enabled_bits that correspond to other fields are still zero.

The read_field template is not implemented by fields or registers by default, and must be explicitly instantiated in order for a method override to have effect. read_field is the interface used for access by registers; in most cases, it is easier to express read operations using the read template.

Note that instantiating read_field on a register means that register reads behave as if the register consists of one single field; a read access will ignore any actual field subobjects in the register.

write_field

Provides a single abstract method write_field(uint64 value, uint64 enabled_bits, void *aux).

The method writes to a field or register, possibly with side-effects. The value is represented in the host's native endianness. The enabled_bits argument is defined as in the read_field method.

The write_field template is not implemented by fields or registers by default, and must be explicitly instantiated in order for a method override to have effect. write_field is the interface used for access by registers; in most cases, it is easier to express write operations using the write template.

Note that instantiating write_field on a register means that register writes behave as if the register consists of one single field; a write access will ignore any actual field subobjects in the register. This is often useful in read-only registers, as it allows reads to propagate to fields, while a violating write can be handled centrally for the whole register.

read

Extends templates read_field and get_val.

Provides a single overrideable method read() -> (uint64).

The method reads from a field or register, possibly with side-effects. The returned value is represented in the host's native endianness. The default behaviour is to retrieve the value using the get method.

The read template is not implemented by fields or registers by default, and must be explicitly instantiated in order for a method override to have effect.

Note that instantiating read on a register means that register reads behave as if the register consists of one single field; a read access will ignore any actual field subobjects in the register.

write

Extends templates write_field, get_val and set_val.

Provides a single overrideable method write(uint64).

The method writes to a field or register, possibly with side-effects. The value is represented in the host's native endianness. The default behaviour is to set the value using the set method.

The write template is not implemented by fields or registers by default, and must be explicitly instantiated in order for a method override to have effect.

Note that instantiating write on a register means that register writes behave as if the register consists of one single field; a write access will ignore any actual field subobjects in the register.

init_val

Extends the init template.

Provides a parameter init_val : uint64, defining the initial value of the register's val member when the object is created. In a field, defines the initial value of the bits of val that are covered by this field. The parameter is also used by default reset methods.

The template is inherited by both registers and fields. The value is 0 by default. In a register with fields, parameter overrides are permitted both in the register and in the field objects:

  • if the parameter is overridden only in the register, then this defines the full value of the register.

  • if the parameter is overridden both in the register and in some fields, then the field overrides take precedence and define the value of the bits covered by the field

On a technical level, the default value of init_val in a field is the field's corresponding bits in the parent register's init_val; furthermore, the init_val template provides a default implementation of the init method which in register objects sets bits in val not covered by fields, and in field objects sets corresponding bits of the parent register's val member.

Event objects

The event template contains little functionality in itself; it requires one of six predefined templates simple_time_event, simple_cycle_event, uint64_time_event, uint64_cycle_event, custom_time_event, and custom_cycle_event to be instantiated. These templates expose the methods event and post, and possibly others.

In addition to the object template, the event template inherits the shown_desc template.

Event templates

Each event object is required to instantiate one of six predefined templates: simple_time_event, simple_cycle_event, uint64_time_event, uint64_cycle_event, custom_time_event, and custom_cycle_event. These are defined as follows:

  • The simple_*_event templates are used for events that carry no data

  • The uint64_*_event templates are used for events parameterized with a single 64-bit integer value

  • The custom_*_event templates are used for events that carry more complex data; the user must supply explicit serialization and deserialization methods.

  • In the *_time_event templates, time is provided in seconds, as a floating-point number

  • In the *_cycle_event templates, time is provided in cycles, as a 64-bit integer

The following methods are defined by all six templates:

event(), event(uint64 data), event(void *data)

Abstract method, called when the event is triggered. When one of the custom_*_event templates is used, the event method is responsible for deallocating the data.

post(time), post(time, uint64 data), post(time, void *data)

Non-overrideable method. Posts the event on the associated queue of the device. The time argument is specified in cycles or seconds, depending on which template was instantiated. The event will be triggered after the specified amount of time has elapsed. The data parameter in a uint64 or custom event will be passed on to the event() method.

The following methods are specific to the simple_time_event, simple_cycle_event, uint64_time_event and uint64_cycle_event templates:

remove(), remove(uint64 data)

Removes all events of this type with matching data from the queue.

posted() -> (bool), posted(uint64 value) -> (bool)

Returns true if the event is in the queue, and false otherwise.

next(), next(uint64 data) -> (double or cycles_t)

Returns the time to the next occurrence of the event in the queue (relative to the current time), in cycles or seconds depending on which event template was instantiated. If there is no such event in the queue, a negative value is returned.

The following methods are specific to the custom_time_event and custom_cycle_event templates:

get_event_info(void *data) -> (attr_value_t)

This method is called once for each pending event instance when saving a checkpoint. It should create an attribute value that can be used to restore the event. The data parameter is the user data provided in the post call. The default implementation always returns a nil value.

set_event_info(attr_value_t info) -> (void *)

This method is used to restore event information when loading a checkpoint. It should use the attribute value to create a user data pointer, as if it had been provided in a post. The default implementation only checks that the checkpointed information is nil.

destroy(void *data)

This method is called on any posted events when the device object is deleted.

If memory was allocated for the data argument to post, then destroy should free this memory.

The destroy method is not called automatically when an event is triggered; therefore, the method should typically also be called explicitly from the event method.