Expand description

Interfaces (traits) to register types

This module contains traits which reflect standardized interfaces to different types of registers. Examples of registers implementing these interfaces are ReadWrite or InMemoryRegister.

Each trait has two associated type parameters, namely:

  • T: UIntLike, indicating the underlying integer type used to represent the register’s raw contents.

  • R: RegisterLongName, functioning as a type to identify this register’s descriptive name and semantic meaning. It is further used to impose type constraints on values passed through the API, such as FieldValue.

Registers can have different access levels, which are mapped to different traits respectively:

  • Readable: indicates that the current value of this register can be read. Implementations will need to provide the get method.

  • Writeable: indicates that the value of this register can be set. Implementations will need to provide the set method.

  • ReadWriteable: indicates that this register can be modified. It is not sufficient for registers to be both read- and writable, they must also have the same semantic meaning when read from and written to. This is not true in general, for example a memory-mapped UART register might transmit when writing and receive when reading.

    If a type implements both Readable and Writeable, and the associated RegisterLongName type parameters are identical, it will automatically implement ReadWriteable. In particular, for Aliased this is – in general – not the case, so

    register_bitfields![u8,
        A [
            DUMMY OFFSET(0) NUMBITS(1) [],
        ],
    ];
    let read_write_reg: &ReadWrite<u8, A::Register> = unsafe {
        core::mem::transmute(Box::leak(Box::new(0_u8)))
    };
    ReadWriteable::modify(read_write_reg, A::DUMMY::SET);

    works, but not

    register_bitfields![u8,
        A [
            DUMMY OFFSET(0) NUMBITS(1) [],
        ],
        B [
            DUMMY OFFSET(0) NUMBITS(1) [],
        ],
    ];
    let aliased_reg: &Aliased<u8, A::Register, B::Register> = unsafe {
        core::mem::transmute(Box::leak(Box::new(0_u8)))
    };
    ReadWriteable::modify(aliased_reg, A::DUMMY::SET);

Example: implementing a custom register type

These traits can be used to implement custom register types, which are compatible to the ones shipped in this crate. For example, to define a register which sets a u8 value using a Cell reference, always reads the bitwise-negated vale and prints every written value to the console:

struct DummyRegister<'a, R: RegisterLongName> {
    cell_ref: &'a Cell<u8>,
    _register_long_name: PhantomData<R>,
}

impl<'a, R: RegisterLongName> Readable for DummyRegister<'a, R> {
    type T = u8;
    type R = R;

    fn get(&self) -> u8 {
        // Return the bitwise-inverse of the current value
        !self.cell_ref.get()
    }
}

impl<'a, R: RegisterLongName> Writeable for DummyRegister<'a, R> {
    type T = u8;
    type R = R;

    fn set(&self, value: u8) {
        println!("Setting Cell to {:02x?}!", value);
        self.cell_ref.set(value);
    }
}

register_bitfields![u8,
    DummyReg [
        HIGH OFFSET(4) NUMBITS(4) [
            A = 0b0001,
            B = 0b0010,
            C = 0b0100,
            D = 0b1000,
        ],
        LOW OFFSET(0) NUMBITS(4) [],
    ],
];

// Create a new DummyRegister over some Cell<u8>
let cell = Cell::new(0);
let dummy = DummyRegister {
    cell_ref: &cell,
    _register_long_name: PhantomData,
};

// Set a value and read it back. This demonstrates the raw getters
// and setters of Writeable and Readable
dummy.set(0xFA);
assert!(dummy.get() == 0x05);

// Use some of the automatically derived APIs, such as
// ReadWriteable::modify and Readable::read
dummy.modify(DummyReg::HIGH::C);
assert!(dummy.read(DummyReg::HIGH) == 0xb);

Traits

Readable and Writeable register, over the same RegisterLongName

Readable register

Writeable register