Registers

A register is a piece of addressable memory stored on the device.

It is accessed as a function on the block it's part of. The function returns a RegisterOperation which can be used to read/write/modify the register.

Example usage:

#![allow(unused)]
fn main() {
let mut device = MyDevice::new(DeviceInterface::new());

device.foo().write(|reg| reg.set_bar(12345)).unwrap();
assert_eq!(device.foo().read().unwrap().bar(), 12345);
}

Below are minimal and full examples of how registers can be defined. Only one field is shown, but more can be added. Details about the fields can be read in their own chapter.

DSL

Minimal:

#![allow(unused)]
fn main() {
register Foo {
    const ADDRESS = 3;
    const SIZE_BITS = 16;

    value: uint = 0..16,
}
}

Full:

#![allow(unused)]
fn main() {
/// Register docs
#[cfg(feature = "bar")]
register Foo {
    type Access = WO;
    type ByteOrder = LE;
    type BitOrder = LSB0;
    const ADDRESS = 3;
    const SIZE_BITS = 16;
    const RESET_VALUE = 0x1234;  // Or [0x34, 0x12]
    const REPEAT = {
        count: 4,
        stride: 2
    };
    const ALLOW_BIT_OVERLAP = false;
    const ALLOW_ADDRESS_OVERLAP = false;

    value: uint = 0..16,
}
}

tip

type or const, which one is it?
It's type if it's overriding a global config and const if it's not.

Manifest

note

The biggest differences with the DSL are the additional type field to specify which type of object this is and the fields field that houses all fields.

Minimal (json):

"Foo": {
    "type": "register",
    "address": 3,
    "size_bits": 16,
    "fields": {
        "value": {
            "base": "uint",
            "start": 0,
            "end": 16
        }
    }
}

Full (json):

"Foo": {
    "type": "register",
    "cfg": "feature = \"foo\"",
    "description": "Register docs",
    "access": "WO",
    "byte_order": "LE",
    "bit_order": "LSB0",
    "address": 3,
    "size_bits": 16,
    "reset_value": 4066, // Or [52, 18] (no hex in json...)
    "repeat": {
        "count": 4,
        "stride": 2
    },
    "allow_bit_overlap": false,
    "allow_address_overlap": false,
    "fields": {
        "value": {
            "base": "uint",
            "start": 0,
            "end": 16
        }
    }
}

Required

address

The address of the register.

Integer value that must fit in the given address type in the global config and can be negative.

size_bits

The size of the register in bits.

Positive integer value. No fields can exceed the size of the register.

type (manifest only)

The type of the object.

For registers this field is a string with the contents "register".

Optional

cfg or #[cfg(...)]

Allows for cfg-gating the register.

In the DSL, the normal Rust syntax is used. Just put the attribute on the register definition. Only one attribute is allowed.

In the manifest it is configured with a string. The string only defines the inner part: #[cfg(foo)] = "cfg": "foo",.

warning

Check the chapter on cfg for more information. The cfg's are not checked by the toolkit and only passed to the generated code and so there are some oddities to be aware of.

description or #[doc = ""]

The doc comments for the generated code.

For the DSL, use the normal doc attributes or triple slash ///. Multiple attributes get concatenated with a newline (just like normal Rust does).

For the manifest, this is a string.

The description is added as normal doc comments to the generated code. So it supports markdown and all other features you're used to. The description is used on the generated register struct and on the function to access the register.

access

Overrides the default register access.

Options are: RW, ReadWrite, WO, WriteOnly, RO, ReadOnly.
They are written 'as is' in the DSL and as a string in the manifest.

Anything that is not ReadWrite will limit the functions you can call for the registers. .write is only available when the register has write access, .read only when the register has read access and .modify only when the register has full access.

note

This only affects the capability of a register being read or written. It does not affect the access specified on the fields.

This means you can have a register you cannot write, but does have setters for one or more fields.
That won't be harmful or break things, but might look weird.

byte_order

Overrides the default byte order.

Options are: LE, BE.
They are written 'as is' in the DSL and as a string in the manifest.

When the size of a register is > 8 bits (more than one byte), then either the byte order has to be defined globally as a default or the register needs to define it.

bit_order

Overrides the default bit order. If the global config does not define it, it's LSB0.

Options are: LSB0, MSB0.
They are written 'as is' in the DSL and as a string in the manifest.

reset_value

Defines the reset or default value of the register.

Can be a number or an array of bytes.

warning

When specified as an array, this must be formatted as the bytes that are returned by the RegisterInterface implementation. This means that when the register has little endian byte order, the reset value number 0x1234 would be encoded as [0x34, 0x12] in the array form.
The same concern is there for the bit order.

It is used in the .write function. To reset a register to the default value, it'd look like .write(|_|()). When a zero value is desired instead of the default, you can use the .write_with_zero function instead.

repeat

Repeat the register a number of times at different addresses.

It is specified with two fields:

  • Count: unsigned integer, the amount of times the register is repeated
  • Stride: signed integer, the amount the address changes per repeat

The calculation is address = base_address + index * stride.

When the repeat field is present, the function to do a register operation will have an extra parameter for the index.

allow_bit_overlap

Allow field addresses to overlap.

This bool value is false by default.

allow_address_overlap

Allow this register to have an address that is equal to another register address. This calculation is also done for any repeat addresses.

Only exact address matches are checked.

This bool value is false by default.

fields (manifest only)

The fields of the register.

A map where the keys are the names of the fields. All values must be fields.