Previous - Up - Next

3.7   Registers

A register is an component of a register bank that contains an integer value, which can be either unsigned (the default) or signed. Normally, a register corresponds to a segment of consecutive locations in the address space of the bank; however, it is also possible (and often useful) to have registers that are not mapped to any address within the bank.

Every register has a fixed size (or width), which is an integral, nonzero number of 8-bit bytes. A single register cannot be wider than 8 bytes, in DML 1.0. The size of the register is given by the size parameter, which can be specified either by a normal parameter assignment, as in

    register r1 {
      parameter size = 4;
or more commonly, using the following short-hand syntax:
    register r1 size 4 {
which has the same meaning. The default size is provided by the register_size parameter of the containing register bank, if that is defined.

The value of the register can be accessed directly by referencing the object, as in

    log "info": "the value of register r1 is %d", $r1;
    $r1 += 1;

Storage for the register value is created by default in the generated C code. If this is not needed (e.g., if write accesses to the register are ignored and read accesses always return a constant value such as 0), it can be disabled in order to save memory by setting the allocate parameter to false. Non-allocated registers cannot be accessed by value.

3.7.1   Mapping Addresses To Registers

For a register to be mapped into the internal address space of the containing bank, its starting address within the bank must be given by setting the offset parameter. The address range occupied by the register is then from offset to offset + size - 1. The offset can be specified by a normal parameter assignment, as in

    register r1 {
      parameter offset = 0x0100;
or using the following short-hand syntax:
    register r1 @ 0x0100 {
similar to the size parameter above. Usually, a normal read/write register does not need any additional specifications apart from the size and offset, and can simply be written like this:
    register r1 size 4 @ 0x0100;
or, if the bank contains several registers of the same size:
    bank b1 {
      parameter register_size = 4;
      register r1 @ 0x0100;
      register r2 @ 0x0103;

The translation from the bank address space to the actual value of the register is controlled by the byte_order parameter. When it is set to "little-endian" (the default), the lowest address, i.e., that defined by offset, corresponds to the least significant byte in the register, and when set to "big-endian", the lowest address corresponds to the most significant byte in the register. The third allowed value of this parameter is undefined, which ensures that neither assumption is ever made.

3.7.2   Register Attributes

For every register, an attribute of integer type is automatically added to the Simics configuration class generated from the device model. The name of the attribute corresponds to the hierarchy in the DML model; e.g., a register named r1 in a bank named bank0 will get a corresponding attribute named bank0_r1.

The register value is automatically saved when Simics creates a checkpoint, unless the configuration parameter indicates otherwise.

3.7.3   Fields

Real hardware registers often have a number of fields with separate meaning. For example, the lowest three bits of the register could be a status code, the next six bits could be a set of flags, and the rest of the bits could be reserved.

To make this easy to express, a register object can contain a number of field objects. Each field is defined to correspond to a bit range of the containing register. (Technically, a register that does not contain any explicitly defined fields automatically gets a single, anonymous field, which covers the whole register.)

Only the values of the fields are stored in the generated C code; the value of the register as a whole is composed from the field values when needed (for example, when creating a Simics checkpoint). Storage for individual fields can be controlled by the allocate parameter, which by default is inherited from the register object.

For example, the register described above could be modeled as follows, using the default little-endian bit numbering.

    bank b2 {
      register r0 size 4 @ 0x0000 {
        field status   [2:0];
        field flags    [8:3];
        field reserved [15:9] {
          parameter allocate = false;
        } ;

Note that the most significant bit number is always the first number (to the left of the colon) in the range, regardless of whether little-endian or big-endian bit numbering is used. (The bit numbering convention used in a source file can be selected by a bitorder declaration.)

The value of the field can be accessed by referencing the object, as for a register, e.g.:

    log "info": "the value of the status field is %d", $r0.status;
Note however that non-allocated fields cannot be accessed this way, since they do not have a stored value.

Previous - Up - Next