rig.bitfield: routing key construction made easy

In many applications there is a need to define bit fields, for example when defining SpiNNaker routing keys. Rig provides the class rig.bitfield.BitField which allows the definition of hierarchical bit fields. The full API documentation is provided below and a tutorial is also available in the Rig documentation.

A tutorial introduction to this class is provided below and is followed by the full API documentation.

class rig.bitfield.BitField(length=32, _fields=None, _field_values=None)[source]

Defines a hierarchical bit field and the values of those fields.

Conceptually, a bit field is a sequence of bits which are logically broken up into individual fields which represent independent, unsigned integer values. For example, one could represent a pair of eight-bit values x and y as a sixteen-bit bit field where the upper eight bits are x and the lower eight bits are y. Bit fields are used when multiple pieces of information must be conveyed by a single binary value.

For example, one method of allocating SpiNNaker routing keys (which are 32-bit values) is to define each route a key as bit field with three fields. The fields x, y, and p can be used to represent the x- and y-chip-coordinate and processor id of a route’s source.

A hierarchical bit field is a bit field with fields which only exist dependent on the values of other fields. For a further routing-key related example, different key formats may be used by external devices and the rest of the SpiNNaker application. In these cases, a single bit could be used in the key to determine which key format is in use. Depending on the value of this bit, different fields would become available.

This class supports the following key features:

  • Construction of guaranteed-safe hierarchical bit field formats.
  • Generation of bit-masks which select only defined fields
  • Automatic allocation of field sizes based on values actually used.
  • Partial-definition of a bit field (i.e. defining only a subset of available fields).
__init__(length=32, _fields=None, _field_values=None)[source]

Create a new BitField.

An instance, b, of BitField represents a fixed-length hierarchical bit field with initially no fields. Fields can be added using BitField.add_field(). Derivatives of this instance with fields set to specific values can be created using the ‘call’ syntax: b(field_name=value, other_field_name=other_value) (see BitField.__call__()).

Note

Only one BitField instance should be explicitly created for each bit field.

Parameters:
length : int

The total number of bits in the bit field.

_fields : _Tree

For internal use only. The shared, global field tree.

_field_values : dict

For internal use only. Mapping of field-identifier to value.

add_field(identifier, length=None, start_at=None, tags=None)[source]

Add a new field to the BitField.

If any existing fields’ values are set, the newly created field will become a child of those fields. This means that this field will exist only when the parent fields’ values are set as they are currently.

Parameters:
identifier : str

A identifier for the field. Must be a valid python identifier. Field names must be unique within the scope in which they exist and are only valid within that scope. For example:

>>> bf = BitField(32)
>>> bf.add_field("a")

>>> # Can add multiple fields with the same name if they exist
>>> # in different scopes
>>> bf0 = bf(a=0)
>>> bf0.add_field("b", length=4)
>>> bf1 = bf(a=1)
>>> bf1.add_field("b", length=8)

>>> # Can't add multiple fields with the same name which exist
>>> # within the same or nested scopes.
>>> bf.add_field("a")
Traceback (most recent call last):
ValueError: Field with identifier 'a' already exists
>>> bf.add_field("b")
Traceback (most recent call last):
ValueError: Field with identifier 'b' already exists

Here three fields are defined, one called “a” and the other two called “b”. The two fields called “b” are completely unrelated (they may differ in size, position and associated set of tags) and are distinguished by the fact that one exists when a=0 and the other when a=1.

length : int or None

The number of bits in the field. If None the field will be automatically assigned a length long enough for the largest value assigned.

start_at : int or None

0-based index of least significant bit of the field within the bit field. If None the field will be automatically located in free space in the bit field.

tags : string or collection of strings or None

A (possibly empty) set of tags used to classify the field. Tags should be valid Python identifiers. If a string, the string must be a single tag or a space-separated list of tags. If None, an empty set of tags is assumed. These tags are applied recursively to all fields of which this field is a child.

Raises:
ValueError

If any the field overlaps with another one or does not fit within the bit field. Note that fields with unspecified lengths and positions do not undergo such checks until their length and position become known when assign_fields() is called.

__call__(**field_values)[source]

Return a new BitField instance with fields assigned values as specified in the keyword arguments.

Returns:
:py:class:`.BitField`

A BitField derived from this one but with the specified fields assigned a value.

Raises:
ValueError

If any field has already been assigned a value or the value is too large for the field.

UnavailableFieldError

If a field is specified which does not exist or is not available.

__getattr__(identifier)[source]

Get the value of a field.

Returns:
int or None

The value of the field (or None if the field has not been given a value).

Raises:
UnavailableFieldError

If the field requested does not exist or is not available given current field values.

get_value(tag=None, field=None)[source]

Generate an integer whose bits are set according to the values of fields in this bit field. All other bits are set to zero.

Parameters:
tag : str

Optionally specifies that the value should only include fields with the specified tag.

field : str

Optionally specifies that the value should only include the specified field.

Raises:
ValueError

If a field’s value, length or position has not been defined. (e.g. assign_fields() has not been called).

UnknownTagError

If the tag specified using the tag argument does not exist.

UnavailableFieldError

If the field specified using the field argument does not exist or is not available.

get_mask(tag=None, field=None)[source]

Get the mask for all fields which exist in the current bit field.

Parameters:
tag : str

Optionally specifies that the mask should only include fields with the specified tag.

field : str

Optionally specifies that the mask should only include the specified field.

Raises:
ValueError

If a field’s length or position has not been defined. (e.g. assign_fields() has not been called).

UnknownTagError

If the tag specified using the tag argument does not exist.

UnavailableFieldError

If the field specified using the field argument does not exist or is not available.

get_tags(field)[source]

Get the set of tags for a given field.

Note

The named field must be accessible given the current set of values defined.

Parameters:
field : str

The field whose tag should be read.

Returns:
set([tag, …])
Raises:
UnavailableFieldError

If the field does not exist or is not available.

get_location_and_length(field)[source]

Get the location and length of a field within the bitfield.

Note

The named field must be accessible given the current set of values defined.

Parameters:
field : str

The field of interest.

Returns:
location, length

A pair of integers defining the bit-number of the least-significant bit in the field and the total number of bits in the field respectively.

Raises:
ValueError

If a field’s length or position has not been defined. (e.g. assign_fields() has not been called).

UnavailableFieldError

If the field does not exist or is not available.

assign_fields()[source]

Assign a position & length to any fields which do not have one.

Users should typically call this method after all field values have been assigned, otherwise fields may be fixed at an inadequate size.

__eq__(other)[source]

Test that this BitField is equivalent to another.

In order to be equal, the other BitField must be a descendent of the same original BitField (and thus will always have exactly the same set of fields). It must also have the same field values defined.

__ne__(other)[source]

x.__ne__(y) <==> x!=y

__repr__()[source]

Produce a human-readable representation of this bit field and its current value.

__weakref__

list of weak references to the object (if defined)

class rig.bitfield.UnknownTagError(tag)[source]

Exception thrown when a tag is specified which does not exist.

class rig.bitfield.UnavailableFieldError(tree, identifier, field_values)[source]

Exception thrown when a field is requested from a BitField which is not does not exist or is unavailable (i.e. not in scope).