14 releases (3 stable)

1.0.2 Jan 1, 2021
1.0.1 Nov 27, 2020
0.5.1 May 31, 2020
0.5.0 Jan 16, 2020
0.1.1 Jul 16, 2018

#4 in #registers

Download history 15/week @ 2023-12-17 11/week @ 2023-12-24 13/week @ 2024-01-07 15/week @ 2024-01-14 4/week @ 2024-01-21 2/week @ 2024-02-04 15/week @ 2024-02-11 11/week @ 2024-02-18 139/week @ 2024-02-25 57/week @ 2024-03-03 55/week @ 2024-03-10 50/week @ 2024-03-17 25/week @ 2024-03-24 232/week @ 2024-03-31

374 downloads per month
Used in 2 crates

MIT/Apache

12KB
90 lines

crates.io crates.io

register-rs

Unified interface for type-safe MMIO and CPU register access in Rust.

Outline

Usage

This crate uses the tock-register-interface, please refer to their Readme for the whole API. Make sure to check the register_structs! explanation, especially the fact that you must explicitly annotate gaps.

Defining MMIO registers

use register::{mmio::*, register_bitfields, register_structs};

register_bitfields! {
    u32,

    GPFSEL1 [
        FSEL14 OFFSET(12) NUMBITS(3) [
            Input = 0b000,
            Output = 0b001,
            TXD0 = 0b100
        ],

        FSEL15 OFFSET(15) NUMBITS(3) [
            Input = 0b000,
            Output = 0b001,
            RXD0 = 0b100
        ]
    ]
}

register_structs! {
    #[allow(non_snake_case)]
    pub RegisterBlock {
        (0x000 => GPFSEL1: ReadWrite<u32, GPFSEL1::Register>),
        (0x004 => SYSTMR_HI: ReadOnly<u32>),
        (0x008 => @END),
    }
}

fn main() {
    let regs = 0x1337_0000 as *const RegisterBlock;

    unsafe { (*regs).SYSTMR_HI.get() };
}

The Deref pattern for drivers

The MMIO part of this crate can and will often be used for implementing device drivers. In this case, you might find the Deref pattern useful for referencing your registers. It alleviates you from manually dereferencing each time a register access is due, and also encapsulates the unsafe keyword.

Here is an example (extending the code snippet from above):

register_bitfields! {
    u32,

    // omitted
}

register_structs! {
    #[allow(non_snake_case)]
    pub RegisterBlock {
        // omitted
    }
}

pub struct DeviceDriver {
    base_addr: usize,
}

impl ops::Deref for DeviceDriver {
    type Target = RegisterBlock;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.ptr() }
    }
}

impl DeviceDriver {
    pub fn new(base_addr: usize) -> Self {
        DeviceDriver { base_addr }
    }

    /// Returns a pointer to the register block
    fn ptr(&self) -> *const RegisterBlock {
        self.base_addr as *const _
    }

    fn do_something(&self) -> u32 {
        self.GPFSEL1.set(0x1337);
        self.SYSTMR_HI.get()
    }
}

Defining CPU registers

For CPU registers, you only need to implement the respective read/write trait. All other methods are provided by default.

#![feature(llvm_asm)]

use register::{cpu::RegisterReadWrite, register_bitfields};

register_bitfields! {u32,
    pub CNTP_CTL_EL0 [
        /// Enables the timer.
        ENABLE        OFFSET(0)  NUMBITS(1) [],

        /// Timer interrupt mask bit.
        IMASK         OFFSET(1)  NUMBITS(1) [],

        /// The status of the timer.
        ISTATUS       OFFSET(2)  NUMBITS(1) []
    ]
}

struct Reg;

impl RegisterReadWrite<u32, CNTP_CTL_EL0::Register> for Reg {
    /// Reads the raw bits of the CPU register.
    #[inline(always)]
    fn get(&self) -> u32 {
        let reg;
        unsafe {
            llvm_asm!("mrs $0, CNTP_CTL_EL0" : "=r"(reg) ::: "volatile");
        }
        reg
    }

    /// Writes raw bits to the CPU register.
    #[inline(always)]
    fn set(&self, value: u32) {
        unsafe {
            llvm_asm!("msr CNTP_CTL_EL0, $0" :: "r"(value) :: "volatile");
        }
    }
}

static CNTP_CTL_EL0: Reg = Reg {};

fn main() {
    CNTP_CTL_EL0.modify(CNTP_CTL_EL0::ENABLE::SET + CNTP_CTL_EL0::IMASK::SET);
}

Testing

In order to use this crate in custom_test_frameworks environments, please set the no_std_unit_tests feature flag in your dependency section.

Otherwise, you might encounter errors like the following:

error[E0463]: can't find crate for `test`
  --> src/bsp/driver/bcm/bcm2xxx_gpio.rs:52:1
   |
52 | / register_structs! {
53 | |     #[allow(non_snake_case)]
54 | |     RegisterBlock {
55 | |         (0x00 => GPFSEL0: ReadWrite<u32>),
...  |
66 | |     }
67 | | }
   | |_^ can't find crate
   |
   = note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)

error: aborting due to previous error

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Dependencies

~90KB