Skip to content

Commit 0880435

Browse files
committed
tock-register-interface: document & move interface traits
Moves the Readable, Writeable and ReadeWriteable traits to a separate module `interfaces`. Further adds a documentation comment to this module, outlining the reasoning behind this interface, how it can be used to create custom register types, and how deriving ReadWriteable works. The introduced documentation comment's code examples also serve as documentation tests, which will test implementing custom types using these traits (which implicitly tests interface compatibility), as well as the ReadWriteable implementation logic on ReadWrite and Aliased types using an asserted compilation failure. Signed-off-by: Leon Schuermann <[email protected]>
1 parent 914e1d1 commit 0880435

File tree

3 files changed

+287
-127
lines changed

3 files changed

+287
-127
lines changed
Lines changed: 276 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,276 @@
1+
//! Interfaces (traits) to register types
2+
//!
3+
//! This module contains traits which reflect standardized interfaces
4+
//! to different types of registers. Examples of registers
5+
//! implementing these interfaces are [`ReadWrite`](crate::registers::ReadWrite) or
6+
//! [`InMemoryRegister`](crate::registers::InMemoryRegister).
7+
//!
8+
//! Each trait has two associated type parameters, namely:
9+
//!
10+
//! - `T`: [`IntLike`](crate::registers::IntLike), indicating the
11+
//! underlying integer type used to represent the register's raw
12+
//! contents.
13+
//!
14+
//! - `R`: [`RegisterLongName`](crate::registers::RegisterLongName),
15+
//! functioning as a type to identify this register's descriptive
16+
//! name and semantic meaning. It is further used to impose type
17+
//! constraints on values passed through the API, such as
18+
//! [`FieldValue`](crate::registers::FieldValue).
19+
//!
20+
//! Registers can have different access levels, which are mapped to
21+
//! different traits respectively:
22+
//!
23+
//! - [`Readable`]: indicates that the current value of this register
24+
//! can be read. Implementations will need to provide the
25+
//! [`get`](crate::interfaces::Readable::get) method.
26+
//!
27+
//! - [`Writeable`]: indicates that the value of this register can be
28+
//! set. Implementations will need to provide the
29+
//! [`set`](crate::interfaces::Writeable::set) method.
30+
//!
31+
//! - [`ReadWriteable`]: indicates that this register can be
32+
//! _modified_. It is not sufficient for registers to be both read-
33+
//! and writable, they must also have the same semantic meaning when
34+
//! read from and written to. This is not true in general, for
35+
//! example a memory-mapped UART register might transmit when
36+
//! writing and receive when reading.
37+
//!
38+
//! If a type implements both [`Readable`] and [`Writeable`], and the
39+
//! associated
40+
//! [`RegisterLongName`](crate::registers::RegisterLongName) type
41+
//! parameters are identical, it will automatically implement
42+
//! [`ReadWriteable`]. In particular, for
43+
//! [`Aliased`](crate::registers::Aliased) this is -- in general --
44+
//! not the case, so
45+
//!
46+
//! ```rust
47+
//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable};
48+
//! # use tock_registers::registers::ReadWrite;
49+
//! # use tock_registers::register_bitfields;
50+
//! register_bitfields![u8,
51+
//! A [
52+
//! DUMMY OFFSET(0) NUMBITS(1) [],
53+
//! ],
54+
//! ];
55+
//! let read_write_reg: &ReadWrite<u8, A::Register> = unsafe {
56+
//! core::mem::transmute(Box::leak(Box::new(0_u8)))
57+
//! };
58+
//! ReadWriteable::modify(read_write_reg, A::DUMMY::SET);
59+
//! ```
60+
//!
61+
//! works, but not
62+
//!
63+
//! ```compile_fail
64+
//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable};
65+
//! # use tock_registers::registers::Aliased;
66+
//! # use tock_registers::register_bitfields;
67+
//! register_bitfields![u8,
68+
//! A [
69+
//! DUMMY OFFSET(0) NUMBITS(1) [],
70+
//! ],
71+
//! B [
72+
//! DUMMY OFFSET(0) NUMBITS(1) [],
73+
//! ],
74+
//! ];
75+
//! let aliased_reg: &Aliased<u8, A::Register, B::Register> = unsafe {
76+
//! core::mem::transmute(Box::leak(Box::new(0_u8)))
77+
//! };
78+
//! ReadWriteable::modify(aliased_reg, A::DUMMY::SET);
79+
//! ```
80+
//!
81+
//! ## Example: implementing a custom register type
82+
//!
83+
//! These traits can be used to implement custom register types, which
84+
//! are compatible to the ones shipped in this crate. For example, to
85+
//! define a register which sets a `u8` value using a Cell reference,
86+
//! always reads the bitwise-negated vale and prints every written
87+
//! value to the console:
88+
//!
89+
//! ```rust
90+
//! # use core::cell::Cell;
91+
//! # use core::marker::PhantomData;
92+
//! #
93+
//! # use tock_registers::interfaces::{Readable, Writeable, ReadWriteable};
94+
//! # use tock_registers::registers::RegisterLongName;
95+
//! # use tock_registers::register_bitfields;
96+
//! #
97+
//! struct DummyRegister<'a, R: RegisterLongName> {
98+
//! cell_ref: &'a Cell<u8>,
99+
//! _register_long_name: PhantomData<R>,
100+
//! }
101+
//!
102+
//! impl<'a, R: RegisterLongName> Readable for DummyRegister<'a, R> {
103+
//! type T = u8;
104+
//! type R = R;
105+
//!
106+
//! fn get(&self) -> u8 {
107+
//! // Return the bitwise-inverse of the current value
108+
//! !self.cell_ref.get()
109+
//! }
110+
//! }
111+
//!
112+
//! impl<'a, R: RegisterLongName> Writeable for DummyRegister<'a, R> {
113+
//! type T = u8;
114+
//! type R = R;
115+
//!
116+
//! fn set(&self, value: u8) {
117+
//! println!("Setting Cell to {:02x?}!", value);
118+
//! self.cell_ref.set(value);
119+
//! }
120+
//! }
121+
//!
122+
//! register_bitfields![u8,
123+
//! DummyReg [
124+
//! HIGH OFFSET(4) NUMBITS(4) [
125+
//! A = 0b0001,
126+
//! B = 0b0010,
127+
//! C = 0b0100,
128+
//! D = 0b1000,
129+
//! ],
130+
//! LOW OFFSET(0) NUMBITS(4) [],
131+
//! ],
132+
//! ];
133+
//!
134+
//! // Create a new DummyRegister over some Cell<u8>
135+
//! let cell = Cell::new(0);
136+
//! let dummy = DummyRegister {
137+
//! cell_ref: &cell,
138+
//! _register_long_name: PhantomData,
139+
//! };
140+
//!
141+
//! // Set a value and read it back. This demonstrates the raw getters
142+
//! // and setters of Writeable and Readable
143+
//! dummy.set(0xFA);
144+
//! assert!(dummy.get() == 0x05);
145+
//!
146+
//! // Use some of the automatically derived APIs, such as
147+
//! // ReadWriteable::modify and Readable::read
148+
//! dummy.modify(DummyReg::HIGH::C);
149+
//! assert!(dummy.read(DummyReg::HIGH) == 0xb);
150+
//! ```
151+
152+
use crate::registers::{
153+
Field, FieldValue, IntLike, LocalRegisterCopy, RegisterLongName, TryFromValue,
154+
};
155+
156+
/// Readable register
157+
///
158+
/// Register which at least supports reading the current value. Only
159+
/// [`Readable::get`] must be implemented, as for other methods a
160+
/// default implementation is provided.
161+
///
162+
/// A register that is both [`Readable`] and [`Writeable`] will also
163+
/// automatically be [`ReadWriteable`], if the [`RegisterLongName`] of
164+
/// [`Readable`] is the same as that of [`Writeable`] (i.e. not for
165+
/// [`Aliased`](crate::registers::Aliased) registers).
166+
pub trait Readable {
167+
type T: IntLike;
168+
type R: RegisterLongName;
169+
170+
/// Get the raw register value
171+
fn get(&self) -> Self::T;
172+
173+
#[inline]
174+
/// Read the value of the given field
175+
fn read(&self, field: Field<Self::T, Self::R>) -> Self::T {
176+
field.read(self.get())
177+
}
178+
179+
#[inline]
180+
/// Set the raw register value
181+
fn read_as_enum<E: TryFromValue<Self::T, EnumType = E>>(
182+
&self,
183+
field: Field<Self::T, Self::R>,
184+
) -> Option<E> {
185+
field.read_as_enum(self.get())
186+
}
187+
188+
#[inline]
189+
/// Make a local copy of the register
190+
fn extract(&self) -> LocalRegisterCopy<Self::T, Self::R> {
191+
LocalRegisterCopy::new(self.get())
192+
}
193+
194+
#[inline]
195+
/// Check if one or more bits in a field are set
196+
fn is_set(&self, field: Field<Self::T, Self::R>) -> bool {
197+
field.is_set(self.get())
198+
}
199+
200+
#[inline]
201+
/// Check if any specified parts of a field match
202+
fn matches_any(&self, field: FieldValue<Self::T, Self::R>) -> bool {
203+
field.matches_any(self.get())
204+
}
205+
206+
#[inline]
207+
/// Check if all specified parts of a field match
208+
fn matches_all(&self, field: FieldValue<Self::T, Self::R>) -> bool {
209+
field.matches_all(self.get())
210+
}
211+
}
212+
213+
/// Writeable register
214+
///
215+
/// Register which at least supports setting a value. Only
216+
/// [`Writeable::set`] must be implemented, as for other methods a
217+
/// default implementation is provided.
218+
///
219+
/// A register that is both [`Readable`] and [`Writeable`] will also
220+
/// automatically be [`ReadWriteable`], if the [`RegisterLongName`] of
221+
/// [`Readable`] is the same as that of [`Writeable`] (i.e. not for
222+
/// [`Aliased`](crate::registers::Aliased) registers).
223+
pub trait Writeable {
224+
type T: IntLike;
225+
type R: RegisterLongName;
226+
227+
/// Set the raw register value
228+
fn set(&self, value: Self::T);
229+
230+
#[inline]
231+
/// Write the value of one or more fields, overwriting the other fields with zero
232+
fn write(&self, field: FieldValue<Self::T, Self::R>) {
233+
self.set(field.value);
234+
}
235+
236+
#[inline]
237+
/// Write the value of one or more fields, maintaining the value of unchanged fields via a
238+
/// provided original value, rather than a register read.
239+
fn modify_no_read(
240+
&self,
241+
original: LocalRegisterCopy<Self::T, Self::R>,
242+
field: FieldValue<Self::T, Self::R>,
243+
) {
244+
self.set(field.modify(original.get()));
245+
}
246+
}
247+
248+
/// [`Readable`] and [`Writeable`] register, over the same
249+
/// [`RegisterLongName`]
250+
///
251+
/// Register which supports both reading and setting a value.
252+
///
253+
/// **This trait does not have to be implemented manually!** It is
254+
/// automatically implemented for every type that is both [`Readable`]
255+
/// and [`Writeable`], as long as [`Readable::R`] == [`Writeable::R`]
256+
/// (i.e. not for [`Aliased`](crate::registers::Aliased) registers).
257+
pub trait ReadWriteable {
258+
type T: IntLike;
259+
type R: RegisterLongName;
260+
261+
/// Write the value of one or more fields, leaving the other fields unchanged
262+
fn modify(&self, field: FieldValue<Self::T, Self::R>);
263+
}
264+
265+
impl<T: IntLike, R: RegisterLongName, S> ReadWriteable for S
266+
where
267+
S: Readable<T = T, R = R> + Writeable<T = T, R = R>,
268+
{
269+
type T = T;
270+
type R = R;
271+
272+
#[inline]
273+
fn modify(&self, field: FieldValue<Self::T, Self::R>) {
274+
self.set(field.modify(self.get()));
275+
}
276+
}

libraries/tock-register-interface/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,5 +5,6 @@
55
#![feature(const_fn_trait_bound)]
66
#![no_std]
77

8+
pub mod interfaces;
89
pub mod macros;
910
pub mod registers;

0 commit comments

Comments
 (0)