Skip to main content

register

Macro register 

Source
macro_rules! register {
    (
        $(
            $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
                $([ $size:expr $(, stride = $stride:expr)? ])?
                $(@ $($base:ident +)? $offset:literal)?
                $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
            { $($fields:tt)* }
        )*
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
            [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident [ $idx:expr ]
            { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
            [ $size:expr, stride = $stride:expr ]
            @ $base:ident + $offset:literal { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
            @ $base:ident + $offset:literal { $($fields:tt)* }
    ) => { ... };
    (
        @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
            => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
    ) => { ... };
    (
        @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
    ) => { ... };
    (@io_base $name:ident($storage:ty) @ $offset:expr) => { ... };
    (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => { ... };
    (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => { ... };
    (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => { ... };
    (
        @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]
            @ $base:ident + $offset:literal
    ) => { ... };
    (@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => { ... };
    (@bitfield_fields $vis:vis $name:ident $storage:ty {
        $($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
            $(?=> $try_into_type:ty)?
            $(=> $into_type:ty)?
        ;
        )*
    }
    ) => { ... };
    (
        @private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
    ) => { ... };
    (
        @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
            $hi:literal:$lo:literal $field:ident => $into_type:ty
    ) => { ... };
    (
        @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
            $hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
    ) => { ... };
    (
        @public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
            $hi:tt:$lo:tt $field:ident
    ) => { ... };
    (@debug $name:ident { $($field:ident;)* }) => { ... };
}
Expand description

Defines a dedicated type for a register, including getter and setter methods for its fields and methods to read and write it from an Io region.

This documentation focuses on how to declare registers. See the module-level documentation for examples of how to access them.

There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of registers, and relative arrays of registers.

§Fixed offset registers

These are the simplest kind of registers. Their location is simply an offset inside the I/O region. For instance:

register! {
    pub FIXED_REG(u16) @ 0x80 {
        ...
    }
}

This creates a 16-bit register named FIXED_REG located at offset 0x80 of an I/O region.

These registers’ location can be built simply by referencing their name:

use kernel::{
    io::{
        register,
        Io,
    },
};

register! {
    FIXED_REG(u32) @ 0x100 {
        16:8 high_byte;
        7:0  low_byte;
    }
}

let val = io.read(FIXED_REG);

// Write from an already-existing value.
io.write(FIXED_REG, val.with_low_byte(0xff));

// Create a register value from scratch.
let val2 = FIXED_REG::zeroed().with_high_byte(0x80);

// The location of fixed offset registers is already contained in their type. Thus, the
// `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
io.write((), val2);

// Or, the single-argument `Io::write_reg` can be used.
io.write_reg(val2);

It is possible to create an alias of an existing register with new field definitions by using the => ALIAS syntax. This is useful for cases where a register’s interpretation depends on the context:

use kernel::io::register;

register! {
    /// Scratch register.
    pub SCRATCH(u32) @ 0x00000200 {
        31:0 value;
    }

    /// Boot status of the firmware.
    pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
        0:0 completed;
    }
}

In this example, SCRATCH_BOOT_STATUS uses the same I/O address as SCRATCH, while providing its own completed field.

§Relative registers

Relative registers can be instantiated several times at a relative offset of a group of bases. For instance, imagine the following I/O space:

          +-----------------------------+
          |             ...             |
          |                             |
 0x100--->+------------CPU0-------------+
          |                             |
 0x110--->+-----------------------------+
          |           CPU_CTL           |
          +-----------------------------+
          |             ...             |
          |                             |
          |                             |
 0x200--->+------------CPU1-------------+
          |                             |
 0x210--->+-----------------------------+
          |           CPU_CTL           |
          +-----------------------------+
          |             ...             |
          +-----------------------------+

CPU0 and CPU1 both have a CPU_CTL register that starts at offset 0x10 of their I/O space segment. Since both instances of CPU_CTL share the same layout, we don’t want to define them twice and would prefer a way to select which one to use from a single definition.

This can be done using the Base + Offset syntax when specifying the register’s address:

register! {
    pub RELATIVE_REG(u32) @ Base + 0x80 {
        ...
    }
}

This creates a register with an offset of 0x80 from a given base.

Base is an arbitrary type (typically a ZST) to be used as a generic parameter of the RegisterBase trait to provide the base as a constant, i.e. each type providing a base for this register needs to implement RegisterBase<Base>.

The location of relative registers can be built using the WithBase::of method to specify its base. All relative registers implement WithBase.

Here is the above layout translated into code:

use kernel::{
    io::{
        register,
        register::{
            RegisterBase,
            WithBase,
        },
        Io,
    },
};

// Type used to identify the base.
pub struct CpuCtlBase;

// ZST describing `CPU0`.
struct Cpu0;
impl RegisterBase<CpuCtlBase> for Cpu0 {
    const BASE: usize = 0x100;
}

// ZST describing `CPU1`.
struct Cpu1;
impl RegisterBase<CpuCtlBase> for Cpu1 {
    const BASE: usize = 0x200;
}

// This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
register! {
    /// CPU core control.
    pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
        0:0 start;
    }
}

// Read the status of `Cpu0`.
let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());

// Stop `Cpu0`.
io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());

// Aliases can also be defined for relative register.
register! {
    /// Alias to CPU core control.
    pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
        /// Start the aliased CPU core.
        1:1 alias_start;
    }
}

// Start the aliased `CPU0`, leaving its other fields untouched.
io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));

