tock_registers/registers.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! Implementation of included register types.
6//!
7//! This module provides a standard set of register types, which can describe
8//! different access levels:
9//!
10//! - [`ReadWrite`] for registers which can be read and written to
11//! - [`ReadOnly`] for registers which can only be read
12//! - [`WriteOnly`] for registers which can only be written to
13//! - [`Aliased`] for registers which can be both read and written, but
14//! represent different registers depending on the operation
15//! - [`InMemoryRegister`] provide a register-type in RAM using volatile
16//! operations
17//!
18//! These types can be disabled by removing the `register_types` crate feature
19//! (part of the default features). This is useful if this crate should be used
20//! only as an interface library, or if all unsafe code should be disabled.
21
22use core::cell::UnsafeCell;
23use core::marker::PhantomData;
24
25use crate::interfaces::{Readable, Writeable};
26use crate::{RegisterLongName, UIntLike};
27
28/// Read/Write registers.
29///
30/// For accessing and manipulating the register contents, the [`Readable`],
31/// [`Writeable`] and [`ReadWriteable`](crate::interfaces::ReadWriteable) traits
32/// are implemented.
33// To successfully alias this structure onto hardware registers in memory, this
34// struct must be exactly the size of the `T` and is thus marked
35// `repr(transparent)` over an `UnsafeCell<T>`, which itself is
36// `repr(transparent)` over `T`.
37//
38// This struct is constructed by casting a pointer to it (or, implicitly, by
39// casting a pointer to a larger struct that containts this type). As such, it
40// does not have a public constructor and Rust thinks it's dead code and should
41// be removed. We `allow(dead_code)` here to suppress this warning.
42#[allow(dead_code)]
43#[repr(transparent)]
44pub struct ReadWrite<T: UIntLike, R: RegisterLongName = ()> {
45 value: UnsafeCell<T>,
46 associated_register: PhantomData<R>,
47}
48impl<T: UIntLike, R: RegisterLongName> Readable for ReadWrite<T, R> {
49 type T = T;
50 type R = R;
51
52 #[inline]
53 fn get(&self) -> Self::T {
54 unsafe { ::core::ptr::read_volatile(self.value.get()) }
55 }
56}
57impl<T: UIntLike, R: RegisterLongName> Writeable for ReadWrite<T, R> {
58 type T = T;
59 type R = R;
60
61 #[inline]
62 fn set(&self, value: T) {
63 unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
64 }
65}
66
67/// Read-only registers.
68///
69/// For accessing the register contents the [`Readable`] trait is implemented.
70// To successfully alias this structure onto hardware registers in memory, this
71// struct must be exactly the size of the `T` and is thus marked
72// `repr(transparent)` over an `UnsafeCell<T>`, which itself is
73// `repr(transparent)` over `T`.
74//
75// This struct is constructed by casting a pointer to it (or, implicitly, by
76// casting a pointer to a larger struct that containts this type). As such, it
77// does not have a public constructor and Rust thinks it's dead code and should
78// be removed. We `allow(dead_code)` here to suppress this warning.
79#[allow(dead_code)]
80#[repr(transparent)]
81pub struct ReadOnly<T: UIntLike, R: RegisterLongName = ()> {
82 value: T,
83 associated_register: PhantomData<R>,
84}
85impl<T: UIntLike, R: RegisterLongName> Readable for ReadOnly<T, R> {
86 type T = T;
87 type R = R;
88
89 #[inline]
90 fn get(&self) -> T {
91 unsafe { ::core::ptr::read_volatile(&self.value) }
92 }
93}
94
95/// Write-only registers.
96///
97/// For setting the register contents the [`Writeable`] trait is implemented.
98// To successfully alias this structure onto hardware registers in memory, this
99// struct must be exactly the size of the `T` and is thus marked
100// `repr(transparent)` over an `UnsafeCell<T>`, which itself is
101// `repr(transparent)` over `T`.
102//
103// This struct is constructed by casting a pointer to it (or, implicitly, by
104// casting a pointer to a larger struct that containts this type). As such, it
105// does not have a public constructor and Rust thinks it's dead code and should
106// be removed. We `allow(dead_code)` here to suppress this warning.
107#[allow(dead_code)]
108#[repr(transparent)]
109pub struct WriteOnly<T: UIntLike, R: RegisterLongName = ()> {
110 value: UnsafeCell<T>,
111 associated_register: PhantomData<R>,
112}
113impl<T: UIntLike, R: RegisterLongName> Writeable for WriteOnly<T, R> {
114 type T = T;
115 type R = R;
116
117 #[inline]
118 fn set(&self, value: T) {
119 unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
120 }
121}
122
123/// Read-only and write-only registers aliased to the same address.
124///
125/// Unlike the [`ReadWrite`] register, this represents a register which has
126/// different meanings based on if it is written or read. This might be found
127/// on a device where control and status registers are accessed via the same
128/// memory address via writes and reads, respectively.
129///
130/// This register implements [`Readable`] and [`Writeable`], but in general does
131/// not implement [`ReadWriteable`](crate::interfaces::ReadWriteable) (only if
132/// the type parameters `R` and `W` are identical, in which case a [`ReadWrite`]
133/// register might be a better choice).
134// To successfully alias this structure onto hardware registers in memory, this
135// struct must be exactly the size of the `T` and is thus marked
136// `repr(transparent)` over an `UnsafeCell<T>`, which itself is
137// `repr(transparent)` over `T`.
138//
139// This struct is constructed by casting a pointer to it (or, implicitly, by
140// casting a pointer to a larger struct that containts this type). As such, it
141// does not have a public constructor and Rust thinks it's dead code and should
142// be removed. We `allow(dead_code)` here to suppress this warning.
143#[allow(dead_code)]
144#[repr(transparent)]
145pub struct Aliased<T: UIntLike, R: RegisterLongName = (), W: RegisterLongName = ()> {
146 value: UnsafeCell<T>,
147 associated_register: PhantomData<(R, W)>,
148}
149impl<T: UIntLike, R: RegisterLongName, W: RegisterLongName> Readable for Aliased<T, R, W> {
150 type T = T;
151 type R = R;
152
153 #[inline]
154 fn get(&self) -> Self::T {
155 unsafe { ::core::ptr::read_volatile(self.value.get()) }
156 }
157}
158impl<T: UIntLike, R: RegisterLongName, W: RegisterLongName> Writeable for Aliased<T, R, W> {
159 type T = T;
160 type R = W;
161
162 #[inline]
163 fn set(&self, value: Self::T) {
164 unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
165 }
166}
167
168/// In memory volatile register.
169///
170/// Like [`ReadWrite`], but can be safely constructed using the
171/// [`InMemoryRegister::new`] method. It will always be initialized to the
172/// passed in, well-defined initial value.
173///
174/// For accessing and manipulating the register contents, the [`Readable`],
175/// [`Writeable`] and [`ReadWriteable`](crate::interfaces::ReadWriteable) traits
176/// are implemented.
177// To successfully alias this structure onto hardware registers in memory, this
178// struct must be exactly the size of the `T`.
179#[repr(transparent)]
180pub struct InMemoryRegister<T: UIntLike, R: RegisterLongName = ()> {
181 value: UnsafeCell<T>,
182 associated_register: PhantomData<R>,
183}
184
185impl<T: UIntLike, R: RegisterLongName> InMemoryRegister<T, R> {
186 pub const fn new(value: T) -> Self {
187 InMemoryRegister {
188 value: UnsafeCell::new(value),
189 associated_register: PhantomData,
190 }
191 }
192}
193impl<T: UIntLike, R: RegisterLongName> Readable for InMemoryRegister<T, R> {
194 type T = T;
195 type R = R;
196
197 #[inline]
198 fn get(&self) -> Self::T {
199 unsafe { ::core::ptr::read_volatile(self.value.get()) }
200 }
201}
202impl<T: UIntLike, R: RegisterLongName> Writeable for InMemoryRegister<T, R> {
203 type T = T;
204 type R = R;
205
206 #[inline]
207 fn set(&self, value: T) {
208 unsafe { ::core::ptr::write_volatile(self.value.get(), value) }
209 }
210}