§Arrays of registers

Some I/O areas contain consecutive registers that share the same field layout. These areas can be defined as an array of identical registers, allowing them to be accessed by index with compile-time or runtime bound checking:

register! {
    pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
        ...
    }
}

This defines REGISTER_ARRAY, an array of 10 byte registers starting at offset 0x100. Each register is separated from its neighbor by 4 bytes.

The stride parameter is optional; if unspecified, the registers are placed consecutively from each other.

A location for a register in a register array is built using the Array::at trait method. All arrays of registers implement Array.

use kernel::{
    io::{
        register,
        register::Array,
        Io,
    },
};

// Array of 64 consecutive registers with the same layout starting at offset `0x80`.
register! {
    /// Scratch registers.
    pub SCRATCH(u32)[64] @ 0x00000080 {
        31:0 value;
    }
}

// Read scratch register 0, i.e. I/O address `0x80`.
let scratch_0 = io.read(SCRATCH::at(0)).value();

// Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
io.write(Array::at(15), SCRATCH::from(0xffeeaabb));

// This is out of bounds and won't build.
// let scratch_128 = io.read(SCRATCH::at(128)).value();

// Runtime-obtained array index.
let idx = get_scratch_idx();
// Access on a runtime index returns an error if it is out-of-bounds.
let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();

// Alias to a specific register in an array.
// Here `SCRATCH[8]` is used to convey the firmware exit code.
register! {
    /// Firmware exit status code.
    pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
        7:0 status;
    }
}

let status = io.read(FIRMWARE_STATUS).status();

// Non-contiguous register arrays can be defined by adding a stride parameter.
// Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
// registers of the two declarations below are interleaved.
register! {
    /// Scratch registers bank 0.
    pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
        31:0 value;
    }

    /// Scratch registers bank 1.
    pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
        31:0 value;
    }
}

§Relative arrays of registers

Combining the two features described in the sections above, arrays of registers accessible from a base can also be defined:

register! {
    pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
        ...
    }
}

Like relative registers, they implement the WithBase trait. However the return value of WithBase::of cannot be used directly as a location and must be further specified using the at method.

use kernel::{
    io::{
        register,
        register::{
            RegisterBase,
            WithBase,
        },
        Io,
    },
};

// Type used as parameter of `RegisterBase` to specify the base.
pub struct CpuCtlBase;

// ZST describing `CPU0`.
struct Cpu0;
impl RegisterBase<CpuCtlBase> for Cpu0 {
    const BASE: usize = 0x100;
}

// ZST describing `CPU1`.
struct Cpu1;
impl RegisterBase<CpuCtlBase> for Cpu1 {
    const BASE: usize = 0x200;
}

// 64 per-cpu scratch registers, arranged as a contiguous array.
register! {
    /// Per-CPU scratch registers.
    pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
        31:0 value;
    }
}

// Read scratch register 0 of CPU0.
let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));

// Write the retrieved value into scratch register 15 of CPU1.
io.write(WithBase::of::<Cpu1>().at(15), scratch);

// This won't build.
// let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();

// Runtime-obtained array index.
let scratch_idx = get_scratch_idx();
// Access on a runtime index returns an error if it is out-of-bounds.
let cpu0_scratch = io.read(
    CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
).value();

// Alias to `SCRATCH[8]` used to convey the firmware exit code.
register! {
    /// Per-CPU firmware exit status code.
    pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
        7:0 status;
    }
}

// Non-contiguous relative register arrays can be defined by adding a stride parameter.
// Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
// registers of the two declarations below are interleaved.
register! {
    /// Scratch registers bank 0.
    pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
        31:0 value;
    }

    /// Scratch registers bank 1.
    pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
        31:0 value;
    }
}

let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();